Commit graph

214 commits

Author SHA1 Message Date
Danila Poyarkov 4e03d262c0 Register system font faces for CSS preview, fix picker highlight width 2026-03-01 03:25:50 +03:00
Danila Poyarkov 621e38e178 Font picker: start with empty search, scroll to current font 2026-03-01 02:59:17 +03:00
Danila Poyarkov 326f5d194d Preload system fonts on app startup
Cache font list in Rust via OnceLock, fire preload request from
main.ts so the font list is ready before user opens the picker.
2026-03-01 02:57:58 +03:00
Danila Poyarkov 5ccae709a0 Add system font enumeration via font-kit
Rust: list_system_fonts and load_system_font Tauri commands using
font-kit crate for cross-platform system font access.

TS: font shim detects Tauri runtime and uses invoke() for font
listing/loading instead of queryLocalFonts (unavailable in WKWebView).
2026-03-01 02:56:12 +03:00
Danila Poyarkov f7d1d59dc1 Rename Cargo crate to open_pencil, binary to OpenPencil
Fixes macOS Dock showing 'open-pencil-app' instead of 'OpenPencil'.
2026-03-01 02:42:12 +03:00
Danila Poyarkov faf7ba6387 Fix app name in macOS Dock: open-pencil-app → OpenPencil 2026-03-01 02:39:32 +03:00
Danila Poyarkov 20544366c7 Add undo/redo for all variable operations
Push undo entries for: create/delete variable, create collection,
rename variable, rename collection, change color/value.
2026-03-01 02:38:31 +03:00
Danila Poyarkov 6d407820eb Fix variables table not updating on create/delete
The variables computed was missing the sceneVersion dependency,
so it never re-evaluated when addVariable bumped sceneVersion.
2026-03-01 02:33:42 +03:00
Danila Poyarkov f9d5c43586 Fix missing type icons in variables table
Import lucide icon components explicitly for TanStack Table h() cells.
The icon-[lucide--*] CSS class syntax doesn't work with unplugin-icons.
2026-03-01 02:21:40 +03:00
Danila Poyarkov 2695cffe6a Extract ColorInput component, fix alpha slider checkerboard
New ColorInput component (swatch + hex, optional editable mode)
replaces duplicated ColorPicker + hex input pattern in:
- PageSection, StrokeSection, EffectsSection
- VariablesDialog (color variable cells use ColorInput with picker)

Fix ColorPicker alpha slider: add checkerboard background behind
transparent-to-color gradient, matching FillPicker's implementation.
2026-03-01 02:18:23 +03:00
Danila Poyarkov 52a31cff2f Use TanStack Table for variables dialog with resizable columns
Replace hand-rolled flex layout with @tanstack/vue-table:
- Proper <table> with column resizing (drag header borders)
- Sticky header row
- FlexRender for cell content (reka-ui Editable inline editing)
- Use culori via parseColor instead of manual hex parsing
2026-03-01 02:12:19 +03:00
Danila Poyarkov e3f8ff623e Redesign variables dialog to match Figma's table layout
- Column per mode (Name | Mode 1 | Mode 2 | ...)
- Color swatch inside value cells, not next to name
- Search bar in top-right header area
- Single '+ Create variable' button at bottom
- Wider dialog (800px) for multi-mode tables
2026-03-01 02:08:13 +03:00
Danila Poyarkov 0ed1bfdf63 Add variable collections to demo: primitives, semantic tokens, spacing
Three collections with multi-mode support:
- Primitives (Light/Dark): 9 color variables with dark mode values
- Semantic (aliases): Background, Text, Border, Accent, Success, Error
- Spacing (Default/Compact): 8 number variables for space + radius tokens

Binds variables to dashboard sidebar, header, frame, and titles.
2026-03-01 02:04:41 +03:00
Danila Poyarkov 04b0efd020 Double-click to rename collection tabs in variables dialog 2026-03-01 02:03:24 +03:00
Danila Poyarkov ff48c03d5a Fix addCollection not triggering reactivity 2026-03-01 01:59:01 +03:00
Danila Poyarkov 5c4c6d7289 Move variables UI to dialog opened from page properties
Match Figma's pattern: Variables section in page-level properties
panel with settings icon that opens a full dialog (reka-ui Dialog +
Tabs + Editable). Remove sidebar panel approach.
2026-03-01 01:54:57 +03:00
Danila Poyarkov e322d53ee3 Merge branch 'tauri-icon' 2026-03-01 01:52:14 +03:00
Danila Poyarkov d30ab4c5d5 Add variables support: collections, modes, bindings, UI panel
Core:
- Variable, VariableCollection types on SceneGraph
- resolveVariable with alias chain resolution + cycle detection
- Mode switching (activeMode per collection)
- Bind/unbind variables to node properties (fills/strokes colors)
- Renderer resolves variable bindings before painting

Import:
- Parse VARIABLE type NodeChanges from .fig files
- Extract paint variable bindings (fills/strokes)

UI:
- VariablesPanel with reka-ui Tabs (collections) + Editable (names/values)
- FillSection: variable picker (reka-ui Popover + Combobox)
- Bound fills show purple variable name badge with detach button
- Toggle variables panel from bottom of layers sidebar

7 new unit tests (83 total)
2026-03-01 01:47:52 +03:00
Danila Poyarkov 88765bd078 Use pencil loader icon as Tauri app icon 2026-03-01 01:37:01 +03:00
Danila Poyarkov aa083206d9 Add minimalist splash loader during WASM initialization 2026-03-01 01:30:01 +03:00
Danila Poyarkov 97428949fc Update README and AGENTS.md for monorepo structure and CLI 2026-03-01 01:28:24 +03:00
Danila Poyarkov eb6f8914a6 Point core exports to src/index.ts for Vite compatibility 2026-03-01 01:27:04 +03:00
Danila Poyarkov 22e9e7af87 Prepare packages for npm publishing
- Core: conditional exports (bun→src, import→dist), prepublishOnly builds
- CLI: publishConfig, files field
- bun publish resolves workspace:* → actual versions
2026-03-01 01:24:54 +03:00
Danila Poyarkov 33435cc1d1 Make CLI available as bun open-pencil in workspace 2026-03-01 01:21:29 +03:00
Danila Poyarkov 1975fae36e Add @open-pencil/cli with headless .fig file operations
Commands:
- open-pencil info <file> — document stats, node types, fonts
- open-pencil tree <file> — visual node tree with agentfmt
- open-pencil find <file> — search by name/type with rich output
- open-pencil export <file> — render to PNG/JPG/WEBP at any scale

Uses canvaskit-wasm/full for headless CPU rasterization.
All commands support --json for machine-readable output.
agentfmt provides consistent colored terminal formatting.
2026-03-01 01:19:24 +03:00
Danila Poyarkov 6d1cec1606 Extract @open-pencil/core package
Move engine (scene-graph, renderer, layout, codec, kiwi) into
packages/core/ as a Bun workspace package. The app's src/engine/
files become thin re-export shims so all existing imports still work.

- Bun workspace with packages/core
- Zero DOM dependencies in core
- Headless-friendly CanvasKit init (locateFile option)
- Constants split: engine constants in core, UI constants in app
- All 76 unit tests pass, lint + typecheck clean, vite build works
2026-03-01 01:03:07 +03:00
Danila Poyarkov 80972c1479 Add loading placeholder while export preview renders 2026-03-01 00:53:21 +03:00
Danila Poyarkov 63cefe651c Add CSS containment to side panels to reduce repaints from canvas 2026-03-01 00:45:20 +03:00
Danila Poyarkov 8eff364dcd Use sceneVersion instead of renderVersion for UI panels
renderVersion bumps on every pan/zoom/hover — it's the canvas redraw signal.
sceneVersion only bumps when the scene graph actually changes.

Panels (properties, layers, context menu, pages, export preview) now
depend on sceneVersion, so they don't re-render during navigation.
2026-03-01 00:33:14 +03:00
Danila Poyarkov fee028b49f Fix export preview flicker: keep old image until new one is ready, avoid renderVersion deps 2026-03-01 00:32:17 +03:00
Danila Poyarkov ee16ce89de Split requestRender/requestRepaint to avoid export preview updates on pan/zoom 2026-03-01 00:29:42 +03:00
Danila Poyarkov cb6c042c07 Merge remote-tracking branch 'origin/export-image' 2026-03-01 00:20:00 +03:00
Danila Poyarkov 103c5617cf Re-encode JPG/WEBP via OffscreenCanvas instead of CanvasKit
The default CanvasKit WASM build lacks JPEG/WEBP encoders. Instead of
switching to the full build (+1MB), render PNG via CanvasKit then
re-encode to the target format using OffscreenCanvas.convertToBlob().
2026-03-01 00:11:48 +03:00
Danila Poyarkov 7d4b821e77 Use full CanvasKit build for JPEG/WEBP encoding support
The default canvaskit.wasm doesn't include JPEG/WEBP encoders
(encodeToBytes returns null). The full build is only ~1MB larger.
2026-03-01 00:09:35 +03:00
Danila Poyarkov 7a5a77c098 Render export preview at panel-appropriate resolution
Scale preview to fit 480px width (covers retina panels) instead of
fixed 0.5x which was far too blurry.
2026-03-01 00:05:19 +03:00
Danila Poyarkov 2fb22786c1 Add types/shared code and test strategy to CLI plan 2026-03-01 00:03:26 +03:00
Danila Poyarkov e8223287df Validate all CLI risks: CanvasKit CPU, headless fonts, extraction scope 2026-03-01 00:00:31 +03:00
Danila Poyarkov 55b0b37789 Add image export (PNG/JPG/WEBP) with shared rendering
Extract renderThumbnail and renderNodesToImage into render-image.ts,
reusing the same offscreen CanvasKit surface approach that fig-export
used for thumbnails.

- ExportSection in properties panel with scale/format selectors,
  multi-export support, and live preview with checkerboard background
- Export via context menu and ⇧⌘E shortcut
- Tauri save dialog, File System Access API, and download fallback
- JPG renders with white background, PNG/WEBP with transparency
2026-02-28 23:59:39 +03:00
Danila Poyarkov 44e914d410 Detailed CLI & headless implementation plan 2026-02-28 23:58:47 +03:00
Danila Poyarkov f61d8c0d83 Add headless rendering plan (CanvasKit CPU rasterizer) 2026-02-28 23:54:10 +03:00
Danila Poyarkov 7ff16f70e0 Fix app identifier to net.dannote.open-pencil, add CLI/headless plan to PLAN.md 2026-02-28 23:51:22 +03:00
Danila Poyarkov 89e48c553e Update README features, highlight ~5 MB install and offline 2026-02-28 23:38:46 +03:00
Danila Poyarkov 00a2923007 Add screenshot to README 2026-02-28 23:36:55 +03:00
Danila Poyarkov d1979473c3 Add figma-use reference to AGENTS.md 2026-02-28 23:27:36 +03:00
Danila Poyarkov 7a803772a5 Expand AGENTS.md with lessons from all project sessions
Added from ~/Development sessions: scene graph behaviors,
auto-layout gotchas, section/frame rendering rules, .fig
round-trip testing, Tauri permissions, icon conventions,
ScrubInput cursor handling, culori usage, vueuse hooks.
2026-02-28 23:23:46 +03:00
Danila Poyarkov 199520ae86 Expand AGENTS.md with lessons from all sessions 2026-02-28 23:22:07 +03:00
Danila Poyarkov fdb3fec236 Add AGENTS.md 2026-02-28 23:18:55 +03:00
Danila Poyarkov d33efa1228 Update PLAN.md with Yoga CSS Grid PR tracking details 2026-02-28 23:17:39 +03:00
Danila Poyarkov 70c88dd72b Replace relative imports with @/ alias
Add @/ → src/ alias in vite.config.ts and tsconfig.json.
Rewrite all ../ imports across 32 files to use @/ paths.
Same-directory ./ imports kept as-is.
2026-02-28 23:08:45 +03:00
Danila Poyarkov 6eca2f8432 Consolidate shared types and remove inline duplicates
- Move Vector, Matrix, Rect to src/types.ts as shared primitives
- GradientTransform is now a type alias for Matrix
- Consolidate Window API declarations (File System Access,
  queryLocalFonts) in src/global.d.ts
- Remove duplicate KiwiNodeChange from fig-export.ts and
  clipboard.ts, reuse NodeChange from kiwi/codec.ts
- Add missing fields to codec NodeChange: frameMaskDisabled,
  stackWrap, stackPositioning, stackChildPrimaryGrow, vectorData
- Widen codec Paint.type and Effect.type unions to match scene-graph
- Replace inline Rect definitions across 5 files with shared type
- fig-export.ts fillPaints now constructs typed Paint objects
2026-02-28 23:06:47 +03:00