diff --git a/TODO.txt b/TODO.txt index 057702e..a9831f0 100644 --- a/TODO.txt +++ b/TODO.txt @@ -192,25 +192,25 @@ R2. Web: Boards list + Dexie "Inspector" drawer -------------------------------------------------------------------------------- /apps/web/src/lib/filebrowser/FileBrowser.svelte: -[ ] Boards panel: +[x] Boards panel: - listBoards() -> render - search filter - open / create / rename / delete Inspector drawer (selected board): -[ ] Show "Storage" -[ ] Show schema info: +[x] Show "Storage" +[x] Show schema info: - declared schema version - installed schema version -[ ] Show board-level stats (computed live): +[x] Show board-level stats (computed live): - row counts: pages/shapes/bindings for this board & doc size bytes for docs row - last updatedAt -[ ] Show migration info: +[x] Show migration info: - list applied migrations from migrations table (id + appliedAt) - show "pending" migrations if any (based on known list vs applied) Safe deletes: -[ ] deleteBoard must be a single atomic transaction (boards + related tables) +[x] deleteBoard must be a single atomic transaction (boards + related tables) (DoD): - Web: you can browse boards, open one, and verify migrations + row counts. @@ -227,8 +227,8 @@ R3. Desktop: real directory + files (Tauri) - tree view with folders [ ] Implement file actions: - [x] New: create new file - - [ ] Rename: rename file - - [ ] Delete: delete file + - [ ] Rename: rename file (Tauri command needed) + - [ ] Delete: delete file (Tauri command needed) - [x] Open: load file into editor - [x] Export: save JSON diff --git a/apps/web/src/lib/canvas/canvas-store.svelte.ts b/apps/web/src/lib/canvas/canvas-store.svelte.ts index a210d60..145088f 100644 --- a/apps/web/src/lib/canvas/canvas-store.svelte.ts +++ b/apps/web/src/lib/canvas/canvas-store.svelte.ts @@ -430,7 +430,6 @@ export function createCanvasController(bindings: CanvasControllerBindings) { let inputAdapter: InputAdapter | null = null; let canvasInitialized = false; - // Initialize canvas-dependent systems when canvas becomes available $effect(() => { if (!canvas || canvasInitialized) return; diff --git a/apps/web/src/lib/tests/Canvas.history.test.ts b/apps/web/src/lib/tests/Canvas.history.test.ts index 672f189..d8a1b9d 100644 --- a/apps/web/src/lib/tests/Canvas.history.test.ts +++ b/apps/web/src/lib/tests/Canvas.history.test.ts @@ -332,7 +332,7 @@ describe("Canvas history integration", () => { it("wraps pointer actions in SnapshotCommands and enqueues persistence", async () => { render(Canvas); - // Wait for onMount to complete and input adapter to be created + await vi.waitFor(() => { expect(actionHandlers.length).toBeGreaterThan(0); }); diff --git a/packages/core/src/export.ts b/packages/core/src/export.ts index 106b823..1ab1501 100644 --- a/packages/core/src/export.ts +++ b/packages/core/src/export.ts @@ -59,18 +59,15 @@ export async function exportSelectionToPNG( return null; } - // Calculate combined bounds const bounds = combineBounds(shapes.map((s) => shapeBounds(s))); if (!bounds) { return null; } - // Add padding const padding = 20; const width = Box2Ops.width(bounds) + padding * 2; const height = Box2Ops.height(bounds) + padding * 2; - // Create temporary canvas const canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; @@ -80,20 +77,16 @@ export async function exportSelectionToPNG( throw new Error("Failed to get 2D context"); } - // Clear background (white) context.fillStyle = "white"; context.fillRect(0, 0, width, height); - // Translate to handle bounds offset + padding context.save(); context.translate(-bounds.min.x + padding, -bounds.min.y + padding); - // Let caller render the shapes renderFunction(context, shapes, bounds); context.restore(); - // Export to PNG return new Promise((resolve, reject) => { canvas.toBlob((blob) => { if (blob) { @@ -122,7 +115,6 @@ export function exportToSVG(state: EditorState, options: ExportOptions = {}): st return ""; } - // Calculate bounds const bounds = combineBounds(shapes.map((s) => shapeBounds(s))); if (!bounds) { return ""; @@ -136,9 +128,6 @@ export function exportToSVG(state: EditorState, options: ExportOptions = {}): st const elements: string[] = [``]; - // Add white background - - // Render each shape for (const shape of shapes) { const svg = shapeToSVG(shape, state); if (svg) { @@ -217,7 +206,6 @@ function lineToSVG(shape: LineShape, transform: string): string { function arrowToSVG(shape: ArrowShape, transform: string, _state: EditorState): string { const { a, b, stroke, width } = shape.props; - // Calculate arrow head const angle = Math.atan2(b.y - a.y, b.x - a.x); const arrowLength = 15; const arrowAngle = Math.PI / 6; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index adffb78..485bc63 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -6,9 +6,9 @@ export * from "./geom"; export * from "./history"; export * from "./math"; export * from "./model"; -export * from "./persist/DocRepo"; export * from "./persistence/db"; export * from "./persistence/desktop"; +export * from "./persistence/repo"; export * from "./persistence/stats"; export * from "./persistence/web"; export * from "./reactivity"; diff --git a/packages/core/src/persistence/db.ts b/packages/core/src/persistence/db.ts index 7814886..271b9ab 100644 --- a/packages/core/src/persistence/db.ts +++ b/packages/core/src/persistence/db.ts @@ -1,6 +1,6 @@ import Dexie, { type Transaction } from "dexie"; import { PageRecord as PageOps } from "../model"; -import type { BoardMeta, Timestamp } from "../persist/DocRepo"; +import type { BoardMeta, Timestamp } from "./repo"; import type { BindingRow, MetaRow, MigrationRow, PageRow, ShapeRow } from "./web"; export const DB_NAME = "inkfinite"; diff --git a/packages/core/src/persistence/desktop.ts b/packages/core/src/persistence/desktop.ts index e1acc18..f5ca10a 100644 --- a/packages/core/src/persistence/desktop.ts +++ b/packages/core/src/persistence/desktop.ts @@ -1,5 +1,5 @@ import type { BindingRecord, Document, PageRecord, ShapeRecord } from "../model"; -import type { BoardMeta } from "../persist/DocRepo"; +import type { BoardMeta } from "./repo"; import type { DocOrder, LoadedDoc } from "./web"; /** diff --git a/packages/core/src/persist/DocRepo.ts b/packages/core/src/persistence/repo.ts similarity index 91% rename from packages/core/src/persist/DocRepo.ts rename to packages/core/src/persistence/repo.ts index b01bdb7..f312306 100644 --- a/packages/core/src/persist/DocRepo.ts +++ b/packages/core/src/persistence/repo.ts @@ -4,7 +4,6 @@ export type BoardMeta = { id: string; name: string; createdAt: Timestamp; update /** * Shared document repository contract used by both web and desktop persistence layers. - * Provides the minimal operations required for listing and managing boards. */ export interface DocRepo { /** diff --git a/packages/core/src/persistence/stats.ts b/packages/core/src/persistence/stats.ts index 5718149..ff66446 100644 --- a/packages/core/src/persistence/stats.ts +++ b/packages/core/src/persistence/stats.ts @@ -1,4 +1,4 @@ -import type { Timestamp } from "../persist/DocRepo"; +import type { Timestamp } from "./repo"; export type BoardStats = { pageCount: number; diff --git a/packages/core/src/persistence/web.ts b/packages/core/src/persistence/web.ts index caa58db..3ba9908 100644 --- a/packages/core/src/persistence/web.ts +++ b/packages/core/src/persistence/web.ts @@ -9,7 +9,7 @@ import { type ShapeRecord, ShapeRecord as ShapeOps, } from "../model"; -import type { BoardMeta, DocRepo, Timestamp } from "../persist/DocRepo"; +import type { BoardMeta, DocRepo, Timestamp } from "./repo"; import type { BoardInspectorData, BoardStats, MigrationInfo, SchemaInfo } from "./stats"; import { BoardStatsOps, getPendingMigrations } from "./stats"; diff --git a/packages/core/src/ui/filebrowser.ts b/packages/core/src/ui/filebrowser.ts index 75b3062..9fa210c 100644 --- a/packages/core/src/ui/filebrowser.ts +++ b/packages/core/src/ui/filebrowser.ts @@ -1,4 +1,4 @@ -import type { BoardMeta, DocRepo } from "../persist/DocRepo"; +import type { BoardMeta, DocRepo } from "../persistence/repo"; export type FileBrowserActions = { open(boardId: string): Promise; diff --git a/packages/core/tests/filebrowser.test.ts b/packages/core/tests/filebrowser.test.ts index e8f3b15..33cc10f 100644 --- a/packages/core/tests/filebrowser.test.ts +++ b/packages/core/tests/filebrowser.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import type { DocRepo } from "../src/persist/DocRepo"; +import type { DocRepo } from "../src/persistence/repo"; import { FileBrowserVM } from "../src/ui/filebrowser"; function createRepoMock(): DocRepo {