@mhamz.01/easyflow-whiteboard 1.0.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 (127) hide show
  1. package/dist/components/node/custom-node-overlay-layer.d.ts +44 -0
  2. package/dist/components/node/custom-node-overlay-layer.d.ts.map +1 -0
  3. package/dist/components/node/custom-node-overlay-layer.js +353 -0
  4. package/dist/components/node/custom-node.d.ts +17 -0
  5. package/dist/components/node/custom-node.d.ts.map +1 -0
  6. package/dist/components/node/custom-node.js +63 -0
  7. package/dist/components/node/document-node.d.ts +14 -0
  8. package/dist/components/node/document-node.d.ts.map +1 -0
  9. package/dist/components/node/document-node.js +58 -0
  10. package/dist/components/toolbar/document-dropdown.d.ts +14 -0
  11. package/dist/components/toolbar/document-dropdown.d.ts.map +1 -0
  12. package/dist/components/toolbar/document-dropdown.js +66 -0
  13. package/dist/components/toolbar/options/arrow-options.d.ts +8 -0
  14. package/dist/components/toolbar/options/arrow-options.d.ts.map +1 -0
  15. package/dist/components/toolbar/options/arrow-options.js +109 -0
  16. package/dist/components/toolbar/options/erase-option.d.ts +2 -0
  17. package/dist/components/toolbar/options/erase-option.d.ts.map +1 -0
  18. package/dist/components/toolbar/options/erase-option.js +20 -0
  19. package/dist/components/toolbar/options/image-options.d.ts +2 -0
  20. package/dist/components/toolbar/options/image-options.d.ts.map +1 -0
  21. package/dist/components/toolbar/options/image-options.js +10 -0
  22. package/dist/components/toolbar/options/line-options.d.ts +2 -0
  23. package/dist/components/toolbar/options/line-options.d.ts.map +1 -0
  24. package/dist/components/toolbar/options/line-options.js +46 -0
  25. package/dist/components/toolbar/options/pen-option.d.ts +2 -0
  26. package/dist/components/toolbar/options/pen-option.d.ts.map +1 -0
  27. package/dist/components/toolbar/options/pen-option.js +53 -0
  28. package/dist/components/toolbar/options/shape-option.d.ts +6 -0
  29. package/dist/components/toolbar/options/shape-option.d.ts.map +1 -0
  30. package/dist/components/toolbar/options/shape-option.js +58 -0
  31. package/dist/components/toolbar/options/text-option.d.ts +2 -0
  32. package/dist/components/toolbar/options/text-option.d.ts.map +1 -0
  33. package/dist/components/toolbar/options/text-option.js +73 -0
  34. package/dist/components/toolbar/task-dropdown.d.ts +15 -0
  35. package/dist/components/toolbar/task-dropdown.d.ts.map +1 -0
  36. package/dist/components/toolbar/task-dropdown.js +85 -0
  37. package/dist/components/toolbar/toolbar-button.d.ts +12 -0
  38. package/dist/components/toolbar/toolbar-button.d.ts.map +1 -0
  39. package/dist/components/toolbar/toolbar-button.js +8 -0
  40. package/dist/components/toolbar/toolbar-seperator.d.ts +6 -0
  41. package/dist/components/toolbar/toolbar-seperator.d.ts.map +1 -0
  42. package/dist/components/toolbar/toolbar-seperator.js +5 -0
  43. package/dist/components/toolbar/tooloptions-panel.d.ts +8 -0
  44. package/dist/components/toolbar/tooloptions-panel.d.ts.map +1 -0
  45. package/dist/components/toolbar/tooloptions-panel.js +88 -0
  46. package/dist/components/toolbar/whiteboard-toolbar.d.ts +28 -0
  47. package/dist/components/toolbar/whiteboard-toolbar.d.ts.map +1 -0
  48. package/dist/components/toolbar/whiteboard-toolbar.js +160 -0
  49. package/dist/components/ui/dropdown-menu.d.ts +26 -0
  50. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  51. package/dist/components/ui/dropdown-menu.js +51 -0
  52. package/dist/components/ui/label.d.ts +5 -0
  53. package/dist/components/ui/label.d.ts.map +1 -0
  54. package/dist/components/ui/label.js +8 -0
  55. package/dist/components/ui/slider.d.ts +5 -0
  56. package/dist/components/ui/slider.d.ts.map +1 -0
  57. package/dist/components/ui/slider.js +14 -0
  58. package/dist/components/whiteboard/whiteboard-test.d.ts +2 -0
  59. package/dist/components/whiteboard/whiteboard-test.d.ts.map +1 -0
  60. package/dist/components/whiteboard/whiteboard-test.js +207 -0
  61. package/dist/components/whiteboard/whiteboard.d.ts +1 -0
  62. package/dist/components/whiteboard/whiteboard.d.ts.map +1 -0
  63. package/dist/components/whiteboard/whiteboard.js +911 -0
  64. package/dist/components/zoomcontrol/zoom-control.d.ts +9 -0
  65. package/dist/components/zoomcontrol/zoom-control.d.ts.map +1 -0
  66. package/dist/components/zoomcontrol/zoom-control.js +7 -0
  67. package/dist/hooks/useCanvasInit.d.ts +15 -0
  68. package/dist/hooks/useCanvasInit.d.ts.map +1 -0
  69. package/dist/hooks/useCanvasInit.js +89 -0
  70. package/dist/hooks/useDrawing.d.ts +23 -0
  71. package/dist/hooks/useDrawing.d.ts.map +1 -0
  72. package/dist/hooks/useDrawing.js +142 -0
  73. package/dist/hooks/useEraser.d.ts +27 -0
  74. package/dist/hooks/useEraser.d.ts.map +1 -0
  75. package/dist/hooks/useEraser.js +143 -0
  76. package/dist/hooks/useLiveUpdate.d.ts +9 -0
  77. package/dist/hooks/useLiveUpdate.d.ts.map +1 -0
  78. package/dist/hooks/useLiveUpdate.js +63 -0
  79. package/dist/hooks/useMouseHandlers.d.ts +25 -0
  80. package/dist/hooks/useMouseHandlers.d.ts.map +1 -0
  81. package/dist/hooks/useMouseHandlers.js +44 -0
  82. package/dist/hooks/usePan.d.ts +17 -0
  83. package/dist/hooks/usePan.d.ts.map +1 -0
  84. package/dist/hooks/usePan.js +80 -0
  85. package/dist/hooks/usePersistance.d.ts +13 -0
  86. package/dist/hooks/usePersistance.d.ts.map +1 -0
  87. package/dist/hooks/usePersistance.js +79 -0
  88. package/dist/hooks/useSelection.d.ts +21 -0
  89. package/dist/hooks/useSelection.d.ts.map +1 -0
  90. package/dist/hooks/useSelection.js +142 -0
  91. package/dist/hooks/useTextStyle.d.ts +9 -0
  92. package/dist/hooks/useTextStyle.d.ts.map +1 -0
  93. package/dist/hooks/useTextStyle.js +32 -0
  94. package/dist/hooks/useToolManager.d.ts +15 -0
  95. package/dist/hooks/useToolManager.d.ts.map +1 -0
  96. package/dist/hooks/useToolManager.js +115 -0
  97. package/dist/hooks/useZoom.d.ts +25 -0
  98. package/dist/hooks/useZoom.d.ts.map +1 -0
  99. package/dist/hooks/useZoom.js +133 -0
  100. package/dist/index.d.ts +4 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +30 -0
  103. package/dist/lib/eraser-brush.d.ts +1 -0
  104. package/dist/lib/eraser-brush.d.ts.map +1 -0
  105. package/dist/lib/eraser-brush.js +21 -0
  106. package/dist/lib/fabric-arrow.d.ts +16 -0
  107. package/dist/lib/fabric-arrow.d.ts.map +1 -0
  108. package/dist/lib/fabric-arrow.js +50 -0
  109. package/dist/lib/fabric-bidirectional-arrow.d.ts +20 -0
  110. package/dist/lib/fabric-bidirectional-arrow.d.ts.map +1 -0
  111. package/dist/lib/fabric-bidirectional-arrow.js +65 -0
  112. package/dist/lib/fabric-frame.d.ts +7 -0
  113. package/dist/lib/fabric-frame.d.ts.map +1 -0
  114. package/dist/lib/fabric-frame.js +25 -0
  115. package/dist/lib/fabric-utils.d.ts +30 -0
  116. package/dist/lib/fabric-utils.d.ts.map +1 -0
  117. package/dist/lib/fabric-utils.js +273 -0
  118. package/dist/lib/utils.d.ts +3 -0
  119. package/dist/lib/utils.d.ts.map +1 -0
  120. package/dist/lib/utils.js +5 -0
  121. package/dist/store/whiteboard-store.d.ts +99 -0
  122. package/dist/store/whiteboard-store.d.ts.map +1 -0
  123. package/dist/store/whiteboard-store.js +137 -0
  124. package/dist/types/canvas-node.d.ts +24 -0
  125. package/dist/types/canvas-node.d.ts.map +1 -0
  126. package/dist/types/canvas-node.js +1 -0
  127. package/package.json +34 -0
@@ -0,0 +1,66 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "../ui/dropdown-menu";
4
+ import { FileText, Plus, Clock, Layers } from "lucide-react";
5
+ import { useWhiteboardStore } from "../../store/whiteboard-store";
6
+ const AVAILABLE_DOCUMENTS = [
7
+ {
8
+ id: "doc-1",
9
+ title: "Product Requirements Document",
10
+ project: "Website Redesign",
11
+ breadcrumb: ["Design", "Specs"],
12
+ preview: "This document outlines the core requirements for the new landing page including user flows, component specs, and acceptance criteria.",
13
+ updatedAt: "2 hours ago",
14
+ },
15
+ {
16
+ id: "doc-2",
17
+ title: "API Integration Guide",
18
+ project: "Backend API",
19
+ breadcrumb: ["Engineering", "Docs"],
20
+ preview: "Step-by-step guide for integrating the REST API endpoints. Covers authentication, rate limiting, and error handling patterns.",
21
+ updatedAt: "Yesterday",
22
+ },
23
+ {
24
+ id: "doc-3",
25
+ title: "Sprint Retrospective Notes",
26
+ project: "Team",
27
+ breadcrumb: ["Meetings"],
28
+ preview: "Summary of what went well, what needs improvement, and action items from the last sprint cycle.",
29
+ updatedAt: "3 days ago",
30
+ },
31
+ {
32
+ id: "doc-4",
33
+ title: "Deployment Runbook",
34
+ project: "DevOps",
35
+ breadcrumb: ["Infrastructure", "Guides"],
36
+ preview: "Step-by-step deployment checklist for production releases. Includes rollback procedures and health check endpoints.",
37
+ updatedAt: "1 week ago",
38
+ },
39
+ {
40
+ id: "doc-5",
41
+ title: "User Research Findings",
42
+ project: "Website Redesign",
43
+ breadcrumb: ["Research"],
44
+ preview: "Key insights from 12 user interviews conducted in Q1. Highlights pain points and opportunities for the onboarding flow.",
45
+ updatedAt: "4 days ago",
46
+ },
47
+ ];
48
+ export default function DocumentDropdown({ onAddDocument, }) {
49
+ const setActiveTool = useWhiteboardStore((state) => state.setActiveTool);
50
+ const activeDropdown = useWhiteboardStore((s) => s.activeDropdown);
51
+ const setActiveDropdown = useWhiteboardStore((s) => s.setActiveDropdown);
52
+ return (_jsxs(DropdownMenu, { open: activeDropdown === "document", onOpenChange: (open) => {
53
+ if (open) {
54
+ setActiveTool("select");
55
+ setActiveDropdown("document");
56
+ }
57
+ else {
58
+ setActiveDropdown(null);
59
+ }
60
+ }, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: "flex items-center justify-center w-10 h-10 rounded-xl transition-all duration-300 text-white hover:bg-white/10", children: _jsx(FileText, { className: "w-5 h-5", strokeWidth: 2.5 }) }) }), _jsxs(DropdownMenuContent, { side: "top", align: "start", sideOffset: 10, className: "w-[380px] bg-[#121214]/95 backdrop-blur-2xl border border-white/10 rounded-2xl shadow-[0_32px_64px_rgba(0,0,0,0.6)] p-0 z-[100]", children: [_jsx("div", { className: "p-4 border-b border-white/5 flex items-center justify-between", children: _jsxs("div", { className: "flex items-center gap-2 text-[#029AFF]", children: [_jsx(Layers, { className: "w-4 h-4" }), _jsx("h3", { className: "text-xs font-bold uppercase tracking-[0.2em]", children: "Documents" })] }) }), _jsx("div", { className: "max-h-80 overflow-y-auto p-2", children: AVAILABLE_DOCUMENTS.map((doc) => (_jsx("button", { onClick: () => {
61
+ // 1. Add the document to the canvas
62
+ onAddDocument(doc);
63
+ // 2. CLOSE the dropdown immediately
64
+ setActiveDropdown(null);
65
+ }, className: "group w-full p-4 hover:bg-white/5 rounded-xl transition-all text-left border border-transparent hover:border-white/5", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "w-10 h-10 rounded-lg bg-[#029AFF]/10 flex items-center justify-center flex-shrink-0 group-hover:bg-[#029AFF] transition-all", children: _jsx(FileText, { className: "w-5 h-5 text-[#029AFF] group-hover:text-white" }) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h4", { className: "text-sm font-bold text-gray-100 truncate mb-1", children: doc.title }), _jsxs("div", { className: "flex items-center gap-1 text-[10px] text-gray-500 font-semibold uppercase tracking-tight", children: [doc.project, doc.breadcrumb?.map((b) => ` › ${b}`)] }), _jsxs("p", { className: "text-[11px] text-gray-500 line-clamp-2 mt-2 leading-relaxed italic group-hover:text-gray-400", children: ["\"", doc.preview, "\""] }), _jsxs("div", { className: "flex items-center gap-1.5 mt-3 text-[9px] text-gray-600", children: [_jsx(Clock, { className: "w-3 h-3" }), " ", doc.updatedAt] })] })] }) }, doc.id))) }), _jsx("div", { className: "p-3 bg-white/[0.02] border-t border-white/5", children: _jsxs("button", { className: "w-full py-3 flex items-center justify-center gap-2 text-xs font-bold text-[#029AFF] hover:bg-[#029AFF] hover:text-white rounded-xl transition-all border border-[#029AFF]/20", children: [_jsx(Plus, { className: "w-4 h-4" }), " CREATE NEW DOC"] }) })] })] }));
66
+ }
@@ -0,0 +1,8 @@
1
+ import { RefObject } from "react";
2
+ import { Canvas } from "fabric";
3
+ interface ArrowOptionsProps {
4
+ fabricCanvas?: RefObject<Canvas | null>;
5
+ }
6
+ export default function ArrowOptions({ fabricCanvas }: ArrowOptionsProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=arrow-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrow-options.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/arrow-options.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAuB,SAAS,EAAE,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAehC,UAAU,iBAAiB;IACzB,YAAY,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACzC;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,YAAY,EAAE,EAAE,iBAAiB,2CAiNvE"}
@@ -0,0 +1,109 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
+ import { cn } from "../../../lib/utils";
5
+ import { useEffect, useState } from "react";
6
+ import { Arrow } from "../../../lib/fabric-arrow";
7
+ import { BidirectionalArrow } from "../../../lib/fabric-bidirectional-arrow";
8
+ const PRESET_COLORS = [
9
+ "#FFFFFF", "#000000", "#DC2626", "#EA580C",
10
+ "#CA8A04", "#16A34A", "#2563EB", "#7C3AED", "#DB2777",
11
+ ];
12
+ const STROKE_STYLES = [
13
+ { id: "solid", label: "Solid", dashArray: null },
14
+ { id: "dashed", label: "Dashed", dashArray: [10, 5] },
15
+ { id: "dotted", label: "Dotted", dashArray: [2, 4] },
16
+ ];
17
+ export default function ArrowOptions({ fabricCanvas }) {
18
+ const toolOptions = useWhiteboardStore((state) => state.toolOptions);
19
+ const setToolOption = useWhiteboardStore((state) => state.setToolOption);
20
+ const { strokeColor, strokeWidth, strokeDashArray } = toolOptions.arrow;
21
+ const [isBidirectional, setIsBidirectional] = useState(false);
22
+ const isPresetColor = PRESET_COLORS.includes(strokeColor);
23
+ const activeStrokeStyle = STROKE_STYLES.find(style => {
24
+ if (!style.dashArray && !strokeDashArray)
25
+ return true;
26
+ if (style.dashArray && strokeDashArray &&
27
+ style.dashArray[0] === strokeDashArray[0] &&
28
+ style.dashArray[1] === strokeDashArray[1])
29
+ return true;
30
+ return false;
31
+ })?.id || "solid";
32
+ // Detect if selected object is bidirectional
33
+ useEffect(() => {
34
+ const canvas = fabricCanvas?.current;
35
+ if (!canvas)
36
+ return;
37
+ const handleSelection = () => {
38
+ const activeObj = canvas.getActiveObject();
39
+ if (activeObj) {
40
+ setIsBidirectional(activeObj.type === "bidirectional-arrow");
41
+ }
42
+ };
43
+ canvas.on("selection:created", handleSelection);
44
+ canvas.on("selection:updated", handleSelection);
45
+ canvas.on("selection:cleared", () => setIsBidirectional(false));
46
+ return () => {
47
+ canvas.off("selection:created", handleSelection);
48
+ canvas.off("selection:updated", handleSelection);
49
+ canvas.off("selection:cleared", () => setIsBidirectional(false));
50
+ };
51
+ }, [fabricCanvas]);
52
+ // Toggle between Arrow and BidirectionalArrow
53
+ const handleBidirectionalToggle = (bidirectional) => {
54
+ const canvas = fabricCanvas?.current;
55
+ if (!canvas)
56
+ return;
57
+ const activeObj = canvas.getActiveObject();
58
+ if (!activeObj || (activeObj.type !== "arrow" && activeObj.type !== "bidirectional-arrow"))
59
+ return;
60
+ // Get current properties
61
+ const props = {
62
+ stroke: activeObj.stroke,
63
+ strokeWidth: activeObj.strokeWidth,
64
+ strokeDashArray: activeObj.strokeDashArray,
65
+ left: activeObj.left,
66
+ top: activeObj.top,
67
+ angle: activeObj.angle,
68
+ scaleX: activeObj.scaleX,
69
+ scaleY: activeObj.scaleY,
70
+ };
71
+ // Get line points
72
+ const points = [
73
+ activeObj.x1,
74
+ activeObj.y1,
75
+ activeObj.x2,
76
+ activeObj.y2,
77
+ ];
78
+ // Remove old arrow
79
+ canvas.remove(activeObj);
80
+ // Create new arrow of the desired type
81
+ let newArrow;
82
+ if (bidirectional) {
83
+ newArrow = new BidirectionalArrow(points, props);
84
+ }
85
+ else {
86
+ newArrow = new Arrow(points, props);
87
+ }
88
+ // Add to canvas and select it
89
+ canvas.add(newArrow);
90
+ canvas.setActiveObject(newArrow);
91
+ canvas.renderAll();
92
+ setIsBidirectional(bidirectional);
93
+ };
94
+ return (_jsxs("div", { className: "space-y-6 text-neutral-200", children: [_jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-neutral-400", children: "Arrow Settings" }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Direction" }), _jsxs("div", { className: "flex gap-2", children: [_jsxs("button", { onClick: () => handleBidirectionalToggle(false), className: cn("flex-1 h-10 rounded-lg border transition-colors duration-150 flex items-center justify-center gap-2", !isBidirectional
95
+ ? "bg-neutral-700 border-neutral-500 text-white"
96
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"), children: [_jsx("span", { className: "text-lg", children: "\u2192" }), _jsx("span", { className: "text-xs font-medium", children: "One-way" })] }), _jsxs("button", { onClick: () => handleBidirectionalToggle(true), className: cn("flex-1 h-10 rounded-lg border transition-colors duration-150 flex items-center justify-center gap-2", isBidirectional
97
+ ? "bg-neutral-700 border-neutral-500 text-white"
98
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"), children: [_jsx("span", { className: "text-lg", children: "\u2194" }), _jsx("span", { className: "text-xs font-medium", children: "Two-way" })] })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Line Style" }), _jsx("div", { className: "flex gap-2", children: STROKE_STYLES.map((style) => {
99
+ const isActive = activeStrokeStyle === style.id;
100
+ return (_jsxs("button", { onClick: () => setToolOption("arrow", "strokeDashArray", style.dashArray), className: cn("flex-1 h-10 rounded-lg border transition-colors duration-150 flex flex-col items-center justify-center gap-1", isActive
101
+ ? "bg-neutral-700 border-neutral-500 text-white"
102
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"), children: [_jsxs("div", { className: "w-8 h-[2px] relative", children: [style.id === "solid" && _jsx("div", { className: "absolute inset-0 bg-current" }), style.id === "dashed" && (_jsx("svg", { width: "32", height: "2", viewBox: "0 0 32 2", className: "absolute inset-0", children: _jsx("line", { x1: "0", y1: "1", x2: "32", y2: "1", stroke: "currentColor", strokeWidth: "2", strokeDasharray: "10 5" }) })), style.id === "dotted" && (_jsx("svg", { width: "32", height: "2", viewBox: "0 0 32 2", className: "absolute inset-0", children: _jsx("line", { x1: "0", y1: "1", x2: "32", y2: "1", stroke: "currentColor", strokeWidth: "2", strokeDasharray: "2 4" }) }))] }), _jsx("span", { className: "text-[10px] font-medium", children: style.label })] }, style.id));
103
+ }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Color" }), _jsx("div", { className: "flex flex-wrap gap-2 overflow-visible", children: PRESET_COLORS.map((preset) => {
104
+ const isActive = strokeColor === preset;
105
+ return (_jsx("button", { onClick: () => setToolOption("arrow", "strokeColor", preset), className: cn("h-7 w-7 rounded-md border transition-colors duration-150", isActive
106
+ ? "border-neutral-200 outline outline-2 outline-neutral-400"
107
+ : "border-neutral-700 hover:border-neutral-500"), style: { backgroundColor: preset } }, preset));
108
+ }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Stroke Width" }), _jsxs("span", { className: "text-xs font-semibold text-neutral-100", children: [strokeWidth, "px"] })] }), _jsx("input", { type: "range", min: 1, max: 10, step: 1, value: strokeWidth, onChange: (e) => setToolOption("arrow", "strokeWidth", parseInt(e.target.value)), className: "w-full h-2 rounded-lg appearance-none cursor-pointer bg-neutral-800 accent-neutral-200" })] }), _jsx("div", { className: "p-3 bg-neutral-900 border border-neutral-800 rounded-lg", children: _jsxs("p", { className: "text-[11px] text-neutral-400 leading-relaxed", children: [_jsx("strong", { className: "text-neutral-300", children: "Tip:" }), " Select an arrow and toggle between one-way (\u2192) and two-way (\u2194) directions."] }) })] }));
109
+ }
@@ -0,0 +1,2 @@
1
+ export default function EraserOptions(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=erase-option.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erase-option.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/erase-option.tsx"],"names":[],"mappings":"AAMA,MAAM,CAAC,OAAO,UAAU,aAAa,4CAgEpC"}
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
+ const PRESET_SIZES = [10, 20, 30, 50];
5
+ export default function EraserOptions() {
6
+ const toolOptions = useWhiteboardStore((state) => state.toolOptions);
7
+ const setToolOption = useWhiteboardStore((state) => state.setToolOption);
8
+ const { size } = toolOptions.eraser;
9
+ return (_jsxs("div", { className: "space-y-6 text-neutral-200", children: [_jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-neutral-400", children: "Eraser Settings" }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Size" }), _jsxs("span", { className: "text-xs font-semibold text-neutral-100", children: [size, "px"] })] }), _jsx("div", { className: "flex gap-2", children: PRESET_SIZES.map((presetSize) => {
10
+ const isActive = size === presetSize;
11
+ return (_jsx("button", { onClick: () => setToolOption("eraser", "size", presetSize), className: `flex-1 h-8 rounded-lg border text-xs font-medium flex items-center justify-center transition-colors duration-150
12
+ ${isActive
13
+ ? "bg-neutral-700 border-neutral-500 text-white"
14
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"}
15
+ `, children: presetSize }, presetSize));
16
+ }) }), _jsx("input", { type: "range", min: 5, max: 100, step: 5, value: size, onChange: (e) => setToolOption("eraser", "size", parseInt(e.target.value)), className: "w-full h-2 rounded-lg appearance-none cursor-pointer bg-neutral-800 accent-neutral-200" })] }), _jsx("div", { className: "flex items-center justify-center p-4 bg-neutral-900 rounded-lg border border-neutral-700", children: _jsx("div", { className: "rounded-full bg-neutral-500", style: {
17
+ width: `${size}px`,
18
+ height: `${size}px`,
19
+ } }) })] }));
20
+ }
@@ -0,0 +1,2 @@
1
+ export default function ImageOptions(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=image-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-options.d.ts","sourceRoot":"","sources":["../../../../src/components/toolbar/options/image-options.tsx"],"names":[],"mappings":"AAMA,MAAM,CAAC,OAAO,UAAU,YAAY,4CA8CnC"}
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
+ import { Label } from "../../../components/ui/label";
5
+ import { Slider } from "../../../components/ui/slider";
6
+ export default function ImageOptions() {
7
+ const toolOptions = useWhiteboardStore((state) => state.toolOptions);
8
+ const setToolOption = useWhiteboardStore((state) => state.setToolOption);
9
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { className: "text-xs text-neutral-400", children: "Opacity" }), _jsxs("span", { className: "text-xs text-neutral-500", children: [Math.round(toolOptions.image.opacity * 100), "%"] })] }), _jsx(Slider, { value: [toolOptions.image.opacity], min: 0, max: 1, step: 0.01, onValueChange: ([value]) => setToolOption("image", "opacity", value), className: "w-full" })] }), _jsxs("div", { className: "mt-4 p-3 bg-neutral-800/50 rounded-lg border border-neutral-700", children: [_jsxs("p", { className: "text-xs text-neutral-400", children: ["Click the ", _jsx("strong", { className: "text-neutral-200", children: "Image" }), " button to upload an image from your computer."] }), _jsx("p", { className: "text-xs text-neutral-500 mt-2", children: "Supported formats: JPG, PNG, GIF, WebP" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-xs text-neutral-400", children: "Tips" }), _jsxs("ul", { className: "text-xs text-neutral-500 space-y-1 list-disc list-inside", children: [_jsx("li", { children: "Drag corners to resize" }), _jsx("li", { children: "Click and drag to move" }), _jsx("li", { children: "Use Delete key to remove" }), _jsx("li", { children: "Double-click to edit properties" })] })] })] }));
10
+ }
@@ -0,0 +1,2 @@
1
+ export default function LineOptions(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=line-options.d.ts.map
@@ -0,0 +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,4CAyJlC"}
@@ -0,0 +1,46 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
+ import { cn } from "../../../lib/utils";
5
+ const PRESET_COLORS = [
6
+ "#FFFFFF", // White
7
+ "#000000", // Black
8
+ "#DC2626", // Red
9
+ "#EA580C", // Orange
10
+ "#CA8A04", // Yellow
11
+ "#16A34A", // Green
12
+ "#2563EB", // Blue
13
+ "#7C3AED", // Purple
14
+ "#DB2777", // Pink
15
+ ];
16
+ const STROKE_STYLES = [
17
+ { id: "solid", label: "Solid", dashArray: null },
18
+ { id: "dashed", label: "Dashed", dashArray: [10, 5] },
19
+ { id: "dotted", label: "Dotted", dashArray: [2, 4] },
20
+ ];
21
+ export default function LineOptions() {
22
+ const toolOptions = useWhiteboardStore((state) => state.toolOptions);
23
+ const setToolOption = useWhiteboardStore((state) => state.setToolOption);
24
+ const { strokeColor, strokeWidth, strokeDashArray } = toolOptions.line;
25
+ const isPresetColor = PRESET_COLORS.includes(strokeColor);
26
+ const activeStrokeStyle = STROKE_STYLES.find(style => {
27
+ if (!style.dashArray && !strokeDashArray)
28
+ return true;
29
+ if (style.dashArray && strokeDashArray &&
30
+ style.dashArray[0] === strokeDashArray[0] &&
31
+ style.dashArray[1] === strokeDashArray[1])
32
+ return true;
33
+ return false;
34
+ })?.id || "solid";
35
+ return (_jsxs("div", { className: "space-y-6 text-neutral-200", children: [_jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-neutral-400", children: "Line Settings" }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Line Style" }), _jsx("div", { className: "flex gap-2", children: STROKE_STYLES.map((style) => {
36
+ const isActive = activeStrokeStyle === style.id;
37
+ return (_jsxs("button", { onClick: () => setToolOption("line", "strokeDashArray", style.dashArray), className: cn("flex-1 h-10 rounded-lg border transition-colors duration-150 flex flex-col items-center justify-center gap-1", isActive
38
+ ? "bg-neutral-700 border-neutral-500 text-white"
39
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"), title: style.label, children: [_jsxs("div", { className: "w-8 h-[2px] relative", children: [style.id === "solid" && (_jsx("div", { className: "absolute inset-0 bg-current" })), style.id === "dashed" && (_jsx("svg", { width: "32", height: "2", viewBox: "0 0 32 2", className: "absolute inset-0", children: _jsx("line", { x1: "0", y1: "1", x2: "32", y2: "1", stroke: "currentColor", strokeWidth: "2", strokeDasharray: "10 5" }) })), style.id === "dotted" && (_jsx("svg", { width: "32", height: "2", viewBox: "0 0 32 2", className: "absolute inset-0", children: _jsx("line", { x1: "0", y1: "1", x2: "32", y2: "1", stroke: "currentColor", strokeWidth: "2", strokeDasharray: "2 4" }) }))] }), _jsx("span", { className: "text-[10px] font-medium", children: style.label })] }, style.id));
40
+ }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Color" }), _jsx("div", { className: "flex items-center gap-3 overflow-visible pr-1", children: _jsx("div", { className: "flex flex-wrap gap-2 overflow-visible", children: PRESET_COLORS.map((preset) => {
41
+ const isActive = strokeColor === preset;
42
+ return (_jsx("button", { onClick: () => setToolOption("line", "strokeColor", preset), className: cn("h-7 w-7 rounded-md border transition-colors duration-150", isActive
43
+ ? "border-neutral-200 outline outline-2 outline-neutral-400"
44
+ : "border-neutral-700 hover:border-neutral-500"), style: { backgroundColor: preset }, "aria-label": `Color ${preset}` }, preset));
45
+ }) }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Stroke Width" }), _jsxs("span", { className: "text-xs font-semibold text-neutral-100", children: [strokeWidth, "px"] })] }), _jsx("input", { type: "range", min: 1, max: 10, step: 1, value: strokeWidth, onChange: (e) => setToolOption("line", "strokeWidth", parseInt(e.target.value)), className: "w-full h-2 rounded-lg appearance-none cursor-pointer bg-neutral-800 accent-neutral-200" })] }), _jsx("div", { className: "p-3 bg-neutral-900 border border-neutral-800 rounded-lg", children: _jsxs("p", { className: "text-[11px] text-neutral-400 leading-relaxed", children: [_jsx("strong", { className: "text-neutral-300", children: "Tip:" }), " Click and drag to draw a line. The line follows your cursor until you release."] }) })] }));
46
+ }
@@ -0,0 +1,2 @@
1
+ export default function PenOptions(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=pen-option.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,53 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
+ import { cn } from "../../../lib/utils";
5
+ const PRESET_COLORS = [
6
+ "#FFFFFF",
7
+ "#DC2626",
8
+ "#EA580C",
9
+ "#CA8A04",
10
+ "#16A34A",
11
+ "#2563EB",
12
+ "#7C3AED",
13
+ "#DB2777",
14
+ ];
15
+ const PRESET_WIDTHS = [1, 2, 4, 8, 12];
16
+ // Stroke styles for pen
17
+ const STROKE_STYLES = [
18
+ { id: "solid", label: "Solid", dashArray: null },
19
+ { id: "dashed", label: "Dashed", dashArray: [2, 2] },
20
+ { id: "dotted", label: "Dotted", dashArray: [0.5, 2] },
21
+ ];
22
+ export default function PenOptions() {
23
+ const toolOptions = useWhiteboardStore((state) => state.toolOptions);
24
+ const setToolOption = useWhiteboardStore((state) => state.setToolOption);
25
+ const { color, strokeWidth, opacity, strokeDashArray } = toolOptions.pen;
26
+ const isPresetColor = PRESET_COLORS.includes(color);
27
+ // Determine active stroke style
28
+ const activeStrokeStyle = STROKE_STYLES.find(style => {
29
+ if (!style.dashArray && !strokeDashArray)
30
+ return true;
31
+ if (style.dashArray && strokeDashArray &&
32
+ style.dashArray[0] === strokeDashArray[0] &&
33
+ style.dashArray[1] === strokeDashArray[1])
34
+ return true;
35
+ return false;
36
+ })?.id || "solid";
37
+ return (_jsxs("div", { className: "space-y-6 text-neutral-200 overflow-hidden", children: [_jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-neutral-400", children: "Pen settings" }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Stroke style" }), _jsx("div", { className: "flex gap-2", children: STROKE_STYLES.map((style) => {
38
+ const isActive = activeStrokeStyle === style.id;
39
+ return (_jsxs("button", { onClick: () => setToolOption("pen", "strokeDashArray", style.dashArray), className: cn("flex-1 h-10 rounded-lg border transition-colors duration-150 flex flex-col items-center justify-center gap-1", isActive
40
+ ? "bg-neutral-700 border-neutral-500 text-white"
41
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"), title: style.label, children: [_jsxs("div", { className: "w-8 h-[2px] relative", children: [style.id === "solid" && (_jsx("div", { className: "absolute inset-0 bg-current" })), style.id === "dashed" && (_jsx("svg", { width: "32", height: "2", viewBox: "0 0 32 2", className: "absolute inset-0", children: _jsx("line", { x1: "0", y1: "1", x2: "32", y2: "1", stroke: "currentColor", strokeWidth: "2", strokeDasharray: "10 5" }) })), style.id === "dotted" && (_jsx("svg", { width: "32", height: "2", viewBox: "0 0 32 2", className: "absolute inset-0", children: _jsx("line", { x1: "0", y1: "1", x2: "32", y2: "1", stroke: "currentColor", strokeWidth: "2", strokeDasharray: "2 4" }) }))] }), _jsx("span", { className: "text-[10px] font-medium", children: style.label })] }, style.id));
42
+ }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Color" }), _jsx("div", { className: "flex items-center gap-3 overflow-visible pr-1", children: _jsx("div", { className: "flex flex-wrap gap-2 overflow-visible", children: PRESET_COLORS.map((preset) => {
43
+ const isActive = color === preset;
44
+ return (_jsx("button", { onClick: () => setToolOption("pen", "color", preset), className: cn("h-7 w-7 rounded-md border transition-colors duration-150", isActive
45
+ ? "border-neutral-200 outline outline-2 outline-neutral-400"
46
+ : "border-neutral-700 hover:border-neutral-500"), style: { backgroundColor: preset }, "aria-label": `Color ${preset}` }, preset));
47
+ }) }) })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Stroke width" }), _jsxs("span", { className: "text-xs font-semibold text-neutral-100", children: [strokeWidth, "px"] })] }), _jsx("div", { className: "flex gap-2", children: PRESET_WIDTHS.map((width) => {
48
+ const isActive = strokeWidth === width;
49
+ return (_jsx("button", { onClick: () => setToolOption("pen", "strokeWidth", width), className: cn("flex-1 h-8 rounded-lg border text-xs font-medium transition-colors duration-150", isActive
50
+ ? "bg-neutral-700 border-neutral-500 text-white"
51
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"), children: width }, width));
52
+ }) }), _jsx("input", { type: "range", min: 1, max: 20, step: 1, value: strokeWidth, onChange: (e) => setToolOption("pen", "strokeWidth", Number(e.target.value)), className: "w-full h-2 rounded-lg appearance-none cursor-pointer bg-neutral-800 accent-neutral-200" })] })] }));
53
+ }
@@ -0,0 +1,6 @@
1
+ interface ShapeOptionsProps {
2
+ shapeType: "rectangle" | "circle" | "frame";
3
+ }
4
+ export default function ShapeOptions({ shapeType }: ShapeOptionsProps): import("react/jsx-runtime").JSX.Element;
5
+ export {};
6
+ //# sourceMappingURL=shape-option.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
+ import { cn } from "../../../lib/utils";
5
+ const PRESET_COLORS = [
6
+ "transparent",
7
+ "#000000",
8
+ "#DC2626",
9
+ "#EA580C",
10
+ "#CA8A04",
11
+ "#16A34A",
12
+ "#2563EB",
13
+ "#7C3AED",
14
+ ];
15
+ // Stroke styles for shapes
16
+ const STROKE_STYLES = [
17
+ { id: "solid", label: "Solid", dashArray: null },
18
+ { id: "dashed", label: "Dashed", dashArray: [10, 5] },
19
+ { id: "dotted", label: "Dotted", dashArray: [2, 4] },
20
+ ];
21
+ export default function ShapeOptions({ shapeType }) {
22
+ const toolOptions = useWhiteboardStore((state) => state.toolOptions);
23
+ const setToolOption = useWhiteboardStore((state) => state.setToolOption);
24
+ const { fillColor, strokeColor, strokeWidth, strokeDashArray } = toolOptions[shapeType];
25
+ // Determine active stroke style
26
+ const activeStrokeStyle = STROKE_STYLES.find(style => {
27
+ if (!style.dashArray && !strokeDashArray)
28
+ return true;
29
+ if (style.dashArray && strokeDashArray &&
30
+ style.dashArray[0] === strokeDashArray[0] &&
31
+ style.dashArray[1] === strokeDashArray[1])
32
+ return true;
33
+ return false;
34
+ })?.id || "solid";
35
+ return (_jsxs("div", { className: "space-y-6 text-neutral-200 overflow-hidden", children: [_jsxs("p", { className: "text-[11px] font-medium uppercase tracking-wide text-neutral-400", children: [shapeType, " settings"] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Stroke style" }), _jsx("div", { className: "flex gap-2", children: STROKE_STYLES.map((style) => {
36
+ const isActive = activeStrokeStyle === style.id;
37
+ return (_jsxs("button", { onClick: () => setToolOption(shapeType, "strokeDashArray", style.dashArray), className: cn("flex-1 h-10 rounded-lg border transition-colors duration-150 flex flex-col items-center justify-center gap-1", isActive
38
+ ? "bg-neutral-700 border-neutral-500 text-white"
39
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"), title: style.label, children: [_jsxs("div", { className: "w-8 h-[2px] relative", children: [style.id === "solid" && (_jsx("div", { className: "absolute inset-0 bg-current" })), style.id === "dashed" && (_jsx("svg", { width: "32", height: "2", viewBox: "0 0 32 2", className: "absolute inset-0", children: _jsx("line", { x1: "0", y1: "1", x2: "32", y2: "1", stroke: "currentColor", strokeWidth: "2", strokeDasharray: "10 5" }) })), style.id === "dotted" && (_jsx("svg", { width: "32", height: "2", viewBox: "0 0 32 2", className: "absolute inset-0", children: _jsx("line", { x1: "0", y1: "1", x2: "32", y2: "1", stroke: "currentColor", strokeWidth: "2", strokeDasharray: "2 4" }) }))] }), _jsx("span", { className: "text-[10px] font-medium", children: style.label })] }, style.id));
40
+ }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Fill Color" }), _jsx("div", { className: "flex items-center gap-3 overflow-visible pr-1", children: _jsx("div", { className: "flex flex-wrap gap-2 overflow-visible", children: PRESET_COLORS.map((presetColor) => {
41
+ const isActive = fillColor === presetColor;
42
+ return (_jsx("button", { onClick: () => setToolOption(shapeType, "fillColor", presetColor), className: cn("w-7 h-7 rounded-md border transition-colors duration-150", isActive
43
+ ? "border-neutral-200 outline outline-2 outline-neutral-400"
44
+ : "border-neutral-700 hover:border-neutral-500"), style: {
45
+ backgroundColor: presetColor === "transparent" ? undefined : presetColor,
46
+ backgroundImage: presetColor === "transparent"
47
+ ? "linear-gradient(45deg, #444 25%, transparent 25%, transparent 75%, #444 75%, #444), linear-gradient(45deg, #444 25%, transparent 25%, transparent 75%, #444 75%, #444)"
48
+ : undefined,
49
+ backgroundSize: presetColor === "transparent" ? "8px 8px" : undefined,
50
+ backgroundPosition: presetColor === "transparent" ? "0 0, 4px 4px" : undefined,
51
+ }, "aria-label": `Fill color ${presetColor}` }, `fill-${presetColor}`));
52
+ }) }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Stroke Color" }), _jsx("div", { className: "flex items-center gap-3 overflow-visible pr-1", children: _jsx("div", { className: "flex flex-wrap gap-2 overflow-visible", children: PRESET_COLORS.slice(1).map((presetColor) => {
53
+ const isActive = strokeColor === presetColor;
54
+ return (_jsx("button", { onClick: () => setToolOption(shapeType, "strokeColor", presetColor), className: cn("w-7 h-7 rounded-md border transition-colors duration-150", isActive
55
+ ? "border-neutral-200 outline outline-2 outline-neutral-400"
56
+ : "border-neutral-700 hover:border-neutral-500"), style: { backgroundColor: presetColor }, "aria-label": `Stroke color ${presetColor}` }, `stroke-${presetColor}`));
57
+ }) }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Stroke Width" }), _jsxs("span", { className: "text-xs font-semibold text-neutral-100", children: [strokeWidth, "px"] })] }), _jsx("input", { type: "range", min: 1, max: 10, step: 1, value: strokeWidth, onChange: (e) => setToolOption(shapeType, "strokeWidth", parseInt(e.target.value)), className: "w-full h-2 rounded-lg appearance-none cursor-pointer bg-neutral-800 accent-neutral-200" })] })] }));
58
+ }
@@ -0,0 +1,2 @@
1
+ export default function TextOptions(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=text-option.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,73 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useWhiteboardStore } from "../../../store/whiteboard-store";
4
+ import { cn } from "../../../lib/utils";
5
+ import { AlignLeft, AlignCenter, AlignRight, AlignJustify, ChevronDown } from "lucide-react";
6
+ import { useState } from "react";
7
+ // Professional fonts for whiteboard use
8
+ const FONT_FAMILIES = [
9
+ { value: "Inter", label: "Inter", style: "font-sans" },
10
+ { value: "Roboto", label: "Roboto", style: "font-sans" },
11
+ { value: "Caveat", label: "Caveat", style: "font-handwriting" },
12
+ { value: "Courier New", label: "Courier", style: "font-mono" },
13
+ { value: "Georgia", label: "Georgia", style: "font-serif" },
14
+ ];
15
+ const FONT_WEIGHTS = [
16
+ { value: "300", label: "Light" },
17
+ { value: "400", label: "Regular" },
18
+ { value: "600", label: "Medium" },
19
+ { value: "700", label: "Bold" },
20
+ ];
21
+ const PRESET_SIZES = [12, 16, 20, 24, 32, 48];
22
+ const PRESET_COLORS = [
23
+ "#000000", // Black
24
+ "#FFFFFF", // White
25
+ "#DC2626", // Red
26
+ "#EA580C", // Orange
27
+ "#CA8A04", // Yellow
28
+ "#16A34A", // Green
29
+ "#2563EB", // Blue
30
+ "#7C3AED", // Purple
31
+ "#DB2777", // Pink
32
+ ];
33
+ const TEXT_ALIGNMENTS = [
34
+ { value: "left", icon: AlignLeft, label: "Left" },
35
+ { value: "center", icon: AlignCenter, label: "Center" },
36
+ { value: "right", icon: AlignRight, label: "Right" },
37
+ { value: "justify", icon: AlignJustify, label: "Justify" },
38
+ ];
39
+ export default function TextOptions() {
40
+ const toolOptions = useWhiteboardStore((state) => state.toolOptions);
41
+ const setToolOption = useWhiteboardStore((state) => state.setToolOption);
42
+ const { fontSize, fontFamily, fontWeight, color, textAlign } = toolOptions.text;
43
+ const [fontDropdownOpen, setFontDropdownOpen] = useState(false);
44
+ const selectedFont = FONT_FAMILIES.find(f => f.value === fontFamily) || FONT_FAMILIES[0];
45
+ const isPresetColor = PRESET_COLORS.includes(color);
46
+ return (_jsxs("div", { className: "space-y-6 text-neutral-200", children: [_jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-neutral-400", children: "Text Settings" }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Font" }), _jsxs("div", { className: "relative", children: [_jsxs("button", { onClick: () => setFontDropdownOpen(!fontDropdownOpen), className: "w-full px-3 py-2.5 border border-neutral-700 bg-neutral-900 text-sm text-neutral-200 rounded-lg hover:bg-neutral-800 transition-colors flex items-center justify-between", children: [_jsx("span", { className: selectedFont.style, children: selectedFont.label }), _jsx(ChevronDown, { className: cn("w-4 h-4 transition-transform", fontDropdownOpen && "rotate-180") })] }), fontDropdownOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-10", onClick: () => setFontDropdownOpen(false) }), _jsx("div", { className: "absolute top-full left-0 right-0 mt-1 bg-neutral-900 border border-neutral-700 rounded-lg shadow-xl z-20 overflow-hidden", children: FONT_FAMILIES.map((font) => (_jsx("button", { onClick: () => {
47
+ setToolOption("text", "fontFamily", font.value);
48
+ setFontDropdownOpen(false);
49
+ }, className: cn("w-full px-3 py-2.5 text-left text-sm transition-colors", font.style, font.value === fontFamily
50
+ ? "bg-neutral-700 text-white"
51
+ : "text-neutral-200 hover:bg-neutral-800"), children: font.label }, font.value))) })] }))] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Weight" }), _jsx("div", { className: "grid grid-cols-4 gap-2", children: FONT_WEIGHTS.map((weight) => {
52
+ const isActive = fontWeight === weight.value;
53
+ return (_jsx("button", { onClick: () => setToolOption("text", "fontWeight", weight.value), className: cn("h-9 rounded-lg border text-xs font-medium transition-colors duration-150", isActive
54
+ ? "bg-neutral-700 border-neutral-500 text-white"
55
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"), children: weight.label }, weight.value));
56
+ }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Size" }), _jsxs("span", { className: "text-xs font-semibold text-neutral-100", children: [fontSize, "px"] })] }), _jsx("div", { className: "grid grid-cols-6 gap-2", children: PRESET_SIZES.map((size) => {
57
+ const isActive = fontSize === size;
58
+ return (_jsx("button", { onClick: () => setToolOption("text", "fontSize", size), className: cn("h-8 rounded-lg border text-xs font-medium transition-colors duration-150", isActive
59
+ ? "bg-neutral-700 border-neutral-500 text-white"
60
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"), children: size }, size));
61
+ }) }), _jsx("input", { type: "range", min: 8, max: 72, step: 2, value: fontSize, onChange: (e) => setToolOption("text", "fontSize", parseInt(e.target.value)), className: "w-full h-2 rounded-lg appearance-none cursor-pointer bg-neutral-800 accent-neutral-200" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Color" }), _jsx("div", { className: "flex items-center gap-3 overflow-visible pr-1", children: _jsx("div", { className: "flex flex-wrap gap-2 overflow-visible", children: PRESET_COLORS.map((preset) => {
62
+ const isActive = color === preset;
63
+ return (_jsx("button", { onClick: () => setToolOption("text", "color", preset), className: cn("h-7 w-7 rounded-md border transition-colors duration-150", isActive
64
+ ? "border-neutral-200 outline outline-2 outline-neutral-400"
65
+ : "border-neutral-700 hover:border-neutral-500"), style: { backgroundColor: preset }, "aria-label": `Color ${preset}` }, preset));
66
+ }) }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-medium text-neutral-300", children: "Alignment" }), _jsx("div", { className: "grid grid-cols-4 gap-2", children: TEXT_ALIGNMENTS.map((alignment) => {
67
+ const isActive = textAlign === alignment.value;
68
+ const Icon = alignment.icon;
69
+ return (_jsx("button", { onClick: () => setToolOption("text", "textAlign", alignment.value), className: cn("h-9 rounded-lg border transition-colors duration-150 flex items-center justify-center", isActive
70
+ ? "bg-neutral-700 border-neutral-500 text-white"
71
+ : "bg-neutral-900 border-neutral-800 text-neutral-300 hover:bg-neutral-800"), title: alignment.label, children: _jsx(Icon, { className: "w-4 h-4" }) }, alignment.value));
72
+ }) })] })] }));
73
+ }
@@ -0,0 +1,15 @@
1
+ export interface TaskTemplate {
2
+ id: string;
3
+ title: string;
4
+ status: "todo" | "in-progress" | "done";
5
+ assignee?: string;
6
+ project?: string;
7
+ priority?: "low" | "medium" | "high";
8
+ dueDate?: string;
9
+ }
10
+ interface TaskDropdownProps {
11
+ onAddTask: (task: TaskTemplate) => void;
12
+ }
13
+ export default function TaskDropdown({ onAddTask }: TaskDropdownProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
15
+ //# sourceMappingURL=task-dropdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-dropdown.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/task-dropdown.tsx"],"names":[],"mappings":"AAWA,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;CACzC;AA2DD,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE,iBAAiB,2CAkGpE"}