From 6a4d59ccf524f4b4d22737015c2414bfd48390e7 Mon Sep 17 00:00:00 2001 From: Sreeram Sreedhar Date: Sun, 21 Jun 2026 22:13:57 -0700 Subject: [PATCH] initial commit --- apps/desktop/.gitignore | 13 ++++ apps/desktop/README.md | 28 ++++++++ apps/desktop/app/globals.css | 71 +++++++++++++++++++ apps/desktop/app/layout.tsx | 15 ++++ apps/desktop/app/page.tsx | 53 ++++++++++++++ apps/desktop/biome.json | 5 ++ apps/desktop/next.config.ts | 14 ++++ apps/desktop/package.json | 24 +++++++ apps/desktop/src-tauri/.gitignore | 5 ++ apps/desktop/src-tauri/Cargo.toml | 19 +++++ apps/desktop/src-tauri/build.rs | 3 + .../src-tauri/capabilities/default.json | 7 ++ apps/desktop/src-tauri/src/lib.rs | 32 +++++++++ apps/desktop/src-tauri/src/main.rs | 7 ++ apps/desktop/src-tauri/tauri.conf.json | 40 +++++++++++ apps/desktop/tsconfig.json | 17 +++++ apps/desktop/turbo.json | 14 ++++ 17 files changed, 367 insertions(+) create mode 100644 apps/desktop/.gitignore create mode 100644 apps/desktop/README.md create mode 100644 apps/desktop/app/globals.css create mode 100644 apps/desktop/app/layout.tsx create mode 100644 apps/desktop/app/page.tsx create mode 100644 apps/desktop/biome.json create mode 100644 apps/desktop/next.config.ts create mode 100644 apps/desktop/package.json create mode 100644 apps/desktop/src-tauri/.gitignore create mode 100644 apps/desktop/src-tauri/Cargo.toml create mode 100644 apps/desktop/src-tauri/build.rs create mode 100644 apps/desktop/src-tauri/capabilities/default.json create mode 100644 apps/desktop/src-tauri/src/lib.rs create mode 100644 apps/desktop/src-tauri/src/main.rs create mode 100644 apps/desktop/src-tauri/tauri.conf.json create mode 100644 apps/desktop/tsconfig.json create mode 100644 apps/desktop/turbo.json diff --git a/apps/desktop/.gitignore b/apps/desktop/.gitignore new file mode 100644 index 00000000..4a18cb48 --- /dev/null +++ b/apps/desktop/.gitignore @@ -0,0 +1,13 @@ +# dependencies +/node_modules + +# next.js +/.next/ +/out/ + +# typescript +next-env.d.ts +*.tsbuildinfo + +# misc +.DS_Store diff --git a/apps/desktop/README.md b/apps/desktop/README.md new file mode 100644 index 00000000..61566582 --- /dev/null +++ b/apps/desktop/README.md @@ -0,0 +1,28 @@ +# @repo/desktop + +Supermemory desktop app — a [Tauri v2](https://v2.tauri.app) shell around the +Supermemory web UI (Next.js static export), talking to `api.supermemory.ai`. + +Design docs live in `/.context/desktop-app-*.md` (spec, roadmap, architecture). + +## Develop + +```sh +# from this directory — launches the native window and spawns `next dev` on :3001 +bun run tauri dev +``` + +`bun run dev` on its own just starts the Next dev server on :3001 (no native window). + +## Build + +```sh +bun run build # Next static export -> ./out (no Rust needed) +bun run tauri build # native .app / .dmg (requires the Rust toolchain) +``` + +## Prerequisites for the native build + +- Rust toolchain (`rustup`) + Xcode Command Line Tools. +- App icons in `src-tauri/icons/` — generate once with + `bun run tauri icon `. diff --git a/apps/desktop/app/globals.css b/apps/desktop/app/globals.css new file mode 100644 index 00000000..d8b6f430 --- /dev/null +++ b/apps/desktop/app/globals.css @@ -0,0 +1,71 @@ +:root { + color-scheme: light dark; + font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, + sans-serif; +} + +* { + box-sizing: border-box; +} + +html, +body { + height: 100%; + margin: 0; +} + +.screen { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + background: radial-gradient(125% 125% at 50% 0%, #1b1b2f 0%, #0c0c12 60%); + color: #ececf1; +} + +.card { + width: 360px; + padding: 2rem; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 1rem; + background: rgba(255, 255, 255, 0.04); + text-align: center; +} + +.title { + margin: 0; + font-size: 1.5rem; + font-weight: 600; +} + +.muted { + margin: 0.25rem 0 1.5rem; + color: #9a9ab0; + font-size: 0.875rem; +} + +.info { + display: grid; + gap: 0.5rem; + margin: 0; +} + +.row { + display: flex; + justify-content: space-between; + font-size: 0.875rem; +} + +.row dt { + color: #9a9ab0; +} + +.row dd { + margin: 0; + font-variant-numeric: tabular-nums; +} + +.error { + color: #ff6b6b; + font-size: 0.875rem; +} diff --git a/apps/desktop/app/layout.tsx b/apps/desktop/app/layout.tsx new file mode 100644 index 00000000..5752c0f8 --- /dev/null +++ b/apps/desktop/app/layout.tsx @@ -0,0 +1,15 @@ +import type { ReactNode } from "react" +import "./globals.css" + +export const metadata = { + title: "Supermemory", + description: "Supermemory Desktop", +} + +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} diff --git a/apps/desktop/app/page.tsx b/apps/desktop/app/page.tsx new file mode 100644 index 00000000..1ddcb967 --- /dev/null +++ b/apps/desktop/app/page.tsx @@ -0,0 +1,53 @@ +"use client" + +import { invoke } from "@tauri-apps/api/core" +import { useEffect, useState } from "react" + +type AppInfo = { + name: string + version: string + platform: string +} + +export default function Home() { + const [info, setInfo] = useState(null) + const [error, setError] = useState(null) + + useEffect(() => { + // Call into the Rust core over Tauri's IPC bridge. If this resolves, the + // native layer is alive and wired to the webview — the Phase 0 goal. + invoke("app_info") + .then(setInfo) + .catch((err) => setError(String(err))) + }, []) + + return ( +
+
+

Supermemory

+

Desktop · Phase 0

+ + {info ? ( +
+
+
App
+
{info.name}
+
+
+
Version
+
{info.version}
+
+
+
Platform
+
{info.platform}
+
+
+ ) : error ? ( +

Native bridge error: {error}

+ ) : ( +

Connecting to the native core…

+ )} +
+
+ ) +} diff --git a/apps/desktop/biome.json b/apps/desktop/biome.json new file mode 100644 index 00000000..8f52e255 --- /dev/null +++ b/apps/desktop/biome.json @@ -0,0 +1,5 @@ +{ + "root": false, + "extends": "//", + "$schema": "https://biomejs.dev/schemas/2.4.6/schema.json" +} diff --git a/apps/desktop/next.config.ts b/apps/desktop/next.config.ts new file mode 100644 index 00000000..9bcc2bf9 --- /dev/null +++ b/apps/desktop/next.config.ts @@ -0,0 +1,14 @@ +import type { NextConfig } from "next" + +const nextConfig: NextConfig = { + // Tauri serves a static bundle from disk — there is no Node server at runtime, + // so the app is exported to plain HTML/JS/CSS in ./out. + output: "export", + // The static export has no server-side image optimizer. + images: { unoptimized: true }, + // Emit `route/index.html` files so the webview can resolve nested routes + // (e.g. /spotlight) directly from the bundled filesystem. + trailingSlash: true, +} + +export default nextConfig diff --git a/apps/desktop/package.json b/apps/desktop/package.json new file mode 100644 index 00000000..23dcba71 --- /dev/null +++ b/apps/desktop/package.json @@ -0,0 +1,24 @@ +{ + "name": "@repo/desktop", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --port 3001", + "build": "next build", + "check-types": "tsc --noEmit", + "tauri": "tauri" + }, + "dependencies": { + "@tauri-apps/api": "^2", + "next": "^16.0.11", + "react": "^19.2.4", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@tauri-apps/cli": "^2", + "@types/node": "^24.0.4", + "@types/react": "^19.2.9", + "@types/react-dom": "^19.2.3", + "typescript": "^5.8.3" + } +} diff --git a/apps/desktop/src-tauri/.gitignore b/apps/desktop/src-tauri/.gitignore new file mode 100644 index 00000000..1a2a9989 --- /dev/null +++ b/apps/desktop/src-tauri/.gitignore @@ -0,0 +1,5 @@ +# Rust build output +/target + +# Tauri-generated schemas / artifacts +/gen/schemas diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml new file mode 100644 index 00000000..ec23ead3 --- /dev/null +++ b/apps/desktop/src-tauri/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "supermemory-desktop" +version = "0.1.0" +description = "Supermemory Desktop" +edition = "2021" +rust-version = "1.77.2" + +[lib] +# The app logic lives in a library crate so it can be shared with a future +# mobile target; `main.rs` is a thin binary that calls into it. +name = "supermemory_desktop_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +serde = { version = "1", features = ["derive"] } diff --git a/apps/desktop/src-tauri/build.rs b/apps/desktop/src-tauri/build.rs new file mode 100644 index 00000000..d860e1e6 --- /dev/null +++ b/apps/desktop/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json new file mode 100644 index 00000000..32e45da5 --- /dev/null +++ b/apps/desktop/src-tauri/capabilities/default.json @@ -0,0 +1,7 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Core capabilities granted to the main window.", + "windows": ["main"], + "permissions": ["core:default"] +} diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs new file mode 100644 index 00000000..d1e5e349 --- /dev/null +++ b/apps/desktop/src-tauri/src/lib.rs @@ -0,0 +1,32 @@ +use serde::Serialize; + +/// Identity of the native app, surfaced to the webview over IPC. +/// `rename_all = "camelCase"` makes the JSON keys match the TypeScript `AppInfo`. +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct AppInfo { + name: String, + version: String, + platform: String, +} + +/// First proof that the webview <-> Rust IPC bridge is wired: the placeholder +/// page calls `invoke("app_info")` on mount and renders the result. +#[tauri::command] +fn app_info() -> AppInfo { + AppInfo { + name: "Supermemory".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + platform: std::env::consts::OS.to_string(), + } +} + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .invoke_handler(tauri::generate_handler![app_info]) + // Bootstrap failure is unrecoverable (no window, no app), so we abort + // loudly here. This is the one sanctioned `expect` — see roadmap quality bar. + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/apps/desktop/src-tauri/src/main.rs b/apps/desktop/src-tauri/src/main.rs new file mode 100644 index 00000000..2a60ad7d --- /dev/null +++ b/apps/desktop/src-tauri/src/main.rs @@ -0,0 +1,7 @@ +// On Windows release builds, prevent a console window from opening alongside +// the app. No-op on macOS/Linux. Kept now so the entry point is cross-platform. +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + supermemory_desktop_lib::run() +} diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json new file mode 100644 index 00000000..666c12e4 --- /dev/null +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "Supermemory", + "version": "0.1.0", + "identifier": "ai.supermemory.desktop", + "build": { + "beforeDevCommand": "bun run dev", + "devUrl": "http://localhost:3001", + "beforeBuildCommand": "bun run build", + "frontendDist": "../out" + }, + "app": { + "windows": [ + { + "label": "main", + "title": "Supermemory", + "width": 1100, + "height": 720, + "minWidth": 720, + "minHeight": 480, + "titleBarStyle": "Overlay", + "hiddenTitle": true + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": ["app", "dmg"], + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +} diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json new file mode 100644 index 00000000..a09fb61b --- /dev/null +++ b/apps/desktop/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "incremental": true, + "jsx": "preserve", + "paths": { + "@/*": ["./*"], + "@ui/*": ["../../packages/ui/*"], + "@repo/ui/*": ["../../packages/ui/*"], + "@lib/*": ["../../packages/lib/*"], + "@hooks/*": ["../../packages/hooks/*"] + }, + "plugins": [{ "name": "next" }] + }, + "exclude": ["node_modules"], + "extends": "@total-typescript/tsconfig/bundler/dom/app", + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"] +} diff --git a/apps/desktop/turbo.json b/apps/desktop/turbo.json new file mode 100644 index 00000000..c42a6512 --- /dev/null +++ b/apps/desktop/turbo.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://turborepo.com/schema.json", + "extends": ["//"], + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["out/**"] + }, + "dev": { + "cache": false, + "persistent": true + } + } +}