@mhamz.01/easyflow-whiteboard 2.178.0 → 2.181.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +429 -0
  2. package/dist/components/node/custom-node-overlay-layer.d.ts.map +1 -1
  3. package/dist/components/node/custom-node-overlay-layer.js +1 -0
  4. package/dist/components/node/custom-node.d.ts.map +1 -1
  5. package/dist/components/node/custom-node.js +1 -0
  6. package/dist/components/node/document-node.d.ts.map +1 -1
  7. package/dist/components/node/document-node.js +1 -0
  8. package/dist/components/node/hooks/useKeyboardShortcuts.d.ts.map +1 -1
  9. package/dist/components/node/hooks/useKeyboardShortcuts.js +5 -1
  10. package/dist/components/node/hooks/useNodeDrag.d.ts.map +1 -1
  11. package/dist/components/node/hooks/useNodeDrag.js +5 -1
  12. package/dist/components/node/hooks/useNodeState.d.ts.map +1 -1
  13. package/dist/components/node/hooks/useNodeState.js +1 -0
  14. package/dist/components/node/types/overlay-types.d.ts.map +1 -1
  15. package/dist/components/node/types/overlay-types.js +1 -0
  16. package/dist/components/toolbar/document-dropdown.d.ts.map +1 -1
  17. package/dist/components/toolbar/document-dropdown.js +1 -0
  18. package/dist/components/toolbar/layers-control.d.ts.map +1 -1
  19. package/dist/components/toolbar/layers-control.js +1 -0
  20. package/dist/components/toolbar/options/arrow-options.d.ts.map +1 -1
  21. package/dist/components/toolbar/options/arrow-options.js +1 -0
  22. package/dist/components/toolbar/options/erase-option.d.ts.map +1 -1
  23. package/dist/components/toolbar/options/erase-option.js +1 -0
  24. package/dist/components/toolbar/options/image-options.d.ts.map +1 -1
  25. package/dist/components/toolbar/options/image-options.js +1 -0
  26. package/dist/components/toolbar/options/line-options.d.ts.map +1 -1
  27. package/dist/components/toolbar/options/line-options.js +1 -0
  28. package/dist/components/toolbar/options/pen-option.d.ts.map +1 -1
  29. package/dist/components/toolbar/options/pen-option.js +1 -0
  30. package/dist/components/toolbar/options/shape-option.d.ts.map +1 -1
  31. package/dist/components/toolbar/options/shape-option.js +1 -0
  32. package/dist/components/toolbar/options/text-option.d.ts.map +1 -1
  33. package/dist/components/toolbar/options/text-option.js +1 -0
  34. package/dist/components/toolbar/task-dropdown.d.ts.map +1 -1
  35. package/dist/components/toolbar/task-dropdown.js +1 -0
  36. package/dist/components/toolbar/toolbar-button.d.ts.map +1 -1
  37. package/dist/components/toolbar/toolbar-seperator.d.ts.map +1 -1
  38. package/dist/components/toolbar/toolbar-skeleton/toolbar-skeleton.d.ts.map +1 -1
  39. package/dist/components/toolbar/toolbar-skeleton/toolbar-skeleton.js +1 -0
  40. package/dist/components/toolbar/tooloptions-panel.d.ts.map +1 -1
  41. package/dist/components/toolbar/tooloptions-panel.js +1 -0
  42. package/dist/components/toolbar/whiteboard-toolbar.d.ts.map +1 -1
  43. package/dist/components/toolbar/whiteboard-toolbar.js +13 -5
  44. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  45. package/dist/components/ui/label.d.ts.map +1 -1
  46. package/dist/components/ui/slider.d.ts.map +1 -1
  47. package/dist/components/ui/slider.js +1 -0
  48. package/dist/components/whiteboard/whiteboard-test.d.ts +5 -1
  49. package/dist/components/whiteboard/whiteboard-test.d.ts.map +1 -1
  50. package/dist/components/whiteboard/whiteboard-test.js +25 -4
  51. package/dist/components/zoomcontrol/zoom-control.d.ts.map +1 -1
  52. package/dist/components/zoomcontrol/zoom-control.js +1 -0
  53. package/dist/hooks/useCanvasInit.d.ts.map +1 -1
  54. package/dist/hooks/useCanvasInit.js +7 -1
  55. package/dist/hooks/useCopyPaste.d.ts.map +1 -1
  56. package/dist/hooks/useCopyPaste.js +6 -1
  57. package/dist/hooks/useDrawing.d.ts.map +1 -1
  58. package/dist/hooks/useDrawing.js +1 -0
  59. package/dist/hooks/useEraser.d.ts.map +1 -1
  60. package/dist/hooks/useEraser.js +1 -0
  61. package/dist/hooks/useLiveUpdate.d.ts.map +1 -1
  62. package/dist/hooks/useLiveUpdate.js +1 -0
  63. package/dist/hooks/useMouseHandlers.d.ts.map +1 -1
  64. package/dist/hooks/useMouseHandlers.js +1 -0
  65. package/dist/hooks/usePan.d.ts.map +1 -1
  66. package/dist/hooks/usePan.js +1 -0
  67. package/dist/hooks/usePersistance.d.ts.map +1 -1
  68. package/dist/hooks/usePersistance.js +1 -0
  69. package/dist/hooks/useSelection.d.ts.map +1 -1
  70. package/dist/hooks/useSelection.js +1 -1
  71. package/dist/hooks/useTextStyle.d.ts.map +1 -1
  72. package/dist/hooks/useTextStyle.js +1 -0
  73. package/dist/hooks/useToolManager.d.ts.map +1 -1
  74. package/dist/hooks/useToolManager.js +19 -0
  75. package/dist/hooks/useZoom.d.ts.map +1 -1
  76. package/dist/hooks/useZoom.js +1 -0
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +1 -0
  79. package/dist/lib/fabric-arrow.d.ts.map +1 -1
  80. package/dist/lib/fabric-arrow.js +1 -0
  81. package/dist/lib/fabric-bidirectional-arrow.d.ts.map +1 -1
  82. package/dist/lib/fabric-bidirectional-arrow.js +1 -0
  83. package/dist/lib/fabric-frame.d.ts.map +1 -1
  84. package/dist/lib/fabric-frame.js +1 -1
  85. package/dist/lib/fabric-utils.d.ts.map +1 -1
  86. package/dist/lib/fabric-utils.js +1 -0
  87. package/dist/lib/utils.d.ts.map +1 -1
  88. package/dist/lib/utils.js +1 -0
  89. package/dist/store/whiteboard-store.d.ts +2 -0
  90. package/dist/store/whiteboard-store.d.ts.map +1 -1
  91. package/dist/store/whiteboard-store.js +4 -0
  92. package/dist/styles.css +6 -0
  93. package/dist/types/canvas-node.d.ts.map +1 -1
  94. package/dist/types/canvas-node.js +1 -0
  95. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"line-options.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/line-options.tsx"],"names":[],"mappings":"AAuBA,MAAM,CAAC,OAAO,UAAU,WAAW,4CA0JlC"}
1
+ {"version":3,"file":"line-options.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/line-options.tsx"],"names":[],"mappings":"AAyBA,MAAM,CAAC,OAAO,UAAU,WAAW,4CA0JlC"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // Line tool options panel — stroke color, stroke width, and dash style for the line drawing tool.
3
4
  import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
5
  import { cn } from "../../../lib/utils";
5
6
  const PRESET_COLORS = [
@@ -1 +1 @@
1
- {"version":3,"file":"pen-option.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/pen-option.tsx"],"names":[],"mappings":"AA0BA,MAAM,CAAC,OAAO,UAAU,UAAU,4CAgMjC"}
1
+ {"version":3,"file":"pen-option.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/pen-option.tsx"],"names":[],"mappings":"AA4BA,MAAM,CAAC,OAAO,UAAU,UAAU,4CAgMjC"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // Pen tool options panel — controls stroke color presets, stroke width presets + slider, and dash style (solid/dashed/dotted).
3
4
  import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
5
  import { cn } from "../../../lib/utils";
5
6
  const PRESET_COLORS = [
@@ -1 +1 @@
1
- {"version":3,"file":"shape-option.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/shape-option.tsx"],"names":[],"mappings":"AAuBA,UAAU,iBAAiB;IACzB,SAAS,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;CAC7C;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE,iBAAiB,2CA2MpE"}
1
+ {"version":3,"file":"shape-option.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/shape-option.tsx"],"names":[],"mappings":"AAyBA,UAAU,iBAAiB;IACzB,SAAS,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;CAC7C;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE,iBAAiB,2CA2MpE"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ // Shape tool options panel — fill color, stroke color, stroke width, and dash style for rect, circle, and frame tools.
3
4
  import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
5
  import { cn } from "../../../lib/utils";
5
6
  const PRESET_COLORS = [
@@ -1 +1 @@
1
- {"version":3,"file":"text-option.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/text-option.tsx"],"names":[],"mappings":"AA4CA,MAAM,CAAC,OAAO,UAAU,WAAW,4CA2NlC"}
1
+ {"version":3,"file":"text-option.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/text-option.tsx"],"names":[],"mappings":"AA8CA,MAAM,CAAC,OAAO,UAAU,WAAW,4CA2NlC"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ // Text tool options panel — font family, font size, font weight, text alignment, and color controls.
3
4
  import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
5
  import { cn } from "../../../lib/utils";
5
6
  import { AlignLeft, AlignCenter, AlignRight, AlignJustify, ChevronDown } from "lucide-react";
@@ -1 +1 @@
1
- {"version":3,"file":"task-dropdown.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/task-dropdown.tsx"],"names":[],"mappings":"AAUA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,iBAAiB;IACzB,SAAS,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA2DD,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,cAAmB,EAAE,aAAa,EAAE,EAAE,iBAAiB,2CA2GxG"}
1
+ {"version":3,"file":"task-dropdown.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/task-dropdown.tsx"],"names":[],"mappings":"AAYA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,iBAAiB;IACzB,SAAS,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA2DD,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,cAAmB,EAAE,aAAa,EAAE,EAAE,iBAAiB,2CA2GxG"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // Task picker dropdown — lists task templates passed by the consumer; clicking one places it on the canvas as a TaskNode.
3
4
  import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, } from "../ui/dropdown-menu";
4
5
  import { CheckSquare, Plus, Calendar, Hash } from "lucide-react";
5
6
  import { useWhiteboardStore } from "../../store/whiteboard-store";
@@ -1 +1 @@
1
- {"version":3,"file":"toolbar-button.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/toolbar-button.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,UAAU,eAAe;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EACjC,IAAI,EAAE,IAAI,EACV,KAAK,EACL,QAAQ,EACR,OAAO,EACP,QAAgB,EAChB,QAAQ,GACT,EAAE,eAAe,2CAkDjB"}
1
+ {"version":3,"file":"toolbar-button.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/toolbar-button.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,UAAU,eAAe;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EACjC,IAAI,EAAE,IAAI,EACV,KAAK,EACL,QAAQ,EACR,OAAO,EACP,QAAgB,EAChB,QAAQ,GACT,EAAE,eAAe,2CAkDjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"toolbar-seperator.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/toolbar-seperator.tsx"],"names":[],"mappings":"AAEA,UAAU,qBAAqB;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,SAAc,GACf,EAAE,qBAAqB,2CAQvB"}
1
+ {"version":3,"file":"toolbar-seperator.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/toolbar-seperator.tsx"],"names":[],"mappings":"AAKA,UAAU,qBAAqB;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,SAAc,GACf,EAAE,qBAAqB,2CAQvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"toolbar-skeleton.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/toolbar-skeleton/toolbar-skeleton.tsx"],"names":[],"mappings":"AAEA,wBAAgB,eAAe,4CA+D9B"}
1
+ {"version":3,"file":"toolbar-skeleton.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/toolbar-skeleton/toolbar-skeleton.tsx"],"names":[],"mappings":"AAIA,wBAAgB,eAAe,4CA+D9B"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // Animated loading skeleton matching the toolbar's pill-and-button layout — shown while the Fabric canvas initialises.
3
4
  export function ToolbarSkeleton() {
4
5
  return (_jsxs("div", { className: "fixed bottom-4 md:bottom-6 left-0 right-0 z-50 flex flex-col items-center gap-2.5 px-4 pointer-events-none", children: [_jsx("div", { className: "w-full md:w-auto max-w-full", children: _jsxs("div", { className: "flex items-center gap-[3px] px-2 py-1.5 bg-[rgba(5,5,5,0.97)] border border-[rgba(161,161,161,0.8)] rounded-[18px] w-fit overflow-hidden", children: [_jsx(SkBtn, { delay: 0 }), _jsx(SkBtn, { delay: 0.1 }), _jsx(Sep, {}), _jsx(SkBtn, { delay: 0.15 }), _jsx(SkBtn, { delay: 0.2 }), _jsx(SkBtn, { delay: 0.25 }), _jsx(SkBtn, { delay: 0.3 }), _jsx(SkBtn, { delay: 0.35 }), _jsx(SkBtn, { delay: 0.4 }), _jsx(SkBtn, { delay: 0.45 }), _jsx(SkBtn, { delay: 0.5 }), _jsx(Sep, {}), _jsx(SkPill, { width: 92, delay: 0.55, labelWidth: 44 }), _jsx("div", { className: "w-[3px]" }), _jsx(SkPill, { width: 106, delay: 0.65, labelWidth: 54 })] }) }), _jsxs("div", { className: "flex gap-[5px] items-center md:hidden", children: [_jsx("div", { className: "h-1 rounded-sm bg-white/55", style: { animation: "sk-dot 2s ease-in-out infinite" } }), _jsx("div", { className: "w-1 h-1 rounded-sm bg-white/15" })] }), _jsx("style", { children: `
5
6
  .sk-sweep {
@@ -1 +1 @@
1
- {"version":3,"file":"tooloptions-panel.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/tooloptions-panel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,SAAS,EAA6C,MAAM,OAAO,CAAC;AAUpF,OAAO,MAA4C,MAAM,QAAQ,CAAC;AAIlE,UAAU,qBAAqB;IAC7B,YAAY,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC/C;;AAWD,wBA2PG"}
1
+ {"version":3,"file":"tooloptions-panel.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/tooloptions-panel.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,SAAS,EAA6C,MAAM,OAAO,CAAC;AAUpF,OAAO,MAA4C,MAAM,QAAQ,CAAC;AAIlE,UAAU,qBAAqB;IAC7B,YAAY,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC/C;;AAWD,wBA2PG"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ // Tool properties sidebar — slides in with the relevant options panel for the active tool or the type of the selected object.
3
4
  import React, { useEffect, useMemo, useState, useCallback } from "react";
4
5
  import { cn } from "../../lib/utils";
5
6
  import { useWhiteboardStore } from "../../store/whiteboard-store";
@@ -1 +1 @@
1
- {"version":3,"file":"whiteboard-toolbar.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/whiteboard-toolbar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAqB,gBAAgB,EAAkC,MAAM,OAAO,CAAC;AAKnG,OAAO,EAAE,MAAM,EAAQ,MAAM,QAAQ,CAAC;AAQtC,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,gBAAgB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkBD,UAAU,sBAAsB;IAC9B,YAAY,EAAE,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9C,cAAc,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC1C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAChC,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;;AAUD,wBA+SG"}
1
+ {"version":3,"file":"whiteboard-toolbar.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/whiteboard-toolbar.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAqB,gBAAgB,EAAkC,MAAM,OAAO,CAAC;AAKnG,OAAO,EAAE,MAAM,EAAQ,MAAM,QAAQ,CAAC;AAQtC,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,gBAAgB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqBD,UAAU,sBAAsB;IAC9B,YAAY,EAAE,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9C,cAAc,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC1C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAChC,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;;AAUD,wBAoTG"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ // Bottom toolbar — renders all tool buttons, keyboard shortcuts, undo/redo, image upload, and node picker dropdowns.
3
4
  import React, { useEffect, useRef, useState, useCallback, useMemo } from "react";
4
5
  import { MousePointer2, Pencil, Square, Circle, Minus, MoveRight, Type, Eraser, Hand, Frame, Undo2, Redo2, Image as ImageIcon, } from "lucide-react";
5
6
  import { util } from "fabric";
@@ -24,6 +25,8 @@ const TOOLS = [
24
25
  { id: "undo", icon: Undo2, label: "Undo", shortcut: "⌘Z", category: "history" },
25
26
  { id: "redo", icon: Redo2, label: "Redo", shortcut: "⌘⇧Z", category: "history" },
26
27
  ];
28
+ // Tools that remain usable in read-only mode — everything else mutates the canvas.
29
+ const READ_ONLY_ALLOWED_TOOLS = new Set(["select", "pan"]);
27
30
  // ── Memoized ToolButton wrapper ───────────────────────────────────────────────
28
31
  const MemoizedToolButton = React.memo(ToolButton);
29
32
  const MemoizedToolbarSeparator = React.memo(ToolbarSeparator);
@@ -37,6 +40,7 @@ export default React.memo(function WhiteboardToolbar({ fabricCanvas, isRestoring
37
40
  const canRedo = useWhiteboardStore((s) => s.canRedo);
38
41
  const undo = useWhiteboardStore((s) => s.undo);
39
42
  const redo = useWhiteboardStore((s) => s.redo);
43
+ const editable = useWhiteboardStore((s) => s.editable);
40
44
  const scrollRef = useRef(null);
41
45
  const fileInputRef = useRef(null);
42
46
  const [scrollPosition, setScrollPosition] = useState(0);
@@ -65,6 +69,8 @@ export default React.memo(function WhiteboardToolbar({ fabricCanvas, isRestoring
65
69
  }, [setActiveTool, isRestoringRef]);
66
70
  // ── Handle tool click (memoized) ──────────────────────────────────────────────
67
71
  const handleToolClick = useCallback((toolId) => {
72
+ if (!editable && !READ_ONLY_ALLOWED_TOOLS.has(toolId))
73
+ return;
68
74
  const canvas = fabricCanvas.current;
69
75
  if (!canvas)
70
76
  return;
@@ -87,7 +93,7 @@ export default React.memo(function WhiteboardToolbar({ fabricCanvas, isRestoring
87
93
  return;
88
94
  }
89
95
  setActiveTool(toolId);
90
- }, [fabricCanvas, undo, redo, restoreCanvas, setActiveTool]);
96
+ }, [fabricCanvas, undo, redo, restoreCanvas, setActiveTool, editable]);
91
97
  // ── Handle image upload (memoized) ────────────────────────────────────────────
92
98
  const handleImageUpload = useCallback((e) => {
93
99
  const file = e.target.files?.[0];
@@ -153,7 +159,7 @@ export default React.memo(function WhiteboardToolbar({ fabricCanvas, isRestoring
153
159
  return;
154
160
  }
155
161
  if (key === "delete" || key === "backspace") {
156
- if (fabricCanvas.current && activeTool === "select") {
162
+ if (editable && fabricCanvas.current && activeTool === "select") {
157
163
  e.preventDefault();
158
164
  deleteSelectedObjects(fabricCanvas.current);
159
165
  }
@@ -172,11 +178,13 @@ export default React.memo(function WhiteboardToolbar({ fabricCanvas, isRestoring
172
178
  };
173
179
  window.addEventListener("keydown", onKey);
174
180
  return () => window.removeEventListener("keydown", onKey);
175
- }, [fabricCanvas, activeTool, handleToolClick]);
181
+ }, [fabricCanvas, activeTool, handleToolClick, editable]);
176
182
  // ── Memoized tool buttons ─────────────────────────────────────────────────────
177
- const toolButtons = useMemo(() => TOOLS.map((tool, index) => (_jsxs("div", { className: "flex items-center snap-center", children: [_jsx(MemoizedToolButton, { icon: tool.icon, label: tool.label, isActive: activeTool === tool.id, onClick: () => handleToolClick(tool.id), disabled: (tool.id === "undo" && !canUndo) || (tool.id === "redo" && !canRedo), shortcut: tool.shortcut }), (index === 1 || index === 9) && (_jsx("div", { className: "hidden md:block mx-1", children: _jsx(MemoizedToolbarSeparator, {}) }))] }, tool.id))), [activeTool, canUndo, canRedo, handleToolClick]);
183
+ const toolButtons = useMemo(() => TOOLS.map((tool, index) => (_jsxs("div", { className: "flex items-center snap-center", children: [_jsx(MemoizedToolButton, { icon: tool.icon, label: tool.label, isActive: activeTool === tool.id, onClick: () => handleToolClick(tool.id), disabled: (tool.id === "undo" && !canUndo) ||
184
+ (tool.id === "redo" && !canRedo) ||
185
+ (!editable && !READ_ONLY_ALLOWED_TOOLS.has(tool.id)), shortcut: tool.shortcut }), (index === 1 || index === 9) && (_jsx("div", { className: "hidden md:block mx-1", children: _jsx(MemoizedToolbarSeparator, {}) }))] }, tool.id))), [activeTool, canUndo, canRedo, handleToolClick, editable]);
178
186
  // ── Memoized dropdown section ─────────────────────────────────────────────────
179
- const dropdownSection = useMemo(() => (onAddTask || onAddDocument) && (_jsxs("div", { className: "flex items-center gap-1 snap-center pr-2", children: [_jsx("div", { className: "mx-1 flex-shrink-0", children: _jsx(MemoizedToolbarSeparator, {}) }), onAddTask && (_jsx(MemoizedTaskDropdown, { onAddTask: onAddTask, availableTasks: availableTasks, createNewHref: createNewTaskHref })), onAddDocument && (_jsx(MemoizedDocumentDropdown, { onAddDocument: onAddDocument, availableDocuments: availableDocuments, createNewHref: createNewDocumentHref }))] })), [onAddTask, onAddDocument, availableTasks, availableDocuments, createNewTaskHref, createNewDocumentHref]);
187
+ const dropdownSection = useMemo(() => editable && (onAddTask || onAddDocument) && (_jsxs("div", { className: "flex items-center gap-1 snap-center pr-2", children: [_jsx("div", { className: "mx-1 flex-shrink-0", children: _jsx(MemoizedToolbarSeparator, {}) }), onAddTask && (_jsx(MemoizedTaskDropdown, { onAddTask: onAddTask, availableTasks: availableTasks, createNewHref: createNewTaskHref })), onAddDocument && (_jsx(MemoizedDocumentDropdown, { onAddDocument: onAddDocument, availableDocuments: availableDocuments, createNewHref: createNewDocumentHref }))] })), [onAddTask, onAddDocument, availableTasks, availableDocuments, createNewTaskHref, createNewDocumentHref, editable]);
180
188
  // ── Scroll indicator visibility ───────────────────────────────────────────────
181
189
  const scrollIndicators = useMemo(() => (_jsxs("div", { className: "flex gap-1.5 md:hidden", children: [_jsx("div", { className: `w-1.5 h-1.5 rounded-full transition-all duration-300 ${scrollPosition < 0.5 ? "bg-white w-6" : "bg-white/30"}` }), _jsx("div", { className: `w-1.5 h-1.5 rounded-full transition-all duration-300 ${scrollPosition >= 0.5 ? "bg-white w-6" : "bg-white/30"}` })] })), [scrollPosition]);
182
190
  return (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", onChange: handleImageUpload, className: "hidden" }), _jsxs("div", { className: "fixed bottom-4 md:bottom-6 left-0 right-0 z-50 flex flex-col items-center gap-2 px-4 pointer-events-none", children: [_jsxs("div", { className: "relative w-full md:w-auto max-w-full pointer-events-auto", children: [_jsx("div", { className: "absolute left-0 top-0 bottom-0 w-8 bg-gradient-to-r from-[#0b0b0b] to-transparent pointer-events-none z-10 md:hidden rounded-l-2xl" }), _jsx("div", { className: "absolute right-0 top-0 bottom-0 w-8 bg-gradient-to-l from-[#0b0b0b] to-transparent pointer-events-none z-10 md:hidden rounded-r-2xl" }), _jsxs("div", { ref: scrollRef, onScroll: handleScroll, className: "flex items-center gap-1 px-2 py-2 bg-black/95 backdrop-blur-md border border-[#A1A1A1] rounded-2xl shadow-2xl overflow-x-auto md:overflow-x-visible snap-x snap-mandatory md:snap-none scrollbar-hide scroll-smooth", children: [toolButtons, dropdownSection] })] }), scrollIndicators] }), _jsx("style", { children: `
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown-menu.d.ts","sourceRoot":"","sources":["../../../src/components/ui/dropdown-menu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,qBAAqB,MAAM,+BAA+B,CAAC;AAGvE,iBAAS,YAAY,CAAC,EACpB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,IAAI,CAAC,2CAEzD;AAED,iBAAS,kBAAkB,CAAC,EAC1B,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,MAAM,CAAC,2CAI3D;AAED,iBAAS,mBAAmB,CAAC,EAC3B,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,OAAO,CAAC,2CAO5D;AAED,iBAAS,mBAAmB,CAAC,EAC3B,SAAS,EACT,UAAc,EACd,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,OAAO,CAAC,2CAc5D;AAED,iBAAS,iBAAiB,CAAC,EACzB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,KAAK,CAAC,2CAI1D;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,KAAK,EACL,OAAmB,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,IAAI,CAAC,GAAG;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,OAAO,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;CACpC,2CAaA;AAED,iBAAS,wBAAwB,CAAC,EAChC,SAAS,EACT,QAAQ,EACR,OAAO,EACP,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,YAAY,CAAC,2CAmBjE;AAED,iBAAS,sBAAsB,CAAC,EAC9B,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,UAAU,CAAC,2CAO/D;AAED,iBAAS,qBAAqB,CAAC,EAC7B,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,SAAS,CAAC,2CAkB9D;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,KAAK,CAAC,GAAG;IAC5D,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,2CAYA;AAED,iBAAS,qBAAqB,CAAC,EAC7B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,SAAS,CAAC,2CAQ9D;AAED,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,2CAW9B;AAED,iBAAS,eAAe,CAAC,EACvB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,GAAG,CAAC,2CAExD;AAED,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,KAAK,EACL,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,UAAU,CAAC,GAAG;IACjE,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,2CAeA;AAED,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,qBAAqB,CAAC,UAAU,CAAC,2CAW/D;AAED,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,sBAAsB,GACvB,CAAA"}
1
+ {"version":3,"file":"dropdown-menu.d.ts","sourceRoot":"","sources":["../../../src/components/ui/dropdown-menu.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../src/components/ui/label.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,cAAc,MAAM,uBAAuB,CAAC;AAIxD,iBAAS,KAAK,CAAC,EACb,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,IAAI,CAAC,2CAWlD;AAED,OAAO,EAAE,KAAK,EAAE,CAAA"}
1
+ {"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../src/components/ui/label.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,cAAc,MAAM,uBAAuB,CAAC;AAIxD,iBAAS,KAAK,CAAC,EACb,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,IAAI,CAAC,2CAWlD;AAED,OAAO,EAAE,KAAK,EAAE,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"slider.d.ts","sourceRoot":"","sources":["../../../src/components/ui/slider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAI1D,iBAAS,MAAM,CAAC,EACd,SAAS,EACT,YAAY,EACZ,KAAK,EACL,GAAO,EACP,GAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,2CA8CnD;AAED,OAAO,EAAE,MAAM,EAAE,CAAA"}
1
+ {"version":3,"file":"slider.d.ts","sourceRoot":"","sources":["../../../src/components/ui/slider.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAI1D,iBAAS,MAAM,CAAC,EACd,SAAS,EACT,YAAY,EACZ,KAAK,EACL,GAAO,EACP,GAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,2CA8CnD;AAED,OAAO,EAAE,MAAM,EAAE,CAAA"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // Radix Slider wrapper — accessible range slider component styled to match the library's dark theme.
3
4
  import * as React from "react";
4
5
  import * as SliderPrimitive from "@radix-ui/react-slider";
5
6
  import { cn } from "../../lib/utils";
@@ -29,6 +29,10 @@ interface FabricWhiteboardProps {
29
29
  documents: any[];
30
30
  }) => void;
31
31
  saveDebounceMs?: number;
32
+ createNewTaskHref?: string;
33
+ createNewDocumentHref?: string;
34
+ /** When false, the canvas becomes fully read-only: no drawing, selecting/moving/deleting objects, pasting, or adding nodes. Defaults to true. */
35
+ editable?: boolean;
32
36
  }
33
- export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs, availableTasks, availableDocuments, isLoadingData, }: FabricWhiteboardProps): import("react/jsx-runtime").JSX.Element;
37
+ export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs, availableTasks, availableDocuments, isLoadingData, createNewTaskHref, createNewDocumentHref, editable: editableProp, }: FabricWhiteboardProps): import("react/jsx-runtime").JSX.Element;
34
38
  //# sourceMappingURL=whiteboard-test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"whiteboard-test.d.ts","sourceRoot":"","sources":["../../../src/components/whiteboard/whiteboard-test.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAkB5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxD,YAAY,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,YAAY,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAIxE,UAAU,IAAI;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,qBAAqB;IAC7B,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;QACf,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;KACxB,CAAC;IACF,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAChC,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,GAAG,EAAE,CAAC;QACb,SAAS,EAAE,GAAG,EAAE,CAAC;KAClB,KAAK,IAAI,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAoHD,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,WAAW,EACX,MAAM,EACN,cAAc,EACd,cAAmB,EACnB,kBAAuB,EACvB,aAAqB,GACtB,EAAE,qBAAqB,2CAkTvB"}
1
+ {"version":3,"file":"whiteboard-test.d.ts","sourceRoot":"","sources":["../../../src/components/whiteboard/whiteboard-test.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAkB5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxD,YAAY,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,YAAY,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAIxE,UAAU,IAAI;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,qBAAqB;IAC7B,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;QACf,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;KACxB,CAAC;IACF,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAChC,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,GAAG,EAAE,CAAC;QACb,SAAS,EAAE,GAAG,EAAE,CAAC;KAClB,KAAK,IAAI,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iJAAiJ;IACjJ,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA0HD,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,WAAW,EACX,MAAM,EACN,cAAc,EACd,cAAmB,EACnB,kBAAuB,EACvB,aAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,QAAQ,EAAE,YAAmB,GAC9B,EAAE,qBAAqB,2CAsUvB"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // Root whiteboard component — composes all canvas hooks, the memoized toolbar, the HTML overlay, and zoom controls into a full editor.
3
4
  import React, { useCallback, useRef, useState } from "react";
4
5
  import { classRegistry } from "fabric";
5
6
  import { useWhiteboardStore } from "../../store/whiteboard-store";
@@ -24,8 +25,8 @@ import { ToolbarSkeleton } from "../toolbar/toolbar-skeleton/toolbar-skeleton";
24
25
  import { useCopyPaste } from "../../hooks/useCopyPaste";
25
26
  classRegistry.setClass(Frame, "frame");
26
27
  // ── Memoized Toolbar wrapper (Issue 10) ───────────────────────────────────────
27
- const MemoizedToolbarWrapper = React.memo(function ToolbarWrapper({ canvasReady, fabricCanvasRef, isRestoringRef, onAddTask, onAddDocument, availableDocuments, availableTasks, isLoadingData, }) {
28
- return canvasReady ? (_jsx(WhiteboardToolbar, { fabricCanvas: fabricCanvasRef, isRestoringRef: isRestoringRef, onAddTask: onAddTask, onAddDocument: onAddDocument, availableDocuments: availableDocuments, availableTasks: availableTasks, isLoadingData: isLoadingData })) : (_jsx(ToolbarSkeleton, {}));
28
+ const MemoizedToolbarWrapper = React.memo(function ToolbarWrapper({ canvasReady, fabricCanvasRef, isRestoringRef, onAddTask, onAddDocument, availableDocuments, availableTasks, isLoadingData, createNewTaskHref, createNewDocumentHref, }) {
29
+ return canvasReady ? (_jsx(WhiteboardToolbar, { fabricCanvas: fabricCanvasRef, isRestoringRef: isRestoringRef, onAddTask: onAddTask, onAddDocument: onAddDocument, availableDocuments: availableDocuments, availableTasks: availableTasks, isLoadingData: isLoadingData, createNewTaskHref: createNewTaskHref, createNewDocumentHref: createNewDocumentHref })) : (_jsx(ToolbarSkeleton, {}));
29
30
  });
30
31
  // ── Memoized Overlay wrapper (Issue 11) ───────────────────────────────────────
31
32
  // This wrapper only updates when tasks/documents/canvasReady change
@@ -65,7 +66,7 @@ selectedCanvasObjects, clearHtmlSelectionRef, // ← destructure from props
65
66
  prev.selectionBox === next.selectionBox &&
66
67
  prev.selectedCanvasObjects === next.selectedCanvasObjects);
67
68
  });
68
- export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs, availableTasks = [], availableDocuments = [], isLoadingData = false, }) {
69
+ export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs, availableTasks = [], availableDocuments = [], isLoadingData = false, createNewTaskHref, createNewDocumentHref, editable: editableProp = true, }) {
69
70
  // Refs
70
71
  const canvasRef = useRef(null);
71
72
  const fabricCanvasRef = useRef(null);
@@ -76,6 +77,9 @@ export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs,
76
77
  const suppressHistoryRef = useRef(false);
77
78
  const isRestoringRef = useRef(false);
78
79
  const clearHtmlSelectionRef = useRef(() => { });
80
+ // Read by the (stable, []-deps) add-task/add-document callbacks below so they
81
+ // stay no-ops in read-only mode without needing to be recreated on toggle.
82
+ const editableRef = useRef(editableProp);
79
83
  // Eraser refs
80
84
  const eraserTraceRef = useRef(null);
81
85
  const eraserTargetsRef = useRef(new Set());
@@ -87,6 +91,19 @@ export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs,
87
91
  const toolOptions = useWhiteboardStore((state) => state.toolOptions);
88
92
  const addCanvasObject = useWhiteboardStore((state) => state.addCanvasObject);
89
93
  const pushHistory = useWhiteboardStore((state) => state.pushHistory);
94
+ const setEditable = useWhiteboardStore((state) => state.setEditable);
95
+ const setActiveTool = useWhiteboardStore((state) => state.setActiveTool);
96
+ // Sync the editable prop into the shared store so every hook/component that
97
+ // needs to gate mutations (tool manager, copy-paste, toolbar, node drag,
98
+ // keyboard shortcuts) can read a single source of truth. Forcing the active
99
+ // tool back to "select" when locking down also neutralizes the drawing/
100
+ // eraser mouse handlers, which key strictly off activeTool.
101
+ React.useEffect(() => {
102
+ editableRef.current = editableProp;
103
+ setEditable(editableProp);
104
+ if (!editableProp)
105
+ setActiveTool("select");
106
+ }, [editableProp, setEditable, setActiveTool]);
90
107
  // State
91
108
  const [tasks, setTasks] = useState([]);
92
109
  const [canvasReady, setCanvasReady] = useState(false);
@@ -224,6 +241,8 @@ export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs,
224
241
  });
225
242
  // ── Memoized static handlers ──────────────────────────────────────────────────
226
243
  const handleAddTaskFromDropdown = useCallback((taskTemplate) => {
244
+ if (!editableRef.current)
245
+ return;
227
246
  const canvas = fabricCanvasRef.current;
228
247
  if (!canvas)
229
248
  return;
@@ -244,6 +263,8 @@ export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs,
244
263
  ]);
245
264
  }, []);
246
265
  const handleAddDocumentFromDropdown = useCallback((docTemplate) => {
266
+ if (!editableRef.current)
267
+ return;
247
268
  const canvas = fabricCanvasRef.current;
248
269
  if (!canvas)
249
270
  return;
@@ -291,5 +312,5 @@ export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs,
291
312
  backgroundImage: `radial-gradient(circle, rgba(255,255,255,0.2) 1.2px, transparent 1.2px)`,
292
313
  backgroundSize: "40px 40px",
293
314
  zIndex: 0,
294
- } }), _jsx("canvas", { ref: canvasRef, className: "absolute inset-0", style: { zIndex: 1 } }), _jsx(MemoizedOverlayWrapper, { tasks: tasks, documents: documents, canvasReady: canvasReady, fabricCanvasRef: fabricCanvasRef, onTasksUpdate: setTasks, onDocumentsUpdate: setDocuments, selectionBox: selectionBox, selectedCanvasObjects: selectedCanvasObjects, clearHtmlSelectionRef: clearHtmlSelectionRef }), _jsxs("div", { className: "absolute inset-0 pointer-events-none", style: { zIndex: 100 }, children: [_jsx("div", { className: "pointer-events-auto", children: _jsx(MemoizedToolbarWrapper, { canvasReady: canvasReady, fabricCanvasRef: fabricCanvasRef, isRestoringRef: isRestoringRef, onAddTask: handleAddTaskFromDropdown, onAddDocument: handleAddDocumentFromDropdown, availableDocuments: availableDocuments, availableTasks: availableTasks, isLoadingData: isLoadingData }) }), _jsx("div", { className: "pointer-events-auto", children: _jsx(ToolOptionsPanel, { fabricCanvas: fabricCanvasRef }) }), _jsx("div", { className: "pointer-events-auto", children: _jsx(ZoomControls, { zoom: canvasZoom, onZoomIn: handleZoomIn, onZoomOut: handleZoomOut, onResetZoom: handleResetZoom }) })] })] }) }));
315
+ } }), _jsx("canvas", { ref: canvasRef, className: "absolute inset-0", style: { zIndex: 1 } }), _jsx(MemoizedOverlayWrapper, { tasks: tasks, documents: documents, canvasReady: canvasReady, fabricCanvasRef: fabricCanvasRef, onTasksUpdate: setTasks, onDocumentsUpdate: setDocuments, selectionBox: selectionBox, selectedCanvasObjects: selectedCanvasObjects, clearHtmlSelectionRef: clearHtmlSelectionRef }), _jsxs("div", { className: "absolute inset-0 pointer-events-none", style: { zIndex: 100 }, children: [_jsx("div", { className: "pointer-events-auto", children: _jsx(MemoizedToolbarWrapper, { canvasReady: canvasReady, fabricCanvasRef: fabricCanvasRef, isRestoringRef: isRestoringRef, onAddTask: handleAddTaskFromDropdown, onAddDocument: handleAddDocumentFromDropdown, availableDocuments: availableDocuments, availableTasks: availableTasks, isLoadingData: isLoadingData, createNewTaskHref: createNewTaskHref, createNewDocumentHref: createNewDocumentHref }) }), _jsx("div", { className: "pointer-events-auto", children: _jsx(ToolOptionsPanel, { fabricCanvas: fabricCanvasRef }) }), _jsx("div", { className: "pointer-events-auto", children: _jsx(ZoomControls, { zoom: canvasZoom, onZoomIn: handleZoomIn, onZoomOut: handleZoomOut, onResetZoom: handleResetZoom }) })] })] }) }));
295
316
  }
@@ -1 +1 @@
1
- {"version":3,"file":"zoom-control.d.ts","sourceRoot":"","sources":["../../../src/components/zoomcontrol/zoom-control.tsx"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,WAAW,GACZ,EAAE,iBAAiB,2CA+DnB"}
1
+ {"version":3,"file":"zoom-control.d.ts","sourceRoot":"","sources":["../../../src/components/zoomcontrol/zoom-control.tsx"],"names":[],"mappings":"AAMA,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,WAAW,GACZ,EAAE,iBAAiB,2CA+DnB"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // Zoom controls pill — displays current zoom percentage; + zooms in, − zooms out, clicking the number resets to 100%.
3
4
  import { Minus, Plus } from "lucide-react";
4
5
  export default function ZoomControls({ zoom, onZoomIn, onZoomOut, onResetZoom, }) {
5
6
  const zoomPercentage = Math.round(zoom * 100);
@@ -1 +1 @@
1
- {"version":3,"file":"useCanvasInit.d.ts","sourceRoot":"","sources":["../../src/hooks/useCanvasInit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAY,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAKhC,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAK,MAAM,CAAC;IACnB,KAAK,CAAC,EAAM,GAAG,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;CACnB;AAED,UAAU,kBAAkB;IAC1B,SAAS,EAAW,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACxD,eAAe,EAAK,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,UAAU,EAAU,MAAM,CAAC;IAC3B,kBAAkB,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,cAAc,EAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,WAAW,EAAS,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAY,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC3C,YAAY,EAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAQ,qBAAqB,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAID,eAAO,MAAM,aAAa,GAAI,4IAW3B,kBAAkB,SA2HpB,CAAC"}
1
+ {"version":3,"file":"useCanvasInit.d.ts","sourceRoot":"","sources":["../../src/hooks/useCanvasInit.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,SAAS,EAAY,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMhC,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAK,MAAM,CAAC;IACnB,KAAK,CAAC,EAAM,GAAG,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;CACnB;AAED,UAAU,kBAAkB;IAC1B,SAAS,EAAW,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACxD,eAAe,EAAK,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,UAAU,EAAU,MAAM,CAAC;IAC3B,kBAAkB,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,cAAc,EAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,WAAW,EAAS,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAY,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC3C,YAAY,EAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAQ,qBAAqB,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAID,eAAO,MAAM,aAAa,GAAI,4IAW3B,kBAAkB,SA+HpB,CAAC"}
@@ -1,8 +1,14 @@
1
+ // Initialises the Fabric canvas on mount, hydrates saved state (canvas JSON + HTML nodes), and wires resize/touch handlers.
1
2
  import { useEffect } from "react";
2
3
  import { Canvas } from "fabric";
3
4
  import { initializeFabricCanvas, addWelcomeContent } from "../lib/fabric-utils";
5
+ import { useWhiteboardStore } from "../store/whiteboard-store";
4
6
  // ─── Hook ─────────────────────────────────────────────────────────────────────
5
7
  export const useCanvasInit = ({ canvasRef, fabricCanvasRef, activeTool, suppressHistoryRef, isRestoringRef, pushHistory, setTasks, setDocuments, initialData, onReady, }) => {
8
+ // Seeds the canvas's initial interactivity. useToolManager takes over from
9
+ // here and reacts to later editable toggles — this only avoids a flash of
10
+ // selectable objects on a read-only mount.
11
+ const editable = useWhiteboardStore((s) => s.editable);
6
12
  useEffect(() => {
7
13
  if (!canvasRef.current || fabricCanvasRef.current)
8
14
  return;
@@ -10,7 +16,7 @@ export const useCanvasInit = ({ canvasRef, fabricCanvasRef, activeTool, suppress
10
16
  width: window.innerWidth,
11
17
  height: window.innerHeight,
12
18
  backgroundColor: "transparent",
13
- selection: true,
19
+ selection: editable,
14
20
  allowTouchScrolling: false,
15
21
  stopContextMenu: true,
16
22
  objectCaching: true,
@@ -1 +1 @@
1
- {"version":3,"file":"useCopyPaste.d.ts","sourceRoot":"","sources":["../../src/hooks/useCopyPaste.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,MAAM,EAAiC,MAAM,QAAQ,CAAC;AAE/D,UAAU,iBAAiB;IACzB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACxC;AAED,eAAO,MAAM,YAAY,GAAI,kBAAkB,iBAAiB,SAmE/D,CAAC"}
1
+ {"version":3,"file":"useCopyPaste.d.ts","sourceRoot":"","sources":["../../src/hooks/useCopyPaste.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,MAAM,EAAiC,MAAM,QAAQ,CAAC;AAG/D,UAAU,iBAAiB;IACzB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACxC;AAED,eAAO,MAAM,YAAY,GAAI,kBAAkB,iBAAiB,SAqE/D,CAAC"}
@@ -1,7 +1,10 @@
1
+ // Ctrl+C / Ctrl+V clipboard for Fabric objects — clones on copy, re-clones on paste with a cascading 10px offset.
1
2
  import { useEffect, useRef } from "react";
2
3
  import { ActiveSelection } from "fabric";
4
+ import { useWhiteboardStore } from "../store/whiteboard-store";
3
5
  export const useCopyPaste = ({ fabricCanvas }) => {
4
6
  const clipboardRef = useRef(null);
7
+ const editable = useWhiteboardStore((s) => s.editable);
5
8
  useEffect(() => {
6
9
  const handleKeyDown = async (e) => {
7
10
  if (e.target instanceof HTMLInputElement ||
@@ -22,6 +25,8 @@ export const useCopyPaste = ({ fabricCanvas }) => {
22
25
  }
23
26
  // ── Paste ────────────────────────────────────────────────────────────
24
27
  if ((e.ctrlKey || e.metaKey) && e.key === "v") {
28
+ if (!editable)
29
+ return;
25
30
  const copied = clipboardRef.current;
26
31
  if (!copied)
27
32
  return;
@@ -58,5 +63,5 @@ export const useCopyPaste = ({ fabricCanvas }) => {
58
63
  };
59
64
  window.addEventListener("keydown", handleKeyDown);
60
65
  return () => window.removeEventListener("keydown", handleKeyDown);
61
- }, [fabricCanvas]);
66
+ }, [fabricCanvas, editable]);
62
67
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useDrawing.d.ts","sourceRoot":"","sources":["../../src/hooks/useDrawing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,SAAS,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,iBAAiB,EAAsB,MAAM,QAAQ,CAAC;AAMrF,UAAU,eAAe;IACvB,YAAY,EAAQ,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,UAAU,EAAU,MAAM,CAAC;IAC3B,WAAW,EAAS,GAAG,CAAC;IACxB,YAAY,EAAQ,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,aAAa,EAAO,SAAS,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC/D,eAAe,EAAK,SAAS,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACnD,kBAAkB,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,WAAW,EAAS,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,eAAe,EAAK,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;CACjD;AAED,eAAO,MAAM,UAAU,GAAI,4IAUxB,eAAe;2BAmBS,iBAAiB;2BAsHjB,iBAAiB;;CA0C3C,CAAC"}
1
+ {"version":3,"file":"useDrawing.d.ts","sourceRoot":"","sources":["../../src/hooks/useDrawing.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,SAAS,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,iBAAiB,EAAsB,MAAM,QAAQ,CAAC;AAMrF,UAAU,eAAe;IACvB,YAAY,EAAQ,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,UAAU,EAAU,MAAM,CAAC;IAC3B,WAAW,EAAS,GAAG,CAAC;IACxB,YAAY,EAAQ,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,aAAa,EAAO,SAAS,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC/D,eAAe,EAAK,SAAS,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACnD,kBAAkB,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,WAAW,EAAS,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,eAAe,EAAK,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;CACjD;AAED,eAAO,MAAM,UAAU,GAAI,4IAUxB,eAAe;2BAmBS,iBAAiB;2BAsHjB,iBAAiB;;CA0C3C,CAAC"}
@@ -1,3 +1,4 @@
1
+ // Provides stable mouse-down/move/up handlers for drawing shapes (rect, circle, line, arrow, frame, text) via drag.
1
2
  import { useRef } from "react";
2
3
  import { Rect, Circle, Line } from "fabric";
3
4
  import { Frame } from "../lib/fabric-frame";
@@ -1 +1 @@
1
- {"version":3,"file":"useEraser.d.ts","sourceRoot":"","sources":["../../src/hooks/useEraser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,UAAU,cAAc;IACtB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,GAAG,CAAC;IACjB,cAAc,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,gBAAgB,EAAE,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/C,eAAe,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACpC,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC7C,mBAAmB,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,kBAAkB,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,eAAO,MAAM,SAAS,GAAI,oKAWvB,cAAc;;;;gCAsBoB;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;;CA0I5D,CAAC"}
1
+ {"version":3,"file":"useEraser.d.ts","sourceRoot":"","sources":["../../src/hooks/useEraser.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,UAAU,cAAc;IACtB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,GAAG,CAAC;IACjB,cAAc,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,gBAAgB,EAAE,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/C,eAAe,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACpC,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC7C,mBAAmB,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,kBAAkB,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,eAAO,MAAM,SAAS,GAAI,oKAWvB,cAAc;;;;gCAsBoB;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;;CA0I5D,CAAC"}
@@ -1,3 +1,4 @@
1
+ // Eraser tool — renders a brush cursor and trail path, then deletes any Fabric objects intersected on mouse-up.
1
2
  import { Circle } from "fabric";
2
3
  import * as fabric from "fabric";
3
4
  export const useEraser = ({ fabricCanvas, activeTool, toolOptions, eraserTraceRef, eraserTargetsRef, eraserActiveRef, eraserPathRef, eraserPathPointsRef, suppressHistoryRef, pushHistory, }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"useLiveUpdate.d.ts","sourceRoot":"","sources":["../../src/hooks/useLiveUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,MAAM,EAA6B,MAAM,QAAQ,CAAC;AAI3D,UAAU,kBAAkB;IAC1B,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,WAAW,EAAE,GAAG,CAAC;CAClB;AAED,eAAO,MAAM,aAAa,GAAI,+BAA+B,kBAAkB,SAuD9E,CAAC"}
1
+ {"version":3,"file":"useLiveUpdate.d.ts","sourceRoot":"","sources":["../../src/hooks/useLiveUpdate.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,MAAM,EAA6B,MAAM,QAAQ,CAAC;AAI3D,UAAU,kBAAkB;IAC1B,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,WAAW,EAAE,GAAG,CAAC;CAClB;AAED,eAAO,MAAM,aAAa,GAAI,+BAA+B,kBAAkB,SAuD9E,CAAC"}
@@ -1,3 +1,4 @@
1
+ // Live-previews tool option changes (fill, stroke, opacity, dash) on whichever Fabric object is currently selected.
1
2
  import { useEffect } from "react";
2
3
  import { FabricImage } from "fabric";
3
4
  import { Frame } from "../lib/fabric-frame";
@@ -1 +1 @@
1
- {"version":3,"file":"useMouseHandlers.d.ts","sourceRoot":"","sources":["../../src/hooks/useMouseHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,UAAU,qBAAqB;IAC7B,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,UAAU,EAAI,MAAM,CAAC;IACrB,WAAW,EAAG,GAAG,CAAC;IAClB,eAAe,EAAE;QACf,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;QACpC,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;QACpC,aAAa,EAAI,MAAM,IAAI,CAAC;KAC7B,CAAC;IACF,cAAc,EAAE;QACd,eAAe,EAAG,MAAM,IAAI,CAAC;QAC7B,cAAc,EAAI,MAAM,IAAI,CAAC;QAC7B,gBAAgB,EAAE,MAAM,OAAO,CAAC;QAChC,gBAAgB,EAAE,CAAC,OAAO,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;QAC9D,cAAc,EAAI,MAAM,OAAO,CAAC;KACjC,CAAC;CACH;AAED,eAAO,MAAM,gBAAgB,GAAI,gEAK9B,qBAAqB,SAqFvB,CAAC"}
1
+ {"version":3,"file":"useMouseHandlers.d.ts","sourceRoot":"","sources":["../../src/hooks/useMouseHandlers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,UAAU,qBAAqB;IAC7B,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,UAAU,EAAI,MAAM,CAAC;IACrB,WAAW,EAAG,GAAG,CAAC;IAClB,eAAe,EAAE;QACf,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;QACpC,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;QACpC,aAAa,EAAI,MAAM,IAAI,CAAC;KAC7B,CAAC;IACF,cAAc,EAAE;QACd,eAAe,EAAG,MAAM,IAAI,CAAC;QAC7B,cAAc,EAAI,MAAM,IAAI,CAAC;QAC7B,gBAAgB,EAAE,MAAM,OAAO,CAAC;QAChC,gBAAgB,EAAE,CAAC,OAAO,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;QAC9D,cAAc,EAAI,MAAM,OAAO,CAAC;KACjC,CAAC;CACH;AAED,eAAO,MAAM,gBAAgB,GAAI,gEAK9B,qBAAqB,SAqFvB,CAAC"}
@@ -1,3 +1,4 @@
1
+ // Canvas event router — registers Fabric mouse events once and dispatches to drawing/eraser handlers with rAF throttle on mouse:move.
1
2
  import { useEffect, useRef } from "react";
2
3
  export const useMouseHandlers = ({ fabricCanvas, activeTool, drawingHandlers, eraserHandlers, }) => {
3
4
  // ── PERF FIX 1: All props in refs — effect registers ONCE ────────────────
@@ -1 +1 @@
1
- {"version":3,"file":"usePan.d.ts","sourceRoot":"","sources":["../../src/hooks/usePan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,UAAU,WAAW;IACnB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACrE,iBAAiB,EAAE,CAAC,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjE;AAED,eAAO,MAAM,MAAM,GAAI,8DAKpB,WAAW,SA8Gb,CAAC"}
1
+ {"version":3,"file":"usePan.d.ts","sourceRoot":"","sources":["../../src/hooks/usePan.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,UAAU,WAAW;IACnB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACrE,iBAAiB,EAAE,CAAC,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjE;AAED,eAAO,MAAM,MAAM,GAAI,8DAKpB,WAAW,SA8Gb,CAAC"}
@@ -1,3 +1,4 @@
1
+ // Pan tool — click-drag to scroll the canvas viewport; two-finger pinch to zoom on touch devices.
1
2
  import { useEffect, useRef } from "react";
2
3
  export const usePan = ({ fabricCanvas, activeTool, handleZoom, setCanvasViewport, }) => {
3
4
  // Refs so the effect registers ONCE but always reads latest values.
@@ -1 +1 @@
1
- {"version":3,"file":"usePersistance.d.ts","sourceRoot":"","sources":["../../src/hooks/usePersistance.ts"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,SAAS,EAAE,GAAG,EAAE,CAAC;CAClB;AAED,UAAU,mBAAmB;IAC3B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,SAAS,EAAE,GAAG,EAAE,CAAC;IACjB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACzC,kBAAkB,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAG7C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAElD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,eAAO,MAAM,cAAc,GAAI,8GAS5B,mBAAmB,SA+GrB,CAAC"}
1
+ {"version":3,"file":"usePersistance.d.ts","sourceRoot":"","sources":["../../src/hooks/usePersistance.ts"],"names":[],"mappings":"AAEA,OAAO,KAA4B,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,SAAS,EAAE,GAAG,EAAE,CAAC;CAClB;AAED,UAAU,mBAAmB;IAC3B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,SAAS,EAAE,GAAG,EAAE,CAAC;IACjB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACzC,kBAAkB,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAG7C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAElD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,eAAO,MAAM,cAAc,GAAI,8GAS5B,mBAAmB,SA+GrB,CAAC"}
@@ -1,3 +1,4 @@
1
+ // Debounced auto-save — pushes canvas snapshots to the undo/redo history and calls the consumer's onSave callback.
1
2
  import { useEffect, useRef } from "react";
2
3
  // ─── Hook ─────────────────────────────────────────────────────────────────────
3
4
  export const usePersistence = ({ fabricCanvas, tasks, documents, pushHistory, isRestoringRef, suppressHistoryRef, onSave, saveDebounceMs = 2000, }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"useSelection.d.ts","sourceRoot":"","sources":["../../src/hooks/useSelection.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAQ,YAAY,EAAe,MAAM,QAAQ,CAAC;AAMjE,UAAU,iBAAiB;IACzB,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,eAAe,EAAE,CAAC,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1F,wBAAwB,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IAC5D,YAAY,EAAE,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC9C,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC;AAGD,eAAO,MAAM,YAAY,GAAI,8GAO1B,iBAAiB,SAwKnB,CAAC"}
1
+ {"version":3,"file":"useSelection.d.ts","sourceRoot":"","sources":["../../src/hooks/useSelection.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAQ,YAAY,EAAe,MAAM,QAAQ,CAAC;AAMjE,UAAU,iBAAiB;IACzB,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,eAAe,EAAE,CAAC,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1F,wBAAwB,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IAC5D,YAAY,EAAE,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC9C,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC;AAGD,eAAO,MAAM,YAAY,GAAI,8GAO1B,iBAAiB,SAwKnB,CAAC"}
@@ -1,4 +1,4 @@
1
- // hooks/useSelection.ts
1
+ // Tracks Fabric canvas selection events, syncs object type to the store, and drives the rubber-band selection box for HTML nodes.
2
2
  import { useEffect, useRef } from "react";
3
3
  import { Rect, FabricImage } from "fabric";
4
4
  import { Frame } from "../lib/fabric-frame";
@@ -1 +1 @@
1
- {"version":3,"file":"useTextStyle.d.ts","sourceRoot":"","sources":["../../src/hooks/useTextStyle.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAE9C,UAAU,iBAAiB;IACzB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,WAAW,EAAE,GAAG,CAAC;CAClB;AAED,eAAO,MAAM,YAAY,GAAI,+BAA+B,iBAAiB,SAiC5E,CAAC"}
1
+ {"version":3,"file":"useTextStyle.d.ts","sourceRoot":"","sources":["../../src/hooks/useTextStyle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAE9C,UAAU,iBAAiB;IACzB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,WAAW,EAAE,GAAG,CAAC;CAClB;AAED,eAAO,MAAM,YAAY,GAAI,+BAA+B,iBAAiB,SAiC5E,CAAC"}
@@ -1,3 +1,4 @@
1
+ // Applies the current text tool options (font, size, weight, color, alignment) to any selected IText object in real time.
1
2
  import { useEffect } from "react";
2
3
  export const useTextStyle = ({ fabricCanvas, toolOptions }) => {
3
4
  useEffect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"useToolManager.d.ts","sourceRoot":"","sources":["../../src/hooks/useToolManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,UAAU,mBAAmB;IAC3B,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,GAAG,CAAC;IACjB,cAAc,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC7C,mBAAmB,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,gBAAgB,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;CACvC;AAED,eAAO,MAAM,cAAc,GAAI,kHAQ5B,mBAAmB,SA8HrB,CAAC"}
1
+ {"version":3,"file":"useToolManager.d.ts","sourceRoot":"","sources":["../../src/hooks/useToolManager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,UAAU,mBAAmB;IAC3B,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,GAAG,CAAC;IACjB,cAAc,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC7C,mBAAmB,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,gBAAgB,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;CACvC;AAED,eAAO,MAAM,cAAc,GAAI,kHAQ5B,mBAAmB,SAiJrB,CAAC"}
@@ -1,6 +1,9 @@
1
+ // Switches the Fabric canvas drawing mode, cursor style, and per-object interactivity when the active tool changes.
1
2
  import { useEffect } from "react";
2
3
  import { calculateDashArray } from "../lib/fabric-utils";
4
+ import { useWhiteboardStore } from "../store/whiteboard-store";
3
5
  export const useToolManager = ({ fabricCanvas, activeTool, toolOptions, eraserTraceRef, eraserPathRef, eraserPathPointsRef, eraserTargetsRef, }) => {
6
+ const editable = useWhiteboardStore((s) => s.editable);
4
7
  useEffect(() => {
5
8
  const canvas = fabricCanvas.current;
6
9
  if (!canvas)
@@ -16,6 +19,21 @@ export const useToolManager = ({ fabricCanvas, activeTool, toolOptions, eraserTr
16
19
  eraserPathPointsRef.current = [];
17
20
  }
18
21
  }
22
+ // Read-only lockdown — regardless of activeTool, no object may be
23
+ // selected, moved, resized, deleted, or drawn into while not editable.
24
+ if (!editable) {
25
+ canvas.isDrawingMode = false;
26
+ canvas.selection = false;
27
+ canvas.defaultCursor = "default";
28
+ canvas.hoverCursor = "default";
29
+ canvas.discardActiveObject();
30
+ canvas.forEachObject((obj) => {
31
+ obj.selectable = false;
32
+ obj.evented = false;
33
+ });
34
+ canvas.renderAll();
35
+ return;
36
+ }
19
37
  switch (activeTool) {
20
38
  case "select":
21
39
  canvas.isDrawingMode = false;
@@ -111,5 +129,6 @@ export const useToolManager = ({ fabricCanvas, activeTool, toolOptions, eraserTr
111
129
  eraserPathRef,
112
130
  eraserPathPointsRef,
113
131
  eraserTargetsRef,
132
+ editable,
114
133
  ]);
115
134
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useZoom.d.ts","sourceRoot":"","sources":["../../src/hooks/useZoom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,SAAS,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,MAAM,EAAuB,MAAM,QAAQ,CAAC;AAErD,UAAU,YAAY;IACpB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,iBAAiB,EAAE,CAAC,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjE;AAED,eAAO,MAAM,OAAO,GAAI,yEAMrB,YAAY;0BAiB4B,MAAM,UAAU;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;CA2IlF,CAAC"}
1
+ {"version":3,"file":"useZoom.d.ts","sourceRoot":"","sources":["../../src/hooks/useZoom.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkC,SAAS,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,MAAM,EAAuB,MAAM,QAAQ,CAAC;AAErD,UAAU,YAAY;IACpB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,iBAAiB,EAAE,CAAC,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjE;AAED,eAAO,MAAM,OAAO,GAAI,yEAMrB,YAAY;0BAiB4B,MAAM,UAAU;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;CA2IlF,CAAC"}
@@ -1,3 +1,4 @@
1
+ // Handles canvas zoom via mouse wheel, toolbar buttons, and Ctrl+/− keyboard shortcuts; prevents browser native zoom.
1
2
  import { useCallback, useEffect, useRef } from "react";
2
3
  import { Point } from "fabric";
3
4
  export const useZoom = ({ fabricCanvas, MIN_ZOOM, MAX_ZOOM, setCanvasZoom, setCanvasViewport, }) => {