@agent-native/core 0.163.4 → 0.164.1

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 (200) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +29 -0
  3. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -2
  4. package/corpus/templates/analytics/netlify.toml +2 -0
  5. package/corpus/templates/assets/netlify.toml +2 -0
  6. package/corpus/templates/brain/netlify.toml +2 -0
  7. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +2 -1
  8. package/corpus/templates/calendar/changelog/2026-08-19-calendar-page-loads-stay-fast-after-idle-periods.md +6 -0
  9. package/corpus/templates/calendar/netlify.toml +2 -0
  10. package/corpus/templates/chat/netlify.toml +2 -0
  11. package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -3
  12. package/corpus/templates/clips/app/components/player/reaction-emojis.ts +12 -0
  13. package/corpus/templates/clips/app/components/player/reactions-tray.tsx +90 -9
  14. package/corpus/templates/clips/app/components/player/scrubber.tsx +43 -10
  15. package/corpus/templates/clips/app/components/player/video-player.tsx +105 -15
  16. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -2
  17. package/corpus/templates/clips/app/routes/_index.tsx +10 -2
  18. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +13 -8
  19. package/corpus/templates/clips/app/routes/share.$shareId.tsx +15 -8
  20. package/corpus/templates/clips/netlify.toml +2 -0
  21. package/corpus/templates/clips/server/routes/[...page].head.ts +5 -0
  22. package/corpus/templates/content/netlify.toml +2 -0
  23. package/corpus/templates/design/.agents/skills/design-editor-architecture/SKILL.md +103 -0
  24. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +2 -1
  25. package/corpus/templates/design/AGENTS.md +3 -1
  26. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +3 -2
  27. package/corpus/templates/design/app/components/design/editor/AgentNativeMenuMark.tsx +25 -0
  28. package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +411 -0
  29. package/corpus/templates/design/app/components/design/editor/DesignCollaborators.tsx +153 -0
  30. package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +231 -0
  31. package/corpus/templates/design/app/components/design/editor/MakeRealDialog.tsx +223 -0
  32. package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +58 -0
  33. package/corpus/templates/design/app/components/design/editor/PendingVisualStyleWarningDialog.tsx +56 -0
  34. package/corpus/templates/design/app/components/design/editor/ReadOnlyEditorPanel.tsx +23 -0
  35. package/corpus/templates/design/app/components/design/editor/SaveTemplateDialog.tsx +161 -0
  36. package/corpus/templates/design/app/components/design/editor/toolbar-controls.tsx +185 -0
  37. package/corpus/templates/design/app/pages/DesignEditor.tsx +20913 -0
  38. package/corpus/templates/design/app/pages/design-editor/command-types.ts +120 -0
  39. package/corpus/templates/design/app/pages/design-editor/commands/README.md +193 -0
  40. package/corpus/templates/design/app/pages/design-editor/commands/add-auto-layout.ts +416 -0
  41. package/corpus/templates/design/app/pages/design-editor/commands/add-screen.ts +112 -0
  42. package/corpus/templates/design/app/pages/design-editor/commands/align-selection.ts +183 -0
  43. package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +243 -0
  44. package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +236 -0
  45. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +304 -0
  46. package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +376 -0
  47. package/corpus/templates/design/app/pages/design-editor/commands/apply-to-source.ts +132 -0
  48. package/corpus/templates/design/app/pages/design-editor/commands/can-move-layer.ts +117 -0
  49. package/corpus/templates/design/app/pages/design-editor/commands/change-selected-z-index.ts +212 -0
  50. package/corpus/templates/design/app/pages/design-editor/commands/commit-relative-style-delta-to-selected-layers.ts +176 -0
  51. package/corpus/templates/design/app/pages/design-editor/commands/commit-styles-to-selected-layers.ts +175 -0
  52. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +710 -0
  53. package/corpus/templates/design/app/pages/design-editor/commands/confirm-make-real.ts +99 -0
  54. package/corpus/templates/design/app/pages/design-editor/commands/copy-as-figma-svg.ts +95 -0
  55. package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +156 -0
  56. package/corpus/templates/design/app/pages/design-editor/commands/create-primitive.ts +291 -0
  57. package/corpus/templates/design/app/pages/design-editor/commands/create-screen-frame.ts +131 -0
  58. package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +577 -0
  59. package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +364 -0
  60. package/corpus/templates/design/app/pages/design-editor/commands/delete-selection.ts +521 -0
  61. package/corpus/templates/design/app/pages/design-editor/commands/detach-instance-menu-action.ts +88 -0
  62. package/corpus/templates/design/app/pages/design-editor/commands/distribute-selection.ts +114 -0
  63. package/corpus/templates/design/app/pages/design-editor/commands/download-all-screens-pdf.ts +116 -0
  64. package/corpus/templates/design/app/pages/design-editor/commands/download-pdf.ts +103 -0
  65. package/corpus/templates/design/app/pages/design-editor/commands/download-svg.ts +159 -0
  66. package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +201 -0
  67. package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +279 -0
  68. package/corpus/templates/design/app/pages/design-editor/commands/editor-paste.ts +81 -0
  69. package/corpus/templates/design/app/pages/design-editor/commands/enter-hotkey.ts +155 -0
  70. package/corpus/templates/design/app/pages/design-editor/commands/enter-single-screen.ts +168 -0
  71. package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +212 -0
  72. package/corpus/templates/design/app/pages/design-editor/commands/frame-selection.ts +119 -0
  73. package/corpus/templates/design/app/pages/design-editor/commands/geometry-commit.ts +169 -0
  74. package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +177 -0
  75. package/corpus/templates/design/app/pages/design-editor/commands/group-selection.ts +127 -0
  76. package/corpus/templates/design/app/pages/design-editor/commands/iframe-context-menu.ts +121 -0
  77. package/corpus/templates/design/app/pages/design-editor/commands/import-figma-clipboard-into-design.ts +119 -0
  78. package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +163 -0
  79. package/corpus/templates/design/app/pages/design-editor/commands/layer-move-to-screen.ts +331 -0
  80. package/corpus/templates/design/app/pages/design-editor/commands/layer-move.ts +517 -0
  81. package/corpus/templates/design/app/pages/design-editor/commands/layer-rename.ts +247 -0
  82. package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +161 -0
  83. package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +122 -0
  84. package/corpus/templates/design/app/pages/design-editor/commands/nudge-selection.ts +213 -0
  85. package/corpus/templates/design/app/pages/design-editor/commands/overview-primitive-reparent.ts +318 -0
  86. package/corpus/templates/design/app/pages/design-editor/commands/paste-copied-screens.ts +114 -0
  87. package/corpus/templates/design/app/pages/design-editor/commands/paste-over-selection.ts +70 -0
  88. package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +436 -0
  89. package/corpus/templates/design/app/pages/design-editor/commands/paste-to-replace.ts +135 -0
  90. package/corpus/templates/design/app/pages/design-editor/commands/pasted-image-files.ts +209 -0
  91. package/corpus/templates/design/app/pages/design-editor/commands/persist-frame-geometry-save.ts +113 -0
  92. package/corpus/templates/design/app/pages/design-editor/commands/persist-tweak-save.ts +160 -0
  93. package/corpus/templates/design/app/pages/design-editor/commands/primitive-created.ts +168 -0
  94. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-layer-state-edit.ts +149 -0
  95. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-structure-edit.ts +200 -0
  96. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-text-edit.ts +152 -0
  97. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-visual-style-edit.ts +247 -0
  98. package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +1086 -0
  99. package/corpus/templates/design/app/pages/design-editor/commands/render-png-blob.ts +200 -0
  100. package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +207 -0
  101. package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +262 -0
  102. package/corpus/templates/design/app/pages/design-editor/commands/screen-text-content-change.ts +216 -0
  103. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +108 -0
  104. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-structure-change.ts +280 -0
  105. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +151 -0
  106. package/corpus/templates/design/app/pages/design-editor/commands/send-overview-annotations.ts +88 -0
  107. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +122 -0
  108. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +143 -0
  109. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +115 -0
  110. package/corpus/templates/design/app/pages/design-editor/commands/start-retry-generation.ts +152 -0
  111. package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +99 -0
  112. package/corpus/templates/design/app/pages/design-editor/commands/style-change.ts +152 -0
  113. package/corpus/templates/design/app/pages/design-editor/commands/styles-change.ts +155 -0
  114. package/corpus/templates/design/app/pages/design-editor/commands/suggest-auto-layout.ts +125 -0
  115. package/corpus/templates/design/app/pages/design-editor/commands/text-content-change.ts +201 -0
  116. package/corpus/templates/design/app/pages/design-editor/commands/tidy-up.ts +123 -0
  117. package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-hidden.ts +167 -0
  118. package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-locked.ts +175 -0
  119. package/corpus/templates/design/app/pages/design-editor/commands/toggle-motion-keyframe.ts +99 -0
  120. package/corpus/templates/design/app/pages/design-editor/commands/tweak-prompt-submit.ts +84 -0
  121. package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +906 -0
  122. package/corpus/templates/design/app/pages/design-editor/commands/ungroup-selection.ts +170 -0
  123. package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +119 -0
  124. package/corpus/templates/design/app/pages/design-editor/commands/visual-structure-change.ts +274 -0
  125. package/corpus/templates/design/app/pages/design-editor/commands/write-frame-geometry-snapshot.ts +99 -0
  126. package/corpus/templates/design/app/pages/design-editor/derive/design-breakpoints.ts +56 -0
  127. package/corpus/templates/design/app/pages/design-editor/derive/overview-screens.ts +146 -0
  128. package/corpus/templates/design/app/pages/design-editor/domains/use-tweaks.ts +326 -0
  129. package/corpus/templates/design/app/pages/design-editor/editor-constants.ts +54 -0
  130. package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +183 -0
  131. package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +270 -0
  132. package/corpus/templates/design/app/pages/design-editor/effects/mirror-selection-to-agent-chat.ts +134 -0
  133. package/corpus/templates/design/app/pages/design-editor/effects/motion-autosave.ts +335 -0
  134. package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +183 -0
  135. package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +252 -0
  136. package/corpus/templates/design/app/pages/design-editor/effects/resume-pending-generation.ts +178 -0
  137. package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +157 -0
  138. package/corpus/templates/design/app/pages/design-editor/preview-html.ts +19 -0
  139. package/corpus/templates/design/netlify.toml +2 -0
  140. package/corpus/templates/design/shared/board-file.ts +2 -1
  141. package/corpus/templates/dispatch/netlify.toml +2 -0
  142. package/corpus/templates/forms/netlify.toml +2 -0
  143. package/corpus/templates/mail/netlify.toml +2 -0
  144. package/corpus/templates/plan/netlify.toml +2 -0
  145. package/corpus/templates/slides/netlify.toml +2 -0
  146. package/dist/agent/engine/builder-engine.d.ts +6 -4
  147. package/dist/agent/engine/builder-engine.js +91 -26
  148. package/dist/agent/engine/registry.js +16 -0
  149. package/dist/cli/code-agent-agent-tools.d.ts +2 -0
  150. package/dist/cli/code-agent-agent-tools.js +188 -0
  151. package/dist/cli/code-agent-collaboration.d.ts +34 -0
  152. package/dist/cli/code-agent-collaboration.js +104 -0
  153. package/dist/cli/code-agent-executor.js +6 -0
  154. package/dist/cli/code-agent-schedules.d.ts +54 -0
  155. package/dist/cli/code-agent-schedules.js +198 -0
  156. package/dist/client/AgentPanel.js +1 -1
  157. package/dist/client/ConnectBuilderCard.js +5 -2
  158. package/dist/client/EnvironmentBadge.js +6 -5
  159. package/dist/client/chat/run-recovery.js +3 -2
  160. package/dist/client/chat/tool-call-display.js +2 -3
  161. package/dist/client/components/CodeRequiredDialog.js +4 -2
  162. package/dist/client/i18n.js +5 -4
  163. package/dist/client/settings/SettingsPanel.js +1 -1
  164. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  165. package/dist/client/settings/useBuilderStatus.d.ts +6 -1
  166. package/dist/client/settings/useBuilderStatus.js +22 -33
  167. package/dist/client/transcription/BuilderTranscriptionCta.js +1 -1
  168. package/dist/code-agents/index.d.ts +2 -0
  169. package/dist/code-agents/index.js +2 -0
  170. package/dist/collab/routes.d.ts +1 -1
  171. package/dist/localization/shared.d.ts +2 -0
  172. package/dist/localization/shared.js +5 -0
  173. package/dist/mcp-client/oauth-client.d.ts +1 -0
  174. package/dist/mcp-client/oauth-client.js +50 -1
  175. package/dist/observability/routes.d.ts +1 -1
  176. package/dist/progress/routes.d.ts +1 -1
  177. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  178. package/dist/resources/handlers.d.ts +1 -1
  179. package/dist/server/auth-marketing-locales.d.ts +7 -0
  180. package/dist/server/auth-marketing-locales.js +1322 -0
  181. package/dist/server/auth-marketing.d.ts +1 -0
  182. package/dist/server/auth-marketing.js +35 -6
  183. package/dist/server/auth.js +4 -17
  184. package/dist/server/builder-browser.d.ts +22 -0
  185. package/dist/server/builder-browser.js +77 -1
  186. package/dist/server/builder-oauth.d.ts +50 -0
  187. package/dist/server/builder-oauth.js +263 -0
  188. package/dist/server/core-routes-plugin.d.ts +11 -0
  189. package/dist/server/core-routes-plugin.js +291 -415
  190. package/dist/server/credential-provider.d.ts +1 -1
  191. package/dist/server/credential-provider.js +1 -1
  192. package/dist/server/csrf.js +2 -2
  193. package/dist/server/onboarding-html.js +52 -120
  194. package/dist/settings/store.d.ts +9 -0
  195. package/dist/settings/store.js +19 -0
  196. package/dist/templates/chat/netlify.toml +2 -0
  197. package/docs/content/deployment.mdx +2 -0
  198. package/docs/content/multi-app-workspace.mdx +1 -1
  199. package/package.json +2 -2
  200. package/src/templates/chat/netlify.toml +2 -0
@@ -0,0 +1,56 @@
1
+ import { useT } from "@agent-native/core/client/i18n";
2
+
3
+ import {
4
+ AlertDialog,
5
+ AlertDialogAction,
6
+ AlertDialogCancel,
7
+ AlertDialogContent,
8
+ AlertDialogDescription,
9
+ AlertDialogFooter,
10
+ AlertDialogHeader,
11
+ AlertDialogTitle,
12
+ } from "@/components/ui/alert-dialog";
13
+
14
+ export function PendingVisualStyleWarningDialog({
15
+ open,
16
+ pendingVisualEditCount,
17
+ onStay,
18
+ onDiscardAndNavigate,
19
+ }: {
20
+ open: boolean;
21
+ pendingVisualEditCount: number;
22
+ onStay: () => void;
23
+ onDiscardAndNavigate: () => void;
24
+ }) {
25
+ const t = useT();
26
+ return (
27
+ <AlertDialog open={open}>
28
+ <AlertDialogContent>
29
+ <AlertDialogHeader>
30
+ <AlertDialogTitle>
31
+ {t("designEditor.pendingVisualStyles.leaveTitle")}
32
+ </AlertDialogTitle>
33
+ <AlertDialogDescription>
34
+ {t(
35
+ pendingVisualEditCount === 1
36
+ ? "designEditor.pendingVisualStyles.leaveDescriptionOne"
37
+ : "designEditor.pendingVisualStyles.leaveDescriptionOther",
38
+ { count: pendingVisualEditCount },
39
+ )}
40
+ </AlertDialogDescription>
41
+ </AlertDialogHeader>
42
+ <AlertDialogFooter>
43
+ <AlertDialogCancel onClick={onStay}>
44
+ {t("designEditor.pendingVisualStyles.stay")}
45
+ </AlertDialogCancel>
46
+ <AlertDialogAction
47
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
48
+ onClick={onDiscardAndNavigate}
49
+ >
50
+ {t("designEditor.pendingVisualStyles.leave")}
51
+ </AlertDialogAction>
52
+ </AlertDialogFooter>
53
+ </AlertDialogContent>
54
+ </AlertDialog>
55
+ );
56
+ }
@@ -0,0 +1,23 @@
1
+ import { IconLock } from "@tabler/icons-react";
2
+
3
+ export function ReadOnlyEditorPanel({
4
+ title,
5
+ description,
6
+ }: {
7
+ title: string;
8
+ description?: string;
9
+ }) {
10
+ return (
11
+ <div className="flex min-h-0 flex-1 flex-col items-center justify-center px-5 text-center">
12
+ <div className="mb-3 flex size-10 items-center justify-center rounded-lg bg-muted text-muted-foreground">
13
+ <IconLock className="size-5" />
14
+ </div>
15
+ <p className="text-sm font-medium text-foreground">{title}</p>
16
+ {description ? (
17
+ <p className="mt-1 max-w-56 text-xs leading-5 text-muted-foreground">
18
+ {description}
19
+ </p>
20
+ ) : null}
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,161 @@
1
+ import { useT } from "@agent-native/core/client/i18n";
2
+ import { useEffect, useState } from "react";
3
+
4
+ import { Button } from "@/components/ui/button";
5
+ import {
6
+ Dialog,
7
+ DialogContent,
8
+ DialogDescription,
9
+ DialogFooter,
10
+ DialogHeader,
11
+ DialogTitle,
12
+ } from "@/components/ui/dialog";
13
+ import { Input } from "@/components/ui/input";
14
+ import {
15
+ Select,
16
+ SelectContent,
17
+ SelectGroup,
18
+ SelectItem,
19
+ SelectTrigger,
20
+ SelectValue,
21
+ } from "@/components/ui/select";
22
+ import { Spinner } from "@/components/ui/spinner";
23
+ import { Textarea } from "@/components/ui/textarea";
24
+
25
+ type TemplateCategory =
26
+ | "ad"
27
+ | "one-pager"
28
+ | "landing-page"
29
+ | "social"
30
+ | "presentation"
31
+ | "other";
32
+
33
+ export function SaveTemplateDialog({
34
+ open,
35
+ onOpenChange,
36
+ defaultTitle,
37
+ defaultDescription,
38
+ screenCount,
39
+ lockedLayerCount,
40
+ saving,
41
+ onSave,
42
+ }: {
43
+ open: boolean;
44
+ onOpenChange: (open: boolean) => void;
45
+ defaultTitle: string;
46
+ defaultDescription: string;
47
+ screenCount: number;
48
+ lockedLayerCount: number;
49
+ saving: boolean;
50
+ onSave: (values: {
51
+ title: string;
52
+ description?: string;
53
+ category: TemplateCategory;
54
+ }) => Promise<void>;
55
+ }) {
56
+ const t = useT();
57
+ const [title, setTitle] = useState(defaultTitle);
58
+ const [description, setDescription] = useState(defaultDescription);
59
+ const [category, setCategory] = useState<TemplateCategory>("other");
60
+
61
+ useEffect(() => {
62
+ if (!open) return;
63
+ setTitle(defaultTitle);
64
+ setDescription(defaultDescription);
65
+ setCategory("other");
66
+ }, [defaultDescription, defaultTitle, open]);
67
+
68
+ return (
69
+ <Dialog open={open} onOpenChange={onOpenChange}>
70
+ <DialogContent className="sm:max-w-lg">
71
+ <DialogHeader>
72
+ <DialogTitle>{t("designEditor.saveAsTemplate")}</DialogTitle>
73
+ <DialogDescription>
74
+ {t("designEditor.saveTemplateDescription")}
75
+ </DialogDescription>
76
+ </DialogHeader>
77
+ <div className="grid gap-4 py-1">
78
+ <label className="grid gap-1.5 text-sm">
79
+ <span className="font-medium">
80
+ {t("designEditor.templateName")}
81
+ </span>
82
+ <Input
83
+ value={title}
84
+ onChange={(event) => setTitle(event.target.value)}
85
+ autoFocus
86
+ />
87
+ </label>
88
+ <label className="grid gap-1.5 text-sm">
89
+ <span className="font-medium">
90
+ {t("designEditor.templateDescription")}
91
+ </span>
92
+ <Textarea
93
+ value={description}
94
+ onChange={(event) => setDescription(event.target.value)}
95
+ rows={3}
96
+ />
97
+ </label>
98
+ <div className="grid gap-1.5 text-sm">
99
+ <span className="font-medium">
100
+ {t("designEditor.templateCategory")}
101
+ </span>
102
+ <Select
103
+ value={category}
104
+ onValueChange={(value) => setCategory(value as TemplateCategory)}
105
+ >
106
+ <SelectTrigger>
107
+ <SelectValue />
108
+ </SelectTrigger>
109
+ <SelectContent>
110
+ <SelectGroup>
111
+ {(
112
+ [
113
+ "ad",
114
+ "social",
115
+ "one-pager",
116
+ "landing-page",
117
+ "presentation",
118
+ "other",
119
+ ] as const
120
+ ).map((value) => (
121
+ <SelectItem key={value} value={value}>
122
+ {t(`templatesPage.categories.${value}`)}
123
+ </SelectItem>
124
+ ))}
125
+ </SelectGroup>
126
+ </SelectContent>
127
+ </Select>
128
+ </div>
129
+ <div className="rounded-lg border bg-muted/25 px-3 py-2 text-xs text-muted-foreground">
130
+ {t("designEditor.templateSnapshotSummary", {
131
+ screens: screenCount,
132
+ locks: lockedLayerCount,
133
+ })}
134
+ </div>
135
+ </div>
136
+ <DialogFooter>
137
+ <Button
138
+ variant="outline"
139
+ onClick={() => onOpenChange(false)}
140
+ disabled={saving}
141
+ >
142
+ {t("home.cancel")}
143
+ </Button>
144
+ <Button
145
+ onClick={() =>
146
+ void onSave({
147
+ title: title.trim(),
148
+ description: description.trim() || undefined,
149
+ category,
150
+ })
151
+ }
152
+ disabled={!title.trim() || saving}
153
+ >
154
+ {saving ? <Spinner className="size-4" /> : null}
155
+ {t("designEditor.saveTemplate")}
156
+ </Button>
157
+ </DialogFooter>
158
+ </DialogContent>
159
+ </Dialog>
160
+ );
161
+ }
@@ -0,0 +1,185 @@
1
+ import { IconCheck, IconChevronDown } from "@tabler/icons-react";
2
+ import type { ReactNode } from "react";
3
+
4
+ import {
5
+ DropdownMenu,
6
+ DropdownMenuContent,
7
+ DropdownMenuItem,
8
+ DropdownMenuShortcut,
9
+ DropdownMenuTrigger,
10
+ } from "@/components/ui/dropdown-menu";
11
+ import {
12
+ Tooltip,
13
+ TooltipContent,
14
+ TooltipTrigger,
15
+ } from "@/components/ui/tooltip";
16
+ import { cn } from "@/lib/utils";
17
+
18
+ export type DesignToolbarOption = {
19
+ key: string;
20
+ label: string;
21
+ icon: ReactNode;
22
+ shortcut?: string;
23
+ active?: boolean;
24
+ disabled?: boolean;
25
+ onSelect: () => void;
26
+ };
27
+
28
+ export function DesignPenToolIcon({ className }: { className?: string }) {
29
+ return (
30
+ <svg
31
+ aria-hidden="true"
32
+ className={className}
33
+ fill="none"
34
+ focusable="false"
35
+ stroke="currentColor"
36
+ strokeLinecap="round"
37
+ strokeLinejoin="round"
38
+ strokeWidth="2"
39
+ viewBox="0 0 24 24"
40
+ >
41
+ <path d="M15.707 21.293a1 1 0 0 1-1.414 0l-1.586-1.586a1 1 0 0 1 0-1.414l5.586-5.586a1 1 0 0 1 1.414 0l1.586 1.586a1 1 0 0 1 0 1.414z" />
42
+ <path d="m18 13-1.375-6.874a1 1 0 0 0-.746-.776L3.235 2.028a1 1 0 0 0-1.207 1.207L5.35 15.879a1 1 0 0 0 .776.746L13 18" />
43
+ <path d="m2.3 2.3 7.286 7.286" />
44
+ <circle cx="11" cy="11" r="2" />
45
+ </svg>
46
+ );
47
+ }
48
+
49
+ export function DesignToolbarTool({
50
+ active,
51
+ label,
52
+ icon,
53
+ options,
54
+ onPrimary,
55
+ }: {
56
+ active: boolean;
57
+ label: string;
58
+ icon: ReactNode;
59
+ options: DesignToolbarOption[];
60
+ onPrimary: () => void;
61
+ }) {
62
+ const hasOptionsMenu = options.length > 1;
63
+ // Item 5 (Figma parity): the hover tooltip should show the shortcut for
64
+ // whichever sub-tool is CURRENTLY active (mirroring how the button's own
65
+ // icon/label already track the active sub-tool above), falling back to the
66
+ // first option's shortcut when none of the options is active — e.g. a
67
+ // freshly-mounted toolbar before any tool has been explicitly selected.
68
+ const primaryShortcut =
69
+ options.find((option) => option.active)?.shortcut ?? options[0]?.shortcut;
70
+ return (
71
+ <div className="flex h-8 items-center text-neutral-200">
72
+ <Tooltip>
73
+ <TooltipTrigger asChild>
74
+ <button
75
+ type="button"
76
+ className={cn(
77
+ "flex size-8 cursor-pointer items-center justify-center rounded-md transition-colors",
78
+ active
79
+ ? // guard:allow-raw-color - fixed dark editor chrome, intentionally theme-independent
80
+ "bg-[var(--design-editor-accent-color)] text-white"
81
+ : // guard:allow-raw-color - fixed dark editor chrome, intentionally theme-independent
82
+ "hover:bg-white/10 hover:text-white",
83
+ )}
84
+ onClick={onPrimary}
85
+ aria-label={label}
86
+ aria-pressed={active}
87
+ >
88
+ {icon}
89
+ </button>
90
+ </TooltipTrigger>
91
+ <TooltipContent side="top" className="flex items-center gap-2">
92
+ <span>{label}</span>
93
+ {primaryShortcut ? (
94
+ <span className="text-muted-foreground">{primaryShortcut}</span>
95
+ ) : null}
96
+ </TooltipContent>
97
+ </Tooltip>
98
+
99
+ {hasOptionsMenu ? (
100
+ <DropdownMenu>
101
+ <DropdownMenuTrigger asChild>
102
+ <button
103
+ type="button"
104
+ className={cn(
105
+ // guard:allow-raw-color - fixed dark editor chrome, intentionally theme-independent
106
+ "flex h-8 w-4 cursor-pointer items-center justify-center rounded-md transition-colors hover:bg-white/10 hover:text-white",
107
+ active && "text-neutral-200",
108
+ )}
109
+ aria-label={`${label} options`}
110
+ >
111
+ <IconChevronDown className="size-3" />
112
+ </button>
113
+ </DropdownMenuTrigger>
114
+ <DropdownMenuContent
115
+ side="top"
116
+ align="center"
117
+ sideOffset={12}
118
+ className="w-56 rounded-2xl border-border bg-popover p-2 text-popover-foreground shadow-md"
119
+ >
120
+ {options.map((option) => (
121
+ <DropdownMenuItem
122
+ key={option.key}
123
+ disabled={option.disabled}
124
+ onSelect={option.onSelect}
125
+ className="h-10 rounded-lg text-sm text-popover-foreground focus:bg-accent focus:text-accent-foreground data-[disabled]:text-muted-foreground"
126
+ >
127
+ <span className="mr-2 flex size-5 items-center justify-center text-popover-foreground">
128
+ {option.active ? (
129
+ <IconCheck className="size-4" />
130
+ ) : (
131
+ option.icon
132
+ )}
133
+ </span>
134
+ <span className="min-w-0 flex-1 truncate">{option.label}</span>
135
+ {option.shortcut && (
136
+ <DropdownMenuShortcut className="ml-3 text-muted-foreground">
137
+ {option.shortcut}
138
+ </DropdownMenuShortcut>
139
+ )}
140
+ </DropdownMenuItem>
141
+ ))}
142
+ </DropdownMenuContent>
143
+ </DropdownMenu>
144
+ ) : null}
145
+ </div>
146
+ );
147
+ }
148
+
149
+ export function DesignModeTab({
150
+ active,
151
+ disabled,
152
+ label,
153
+ icon,
154
+ onClick,
155
+ }: {
156
+ active: boolean;
157
+ disabled?: boolean;
158
+ label: string;
159
+ icon: ReactNode;
160
+ onClick: () => void;
161
+ }) {
162
+ return (
163
+ <Tooltip>
164
+ <TooltipTrigger asChild>
165
+ <button
166
+ type="button"
167
+ disabled={disabled}
168
+ aria-label={label}
169
+ aria-pressed={active}
170
+ onClick={onClick}
171
+ className={cn(
172
+ // guard:allow-raw-color - fixed dark editor chrome, intentionally theme-independent
173
+ "flex size-8 cursor-pointer items-center justify-center rounded-md text-neutral-300 transition-colors hover:bg-white/10 hover:text-white disabled:pointer-events-none disabled:opacity-40",
174
+ active &&
175
+ // guard:allow-raw-color - fixed dark editor chrome, intentionally theme-independent
176
+ "bg-neutral-950/70 text-[#38bdf8] shadow-[inset_0_0_0_1px_rgba(255,255,255,0.08),0_8px_18px_-12px_rgba(0,0,0,0.95)] hover:bg-neutral-950/70 hover:text-[#38bdf8]",
177
+ )}
178
+ >
179
+ {icon}
180
+ </button>
181
+ </TooltipTrigger>
182
+ <TooltipContent side="top">{label}</TooltipContent>
183
+ </Tooltip>
184
+ );
185
+ }