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)
This commit is contained in:
Hiren Kangad
2026-03-11 17:22:25 +05:30
parent 4ed475c62e
commit 58da934ec1
14 changed files with 785 additions and 50 deletions
+8
View File
@@ -64,6 +64,7 @@ export class SelectionManager {
private static readonly DOUBLE_CLICK_MS = 400;
private _onDoubleClickText: ((item: SceneItem) => void) | null = null;
private _onDoubleClickImage: ((item: SceneItem) => void) | null = null;
private _enabled = true;
@@ -142,6 +143,11 @@ export class SelectionManager {
this._onDoubleClickText = fn;
}
/** Called on double-click of an image item (zoom-to-fit). */
set onDoubleClickImage(fn: (item: SceneItem) => void) {
this._onDoubleClickImage = fn;
}
/** Select only this item, deselecting everything else. */
selectOnly(id: string): void {
this.selectedIds.clear();
@@ -357,6 +363,8 @@ export class SelectionManager {
item.displayObject.togglePlayPause();
} else if (item.type === 'text') {
this._onDoubleClickText?.(item);
} else if (item.type === 'image' || item.type === 'drawing') {
this._onDoubleClickImage?.(item);
}
this._lastClickTime = 0;
this._lastClickItemId = null;