Commit Graph
165 Commits
Author SHA1 Message Date
Hiren Kangad 6ee6060600 feat(sticky): add StickyObject to scene format schema 2026-03-12 22:09:12 +05:30
Hiren Kangad 69fcab717c fix(review): add recursion depth guard to group child resolution
Prevents stack overflow from corrupted data with circular group references.
2026-03-12 22:09:12 +05:30
Hiren Kangad ecd4408d8a feat(review): group-aware comment targeting — resolve clicks to stable children
New reviewTargeting.ts provides resolveReviewTargetAtPoint() which:
- descends into groups to find the deepest commentable child under the click
- walks children in z-order (front-to-back) for correct visual stacking
- handles nested groups recursively
- returns null if no valid child exists under the point

Editor.tsx review click handler now delegates to the resolver instead of
raw queryRegion + topmost-z pick. Defensive guard rejects group objectIds
before draft creation.

Existing group-anchored threads continue to render and open normally.
2026-03-12 22:09:12 +05:30
Hiren Kangad 1134ddb5c9 refactor(review): use shared getPointAnchorWorld in jump-to-object 2026-03-12 22:09:12 +05:30
Hiren Kangad 40153751f6 refactor(review): remove panel draft composer, move suppression to Editor 2026-03-12 22:09:12 +05:30
Hiren Kangad 54a7557233 feat(review): wire InlineCommentComposer into Editor with anchored positioning 2026-03-12 22:09:12 +05:30
Hiren Kangad e5cf3d3632 feat(review): add useAnchoredOverlayPosition hook for world→screen conversion 2026-03-12 22:09:12 +05:30
Hiren Kangad cf879b34d5 feat(review): add draftCommentText state and double-submit guard 2026-03-12 22:09:12 +05:30
Hiren Kangad 6e1d9df7f0 feat(review): add InlineCommentComposer presentational component 2026-03-12 22:09:12 +05:30
Hiren Kangad 7aa55b66f0 refactor(review): extract shared anchor math into reviewAnchors.ts 2026-03-12 22:09:12 +05:30
Hiren Kangad 318b7358e1 fix(review): address code review findings — tool conflicts, dual inputs, pin animation
1. TEXT and ERASER tool clicks now suppressed during review mode via
   reviewMode flag on ToolContext (read from ref for live value)
2. Object-comment input hidden when draftPin is active — only one
   creation input visible at a time
3. PinOverlay entrance animation removed — focus styling (scale 1.15x,
   alpha changes) no longer fights with fade-in animation loop
2026-03-12 22:09:12 +05:30
Hiren Kangad 16a677dc89 fix: remove entrance animation so dropped items are instantly draggable
Items were fading in from alpha 0 via spring animation, preventing
immediate interaction. Removed the entrance animation entirely — items
now appear at full opacity and are movable on first frame.
2026-03-12 22:09:12 +05:30
Hiren Kangad 5f60cc826a fix(review): gate pin visibility on review mode per spec 2026-03-12 22:09:12 +05:30
Hiren Kangad c6b2c325a5 feat(review): wire FeedbackPanel with draft pin, expandRequest, and point-aware jump-to-object
- Pass expandRequest, focusedThreadId, draftPin, onCreatePointThread to FeedbackPanel
- Lift thread detail state via onThreadDetailChange → setOpenThreadDetailId
- Jump-to-object centers on pin location for point-pinned threads
- Pan-only by default; zoom in if object appears <50px on screen
- Show toast when jumping to deleted object
2026-03-12 22:09:12 +05:30
Hiren Kangad a1efdedb2a feat(review): add focused thread highlight and headerSlot to ThreadList
- FOCUSED_BG and FOCUSED_BORDER tokens in feedbackStyles
- headerSlot prop for draft pin comment input above thread list
- focusedThreadId prop highlights active thread row with accent border
- ThreadListItem gains focused prop with left border + subtle background
2026-03-12 22:09:12 +05:30
Hiren Kangad 9bd2810f5a feat(review): update FeedbackPanel with expandRequest, draftPin input, and lifted thread detail state
- Replace expandThreadId with expandRequest pulse pattern (seq counter)
- Add draftPin, onCreatePointThread, focusedThreadId, onThreadDetailChange props
- Lift expandedThreadId changes to parent via onThreadDetailChange callback
- Draft pin comment input section shown above thread list
- Thread detail collapse signal via expandRequest with null threadId
- Pass thread object to onJumpToObject for point-aware navigation
2026-03-12 22:09:12 +05:30
Hiren Kangad 3b15166c32 feat(review): wire PinOverlay ghost/focus from Editor state 2026-03-12 22:09:12 +05:30
Hiren Kangad f05a2c8c34 feat(review): add Escape handler and point thread submit
- Escape priority: draft pin → thread detail collapse → review mode exit
- Draft/detail clearing works even when focus is in input field
- Review mode exit respects input/textarea focus context
- handleCreatePointThread: REST POST with auto-focus on success
- Clear draft/focus/expandRequest when review mode exits
2026-03-12 22:09:12 +05:30
Hiren Kangad 08fe062b92 feat(review): add review-mode pointer handlers for pin click + draft placement
- pointerdown records position, pointermove detects drag (>5px)
- pointerup fires review logic only if no drag occurred
- Hit-test priority: existing pin → scene object → empty canvas
- 10px screen-space click radius for forgiving object detection
- Permission gating: viewer role cannot place draft pins
2026-03-12 22:09:12 +05:30
Hiren Kangad e854ad59e2 feat(review): add ghost pin rendering and focused pin visuals to PinOverlay
- setGhostPin(): renders white semi-transparent "+" bubble at relative coords
- setFocusedThread(): scales focused pin 1.15x, dims others to 0.7 alpha
- Ghost and focus visuals reapplied on refresh()
- Ghost container cleaned up on destroy()
2026-03-12 22:09:12 +05:30
Hiren Kangad 358d30beb3 refactor(review): split focusedThreadId into persistent focus + expandRequest pulse
- Add DraftPin interface (exported for FeedbackPanel)
- Add expandRequest state with seq counter for re-trigger
- Add draftPin, openThreadDetailId, expandSeqRef state
- Remove old pointerdown pin-click handler with requestAnimationFrame clear
2026-03-12 22:09:12 +05:30
Hiren Kangad 2d1c06cec9 feat(review): make review mode independent of tool selection
- Tool button clicks no longer exit review mode
- Review button moved outside tool group with divider separator
- Tool active state no longer dimmed during review mode
- Updated hint text for review mode
2026-03-12 22:09:12 +05:30
Hiren Kangad d3f7c45dda fix(crop): ensure overlay renders on top of scene items
CropOverlay was hidden behind images because SceneManager._applyZOrder()
reorders viewport children by z-index, pushing non-scene children behind.
Now start() moves itself to the top of the viewport child list.
2026-03-11 18:17:43 +05:30
Hiren Kangad 3413d58f83 fix(crop): return cropOverlayRef instead of .current to fix null timing
The cropOverlay was created asynchronously inside a polling interval but
returned synchronously as .current (null). By returning the ref itself,
Editor.tsx reads .current at call time when the overlay actually exists.
2026-03-11 17:39:34 +05:30
Hiren Kangad 01f22c9d43 fix: address code review findings from crop/text commit
- CropOverlay: register move/up handlers dynamically on drag start (single
  handler instead of 8x per-handle), remove on drag end. Override destroy()
  to call _cleanup() preventing keyboard listener leaks.
- Text format toolbar: re-measure text bounds (w/h) after fontSize/fontFamily
  changes, update spatial index and transform box.
- TextEditor: add clearText() method; tools.ts uses it instead of fragile
  document.querySelector('textarea').
- SceneManager._updateItem: apply crop mask on remote sync for image items.
- useCanvasSetup: stop TextEditor on unmount to prevent orphaned textarea.
- Double-click zoom: use item.data dimensions instead of getBounds() (which
  includes shadow offset).
2026-03-11 17:28:52 +05:30
Hiren Kangad 58da934ec1 feat: text tool UX, image crop, text format toolbar, double-click focus
- Text tool: click-to-place immediately opens inline editor, auto-switches
  back to select tool. Empty text cleanup on save.
- Crop: select image + press C (or right-click > Crop) to enter crop mode.
  8 drag handles with rule-of-thirds grid, dimmed outside area.
  Enter confirms, Escape cancels. Non-destructive (stored as normalized rect).
- Text format toolbar: appears when text items selected, with font size +/-,
  font family dropdown, and color picker with presets.
- Double-click image: zoom-to-fit (PureRef-style focus)
- Entrance animation: replaced bounce with simple fade-in (no delay before
  items become interactive)
- TextEditor: allow empty text (caller handles cleanup)
2026-03-11 17:22:25 +05:30
Hiren Kangad 4ed475c62e fix(canvas): zoom-stable handles and group-scale multi-selection
- Transform handles counter-scale by 1/zoom so they stay constant screen size
- Selection border stroke also counter-scales
- Multi-select resize scales items as a unit from bounding box anchor
- Redraw transform box on viewport zoom/pan via 'moved' listener
2026-03-11 16:59:10 +05:30
Hiren 155dc6ac01 - Add mattermost_id column to RefBoard users for OAuth linking
- Admin panel shows per-user RefBoard login status (Active vs Not signed in)
- Add RefBoard config: REFBOARD_PUBLIC_URL, REFBOARD_INTERNAL_URL, REFBOARD_API_KEY
- Update .env.example with RefBoard section
2026-03-11 13:40:00 +05:30
Hiren Kangad 6518ed6763 fix: hardening pass — permissions, socket reconnect, canvas setup, arrangements
- Fix 403 on save for public collection viewers (return role in GET board response)
- Add read-only status indicator (StatusBar + StatusIndicator)
- Fix beforeunload save to use fetch+keepalive with auth header
- Socket reconnect now rejoins board room automatically
- Canvas setup uses polling instead of brittle 200ms timer
- Fix double user:left on disconnect (use disconnecting event, snapshot rooms)
- Thread + comment creation wrapped in db.transaction
- Prevent owner downgrade via addCollectionMember (check existing member)
- Bound redirect depth in downloadImage to 5
- Arrangement operations anchor to bounding box top-left (no drift)
- Distribute H/V also anchor to top-left
- Fix annotations fetch to use axios api instance (401 interceptor)
- Replace require() with static import in shortcut-definitions
2026-03-11 08:08:21 +05:30
Hiren Kangad fc2d9df741 feat: annotation UX overhaul, animated packing, toolbar redesign
- PinOverlay: zero-allocation rendering with in-place Graphics/Text updates,
  fix WebGL texture crash (addressModeU null) from orphaned Text objects
- Pins always visible on media, track during drag via updatePositions() fast path
- Replace eraser with Review mode in toolbar tool group (orange gradient)
- Add tool instruction hints bar below toolbar (context-sensitive per tool)
- Feedback panel: glassmorphism, improved ThreadDetail/CommentItem/ThreadListItem
- Timestamps: relative with "ago" suffix, full date on hover tooltip
- De-emphasize pin numbers, show status text in thread list
- Animated arrangement transitions (easeOutCubic, 320ms) for pack/grid/row/column
- Disable snap guides (user requested)
- Paste now selects newly created items
- Remove eraser keyboard shortcuts, remap tool shortcuts
- Orphaned thread cleanup: safe destroy check for deleted media pins
2026-03-11 01:53:56 +05:30
Hiren Kangad ed8424d9a1 feat(annotations): Figma-style UI overhaul
- Redesign PinOverlay: teardrop pins with author initials, colors, numbers
- Split FeedbackPanel into modular components:
  - feedback/FeedbackPanel.tsx (orchestrator)
  - feedback/ThreadList.tsx (list view with filters)
  - feedback/ThreadListItem.tsx (individual thread row)
  - feedback/ThreadDetail.tsx (expanded thread with comments)
  - feedback/CommentItem.tsx (single comment)
  - feedback/CommentInput.tsx (reusable input with Enter/Shift+Enter)
  - feedback/feedbackStyles.ts (shared design tokens)
- Add status colors: red (open), green (resolved)
- Add relative timestamps (2h ago, 3d, etc.)
- Wire pin clicks to expand threads in panel
- Pins follow media transforms in real-time
- Add delete thread with inline confirmation
- Register review mode shortcut (. key)
- Better empty states and filter bar (Open/Resolved/All/Mine)
2026-03-11 01:08:05 +05:30
Hiren Kangad f24414e25c refactor: remove voting system, add author colors + pin numbering
- Remove votes backend (route, db functions, server mount)
- Remove votes frontend (store, socket, PinOverlay badges, FeedbackPanel UI)
- Add authorColors utility (8-color palette, deterministic hash)
- Add getPinNumber() to annotationStore
- Fix JWT to include username/display_name
- Add resolveAuthorName DB fallback for old tokens
2026-03-11 00:54:48 +05:30
Hiren Kangad f7a39a1726 fix(annotations): address code review findings (1-8, 10)
1. toggleVote wrapped in transaction (race condition fix)
2. FeedbackPanel fetch calls now surface errors via onError/toast
3. Extracted resolveBoard/hasCollectionRole to shared board-access.js
4. AnnotationStore uses monotonic version counter for snapshots
5. PinOverlay uses object pool instead of destroy/recreate on refresh
6. canvasObjects prop memoized with useMemo
7. PinOverlay store subscription cleaned up on unmount
8. Comment content capped at 5000 chars (backend validation)
10. anchor_type validated to 'object' or 'point'
2026-03-10 21:16:08 +05:30
Hiren Kangad 303124f518 feat(annotations): comments, threads, voting system with review mode
Backend:
- comment_threads + comments + object_votes tables with indexes
- Thread/comment CRUD endpoints with socket broadcast
- Vote toggle endpoint with socket broadcast

Frontend:
- AnnotationStore for reactive thread/comment/vote state
- FeedbackPanel with thread list, expanded view, replies, filtering
- Vote toggle buttons in panel
- PinOverlay for canvas pin markers + vote badges
- Review Mode toggle in toolbar (comment bubble icon)
- Jump-to-object from thread view
- Orphaned thread detection for deleted objects
- New comment creation from panel when object selected
- Socket event wiring for real-time sync
2026-03-10 21:04:38 +05:30
Hiren Kangad b6b615957b feat: show queued upload count in panel header 2026-03-10 20:51:25 +05:30
Hiren Kangad 9dc0815a12 feat: upload queue with queued status and cancel button
Multi-file drops now show queued/uploading distinction. Jobs start
as 'queued' and transition to 'uploading' when the HTTP request
begins. Queued items show a cancel button in the upload panel.
Cancelled jobs are skipped in the sequential upload loop.
2026-03-10 20:42:17 +05:30
Hiren Kangad 9af4051b67 fix: release GIF ref on sprite destroy to prevent gifCache leak
Override destroy() in AnimatedGifSprite to call releaseGif() when
a loaded GIF item is deleted from the board. Without this, the
gifCache refCount stays elevated until full canvas teardown.
2026-03-10 20:09:24 +05:30
Hiren Kangad 1f5ab883ce fix: file drop uses extension fallback when MIME type is empty
Browsers sometimes report empty or non-standard MIME types for
dragged files. Now checks file extension as fallback when MIME
doesn't match the allowlist. Only shows rejection feedback for
files that are recognizably media but unsupported.
2026-03-10 20:03:18 +05:30
Hiren Kangad bc2c5fb974 fix: reject unsupported media URL drops with feedback
URLs with media-like extensions (.avif, .tiff, .heic, .bmp, etc.)
that aren't in our supported set now show a failed entry in the
upload panel. Plain webpage URLs without media extensions are still
silently ignored (not our concern).
2026-03-10 19:49:05 +05:30
Hiren Kangad 065762d273 fix: synthesize readable filename for rejected clipboard pastes
Rejected clipboard items now show as "pasted-item.tiff" instead
of raw MIME subtype, so the upload panel entry is readable.
2026-03-10 19:46:17 +05:30
Hiren Kangad 45891a278c fix: reject unsupported file types with explicit feedback
Replace broad image/*/video/* prefix matching with explicit MIME
allowlist matching backend. Unsupported files (BMP, TIFF, AVIF,
HEIC, PDF, etc.) now show as failed rows in the upload panel
with a clear "Unsupported format: .ext" message instead of being
silently ignored. Applies to both drag-drop and clipboard paste.
2026-03-10 19:44:04 +05:30
Hiren Kangad e45fce3e67 fix: harden GIF support — ref-counted sources, defensive detection, play budget
1. Move GIF loading behind TextureManager.loadGif/releaseGif with
   ref-counting (fixes shared-source invalidation on duplicate GIFs)
2. Strip query strings/fragments before .gif extension check
3. GIFs now load paused — separate MAX_PLAYING_GIFS=8 budget in
   culling system, only nearest N animate (others show static frame)
2026-03-10 19:41:50 +05:30
Hiren Kangad 5a64708bd4 feat: add animated GIF support using PixiJS built-in GifSprite
Register GifAsset extension for .gif loading, create AnimatedGifSprite
wrapper with lazy load/unload matching ImageSprite pattern, and detect
.gif assets in SceneManager to use animated rendering. GIFs stored as
type 'image' in scene format — no schema changes needed.
2026-03-10 19:26:41 +05:30
Hiren Kangad 51e53a323b feat: add export-as-image with format, scale, and background options
New ExportDialog component with scope (selection/all), filename,
format (PNG/JPEG/WebP), quality slider, scale multiplier, and
background color picker. Wired into Toolbar and Editor.
2026-03-10 19:16:05 +05:30
Hiren Kangad c126885e53 fix: upload manager — URL imports, active count, board reset
1. URL imports now participate in upload manager pipeline:
   addUrlJob() creates a job row, uploadComplete/setFailed called
   on success/failure — consistent with local file uploads

2. activeCount only counts uploading/processing jobs, not failed.
   Failed jobs no longer inflate the "Uploads (n)" header count.

3. uploadManager.clear() called on boardId change so stale jobs
   from a previous board don't linger in the next board's panel.
2026-03-10 19:10:14 +05:30
Hiren Kangad 784554e40d fix: video size error handling — validation, timeouts, failure surfacing
Backend:
- Increase ffmpeg/ffprobe timeouts from 15s to 60s for large videos
- Classify processing errors (timeout, OOM, corrupt) with user-facing messages
- Emit failure details via media:job:update socket event (error field)
- Bump refboard container memory 512M → 1G

Frontend:
- Client-side file size validation (200MB) before upload starts
- Oversized files show immediate error in upload manager, skip upload
- Handle media:job:update status='failed' — surface error in upload panel
- Add processingFailed() to UploadManager for video processing errors
2026-03-10 19:04:42 +05:30
Hiren Kangad 38fb3edf14 feat: add Upload Manager with progress tracking and status panel
- UploadManager store: tracks jobs through uploading → processing → done/failed
- UploadPanel component: floating bottom-left widget showing upload progress,
  file names, sizes, status with auto-dismiss for completed items
- axios onUploadProgress wired for real-time upload percentage
- Video jobs transition to "processing" after upload, then "done" when
  media:job:update socket event arrives (reuses existing pipeline)
- Failed uploads show error message from server response
- Clear button to dismiss finished/failed items
2026-03-10 18:56:25 +05:30
Hiren Kangad 26527006a6 feat: auto-select imported items after upload
Pass SelectionManager to setupDragDrop and setupPaste. After each
upload completes, the newly created scene items are selected:
- Single file/URL: selectOnly
- Multi-file drop/paste: select all new items
2026-03-10 18:51:28 +05:30
Hiren Kangad 3c6a5bf29e docs: add team feedback tracker with prioritized issues
5 items: auto-select after import, upload manager, video size handling,
export as image, GIF support (product decision pending).
2026-03-10 18:46:40 +05:30
Hiren Kangad d10010c4d2 docs: add annotations & voting implementation plan
14-task phased plan covering backend (DB schema, REST endpoints, socket events),
frontend state management, FeedbackPanel, canvas PinOverlay, voting, and polish.
2026-03-10 17:28:05 +05:30