Commit Graph
100 Commits
Author SHA1 Message Date
Hiren Kangad c78325227f refboard: drive crop drag from DOM pointer events 2026-03-13 11:57:26 +05:30
Hiren Kangad 421ad176d4 refboard: align crop drag handling with transform box 2026-03-13 11:48:13 +05:30
Hiren Kangad e1b97d52f8 refboard: route crop drag events through viewport 2026-03-13 11:41:03 +05:30
Hiren Kangad 8e23c3e884 refboard: hide transform box during crop mode 2026-03-13 11:37:03 +05:30
Hiren Kangad f978153a83 fix: improve text sharpness on zoom with tighter buckets and reliable init
Fix text staying blurry on zoom by ensuring the first setZoomBucket call
always applies (PixiJS v8 defaults to auto-resolution which skipped the
initial bucket=1 assignment), listening on both 'moved' and 'zoomed'
viewport events, and tightening bucket thresholds from 6 to 9 levels.
2026-03-13 11:33:57 +05:30
Hiren Kangad 470693266e refboard: stabilize crop interaction bounds 2026-03-13 11:31:18 +05:30
Hiren Kangad accbe2e0f3 fix(markdown): polish editor UX — side panel, overlay sync, paste segregation
- Move markdown editor from canvas overlay to side panel (70% width)
  for better performance and editing experience
- Add @blocknote/mantine for full BlockNoteView with default UI components
- Fix socket reconnect loop caused by unstable pasteOpts object reference
  (memoize with useMemo)
- Add ResizeObserver to markdown overlay cards for automatic height sync
- Call mdOverlay.refreshAll() on every canvas change so overlays track
  pack/grid/arrange/save operations
- Paste goes to BlockNote editor when contentEditable is focused
- Simplify toolbar: single color picker (accent + auto-derived bg),
  title/name field, width presets S/M/L
- Guard normalize/flip operations to skip markdown and sticky items
- Fix title not updating on card preview (pass name prop, bump revision)
- Fix preview not refreshing after editor save (revision counter + overlay refresh)
2026-03-13 11:21:28 +05:30
Hiren Kangad a7579a9ff5 refboard: add manual board preview refresh button 2026-03-13 11:21:28 +05:30
Hiren Kangad b350951bf1 refboard: merge crop mode polish with markdown editor 2026-03-13 11:21:28 +05:30
Hiren Kangad 04d499b755 fix(markdown): address code review findings — updateItem, height measurement, CSS injection, paste fallback, resize sprite update 2026-03-13 11:21:28 +05:30
Hiren Kangad dfdfd07a1d feat(markdown): add contextual toolbar for background color, accent color, and width presets 2026-03-13 11:21:28 +05:30
Hiren Kangad aa3112a5bc feat(markdown): add paste detection with PasteChoicePopup for text/markdown/image
Simplify Ctrl+V shortcut to only handle recent internal copies, letting
native paste events flow to setupPaste for external clipboard content.
Extend setupPaste with text/HTML detection and popup callbacks. Wire
PasteChoicePopup in Editor.tsx with markdown (turndown HTML-to-MD),
plain text, and image paste choices.
2026-03-13 11:21:28 +05:30
Hiren Kangad 375e7adc4a feat(markdown): add PasteChoicePopup component for paste-as choice 2026-03-13 11:21:28 +05:30
Hiren Kangad 11fc3c6d8e feat(markdown): horizontal-only resize with ResizeConstraint enum in TransformBox 2026-03-13 11:21:28 +05:30
Hiren Kangad b1163c3232 feat(markdown): integrate undo/redo for edit mode and checkbox toggles 2026-03-13 11:21:28 +05:30
Hiren Kangad 6118193f19 feat(markdown): add MARKDOWN tool, toolbar button, and m/6 shortcuts 2026-03-13 11:21:28 +05:30
Hiren Kangad 2d5396ef09 feat(markdown): add double-click → edit mode via SelectionManager callback 2026-03-13 11:21:27 +05:30
Hiren Kangad 02597abc9d fix: suppress canvas shortcuts when contenteditable is focused (BlockNote) 2026-03-13 11:21:27 +05:30
Hiren Kangad 2cede1ae95 feat(markdown): add MarkdownEditView with lazy BlockNote editor + dark theme styles 2026-03-13 11:21:27 +05:30
Hiren Kangad 10135463b1 feat(markdown): wire MarkdownOverlay into canvas lifecycle with React portals 2026-03-13 11:21:27 +05:30
Hiren Kangad ced187bbd3 feat(markdown): add MarkdownReadView component with dark theme and checkbox toggling 2026-03-13 11:21:27 +05:30
Hiren Kangad 0d40b2b973 feat(markdown): add MarkdownOverlay DOM overlay manager 2026-03-13 11:21:27 +05:30
Hiren Kangad de527954da feat(markdown): add MarkdownSprite and register in SceneManager
Creates MarkdownSprite (Graphics-only background rect with header bar)
and wires it into SceneManager._createItem() under case 'markdown'.
2026-03-13 11:21:27 +05:30
Hiren Kangad f989a8111b feat(markdown): add markdownDefaults module with constants and extractTitle 2026-03-13 11:21:27 +05:30
Hiren Kangad 50c34a409f feat(markdown): add MarkdownObject type to scene format 2026-03-13 11:21:27 +05:30
Hiren Kangad cc38adb1d6 chore: add markdown block dependencies (react-markdown, blocknote, turndown) 2026-03-13 11:21:27 +05:30
Hiren Kangad dd1578110b refboard: support rotated image crop editing 2026-03-13 11:21:27 +05:30
Hiren Kangad 9502f89a06 refboard: tighten rotated image hit testing 2026-03-13 11:21:27 +05:30
Hiren Kangad 12410c0098 refboard: stabilize image flip and crop transforms 2026-03-13 11:21:27 +05:30
Hiren Kangad c40304464f chore: add .worktrees/ and .superpowers/ to .gitignore 2026-03-13 11:21:27 +05:30
Hiren Kangad 75a975447b chore: commit pending workspace changes 2026-03-13 08:43:44 +05:30
Hiren Kangad f77ac59e6c fix(sticky): make size presets absolute again 2026-03-13 00:50:21 +05:30
Hiren Kangad 9f69eae308 refine(sticky): reduce preset sizes and remove pan tool shortcuts 2026-03-13 00:46:37 +05:30
Hiren Kangad ce1cc60d67 fix(sticky): restore zoom-aware preset creation and live editing sync 2026-03-13 00:42:32 +05:30
Hiren Kangad 2d05226ddd fix(sticky): fixed-size creation, shared presets module, live resize sync
Fixes three review findings:

1. Sticky creation no longer zoom-adapts — always creates at M preset
   (28px/260w) in world space. Consistent at any zoom level.

2. Preset definitions extracted to stickyPresets.ts (shared domain module).
   TextFormatToolbar and tools.ts both import from there — no more
   canvas→component dependency inversion.

3. TextEditor.onLiveResize callback syncs spatial index and transform
   box as sticky background grows during typing.
2026-03-13 00:31:44 +05:30
Hiren Kangad 706b00cf35 feat(sticky): proportional card width per text size preset
Each S/M/L/XL/XXL preset now sets both fontSize and card width:
S(20px/200w), M(28px/260w), L(36px/320w), XL(48px/400w), XXL(60px/480w).

Creation picks preset based on zoom, toolbar toggle updates both.
Wider size toggle buttons for XL/XXL labels, font name maxWidth 110px.
Backward compatible — existing stickies keep original width.
2026-03-13 00:26:09 +05:30
Hiren Kangad b081ed7169 refactor(sticky): fixed-width card with S/M/L/XL/XXL font presets
Stickies are now fixed-width cards — no resize handles shown.
Text size controlled via 5 presets (10/14/18/24/32px).
On creation, zoom-aware screenToWorld snaps to nearest preset.
Height auto-adjusts from content. Plain text resize unchanged.

Removes: sticky width bake, live reflow drag, min width constraint.
Backward compatible — existing stickies map to nearest preset.
2026-03-13 00:18:41 +05:30
Hiren Kangad 1631185e4f feat(sticky): S/M/L text size toggle, live reflow resize, min width
Sticky UX overhaul — clear separation between card resize and text size:

- S/M/L toggle (12/14/18px) in contextual toolbar for sticky text size
- TransformBox: horizontal-only resize for stickies with live text
  reflow during drag (no more stretch-then-snap)
- Hide vertical handles (tc/bc) for sticky-only selections
- Minimum sticky width of 80px enforced in both drag and bake
- TextEditor: sticky background auto-resizes as user types
- textLimits.ts comment corrected to match actual usage

Plain text unchanged: resize gesture scales fontSize via bake.
Fully backward compatible — existing stickies map to nearest preset.
2026-03-13 00:12:23 +05:30
Hiren Kangad a20cab3fcd refactor: extract TextCore shared text engine + sticky width-only resize
TextCore.ts: shared text rendering with dirty-check caching, zoom-bucket
resolution, and optional word-wrap. TextSprite and StickySprite now
compose TextCore instead of duplicating text logic.

Sticky resize now bakes width only (fontSize stays stable), so dragging
a sticky wider/narrower reflows text with auto-adjusted height — like
a resizable text box.
2026-03-12 23:53:06 +05:30
Hiren Kangad d9c1f4c600 fix(sticky): bake scale into fontSize + width on sticky resize
Sticky text was blurry after scaling because sx/sy stretched the
texture without re-rasterizing. Now bakes fontSize and card width
on drag-end (same as plain text), so StickySprite recomputes layout
at native resolution.
2026-03-12 23:44:43 +05:30
Hiren Kangad eb4591dca0 fix: remove font-size clamp from resize-bake path
Direct manipulation (scaling text up/down) should respect user intent
without artificial caps. Clamps remain on creation defaults only.
2026-03-12 23:38:47 +05:30
Hiren Kangad 150bb91a27 refactor: centralize font-size clamps in textLimits.ts
Single source of truth for font-size constraints (text 6–160, sticky
8–96) used by creation defaults, resize-bake, and any future mutation
paths. Replaces scattered hardcoded min/max values.
2026-03-12 23:35:25 +05:30
Hiren Kangad 1ac727ea60 feat: zoom-bucket text sharpness + remove fontSize toolbar controls
Add TextSharpnessManager for crisp text rendering at all zoom levels:
- Discrete zoom buckets (0.5–3x) avoid texture churn on small zoom changes
- Initial bucket applied to all items on setup and newly created items
  via SceneManager.onItemCreated callback (fixes blurry-on-load)
- Visibility check uses getItemWorldBounds for correct grouped item coords
- TextSprite/StickySprite gain setZoomBucket() for resolution control

Remove fontSize +/- controls from contextual toolbar — font size is now
controlled purely through direct manipulation (resize → bake).
2026-03-12 23:23:30 +05:30
Hiren Kangad 9fac16e6a4 feat: contextual text formatting toolbar + resize fontSize baking
- Remove persistent font-size slider from main toolbar (tools only)
- Lock in zoom-aware creation defaults as named constants
- Split contextual toolbar by object type: text vs sticky
  - Text: fontSize + fontFamily + text color
  - Sticky: fontSize + fontFamily + text color + note fill color
  - Mixed selection: hide toolbar
- Bake scale into fontSize on text resize (reset sx/sy to 1)
- Sticky resize stays layout-driven (no font change)
2026-03-12 22:47:48 +05:30
Hiren Kangad 83e753bb69 perf: remove thumbnail generation from hot autosave path
extract.canvas(viewport) snapshots the entire viewport to a WebGL
texture on the main thread. When viewport dimensions × devicePixelRatio
exceed GPU limits, this causes texImage2D errors and ~1000ms stalls
that block focus, typing, and scroll. Thumbnails can be generated on
idle or server-side instead.
2026-03-12 22:24:59 +05:30
Hiren Kangad 93c7c792db fix(text): use requestAnimationFrame for editor open timing
queueMicrotask fires before the click event settles, causing the
textarea to blur immediately and trigger empty-text cleanup.
requestAnimationFrame waits one paint frame — enough for the pointer
event to fully resolve without the sluggishness of setTimeout(50).
2026-03-12 22:15:20 +05:30
Hiren Kangad d365049470 perf(text): remove 50ms editor delay + zoom-aware creation defaults
Replace setTimeout(50) with queueMicrotask for TEXT tool editor open
(matching STICKY tool). Add screenToWorld helper for zoom-aware font
sizes and sticky card dimensions. Remove unused Text/TextStyle imports.
2026-03-12 22:09:12 +05:30
Hiren Kangad 5158938eb3 refactor(text): update Editor TextFormatToolbar to use TextSprite API 2026-03-12 22:09:12 +05:30
Hiren Kangad 2efcd0e622 refactor(text): update TextEditor to use TextSprite API 2026-03-12 22:09:12 +05:30
Hiren Kangad 2e24500746 feat(text): wire TextSprite into SceneManager, eliminate inline text mutation 2026-03-12 22:09:12 +05:30
Hiren Kangad d85de4ba9e feat(text): add TextSprite with dirty-check caching 2026-03-12 22:09:12 +05:30
Hiren Kangad f12b75395a perf(sticky): remove creation delay + add dirty-checking to StickySprite
- Replace 50ms setTimeout with queueMicrotask for editor open
- Skip updateFromData() entirely when no inputs changed
- Cache background redraw inputs, skip Graphics clear+redraw when shape unchanged
- Separate text-only, style-only, and bg-only update paths
2026-03-12 22:09:12 +05:30
Hiren Kangad 565b7c6c19 fix(sticky): prevent crash on editor re-entry during sticky lifecycle
Clear editor state before scene callbacks to prevent blur timer or
startEditing re-entry from calling updateFromData on a destroyed sprite.
Guard all display object access with .destroyed checks.
2026-03-12 22:09:12 +05:30
Hiren Kangad a191f74c31 fix(sticky): wire fontSize from toolbar slider + update shortcuts help 2026-03-12 22:09:12 +05:30
Hiren Kangad a7faf004ae feat(sticky): enable double-click editing for sticky notes 2026-03-12 22:09:12 +05:30
Hiren Kangad e183e4b9d2 feat(sticky): extend TextEditor to support sticky note editing 2026-03-12 22:09:12 +05:30
Hiren Kangad 6eb78a2608 feat(sticky): add sticky note button to toolbar 2026-03-12 22:09:12 +05:30
Hiren Kangad 17680a4d8f feat(sticky): add STICKY tool type with click-to-place handler 2026-03-12 22:09:12 +05:30
Hiren Kangad 9fc62dbdc7 feat(sticky): wire StickySprite into SceneManager create/update 2026-03-12 22:09:12 +05:30
Hiren Kangad 92f6200f4b feat(sticky): add StickySprite renderer 2026-03-12 22:09:12 +05:30
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