diff --git a/README.md b/README.md --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ pnpm dev:web ``` +Open the printed local URL for the documentation site, or add `/app` to open the editor. + Start the desktop app: ```sh diff --git a/apps/web/svelte.config.js b/apps/web/svelte.config.js --- a/apps/web/svelte.config.js +++ b/apps/web/svelte.config.js @@ -7,7 +7,7 @@ extensions: ['.svelte', '.svx', '.md'], preprocess: [vitePreprocess(), mdsvex({ extensions: ['.svx', '.md'] })], kit: { - adapter: adapter({ fallback: 'index.html' }), + adapter: adapter(), prerender: { entries: ['*'] }, alias: { $editor: '../../packages/ui/src/lib/editor', diff --git a/apps/web/src/routes/+page.svelte b/apps/web/src/routes/+page.svelte --- a/apps/web/src/routes/+page.svelte +++ b/apps/web/src/routes/+page.svelte @@ -1,30 +1,525 @@ - - - + + Inkfinite + + + + + + + + Documentation + Give your ideas + infinite room. + + Inkfinite is an open canvas for people and coding agents to sketch, connect, + and refine ideas together. + + + + Open the app + + + Quick start + + + + + + + + new_idea.inkfinite + + + + Sketch + + Iterate + + Ship + you + + + + + + + One whiteboard, many ways in + Draw or automate. + + + {#each features as feature, index (feature.title)} + + + + {feature.title} + {feature.description} + Read the guide + + + {/each} + + + + + + Start here + Open a canvas and make your first mark. + + + Download + + + + diff --git a/apps/web/src/routes/+page.ts b/apps/web/src/routes/+page.ts deleted file mode 100644 --- a/apps/web/src/routes/+page.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const ssr = false; -export const csr = true; diff --git a/apps/web/src/lib/docs/DocsHeader.svelte b/apps/web/src/lib/docs/DocsHeader.svelte --- a/apps/web/src/lib/docs/DocsHeader.svelte +++ b/apps/web/src/lib/docs/DocsHeader.svelte @@ -6,8 +6,8 @@ import DocsSearch from './DocsSearch.svelte'; - type Props = { sidebarOpen: boolean; toggleSidebar: () => void }; - let { sidebarOpen, toggleSidebar }: Props = $props(); + type Props = { sidebarOpen: boolean; toggleSidebar: () => void; landing?: boolean }; + let { sidebarOpen, toggleSidebar, landing = false }: Props = $props(); @@ -21,11 +21,13 @@ onclick={toggleSidebar}> - + Inkfinite - Docs + {#if !landing} + Docs + {/if} diff --git a/apps/web/src/lib/docs/DocsShell.svelte b/apps/web/src/lib/docs/DocsShell.svelte --- a/apps/web/src/lib/docs/DocsShell.svelte +++ b/apps/web/src/lib/docs/DocsShell.svelte @@ -7,12 +7,12 @@ let { children }: { children: Snippet } = $props(); let sidebarOpen = $state(false); - let landing = $derived(page.url.pathname === '/docs' || page.url.pathname === '/docs/'); + let landing = $derived(page.url.pathname === '/'); Skip to content - (sidebarOpen = !sidebarOpen)} /> + (sidebarOpen = !sidebarOpen)} /> {#if sidebarOpen} + import { Editor } from '@inkfinite/ui/editor'; + import { createDexiePlatformAdapter } from '$lib/persistence/dexie'; + + const platform = createDexiePlatformAdapter(); + + + + + + + diff --git a/apps/web/src/routes/app/+page.ts b/apps/web/src/routes/app/+page.ts new file mode 100644 --- /dev/null +++ b/apps/web/src/routes/app/+page.ts @@ -0,0 +1,2 @@ +export const ssr = false; +export const csr = true; diff --git a/apps/web/src/routes/docs/+page.svelte b/apps/web/src/routes/docs/+page.svelte deleted file mode 100644 --- a/apps/web/src/routes/docs/+page.svelte +++ /dev/null @@ -1,519 +0,0 @@ - - - - Inkfinite documentation - - - - - - - Documentation - Give your ideas - infinite room. - - Inkfinite is an open canvas for people and coding agents to sketch, connect, and - refine ideas together. - - - - Get started - - - View on GitHub - - - - - - - - new_idea.inkfinite - - - - Sketch - - Iterate - - Ship - you - - - - - - - One whiteboard, many ways in - Draw or automate. - - - {#each features as feature, index (feature.title)} - - - - {feature.title} - {feature.description} - Read the guide - - - {/each} - - - - - - Start here - Open a canvas and make your first mark. - - - Read the quick start - - - - - diff --git a/apps/web/src/routes/docs/+page.ts b/apps/web/src/routes/docs/+page.ts new file mode 100644 --- /dev/null +++ b/apps/web/src/routes/docs/+page.ts @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export function load(): never { + redirect(308, '/'); +} diff --git a/packages/ui/src/lib/components/Dialog.svelte b/packages/ui/src/lib/components/Dialog.svelte --- a/packages/ui/src/lib/components/Dialog.svelte +++ b/packages/ui/src/lib/components/Dialog.svelte @@ -98,8 +98,8 @@ .dialog__content { background-color: var(--ink-surface-raised); color: var(--ink-text); - border: var(--ink-line-width) solid var(--ink-border-strong); - border-radius: var(--ink-radius-panel); + border: 0.75px solid var(--ink-border-strong); + border-radius: var(--ink-dialog-radius, var(--ink-radius-panel)); box-shadow: var(--ink-shadow-panel); max-width: 90vw; max-height: 90vh; diff --git a/packages/ui/src/lib/editor/constants.ts b/packages/ui/src/lib/editor/constants.ts --- a/packages/ui/src/lib/editor/constants.ts +++ b/packages/ui/src/lib/editor/constants.ts @@ -3,7 +3,7 @@ export const HELP_LINKS = [ { label: 'README', href: 'https://github.com/stormlightlabs/inkfinite', external: true }, - { label: 'Read the Docs', href: '/docs', external: true }, + { label: 'Read the Docs', href: '/', external: false }, { label: 'Issue Tracker', href: 'https://github.com/stormlightlabs/inkfinite/issues', diff --git a/apps/web/src/routes/docs/getting-started/+page.svx b/apps/web/src/routes/docs/getting-started/+page.svx --- a/apps/web/src/routes/docs/getting-started/+page.svx +++ b/apps/web/src/routes/docs/getting-started/+page.svx @@ -5,22 +5,52 @@ # Getting started -Install Inkfinite, run an editor, and create your first document. +Run Inkfinite from the repository, then choose the web or desktop editor based on where you want +to keep your documents. Requirements -*todo* +You need Node.js 18 or newer, pnpm, and Rust 1.89. Building the desktop app also requires the +[Tauri 2 prerequisites](https://v2.tauri.app/start/prerequisites/) for your operating system. + +Clone the repository, open a terminal in its root directory, and install the JavaScript packages: + +```sh +pnpm install +``` Installation -*todo* +Start the web editor: -*todo - link to the web app at* `/` +```sh +pnpm dev:web +``` + +The command prints a local URL for the documentation site. Open its `/app` route to use the +editor. Documents created there stay in that browser profile. + +To run the desktop editor instead: + +```sh +pnpm tauri dev +``` + +The desktop app can open and save `.inkfinite` files on your computer. Create a document -*todo* +Open the editor and use the file browser to create a board. Add shapes from the toolbar, then drag +on the canvas to place them. The editor saves web documents to IndexedDB; the desktop app keeps a +draft until you choose **Save As** and select a file. + +Use **Import** when you already have an Excalidraw or Obsidian Canvas file. Import creates a new +Inkfinite document and leaves the source file unchanged. Next steps -*todo* +- Read [Documents](/docs/concepts/documents/) to understand pages, layers, and shapes. +- Read [Web editor](/docs/applications/web/) or [Desktop editor](/docs/applications/desktop/) for + storage details. +- Read [Command-line interface](/docs/reference/cli/) before changing a saved document from a + script or coding agent. diff --git a/apps/web/src/routes/docs/introduction/+page.svx b/apps/web/src/routes/docs/introduction/+page.svx --- a/apps/web/src/routes/docs/introduction/+page.svx +++ b/apps/web/src/routes/docs/introduction/+page.svx @@ -27,7 +27,8 @@ Ways to work -- Use the [web editor](/docs/applications/web/) for a browser-based canvas. +- Open the [web editor](/app) for a browser-based canvas, or read its + [storage guide](/docs/applications/web/). - Use the [desktop editor](/docs/applications/desktop/) for native files and desktop menus. - Use the [command-line interface](/docs/reference/cli/) to inspect, edit, validate, or render a document from a script. diff --git a/packages/ui/src/lib/editor/components/StatusBar.svelte b/packages/ui/src/lib/editor/components/StatusBar.svelte --- a/packages/ui/src/lib/editor/components/StatusBar.svelte +++ b/packages/ui/src/lib/editor/components/StatusBar.svelte @@ -237,7 +237,11 @@ - (infoOpen = false)} title="About Inkfinite"> + (infoOpen = false)} + title="About Inkfinite" + class="about-dialog"> About Inkfinite @@ -384,6 +388,10 @@ .about { max-width: 480px; padding: var(--ink-space-5); + } + + :global(.about-dialog) { + --ink-dialog-radius: 1rem; } .about h1 { diff --git a/packages/ui/src/lib/editor/components/StatusBar.svelte.test.ts b/packages/ui/src/lib/editor/components/StatusBar.svelte.test.ts --- a/packages/ui/src/lib/editor/components/StatusBar.svelte.test.ts +++ b/packages/ui/src/lib/editor/components/StatusBar.svelte.test.ts @@ -84,8 +84,8 @@ expect(onOpenBrowser).toHaveBeenCalledOnce(); expect(onHistoryClick).toHaveBeenCalledOnce(); - await expect - .element(screen.getByRole('dialog', { name: 'About Inkfinite' })) - .toBeInTheDocument(); + const dialog = screen.getByRole('dialog', { name: 'About Inkfinite' }); + await expect.element(dialog).toBeInTheDocument(); + expect(window.getComputedStyle(dialog.element()).borderRadius).toBe('16px'); }); }); diff --git a/apps/web/src/routes/docs/applications/desktop/+page.svx b/apps/web/src/routes/docs/applications/desktop/+page.svx --- a/apps/web/src/routes/docs/applications/desktop/+page.svx +++ b/apps/web/src/routes/docs/applications/desktop/+page.svx @@ -1,20 +1,43 @@ Desktop editor · Inkfinite documentation - + # Desktop editor -Build and use Inkfinite as a native desktop application. +The desktop editor combines the shared canvas interface with native file handling and authenticated +local CLI access. Run locally -*todo* +Install Node.js 18 or newer, pnpm, Rust 1.89, and the +[Tauri 2 prerequisites](https://v2.tauri.app/start/prerequisites/) for your operating system. From +the repository root, run: -Document Sessions +```sh +pnpm install +pnpm tauri dev +``` -*todo* +This starts the web frontend and native Tauri shell in development mode. -Local Control +Document sessions -*todo* +Use **Open** to load a canonical `.inkfinite` file. New documents remain drafts in the app's local +data directory until you choose **Save As**. The app tracks the current native path separately from +imports and exports, so exporting a PNG, SVG, Excalidraw file, or Obsidian Canvas file does not mark +the native document as saved elsewhere. + +The desktop backend uses file locks, recovery data, and atomic replacement for canonical writes. +Do not edit the same file with a file-mode CLI command while it is open in the desktop app. Use the +live proposal workflow instead. + +Local CLI access + +The desktop app exposes authenticated IPC to the local `inkfinite` CLI. Read-only commands can list +sessions, inspect the open document, query records, or focus the window. Mutation commands normally +create a proposal that appears as a ghost preview for human review. + +Direct live apply requires a one-time authorization token issued by the desktop UI. Discovery and +authentication remain local to the current user account. See [Agent workflows](/docs/reference/agents/) +for the review process and [Command-line interface](/docs/reference/cli/) for commands. diff --git a/apps/web/src/routes/docs/applications/web/+page.svx b/apps/web/src/routes/docs/applications/web/+page.svx --- a/apps/web/src/routes/docs/applications/web/+page.svx +++ b/apps/web/src/routes/docs/applications/web/+page.svx @@ -1,16 +1,34 @@ Web editor · Inkfinite documentation - + # Web editor -Run Inkfinite in the browser. +The web editor provides the full canvas interface without access to native `.inkfinite` files. -Web App +Open the editor -*todo* +The hosted editor lives at [`/app`](/app). To run it from a source checkout: -Storage & Backups +```sh +pnpm install +pnpm dev:web +``` -*todo* +Open the local URL printed by Vite and add `/app` to its path. The site root contains this +documentation. + +Storage and backups + +The web editor stores boards in IndexedDB for the current origin and browser profile. It does not +write canonical files to your filesystem, and another browser or deployment origin has a separate +database. + +Export work that you cannot afford to lose. Browser storage may disappear when you clear site data, +remove a profile, or use private browsing. Use the desktop app when you want a canonical +`.inkfinite` file that can be copied, versioned, or changed by the CLI. + +Excalidraw and Obsidian Canvas exports preserve supported editable content but may omit features +that the target format cannot represent. PNG and SVG exports are presentation copies. See +[File format](/docs/reference/file-format/) before relying on an export as a backup. diff --git a/apps/web/src/routes/docs/concepts/documents/+page.svx b/apps/web/src/routes/docs/concepts/documents/+page.svx --- a/apps/web/src/routes/docs/concepts/documents/+page.svx +++ b/apps/web/src/routes/docs/concepts/documents/+page.svx @@ -1,20 +1,42 @@ Documents · Inkfinite documentation - + # Documents -Learn how Inkfinite represents an infinite-canvas document. +An Inkfinite document stores canvas content and the history needed to merge edits from different +peers. Document structure -*todo* +A document contains pages. Each page has an ordered tree of layers and shapes, along with bindings +that connect related shapes. Stable IDs identify every record, so an edit can refer to one object +without relying on its current position or display name. -Shapes and Layers +Pages separate canvases within one document. The editor displays one selected page at a time, but +the native file preserves every page and its history. -*todo* +Shapes and layers + +Shapes include rectangles, ellipses, lines, arrows, text, Markdown cards, and freehand strokes. +Each shape has a kind, transform, kind-specific properties, and metadata. A shape can also carry a +name, semantic role, and tags for reliable CLI queries. + +Layers control stacking and visibility. Container shapes can own other shapes, which lets a group +move as one unit. Bindings connect arrows to source and target shapes without making either shape +a child of the other. + +Locks prevent edits to a layer or shape. The `agent_editable` metadata flag creates a separate +boundary for agent-originated transactions, even when a human can still edit the object in the +canvas. Persistence -*todo* +The web editor stores documents in the browser's IndexedDB database. Clearing site data or using a +different browser profile can make those documents unavailable, so export important work. + +The desktop editor and CLI use canonical `.inkfinite` files. Each file contains Automerge state +and change history, not just the visible canvas snapshot. Writers use a lock, recovery data, and +atomic file replacement to avoid partial saves. See [File format](/docs/reference/file-format/) for +editable interchange and presentation exports. diff --git a/apps/web/src/routes/docs/concepts/transactions-and-sync/+page.svx b/apps/web/src/routes/docs/concepts/transactions-and-sync/+page.svx --- a/apps/web/src/routes/docs/concepts/transactions-and-sync/+page.svx +++ b/apps/web/src/routes/docs/concepts/transactions-and-sync/+page.svx @@ -1,20 +1,42 @@ Transactions and sync · Inkfinite documentation - + # Transactions and sync -Understand how Inkfinite applies durable edits and converges between peers. +Inkfinite groups related operations into transactions, validates them against the current document, +and records accepted changes in Automerge history. Transactions -*todo* +A transaction has an ID, an actor, optional preconditions, and one or more operations. Operations +create, patch, move, or delete records. Grouping a coherent edit in one transaction keeps +validation, history, and review tied to the user's request. + +Preconditions can require specific document heads or record versions. Inkfinite rejects the whole +transaction when those expectations are stale, when a selector is ambiguous, or when an operation +would cross a lock or agent-editability boundary. A rejected transaction does not partially modify +the canonical file. Undo and redo -*todo* +The editor records transactions in its history. Undo applies the inverse of an accepted local edit; +redo reapplies an edit that was undone. Because history belongs to the document model, both the web +and desktop editors use the same behavior. -Syncing +An edit may become impossible to undo after later changes remove or replace the records it depended +on. Inkfinite reports the conflict instead of reconstructing an uncertain result. -*todo* +Synchronization + +Automerge assigns document heads to the current causal state. Two peers can make changes from a +shared base and merge them without choosing one entire file over the other. + +The CLI includes the heads returned by `inspect` and `query` as transaction preconditions. If a +human or another agent edits the document first, the stale transaction fails. Inspect the new +heads, query the affected records again, and rebuild the intended change from current state. + +The current local file and desktop proposal workflows provide this concurrency model without +claiming a hosted synchronization service. A live desktop proposal can also become stale while it +waits for review; review the refreshed preview before accepting it. diff --git a/apps/web/src/routes/docs/reference/agents/+page.svx b/apps/web/src/routes/docs/reference/agents/+page.svx --- a/apps/web/src/routes/docs/reference/agents/+page.svx +++ b/apps/web/src/routes/docs/reference/agents/+page.svx @@ -1,28 +1,66 @@ Agent workflows · Inkfinite documentation - + -# Agentic Workflows +# Agent workflows Use Inkfinite's command-line and proposal tools to make reviewable document changes. Workflow -*todo* +For a saved file, follow the same sequence for every change: + +1. Run `inspect --json` and keep the returned heads. +2. Use `query --json` to read only the relevant records. +3. Build the smallest transaction that satisfies the request. +4. Run the mutation with `--dry-run --json`. +5. Apply it only after the dry run passes. +6. Validate the document and render the affected page, role, or selection. + +Prefer semantic roles, names, and tags over coordinates when they identify one record. If a selector +matches multiple shapes, narrow it with an exact ID. Do not guess which one the human meant. Proposals -*todo* +For a document open in the desktop app, use `app propose` instead of changing its file. The app +shows created, changed, and deleted records as a ghost preview. The human can accept all operations, +accept selected operations, or reject the proposal. + +Partial acceptance creates a transaction from the approved operations and validates it again. A +stale proposal may need a refreshed preview after another edit changes the document. Review the +refresh rather than accepting an outdated result. Permissions -*todo* +Locks and `agent_editable` metadata are document boundaries. An agent must not work around a locked +layer, locked shape, or record marked `agent_editable: false` by editing a child or bypassing the +CLI. Report the boundary and ask the human to make or authorize the change. + +Direct `app apply` requires a one-time token issued by the desktop UI. The token authorizes a single +live action; it is not a reusable credential and never belongs in a transaction file. Conflicts -*todo* +Document heads are preconditions. If a file mutation exits with code `5`, inspect the current heads, +query the affected records again, and rebuild the transaction from that state. Do not remove lock +files, copy an older document over the current file, or silently drop intervening edits. + +Record-version preconditions protect individual objects in the same way. A failed version check +means the object changed after the agent inspected it. Skills -*todo* +The repository includes an installable agent skill at `skills/inkfinite`. It documents the safe +workflow and includes runnable examples for file edits, proposal review, partial acceptance, and +stale-head recovery. + +After building the CLI, verify the examples from the repository root: + +```sh +INKFINITE_CLI="$PWD/target/debug/inkfinite" \ + bash skills/inkfinite/scripts/verify-examples.sh +``` + +The fixture server used by those examples tests the protocol. It does not replace a real desktop +session or authorize direct apply. diff --git a/apps/web/src/routes/docs/reference/cli/+page.svx b/apps/web/src/routes/docs/reference/cli/+page.svx --- a/apps/web/src/routes/docs/reference/cli/+page.svx +++ b/apps/web/src/routes/docs/reference/cli/+page.svx @@ -1,6 +1,6 @@ Command-line interface · Inkfinite documentation - + # Command-line interface @@ -9,16 +9,86 @@ Command overview -*todo* +Run `inkfinite --help` or `inkfinite --help` for the complete option reference. The main +commands are: + +| Command | Purpose | +| --- | --- | +| `new` | Create a blank canonical document | +| `inspect` | Print a document summary or materialized snapshot | +| `query` | Find records by semantic, structural, kind, or bounds filters | +| `validate` | Load and validate a canonical document | +| `shape`, `connect`, `layout` | Build and apply structured transactions | +| `apply` | Validate and apply a transaction draft from JSON | +| `render` | Write a deterministic SVG of a document or filtered view | +| `app` | Inspect or work with a running desktop session | +| `schema`, `capabilities` | Print machine-readable contracts for integrations | + +During development, build the binary with: + +```sh +cargo build -p inkfinite-cli --bin inkfinite +``` File mode -*todo* +File-mode commands operate on a closed `.inkfinite` file. Start by inspecting its heads and querying +only the records you need: + +```sh +inkfinite inspect architecture.inkfinite --json +inkfinite query architecture.inkfinite --role architecture.service --json +``` + +Prefer `shape create`, `shape patch`, `shape delete`, `connect`, and `layout` when one of them +expresses the edit. Use `apply` for a transaction with operations that the structured commands do +not cover. Test mutations with `--dry-run` before saving: + +```sh +inkfinite shape patch architecture.inkfinite \ + --role architecture.service \ + --patch '@service-patch.json' \ + --dry-run --json + +inkfinite apply architecture.inkfinite \ + --transaction transaction.json \ + --dry-run --json +``` + +File commands never prompt. Close the desktop editor before changing its file; a lock or stale-head +error is a signal to inspect current state, not a reason to overwrite the file. Live mode -*todo* +With the desktop app running, use `app status`, `app inspect`, and `app query` for read-only access. +`app propose` sends a transaction to the canvas as a reviewable ghost preview: -Output Format +```sh +inkfinite app status --json +inkfinite app query --role architecture.service --json +inkfinite app propose --transaction transaction.json --json +``` -*todo* +The human can accept the whole proposal or selected operations, or reject it without changing the +document. `app apply` bypasses proposal review only when the desktop UI has issued a one-time +authorization token for that action. Do not store that token in a document or script. + +Output format + +Pass `--json` for deterministic machine-readable output. Successful mutations report the previous +and current heads, transaction ID, created, updated, and deleted records, repairs, and warnings. +Diagnostics go to standard error so standard output remains safe to pipe into another program. + +`capabilities --json` reports supported commands and stable exit codes. Current exit codes are: + +| Code | Meaning | +| ---: | --- | +| `0` | Success | +| `2` | Invalid command usage | +| `3` | File or input error | +| `4` | Invalid document or data | +| `5` | Existing file, lock, or state conflict | + +Use `schema document`, `schema transaction`, and `schema protocol` instead of inferring JSON shapes +from examples. Global `--json` and `--non-interactive` options may appear before or after a +subcommand.
Documentation
+ Inkfinite is an open canvas for people and coding agents to sketch, connect, + and refine ideas together. +
One whiteboard, many ways in
{feature.description}
Start here
- Inkfinite is an open canvas for people and coding agents to sketch, connect, and - refine ideas together. -
@@ -384,6 +388,10 @@ .about { max-width: 480px; padding: var(--ink-space-5); + } + + :global(.about-dialog) { + --ink-dialog-radius: 1rem; } .about h1 { diff --git a/packages/ui/src/lib/editor/components/StatusBar.svelte.test.ts b/packages/ui/src/lib/editor/components/StatusBar.svelte.test.ts --- a/packages/ui/src/lib/editor/components/StatusBar.svelte.test.ts +++ b/packages/ui/src/lib/editor/components/StatusBar.svelte.test.ts @@ -84,8 +84,8 @@ expect(onOpenBrowser).toHaveBeenCalledOnce(); expect(onHistoryClick).toHaveBeenCalledOnce(); - await expect - .element(screen.getByRole('dialog', { name: 'About Inkfinite' })) - .toBeInTheDocument(); + const dialog = screen.getByRole('dialog', { name: 'About Inkfinite' }); + await expect.element(dialog).toBeInTheDocument(); + expect(window.getComputedStyle(dialog.element()).borderRadius).toBe('16px'); }); }); diff --git a/apps/web/src/routes/docs/applications/desktop/+page.svx b/apps/web/src/routes/docs/applications/desktop/+page.svx --- a/apps/web/src/routes/docs/applications/desktop/+page.svx +++ b/apps/web/src/routes/docs/applications/desktop/+page.svx @@ -1,20 +1,43 @@ Desktop editor · Inkfinite documentation - + # Desktop editor -Build and use Inkfinite as a native desktop application. +The desktop editor combines the shared canvas interface with native file handling and authenticated +local CLI access.