Danila Poyarkov
5e209eca1c
Fix trackpad pinch-to-zoom on Safari macOS
2026-03-01 22:07:47 +03:00
Danila Poyarkov
13a95bf517
Release v0.2.1
2026-03-01 21:25:52 +03:00
Danila Poyarkov
091861f32a
Update changelog for v0.2.1
2026-03-01 21:17:07 +03:00
Danila Poyarkov
3e81c15689
Center empty chat placeholder text
2026-03-01 21:15:32 +03:00
Danila Poyarkov
0ba1f00e74
Hide panels by default on mobile
2026-03-01 21:13:51 +03:00
Danila Poyarkov
d4b8a2a310
Always show local user avatar in collab header
2026-03-01 21:12:22 +03:00
Danila Poyarkov
ab658ac33f
Add ⌘\ to toggle side panels, header with logo and editable filename
...
Header row with app icon, double-click-to-rename document name, and
sidebar toggle button. Shows in both Tauri and browser.
When panels are hidden, a floating bar on the canvas provides the same
controls to restore the UI.
2026-03-01 21:07:02 +03:00
Danila Poyarkov
575914b194
Add touch support for pan and pinch-zoom on iOS
2026-03-01 20:13:21 +03:00
Danila Poyarkov
c3844db2bd
Stub shiki to eliminate 9MB of unused language grammars
...
vue-stream-markdown bundles all 359 shiki grammar chunks by default.
We only need plain text code blocks in chat. Alias shiki to an empty
module — build drops from 20MB/377 files to 11MB/103 files.
2026-03-01 20:05:52 +03:00
Danila Poyarkov
e8b685ad0a
Update screenshot
2026-03-01 18:44:52 +03:00
Danila Poyarkov
deac6e6019
Release v0.2.0
2026-03-01 18:43:09 +03:00
Danila Poyarkov
6e4332a6ba
Release v0.2.0
2026-03-01 18:27:12 +03:00
Danila Poyarkov
56aa194785
Toast notifications via Reka UI Toast
...
- AppToast component: top-center blue pill (info), red pill (errors)
- Global error handler catches window.error + unhandledrejection
- Link copied toast on share and copy link button
- use-toast composable: toast.show(message, variant)
2026-03-01 18:25:04 +03:00
Danila Poyarkov
f90e1400b7
P2P collaboration: cursors, follow mode, cleanup
...
- Broadcast cursor position from canvas mouse move via awareness
- Broadcast selection changes via reactive watcher on selectedIds
- Follow mode: click peer avatar to track their viewport (pan + zoom)
- Zoom broadcast via watcher on store.state.zoom, not just mouse move
- Figma-style cursor arrows: colored fill, white border, name pill
- Stale cursor cleanup: removeAwarenessStates on peer leave
- MQTT signaling (replace Nostr), STUN + TURN ICE servers
- Collab constants extracted to src/constants.ts
- crypto.getRandomValues() replaces Math.random() everywhere
- Provide/inject for collab composable (COLLAB_KEY)
- Update README (collab section, tech stack), CHANGELOG, AGENTS.md
- AGENTS.md: release process, CI workflows, documentation guidelines
2026-03-01 18:11:07 +03:00
Danila Poyarkov
7a2694b6bc
Emphasize no subscription, bring your own API key
2026-03-01 17:26:30 +03:00
Danila Poyarkov
3427b7fd3c
Replace Durable Object relay with Trystero P2P + y-indexeddb
...
- Remove packages/collab/ (CF Worker + Durable Object server)
- Replace WebSocket relay with Trystero (serverless WebRTC P2P)
- Peers connect via Nostr relays for signaling (free, public)
- All sync traffic flows directly peer-to-peer
- Automatic chunking for large Yjs updates
- Add y-indexeddb for local persistence
- Each client persists Y.Doc to IndexedDB
- Room survives browser refresh, loads instantly
- Yjs sync protocol over Trystero actions:
- yjs-update: incremental doc updates
- sync-step1/sync-reply: full state sync on peer join
- awareness: cursor, selection, presence
- Zero server cost at any scale
2026-03-01 17:25:46 +03:00
Danila Poyarkov
8695ba6d1b
Emphasize no subscription, bring your own API key
2026-03-01 17:24:54 +03:00
Danila Poyarkov
76134ccc1a
Collab UI polish, vue-router, extract HsvColorArea
...
- Move CollabPanel to right panel header (like Figma)
- /share/:roomId shows join dialog with name input before connecting
- Add vue-router with / and /share/:roomId routes
- Extract EditorView from App.vue
- Extract HsvColorArea component from ColorPicker + FillPicker
(eliminates ~160 lines of duplicated HSV logic)
- Extract syncNodePropsToYMap in use-collab.ts
- Register Yjs update listener before opening WebSocket (fix race)
- Scrollable AppMenu without scrollbar (scrollbar-none utility)
- Overflow-x hidden on LayersPanel
- Gitignore .wrangler build artifacts
- SPA catch-all redirect for Cloudflare Pages
2026-03-01 17:15:08 +03:00
Danila Poyarkov
2add3ac4c0
Real-time collaboration with Yjs CRDT over Cloudflare Durable Objects
...
- Cloudflare Worker + Durable Object server (packages/collab/)
- One DO per room with WebSocket Hibernation for near-zero idle cost
- Yjs sync protocol for document state (CRDT merge)
- Yjs awareness protocol for cursors, selection, presence
- Persists doc state to DO storage
- CORS headers for cross-origin WebSocket upgrade
- Client composable (src/composables/use-collab.ts)
- Y.Doc ↔ SceneGraph bidirectional binding
- Awareness: broadcasts cursor position, selection, name, color
- Auto-join from /share/:roomId URL
- Suppresses echo loops between Yjs and graph updates
- CollabPanel component (src/components/CollabPanel.vue)
- Share button generates room link, copies to clipboard
- Join flow: paste link or room ID
- Avatar stack with colored initials and tooltips
- Name input persisted to localStorage
- Remote cursor rendering in CanvasKit
- Colored arrow cursor + name label pill per remote peer
- Remote selection outlines in peer's color
- Only shows cursors on the same page
- SPA redirect for /share/* routes (Cloudflare Pages _redirects)
2026-03-01 16:44:37 +03:00
Danila Poyarkov
9f8242bce5
Add download links to README, docs landing, and getting started
2026-03-01 16:32:45 +03:00
Danila Poyarkov
fb8891c431
Fix case-sensitive filename: ApiKeySetup.vue → APIKeySetup.vue
2026-03-01 16:29:54 +03:00
Danila Poyarkov
0acfd50a67
Add npm publish with trusted publishing to release workflow
2026-03-01 16:27:41 +03:00
Danila Poyarkov
1e873d64b1
Add repository field and provenance to package configs
2026-03-01 16:23:30 +03:00
Danila Poyarkov
c4e6514ea2
Update repo references to open-pencil/open-pencil
2026-03-01 16:18:11 +03:00
Danila Poyarkov
74d4b6862e
Use app favicon for docs site (symlink to public/favicon-32.png)
2026-03-01 16:14:06 +03:00
Danila Poyarkov
baee65e599
Add CHANGELOG.md, update build workflow for v0.1.0-alpha
...
- Add Linux x64 build target
- Add patchelf for Linux AppImage builds
- Release body links to CHANGELOG.md
- Release name includes 'OpenPencil' prefix
2026-03-01 16:09:25 +03:00
Danila Poyarkov
515c8125ed
Add app and docs links to README
2026-03-01 16:03:25 +03:00
Danila Poyarkov
10848d8319
Add app.openpencil.dev links to docs
2026-03-01 16:02:08 +03:00
Danila Poyarkov
268c4029e9
Add CI workflow for app.openpencil.dev deploy
2026-03-01 16:00:19 +03:00
Danila Poyarkov
0f0ca8db4b
Update screenshot, remove duplicate from repo root
...
Single screenshot lives in packages/docs/public/. README references it
from there. Custom VitePress layout places it between hero and features.
2026-03-01 15:57:01 +03:00
Danila Poyarkov
503a561753
Symlink favicons from desktop/icons, add all sizes for Safari/Chrome
...
.ico (any), 32px PNG, 128px PNG, apple-touch-icon (512px).
Symlinks instead of copies so icons stay in sync.
2026-03-01 15:46:31 +03:00
Danila Poyarkov
f8f85970e1
Add favicon, persist banner dismissal in localStorage
2026-03-01 15:44:25 +03:00
Danila Poyarkov
c8af1c35e7
Improve Save for browsers without File System Access API
...
- Save As prompts for filename via prompt() before downloading
- Save reuses the filename from last Save As (no repeated prompts)
- Banner in Safari/Firefox explaining the limitation with Chrome link
- Banner is dismissible
2026-03-01 15:41:27 +03:00
Danila Poyarkov
c886daded2
Fix .fig export: map COMPONENT/COMPONENT_SET to SYMBOL (Kiwi enum)
...
The Kiwi binary schema uses SYMBOL (15) for components, not COMPONENT.
Import already mapped SYMBOL→COMPONENT; export now reverses it.
2026-03-01 15:35:20 +03:00
Danila Poyarkov
1e1feb9cb0
Merge branch 'add-vitepress-docs'
2026-03-01 15:32:45 +03:00
Danila Poyarkov
22f8599408
Fix Save in Safari: append <a> to DOM, defer revokeObjectURL
...
Safari requires the <a> element to be in the document for .click()
downloads to work. Also defers URL.revokeObjectURL by 100ms so the
browser has time to start the download. Extracted shared downloadBlob
helper used by both file save and image export fallbacks.
2026-03-01 15:31:56 +03:00
Danila Poyarkov
ca20bfb2ac
Fix layers-panel tests: match current demo shapes (Components, App Preview)
2026-03-01 15:28:13 +03:00
Danila Poyarkov
20290f61b5
Add integration tests for app menu and autosave
...
9 app-menu tests: menu visibility, all 6 submenus content, Undo/Duplicate/
Zoom to fit via menu actions.
2 autosave tests: write triggers after scene change with mock file handle,
no write without file handle.
2026-03-01 15:26:10 +03:00
Danila Poyarkov
90e47e07ab
Add autosave: debounced write 3s after last scene change
...
Triggers only when a file handle (browser File System Access API) or
file path (Tauri) exists. Tracks savedVersion to skip redundant writes.
Silent failure — user can always save manually.
2026-03-01 15:17:03 +03:00
Danila Poyarkov
96d8ab5789
Move docs to packages/docs, add Cloudflare Pages deploy
...
- Move docs/ → packages/docs/ as @open-pencil/docs workspace package
- Add screenshot.png as hero image on landing page
- Add GitHub Actions workflow for Cloudflare Pages deploy (openpencil.dev)
- Remove vitepress from root devDependencies
- Root docs:* scripts delegate via bun --filter
2026-03-01 15:16:16 +03:00
Danila Poyarkov
7809fb60f9
Add app menu bar for browser mode (File, Edit, View, Object, Text, Arrange)
...
Reka-ui Menubar in the left panel header, hidden when running in Tauri
(native menu handles it). Includes Open, Save, Save As, Export, Undo/Redo,
Zoom, Group/Ungroup, Component creation, Bring to front/Send to back,
Auto layout, Text formatting, Alignment.
2026-03-01 15:09:08 +03:00
Danila Poyarkov
3849872b32
Unify tool definitions: define once, adapt for AI/CLI/MCP
...
Move tool logic to @open-pencil/core/tools/schema.ts as framework-agnostic
ToolDef objects. AI adapter generates valibot schemas + Vercel AI tool()
wrappers automatically.
26 tools (was 10): create_shape, render (JSX), set_fill, set_stroke,
set_effects, set_layout, set_constraints, update_node, delete, clone,
rename, reparent, group/ungroup, find_nodes, get_node, get_page_tree,
get_selection, select, list_pages, switch_page, list_variables,
list_collections, create_component, create_instance, eval.
src/ai/tools.ts: 269→28 lines (adapter only).
Tests for all 3 interfaces:
- 26 core tool tests (FigmaAPI directly)
- 11 AI adapter tests (valibot + Vercel AI SDK tool())
- 12 CLI integration tests (eval command on .fig fixture)
323 total, all passing.
2026-03-01 15:02:05 +03:00
Anton A S
5b7de9fe99
Sync docs with README motivation: landing, features, comparison, user guide
2026-03-01 13:58:54 +03:00
Danila Poyarkov
f22bd2e133
Merge eval-command: Figma Plugin API + eval CLI command
...
FigmaAPI wraps SceneGraph with Figma-compatible interface (Symbol-hidden internals).
CLI eval command runs Plugin API scripts headless on .fig files.
106 new tests (60 figma-api + 17 cli integration + 29 gap-filling).
2026-03-01 13:55:39 +03:00
Danila Poyarkov
123dbe491b
Extract DEFAULT_STROKE_MITER_LIMIT constant
2026-03-01 13:53:25 +03:00
Anton A S
2b5f6d58c0
Merge remote-tracking branch 'origin/master' into add-vitepress-docs
2026-03-01 13:51:56 +03:00
Anton A S
5eb1b30390
Separate User Guide sidebar scope, remove redundant shortcut notes
2026-03-01 13:51:31 +03:00
Danila Poyarkov
3e9cd236e9
Fix bogus fig-import mappings: expanded and strokeMiterLimit
...
expanded: was mapped from frameMaskDisabled (unrelated Kiwi field),
now defaults to true. strokeMiterLimit: was mapped from speculative
strokeMiterAngle field that doesn't exist in Kiwi codec, now uses
hardcoded default 4.
2026-03-01 13:48:48 +03:00
Danila Poyarkov
48219af2fb
Fill Figma Plugin API gaps: 29 new tests, 30+ new properties and methods
...
SceneNode: add minWidth/maxWidth/minHeight/maxHeight, isMask, maskType,
counterAxisAlignContent, itemReverseZIndex, strokesIncludedInLayout,
expanded, textTruncation, autoRename, strokeMiterLimit. All with defaults
and .fig import mapping.
FigmaNodeProxy: add clone(), strokeCap, strokeJoin, strokeMiterLimit,
strokeTopWeight/BottomWeight/LeftWeight/RightWeight, minWidth/maxWidth/
minHeight/maxHeight, isMask, maskType, expanded, textTruncation,
autoRename, insertCharacters, deleteCharacters, primaryAxisSizingMode,
counterAxisSizingMode, counterAxisAlignContent, itemReverseZIndex,
strokesIncludedInLayout, layoutAlign, findAllWithCriteria.
FigmaAPI: add createComponentFromNode, getVariableById, getLocalVariables,
getLocalVariableCollections, getVariableCollectionById.
Fix layoutSizingHorizontal/Vertical for self-framing auto-layout nodes
with no auto-layout parent — now checks the node's own layoutMode.
2026-03-01 13:35:02 +03:00
Danila Poyarkov
2a3e848e3b
Deduplicate AI tools: use FigmaAPI.toJSON() and core parseColor
...
- get_page_tree: replace hand-rolled nodeToJSON with FigmaAPI.wrapNode().toJSON()
- get_selection: same — use FigmaAPI proxy serialization
- resolveColor: delegate hex parsing to core parseColor() (culori) instead of manual parseInt
2026-03-01 13:20:01 +03:00