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
This commit is contained in:
Hiren Kangad
2026-03-11 01:53:56 +05:30
parent ed8424d9a1
commit fc2d9df741
16 changed files with 611 additions and 360 deletions
+6 -5
View File
@@ -9,6 +9,7 @@ import type { Viewport } from 'pixi-viewport';
import type { GroupObject } from './scene-format';
import { FrameSprite } from './sprites/FrameSprite';
import * as ops from './operations';
import { onArrangeAnimationDone } from './operations';
export interface MenuItem {
label: string;
@@ -135,11 +136,11 @@ export function buildContextMenuItems(ctx: MenuContext): MenuItem[] {
{ label: '', shortcut: '', onClick: () => {}, divider: true },
// -- Arrangement --
{ label: 'Arrange Pack', shortcut: 'Ctrl+Shift+P', onClick: () => { ops.arrangeOptimal(selected); selection?.transformBox.update(selected); ctx.onChange(ids); }, disabled: !multiSel },
{ label: 'Arrange Grid', shortcut: '', onClick: () => { ops.arrangeGrid(selected); selection?.transformBox.update(selected); ctx.onChange(ids); }, disabled: !multiSel },
{ label: 'Arrange Row', shortcut: '', onClick: () => { ops.arrangeRow(selected); selection?.transformBox.update(selected); ctx.onChange(ids); }, disabled: !multiSel },
{ label: 'Arrange Column', shortcut: '', onClick: () => { ops.arrangeColumn(selected); selection?.transformBox.update(selected); ctx.onChange(ids); }, disabled: !multiSel },
{ label: 'Stack', shortcut: 'Ctrl+Alt+S', onClick: () => { ops.stackObjects(selected); selection?.transformBox.update(selected); ctx.onChange(ids); }, disabled: !multiSel },
{ label: 'Arrange Pack', shortcut: 'Ctrl+Shift+P', onClick: () => { onArrangeAnimationDone(() => selection?.transformBox.update(selected)); ops.arrangeOptimal(selected); ctx.onChange(ids); }, disabled: !multiSel },
{ label: 'Arrange Grid', shortcut: '', onClick: () => { onArrangeAnimationDone(() => selection?.transformBox.update(selected)); ops.arrangeGrid(selected); ctx.onChange(ids); }, disabled: !multiSel },
{ label: 'Arrange Row', shortcut: '', onClick: () => { onArrangeAnimationDone(() => selection?.transformBox.update(selected)); ops.arrangeRow(selected); ctx.onChange(ids); }, disabled: !multiSel },
{ label: 'Arrange Column', shortcut: '', onClick: () => { onArrangeAnimationDone(() => selection?.transformBox.update(selected)); ops.arrangeColumn(selected); ctx.onChange(ids); }, disabled: !multiSel },
{ label: 'Stack', shortcut: 'Ctrl+Alt+S', onClick: () => { onArrangeAnimationDone(() => selection?.transformBox.update(selected)); ops.stackObjects(selected); ctx.onChange(ids); }, disabled: !multiSel },
{ label: '', shortcut: '', onClick: () => {}, divider: true },
// -- Normalize --