diff --git a/src/Drawd.jsx b/src/Drawd.jsx
index b15d3c8..5047e85 100644
--- a/src/Drawd.jsx
+++ b/src/Drawd.jsx
@@ -19,6 +19,7 @@ import { useFileActions } from "./hooks/useFileActions";
import { useCollabSync } from "./hooks/useCollabSync";
import { useInteractionCallbacks } from "./hooks/useInteractionCallbacks";
import { useDerivedCanvasState } from "./hooks/useDerivedCanvasState";
+import { useTemplateInserter } from "./hooks/useTemplateInserter";
import { TopBar } from "./components/TopBar";
import { Sidebar } from "./components/Sidebar";
import { StickyNoteSidebar } from "./components/StickyNoteSidebar";
@@ -158,6 +159,18 @@ export default function Drawd({ initialRoomCode }) {
const [renameModal, setRenameModal] = useState(null);
const [showShortcuts, setShowShortcuts] = useState(false);
const [formSummaryScreen, setFormSummaryScreen] = useState(null);
+ const [showTemplateBrowser, setShowTemplateBrowser] = useState(false);
+
+ // ── Template inserter ─────────────────────────────────────────────────
+ const { insertTemplate } = useTemplateInserter({
+ screens, mergeAll, replaceAll, pan, zoom, canvasRef,
+ });
+
+ const onTemplates = useCallback(() => setShowTemplateBrowser(true), []);
+ const onInsertTemplate = useCallback((data) => {
+ insertTemplate(data);
+ setShowTemplateBrowser(false);
+ }, [insertTemplate]);
// ── Instruction generation ─────────────────────────────────────────────
const { instructions, showInstructions, setShowInstructions, onGenerate, buildInstructionResult } =
@@ -315,6 +328,7 @@ export default function Drawd({ initialRoomCode }) {
selectedScreenGroup, setSelectedScreenGroup, deleteScreenGroup,
undo, redo, saveNow, isFileSystemSupported, onSaveAs, onExport, onOpen,
setActiveTool,
+ onTemplates,
isReadOnly,
});
@@ -379,6 +393,7 @@ export default function Drawd({ initialRoomCode }) {
) : null}
onToggleParticipants={() => setShowParticipants((v) => !v)}
showParticipants={showParticipants}
+ onTemplates={onTemplates}
/>
@@ -488,6 +503,7 @@ export default function Drawd({ initialRoomCode }) {
setGroupContextMenu={setGroupContextMenu}
handleImageUpload={handleImageUpload}
addScreenAtCenter={addScreenAtCenter}
+ onTemplates={onTemplates}
/>
{selectedScreenData && (
@@ -573,6 +589,9 @@ export default function Drawd({ initialRoomCode }) {
setFigmaError={setFigmaError}
formSummaryScreen={formSummaryScreen}
setFormSummaryScreen={setFormSummaryScreen}
+ showTemplateBrowser={showTemplateBrowser}
+ setShowTemplateBrowser={setShowTemplateBrowser}
+ onInsertTemplate={onInsertTemplate}
/>
);
diff --git a/src/components/CanvasArea.jsx b/src/components/CanvasArea.jsx
index 3689349..6c65c17 100644
--- a/src/components/CanvasArea.jsx
+++ b/src/components/CanvasArea.jsx
@@ -52,6 +52,8 @@ export function CanvasArea({
groupContextMenu, setGroupContextMenu,
// ToolBar
setActiveTool, handleImageUpload, addScreenAtCenter,
+ // Templates
+ onTemplates,
}) {
return (
- {screens.length === 0 &&
}
+ {screens.length === 0 &&
}
{/* Zoom indicator */}
);
diff --git a/src/components/EmptyState.jsx b/src/components/EmptyState.jsx
index e17205b..b6cde2a 100644
--- a/src/components/EmptyState.jsx
+++ b/src/components/EmptyState.jsx
@@ -1,6 +1,6 @@
import { COLORS, FONTS } from "../styles/theme";
-export function EmptyState() {
+export function EmptyState({ onTemplates }) {
return (
wireframes, screenshots, or mockups
+ {onTemplates && (
+
+ )}
);
}
diff --git a/src/components/ModalsLayer.jsx b/src/components/ModalsLayer.jsx
index 8fba927..86ca49a 100644
--- a/src/components/ModalsLayer.jsx
+++ b/src/components/ModalsLayer.jsx
@@ -11,6 +11,7 @@ import { ShortcutsPanel } from "./ShortcutsPanel";
import { ShareModal } from "./ShareModal";
import { HostLeftModal } from "./HostLeftModal";
import { FormSummaryPanel } from "./FormSummaryPanel";
+import { TemplateBrowserModal } from "./TemplateBrowserModal";
export function ModalsLayer({
// Hotspot modal
@@ -43,6 +44,8 @@ export function ModalsLayer({
figmaProcessing, figmaError, setFigmaError,
// Form summary
formSummaryScreen, setFormSummaryScreen,
+ // Template browser
+ showTemplateBrowser, setShowTemplateBrowser, onInsertTemplate,
}) {
return (
<>
@@ -212,6 +215,13 @@ export function ModalsLayer({
)}
+ {showTemplateBrowser && (
+ setShowTemplateBrowser(false)}
+ />
+ )}
+
{formSummaryScreen && (
+
+
+
+
+
+ );
+}
+
+function TemplateCard({ template, onSelect, loading }) {
+ return (
+
+ );
+}
+
+export function TemplateBrowserModal({ onInsert, onClose }) {
+ const [loading, setLoading] = useState(false);
+
+ const handleSelect = async (template) => {
+ setLoading(true);
+ try {
+ const data = await template.getData();
+ setLoading(false);
+ onInsert(data);
+ } catch {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
e.stopPropagation()}
+ style={{
+ ...styles.modalCard,
+ width: 540,
+ maxHeight: "80vh",
+ display: "flex",
+ flexDirection: "column",
+ }}
+ >
+
+
+
+
+
Templates
+
+ Start with a pre-built flow and customize it
+
+
+
+
+
+
+
+ {TEMPLATES.map((template) => (
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/ToolBar.jsx b/src/components/ToolBar.jsx
index 0b22044..c861c1c 100644
--- a/src/components/ToolBar.jsx
+++ b/src/components/ToolBar.jsx
@@ -1,4 +1,5 @@
import { COLORS, FONTS } from "../styles/theme";
+import { TemplateIcon as TemplateIconBase } from "./TemplateBrowserModal";
const SelectIcon = () => (