diff --git a/.gitignore b/.gitignore index 676fb99..481784a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ dist coverage __screenshots__ target/ +.vscode diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 04b90c7..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "[json]": { "editor.formatOnSave": true, "editor.defaultFormatter": "dprint.dprint" }, - "[jsonc]": { "editor.formatOnSave": true, "editor.defaultFormatter": "dprint.dprint" }, - "[javascript]": { - "editor.formatOnSave": true, - "editor.defaultFormatter": "dprint.dprint", - "editor.codeActionsOnSave": { "source.fixAll.eslint": "always" } - }, - "[typescript]": { - "editor.formatOnSave": true, - "editor.defaultFormatter": "dprint.dprint", - "editor.codeActionsOnSave": { "source.fixAll.eslint": "always" } - } -} diff --git a/ROADMAP.md b/ROADMAP.md index f7b1e52..ac4f323 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -36,7 +36,7 @@ the CLI and a bundled `SKILL.md`; MCP and UI automation are not part of vNext. - The pnpm monorepo contains a TypeScript core, Canvas 2D renderer, SvelteKit web UI, and Tauri 2 wrapper. -- `inkfinite-ui` provides shared Svelte components, theme tokens, fonts, and +- `@inkfinite/ui` provides shared Svelte components, theme tokens, fonts, and icons for the browser and the Tauri-hosted frontend. Application-specific components still live under `apps/web` and must move to the package as their dependencies are separated. @@ -70,18 +70,18 @@ TypeScript editor runtime ### Technology -| Concern | Choice | -| ------------------------ | --------------------------------------------------------- | -| Desktop | Tauri 2 | -| UI | SvelteKit, Svelte 5, and shared `inkfinite-ui` components | -| Interactive rendering | Native Canvas 2D with positioned DOM editors | -| Durable model and engine | Rust | -| Local-first state | Automerge, isolated behind Inkfinite interfaces | -| CLI | Rust and `clap` | -| Schemas and bindings | Serde, Schemars, and `ts-rs` | -| Desktop control | `interprocess` local sockets and length-prefixed messages | -| Async I/O | Tokio where needed | -| Agent integration | CLI and bundled `SKILL.md` | +| Concern | Choice | +| ------------------------ | ---------------------------------------------------------- | +| Desktop | Tauri 2 | +| UI | SvelteKit, Svelte 5, and shared `@inkfinite/ui` components | +| Interactive rendering | Native Canvas 2D with positioned DOM editors | +| Durable model and engine | Rust | +| Local-first state | Automerge, isolated behind Inkfinite interfaces | +| CLI | Rust and `clap` | +| Schemas and bindings | Serde, Schemars, and `ts-rs` | +| Desktop control | `interprocess` local sockets and length-prefixed messages | +| Async I/O | Tokio where needed | +| Agent integration | CLI and bundled `SKILL.md` | ### Ownership boundary @@ -97,15 +97,15 @@ patch for the frontend's read-only document mirror. ### Crate and package boundaries -| Unit | Responsibility | -| ------------------ | ----------------------------------------------------------------------- | -| `inkfinite-core` | Document model and engine plus CRDT, file, protocol, rendering, and IPC | -| `inkfinite-cli` | `clap`, formatting, exit codes, and calls into shared Rust code | -| `editor-runtime` | Framework-neutral tools, camera, selection, and previews | -| `renderer-canvas` | Svelte-independent Canvas 2D renderer | -| `input-dom` | Browser input normalization | -| `bindings` | Generated TypeScript records; never hand-edited | -| `inkfinite-ui` | Shared Svelte components, themes, fonts, icons, stories, and UI tests | +| Unit | Responsibility | +| ----------------------- | ----------------------------------------------------------------------- | +| `inkfinite-core` | Document model and engine plus CRDT, file, protocol, rendering, and IPC | +| `inkfinite-cli` | `clap`, formatting, exit codes, and calls into shared Rust code | +| `@inkfinite/runtime` | Framework-neutral tools, camera, selection, and previews | +| `@inkfinite/renderer` | Svelte-independent Canvas 2D renderer | +| `@inkfinite/input-dom` | Browser input normalization | +| `@inkfinite/bindings` | Generated TypeScript records; never hand-edited | +| `@inkfinite/ui` | Shared Svelte components, themes, fonts, icons, stories, and UI tests | Business logic must not depend on Tauri, Svelte, CLI parsing, or a transport. @@ -285,7 +285,7 @@ optional transaction preview. DOM overlays remain limited to active text and Markdown editing, menus, tooltips, accessibility controls, and proposal review. `apps/desktop` packages the `apps/web` SvelteKit build. Milestone 3 must update -that shared frontend to consume `inkfinite-ui`; it must not introduce a second +that shared frontend to consume `@inkfinite/ui`; it must not introduce a second desktop component tree or duplicate theme. Desktop-only behavior belongs in Tauri commands and thin frontend adapters around shared components. @@ -387,13 +387,13 @@ pointer normalization, patch reconciliation, Canvas hit testing, and SVG output. Current commands, before the Cargo workspace exists: ```sh -pnpm --filter inkfinite-core test --run -pnpm --filter inkfinite-renderer test --run -pnpm --filter inkfinite-web test -pnpm --filter inkfinite-core typecheck -pnpm --filter inkfinite-renderer typecheck -pnpm --filter inkfinite-web check -pnpm --filter inkfinite-web lint +pnpm --filter @inkfinite/core test --run +pnpm --filter @inkfinite/renderer test --run +pnpm --filter @inkfinite/web test +pnpm --filter @inkfinite/core typecheck +pnpm --filter @inkfinite/renderer typecheck +pnpm --filter @inkfinite/web check +pnpm --filter @inkfinite/web lint cargo test --manifest-path apps/desktop/src-tauri/Cargo.toml ``` @@ -411,7 +411,7 @@ recovery prompts, and release migrations. transactions, validation, migrations, schemas, and generated bindings. Exit with convergent Rust tests and lossless v1 imports. 3. **Desktop vertical slice:** make Rust own open, edit, undo, save, recovery, and - the frontend mirror. Update `apps/web` to use `inkfinite-ui`, and package that + the frontend mirror. Update `apps/web` to use `@inkfinite/ui`, and package that same frontend through `apps/desktop`. Exit with an end-to-end drag, reopen, and undo test against the shared UI. 4. **Editor structure and scale:** extract the editor runtime, fix resize cursor @@ -435,7 +435,7 @@ recovery prompts, and release migrations. - Adapt the standalone browser runtime and Dexie persistence to the v2 Rust/CRDT authority after the desktop contracts and file format stabilize. Its migration must backfill every existing board with a default layer and preserve shape - order. Shared `apps/web` components and `inkfinite-ui` adoption remain part of + order. Shared `apps/web` components and `@inkfinite/ui` adoption remain part of milestone 3; keep the browser build green while desktop behavior changes. - Add hosted sync relay, identity, invitations, permissions, and ephemeral presence after local peer sync is correct and threat-modeled. diff --git a/TODO.md b/TODO.md index b923908..8e811cd 100644 --- a/TODO.md +++ b/TODO.md @@ -30,13 +30,13 @@ Acceptance criteria: Verification: ```sh -pnpm --filter inkfinite-core test --run -pnpm --filter inkfinite-renderer test --run -pnpm --filter inkfinite-web test -pnpm --filter inkfinite-core typecheck -pnpm --filter inkfinite-renderer typecheck -pnpm --filter inkfinite-web check -pnpm --filter inkfinite-web lint +pnpm --filter @inkfinite/core test --run +pnpm --filter @inkfinite/renderer test --run +pnpm --filter @inkfinite/web test +pnpm --filter @inkfinite/core typecheck +pnpm --filter @inkfinite/renderer typecheck +pnpm --filter @inkfinite/web check +pnpm --filter @inkfinite/web lint cargo test --manifest-path apps/desktop/src-tauri/Cargo.toml ``` @@ -84,9 +84,10 @@ Acceptance criteria: - [x] A root Cargo workspace contains the model, CRDT, engine, file, protocol, SVG, IPC, and CLI crates named in the roadmap. -- [x] The pnpm workspace contains `editor-runtime`, `renderer-canvas`, - `input-dom`, and `bindings`; the existing apps and packages keep - working in place during the scaffold step. +- [x] The pnpm workspace contains `@inkfinite/runtime`, + `@inkfinite/input-dom`, `@inkfinite/bindings`, and + `@inkfinite/renderer`; the existing apps and packages keep working in + place during the scaffold step. - [x] New crates and packages contain only minimal compileable entry points, manifests, and dependency edges. This ticket moves no product behavior. - [x] Workspace commands can discover, build, test, type-check, and lint the new @@ -186,7 +187,7 @@ Verification: when any generated artifact is stale. - `cargo test -p inkfinite-core` runs the Rust registry and shared-fixture conformance tests. -- `pnpm --filter bindings test` typechecks the bindings and runs the +- `pnpm --filter @inkfinite/bindings test` typechecks the bindings and runs the TypeScript fixture conformance test. ### V2-07: Import v1 and persist v2 safely @@ -218,7 +219,7 @@ cargo test -p inkfinite-core file:: Exit when a desktop gesture commits through Rust, redraws from a returned patch, survives reopen, and can be undone. The implementation must update `apps/web` to -consume `inkfinite-ui`, because `apps/desktop` packages that SvelteKit build. Do +consume `@inkfinite/ui`, because `apps/desktop` packages that SvelteKit build. Do not create a separate desktop component tree or theme. ### V2-08: Make Tauri own document sessions @@ -236,12 +237,12 @@ Acceptance criteria: commands call shared crates and return typed errors and patches. - [ ] File I/O leaves the frontend and plugin capabilities are reduced to the minimum still required. -- [ ] `apps/web` imports `inkfinite-ui/styles.css` once and uses shared package +- [ ] `apps/web` imports `@inkfinite/ui/styles.css` once and uses shared package components for the desktop vertical slice instead of adding new local copies under `apps/web/src/lib/components`. - [ ] `apps/desktop` builds and packages the same `apps/web` frontend. Desktop-only behavior stays in Tauri commands or thin adapters, while components, - themes, fonts, and icons come from `inkfinite-ui`. + themes, fonts, and icons come from `@inkfinite/ui`. - [ ] Integration tests cover open, edit, save, reopen, undo, failed validation, stale heads, and a simulated write failure. @@ -249,9 +250,9 @@ Verification: ```sh cargo test -p desktop -pnpm --filter inkfinite-web test -pnpm --filter inkfinite-ui check -pnpm --filter inkfinite-ui test +pnpm --filter @inkfinite/web test +pnpm --filter @inkfinite/ui check +pnpm --filter @inkfinite/ui test ``` ### V2-09: Extract the editor runtime and transaction drafts @@ -270,8 +271,9 @@ Acceptance criteria: stencil insertion, or shortcut produces one transaction draft. - [ ] The frontend document mirror changes only through snapshots or commit/sync patches from Rust. -- [ ] Svelte adapters compose controls from `inkfinite-ui`; reusable visual - components do not gain editor-runtime, persistence, or Tauri dependencies. +- [ ] Svelte adapters compose controls from `@inkfinite/ui`; reusable visual + components do not gain `@inkfinite/runtime`, persistence, or Tauri + dependencies. - [ ] Existing keyboard, selection, text, Markdown, arrow, pen, grid snap, stencil, and history behavior remains covered. - [ ] One browser integration test performs drag → Rust commit → patch → redraw @@ -280,9 +282,9 @@ Acceptance criteria: Verification: ```sh -pnpm --filter inkfinite-core test --run -pnpm --filter inkfinite-web test -pnpm --filter inkfinite-web check +pnpm --filter @inkfinite/core test --run +pnpm --filter @inkfinite/web test +pnpm --filter @inkfinite/web check ``` ## Milestone 4: Editor structure and scale @@ -605,13 +607,13 @@ Verification: ```sh cargo test --workspace cargo clippy --workspace --all-targets -- -D warnings -pnpm --filter inkfinite-core test --run -pnpm --filter inkfinite-renderer test --run -pnpm --filter inkfinite-web test -pnpm --filter inkfinite-core typecheck -pnpm --filter inkfinite-renderer typecheck -pnpm --filter inkfinite-web check -pnpm --filter inkfinite-web lint +pnpm --filter @inkfinite/core test --run +pnpm --filter @inkfinite/renderer test --run +pnpm --filter @inkfinite/web test +pnpm --filter @inkfinite/core typecheck +pnpm --filter @inkfinite/renderer typecheck +pnpm --filter @inkfinite/web check +pnpm --filter @inkfinite/web lint ``` ## Frontier diff --git a/apps/desktop/.vscode/extensions.json b/apps/desktop/.vscode/extensions.json deleted file mode 100644 index 61343e9..0000000 --- a/apps/desktop/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "recommendations": [ - "svelte.svelte-vscode", - "tauri-apps.tauri-vscode", - "rust-lang.rust-analyzer" - ] -} diff --git a/apps/desktop/.vscode/settings.json b/apps/desktop/.vscode/settings.json deleted file mode 100644 index 2f86c50..0000000 --- a/apps/desktop/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "svelte.enable-ts-plugin": true -} diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 9334221..8c58677 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,5 +1,5 @@ { - "name": "desktop", + "name": "@inkfinite/desktop", "version": "0.1.0", "description": "Inkfinite desktop app (Tauri wrapper for web app)", "type": "module", diff --git a/apps/web/package.json b/apps/web/package.json index 63f8998..86bddad 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,5 +1,5 @@ { - "name": "inkfinite-web", + "name": "@inkfinite/web", "private": true, "version": "0.0.1", "type": "module", @@ -21,8 +21,8 @@ "@tauri-apps/plugin-fs": "^2.4.4", "@tauri-apps/plugin-store": "^2.4.1", "dexie": "^4.2.1", - "inkfinite-core": "workspace:*", - "inkfinite-renderer": "workspace:*" + "@inkfinite/core": "workspace:*", + "@inkfinite/renderer": "workspace:*" }, "devDependencies": { "@eslint/compat": "^1.4.0", diff --git a/apps/web/src/lib/canvas/Canvas.svelte b/apps/web/src/lib/canvas/Canvas.svelte index 305f3d8..0e21d68 100644 --- a/apps/web/src/lib/canvas/Canvas.svelte +++ b/apps/web/src/lib/canvas/Canvas.svelte @@ -6,7 +6,7 @@ import StencilPalette from '$lib/components/StencilPalette.svelte'; import { createCanvasController } from './canvas-store.svelte.ts'; import { draggingStencil, endDrag } from '$lib/dnd.svelte'; - import { Camera, stencils } from 'inkfinite-core'; + import { Camera, stencils } from '@inkfinite/core'; let canvasEl = $state(null); let textEditorEl = $state(null); diff --git a/apps/web/src/lib/canvas/canvas-helpers.ts b/apps/web/src/lib/canvas/canvas-helpers.ts index 33b3e1a..6f251ef 100644 --- a/apps/web/src/lib/canvas/canvas-helpers.ts +++ b/apps/web/src/lib/canvas/canvas-helpers.ts @@ -1,4 +1,4 @@ -import type { Action, CommandKind, EditorState } from "inkfinite-core"; +import type { Action, CommandKind, EditorState } from "@inkfinite/core"; export const handleCursorMap: Record = { n: "ns-resize", diff --git a/apps/web/src/lib/canvas/canvas-store.svelte.ts b/apps/web/src/lib/canvas/canvas-store.svelte.ts index ec3efa5..edf4236 100644 --- a/apps/web/src/lib/canvas/canvas-store.svelte.ts +++ b/apps/web/src/lib/canvas/canvas-store.svelte.ts @@ -27,10 +27,10 @@ import { SnapshotCommand, Store, TextTool, -} from "inkfinite-core"; -import type { Action, Box2, LoadedDoc, PersistenceSink, PersistentDocRepo, Viewport } from "inkfinite-core"; -import { stencils } from "inkfinite-core"; -import { createRenderer, type Renderer } from "inkfinite-renderer"; +} from "@inkfinite/core"; +import type { Action, Box2, LoadedDoc, PersistenceSink, PersistentDocRepo, Viewport } from "@inkfinite/core"; +import { stencils } from "@inkfinite/core"; +import { createRenderer, type Renderer } from "@inkfinite/renderer"; type Stencil = stencils.Stencil; import { onDestroy, onMount } from "svelte"; @@ -614,7 +614,7 @@ export function createCanvasController(bindings: CanvasControllerBindings) { } } else { webDb = new InkfiniteDB(); - const { createWebDocRepo, createPersistenceSink } = await import("inkfinite-core"); + const { createWebDocRepo, createPersistenceSink } = await import("@inkfinite/core"); const { liveQuery } = await import("dexie"); repo = createWebDocRepo(webDb); sink = createPersistenceSink(repo); diff --git a/apps/web/src/lib/canvas/controllers/arrowlabel-controller.svelte.ts b/apps/web/src/lib/canvas/controllers/arrowlabel-controller.svelte.ts index 725e091..2d3abfd 100644 --- a/apps/web/src/lib/canvas/controllers/arrowlabel-controller.svelte.ts +++ b/apps/web/src/lib/canvas/controllers/arrowlabel-controller.svelte.ts @@ -7,7 +7,7 @@ import { SnapshotCommand, type Store, type Viewport, -} from "inkfinite-core"; +} from "@inkfinite/core"; export class ArrowLabelEditorController { current = $state<{ shapeId: string; value: string } | null>(null); diff --git a/apps/web/src/lib/canvas/controllers/desktop-file-controller.svelte.ts b/apps/web/src/lib/canvas/controllers/desktop-file-controller.svelte.ts index d8bc528..bb9e784 100644 --- a/apps/web/src/lib/canvas/controllers/desktop-file-controller.svelte.ts +++ b/apps/web/src/lib/canvas/controllers/desktop-file-controller.svelte.ts @@ -1,5 +1,5 @@ import type { DesktopDocRepo } from "$lib/persistence/desktop"; -import type { BoardMeta, LoadedDoc, PersistentDocRepo } from "inkfinite-core"; +import type { BoardMeta, LoadedDoc, PersistentDocRepo } from "@inkfinite/core"; function isUserCancelled(error: unknown) { return error instanceof Error && /cancel/i.test(error.message); diff --git a/apps/web/src/lib/canvas/controllers/filebrowser-controller.svelte.ts b/apps/web/src/lib/canvas/controllers/filebrowser-controller.svelte.ts index 9126cc2..3e76c79 100644 --- a/apps/web/src/lib/canvas/controllers/filebrowser-controller.svelte.ts +++ b/apps/web/src/lib/canvas/controllers/filebrowser-controller.svelte.ts @@ -6,7 +6,7 @@ import { type BoardInspectorData, type FileBrowserViewModel, type PersistentDocRepo, -} from "inkfinite-core"; +} from "@inkfinite/core"; export class FileBrowserController { open = $state(false); diff --git a/apps/web/src/lib/canvas/controllers/markdown-controller.svelte.ts b/apps/web/src/lib/canvas/controllers/markdown-controller.svelte.ts index 1fef455..fa85aea 100644 --- a/apps/web/src/lib/canvas/controllers/markdown-controller.svelte.ts +++ b/apps/web/src/lib/canvas/controllers/markdown-controller.svelte.ts @@ -1,4 +1,4 @@ -import { Camera, EditorState, SnapshotCommand, type Store, type Viewport } from "inkfinite-core"; +import { Camera, EditorState, SnapshotCommand, type Store, type Viewport } from "@inkfinite/core"; /** * Controller for markdown block editing diff --git a/apps/web/src/lib/canvas/controllers/texteditor-controller.svelte.ts b/apps/web/src/lib/canvas/controllers/texteditor-controller.svelte.ts index 07f9595..6cb39f3 100644 --- a/apps/web/src/lib/canvas/controllers/texteditor-controller.svelte.ts +++ b/apps/web/src/lib/canvas/controllers/texteditor-controller.svelte.ts @@ -1,4 +1,4 @@ -import { Camera, EditorState, SnapshotCommand, type Store, type Viewport } from "inkfinite-core"; +import { Camera, EditorState, SnapshotCommand, type Store, type Viewport } from "@inkfinite/core"; export class TextEditorController { current = $state<{ shapeId: string; value: string } | null>(null); diff --git a/apps/web/src/lib/canvas/controllers/tool-controller.svelte.ts b/apps/web/src/lib/canvas/controllers/tool-controller.svelte.ts index 544a6ec..16b71e7 100644 --- a/apps/web/src/lib/canvas/controllers/tool-controller.svelte.ts +++ b/apps/web/src/lib/canvas/controllers/tool-controller.svelte.ts @@ -1,4 +1,4 @@ -import { switchTool, type Store, type Tool, type ToolId } from "inkfinite-core"; +import { switchTool, type Store, type Tool, type ToolId } from "@inkfinite/core"; export class ToolController { currentToolId = $state("select"); diff --git a/apps/web/src/lib/components/ArrowPopover.svelte b/apps/web/src/lib/components/ArrowPopover.svelte index 2e394f3..bcfa922 100644 --- a/apps/web/src/lib/components/ArrowPopover.svelte +++ b/apps/web/src/lib/components/ArrowPopover.svelte @@ -1,6 +1,6 @@