@agent-native/core 0.163.3 → 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 (202) 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/provider-api/actions/provider-api.d.ts +2 -2
  177. package/dist/resources/handlers.d.ts +1 -1
  178. package/dist/server/auth-marketing-locales.d.ts +7 -0
  179. package/dist/server/auth-marketing-locales.js +1322 -0
  180. package/dist/server/auth-marketing.d.ts +1 -0
  181. package/dist/server/auth-marketing.js +35 -6
  182. package/dist/server/auth.js +4 -17
  183. package/dist/server/builder-browser.d.ts +22 -0
  184. package/dist/server/builder-browser.js +77 -1
  185. package/dist/server/builder-oauth.d.ts +50 -0
  186. package/dist/server/builder-oauth.js +263 -0
  187. package/dist/server/core-routes-plugin.d.ts +11 -0
  188. package/dist/server/core-routes-plugin.js +291 -415
  189. package/dist/server/credential-provider.d.ts +1 -1
  190. package/dist/server/credential-provider.js +1 -1
  191. package/dist/server/csrf.js +2 -2
  192. package/dist/server/onboarding-html.js +52 -120
  193. package/dist/server/realtime-token.d.ts +1 -1
  194. package/dist/server/transcribe-voice.d.ts +1 -1
  195. package/dist/settings/store.d.ts +9 -0
  196. package/dist/settings/store.js +19 -0
  197. package/dist/templates/chat/netlify.toml +2 -0
  198. package/dist/vite/client.js +35 -37
  199. package/docs/content/deployment.mdx +2 -0
  200. package/docs/content/multi-app-workspace.mdx +1 -1
  201. package/package.json +2 -2
  202. package/src/templates/chat/netlify.toml +2 -0
@@ -0,0 +1,153 @@
1
+ import type { CollabUser } from "@agent-native/core/client/collab";
2
+ import { emailToColor, emailToName } from "@agent-native/core/client/collab";
3
+ import { useAvatarUrl } from "@agent-native/core/client/hooks";
4
+ import { IconCheck, IconChevronDown } from "@tabler/icons-react";
5
+
6
+ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
7
+ import {
8
+ DropdownMenu,
9
+ DropdownMenuContent,
10
+ DropdownMenuItem,
11
+ DropdownMenuLabel,
12
+ DropdownMenuTrigger,
13
+ } from "@/components/ui/dropdown-menu";
14
+ import { cn } from "@/lib/utils";
15
+
16
+ export interface DesignCollaborator {
17
+ user: CollabUser;
18
+ image?: string;
19
+ isCurrent?: boolean;
20
+ }
21
+
22
+ function userInitial(nameOrEmail: string): string {
23
+ const trimmed = nameOrEmail.trim();
24
+ if (!trimmed) return "?";
25
+ return trimmed.charAt(0).toUpperCase();
26
+ }
27
+
28
+ function userColor(user: CollabUser): string {
29
+ return user.color || emailToColor(user.email);
30
+ }
31
+
32
+ export function DesignCollaboratorAvatar({
33
+ collaborator,
34
+ className,
35
+ }: {
36
+ collaborator: DesignCollaborator;
37
+ className?: string;
38
+ }) {
39
+ const label = collaborator.user.name || emailToName(collaborator.user.email);
40
+ const storedAvatarUrl = useAvatarUrl(collaborator.user.email);
41
+ const avatarUrl = storedAvatarUrl ?? collaborator.image;
42
+
43
+ return (
44
+ <Avatar
45
+ className={cn(
46
+ "size-7 border-2 border-[var(--design-editor-panel-bg)] shadow-sm",
47
+ className,
48
+ )}
49
+ >
50
+ {avatarUrl ? <AvatarImage src={avatarUrl} alt={label} /> : null}
51
+ <AvatarFallback
52
+ /* guard:allow-raw-color — avatar initials sit on a generated user color, not a themed surface */
53
+ className="text-[10px] font-semibold text-white"
54
+ style={{ backgroundColor: userColor(collaborator.user) }}
55
+ >
56
+ {userInitial(label || collaborator.user.email)}
57
+ </AvatarFallback>
58
+ </Avatar>
59
+ );
60
+ }
61
+
62
+ export function DesignCollaboratorsMenu({
63
+ collaborators,
64
+ followingEmail,
65
+ label,
66
+ onAvatarClick,
67
+ }: {
68
+ collaborators: DesignCollaborator[];
69
+ followingEmail?: string | null;
70
+ label: string;
71
+ onAvatarClick?: (user: CollabUser | null) => void;
72
+ }) {
73
+ if (collaborators.length === 0) return null;
74
+
75
+ const visibleCollaborators = collaborators.slice(0, 3);
76
+ const hasMultipleCollaborators = collaborators.length > 1;
77
+ const followingLower = followingEmail?.trim().toLowerCase() ?? null;
78
+
79
+ return (
80
+ <DropdownMenu>
81
+ <DropdownMenuTrigger asChild>
82
+ <button
83
+ type="button"
84
+ className="flex h-8 min-w-0 cursor-pointer items-center rounded-md pr-1 text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground"
85
+ aria-label={label}
86
+ >
87
+ <span className="flex items-center">
88
+ {visibleCollaborators.map((collaborator, index) => (
89
+ <DesignCollaboratorAvatar
90
+ key={`${collaborator.user.email}:${index}`}
91
+ collaborator={collaborator}
92
+ className={index === 0 ? undefined : "-ml-2"}
93
+ />
94
+ ))}
95
+ </span>
96
+ {hasMultipleCollaborators ? (
97
+ <IconChevronDown className="ml-0.5 size-3 opacity-70" />
98
+ ) : null}
99
+ </button>
100
+ </DropdownMenuTrigger>
101
+ <DropdownMenuContent align="start" className="w-64">
102
+ <DropdownMenuLabel className="text-xs text-muted-foreground">
103
+ {label}
104
+ </DropdownMenuLabel>
105
+ {collaborators.map((collaborator) => {
106
+ const user = collaborator.user;
107
+ const email = user.email.trim().toLowerCase();
108
+ const isFollowing =
109
+ followingLower != null && email === followingLower;
110
+ const name = user.name || emailToName(user.email);
111
+
112
+ return (
113
+ <DropdownMenuItem
114
+ key={user.email}
115
+ onSelect={(event) => {
116
+ if (collaborator.isCurrent) {
117
+ event.preventDefault();
118
+ return;
119
+ }
120
+ onAvatarClick?.(user);
121
+ }}
122
+ className={cn(
123
+ "gap-2",
124
+ collaborator.isCurrent && "cursor-default",
125
+ )}
126
+ >
127
+ <DesignCollaboratorAvatar collaborator={collaborator} />
128
+ <span className="min-w-0 flex-1">
129
+ <span className="block truncate text-sm font-medium">
130
+ {name}
131
+ </span>
132
+ <span className="block truncate text-xs text-muted-foreground">
133
+ {user.email}
134
+ </span>
135
+ </span>
136
+ {collaborator.isCurrent ? (
137
+ <span className="text-xs text-muted-foreground">
138
+ {"You" /* i18n-ignore collaborator row */}
139
+ </span>
140
+ ) : isFollowing ? (
141
+ <IconCheck className="size-3.5 text-[var(--design-editor-accent-color)]" />
142
+ ) : (
143
+ <span className="text-xs text-muted-foreground">
144
+ {"Follow" /* i18n-ignore collaborator row */}
145
+ </span>
146
+ )}
147
+ </DropdownMenuItem>
148
+ );
149
+ })}
150
+ </DropdownMenuContent>
151
+ </DropdownMenu>
152
+ );
153
+ }
@@ -0,0 +1,231 @@
1
+ import { useT } from "@agent-native/core/client/i18n";
2
+ import {
3
+ IconAssembly,
4
+ IconChevronDown,
5
+ IconChevronUp,
6
+ IconCode,
7
+ IconFile,
8
+ IconFileImport,
9
+ IconMessage,
10
+ IconPhoto,
11
+ IconPuzzle,
12
+ } from "@tabler/icons-react";
13
+ import type { ReactNode } from "react";
14
+
15
+ import { preloadCodeWorkbench } from "@/components/design/code-workbench/CodeWorkbenchLoader";
16
+ import {
17
+ Tooltip,
18
+ TooltipContent,
19
+ TooltipTrigger,
20
+ } from "@/components/ui/tooltip";
21
+ import { cn } from "@/lib/utils";
22
+ import {
23
+ SHOW_DESIGN_CODE_LEFT_PANEL,
24
+ SHOW_DESIGN_SECONDARY_LEFT_PANELS,
25
+ type DesignLeftPanel,
26
+ } from "@/pages/design-editor/types";
27
+
28
+ export const INITIAL_GENERATION_DISABLED_LEFT_PANELS = new Set<DesignLeftPanel>(
29
+ ["file", "assets", "tools", "tokens", "import", "code"],
30
+ );
31
+
32
+ export function DesignWorkspaceRail({
33
+ activePanel,
34
+ disabledPanels,
35
+ hiddenPanels,
36
+ motionOpen,
37
+ motionDisabled,
38
+ projectMenu,
39
+ onMotionToggle,
40
+ onPanelChange,
41
+ }: {
42
+ activePanel: DesignLeftPanel;
43
+ disabledPanels?: ReadonlySet<DesignLeftPanel>;
44
+ hiddenPanels?: ReadonlySet<DesignLeftPanel>;
45
+ motionOpen?: boolean;
46
+ motionDisabled?: boolean;
47
+ projectMenu: ReactNode;
48
+ onMotionToggle?: () => void;
49
+ onPanelChange: (panel: DesignLeftPanel) => void;
50
+ }) {
51
+ const t = useT();
52
+ const items: Array<{
53
+ panel: DesignLeftPanel;
54
+ label: string;
55
+ icon: ReactNode;
56
+ separatorBefore?: boolean;
57
+ }> = [
58
+ {
59
+ panel: "file",
60
+ label: t("designEditor.leftRail.file"),
61
+ icon: <IconFile className="size-[15px]" />,
62
+ },
63
+ {
64
+ panel: "agent",
65
+ label: t("designEditor.leftRail.agent"),
66
+ icon: <IconMessage className="size-[15px]" />,
67
+ },
68
+ ...(SHOW_DESIGN_SECONDARY_LEFT_PANELS
69
+ ? [
70
+ {
71
+ panel: "assets" as const,
72
+ label: t("designEditor.leftRail.assets"),
73
+ icon: <IconPhoto className="size-[15px]" />,
74
+ },
75
+ ]
76
+ : []),
77
+ {
78
+ panel: "import",
79
+ label: t("designEditor.leftRail.import"),
80
+ icon: <IconFileImport className="size-[15px]" />,
81
+ },
82
+ ...(SHOW_DESIGN_SECONDARY_LEFT_PANELS
83
+ ? [
84
+ {
85
+ panel: "tools" as const,
86
+ label: t("designEditor.leftRail.tools"),
87
+ icon: <IconPuzzle className="size-[15px]" />,
88
+ },
89
+ {
90
+ panel: "tokens" as const,
91
+ label: t("designEditor.leftRail.tokens"),
92
+ icon: <IconAssembly className="size-[15px]" />,
93
+ },
94
+ ]
95
+ : []),
96
+ ...(SHOW_DESIGN_CODE_LEFT_PANEL
97
+ ? [
98
+ {
99
+ panel: "code" as const,
100
+ label: "Code" /* i18n-ignore */,
101
+ icon: <IconCode className="size-[15px]" />,
102
+ separatorBefore: true,
103
+ },
104
+ ]
105
+ : []),
106
+ ];
107
+
108
+ return (
109
+ <nav
110
+ aria-label={t("designEditor.leftRail.label")}
111
+ className="flex min-h-0 w-[57px] shrink-0 flex-col items-center overflow-y-auto overscroll-contain border-r border-[var(--design-editor-panel-divider-color)] bg-[var(--design-editor-panel-bg)] py-3"
112
+ >
113
+ <div className="mb-3 flex h-8 items-center justify-center">
114
+ {projectMenu}
115
+ </div>
116
+ <div className="mb-5 h-px w-8 bg-border/70" />
117
+ <div className="flex min-h-0 flex-1 flex-col items-center gap-4">
118
+ {items.map((item) => {
119
+ if (hiddenPanels?.has(item.panel)) return null;
120
+ const active = item.panel === activePanel;
121
+ const disabled = disabledPanels?.has(item.panel) ?? false;
122
+ return (
123
+ <div key={item.panel} className="flex w-full flex-col items-center">
124
+ {item.separatorBefore ? (
125
+ <div className="-mt-1 mb-3 h-px w-8 bg-border/70" />
126
+ ) : null}
127
+ <Tooltip>
128
+ <TooltipTrigger asChild>
129
+ <button
130
+ type="button"
131
+ aria-label={item.label}
132
+ aria-disabled={disabled || undefined}
133
+ aria-current={active ? "page" : undefined}
134
+ tabIndex={disabled ? -1 : undefined}
135
+ onClick={(event) => {
136
+ if (disabled) {
137
+ event.preventDefault();
138
+ return;
139
+ }
140
+ onPanelChange(item.panel);
141
+ }}
142
+ onPointerEnter={() => {
143
+ if (item.panel === "code") preloadCodeWorkbench();
144
+ }}
145
+ onFocus={() => {
146
+ if (item.panel === "code") preloadCodeWorkbench();
147
+ }}
148
+ className={cn(
149
+ "group flex w-12 cursor-pointer flex-col items-center justify-start gap-1 rounded-none text-[10px] font-[450] leading-none text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
150
+ disabled &&
151
+ "cursor-default opacity-35 hover:text-muted-foreground",
152
+ active && "text-foreground",
153
+ )}
154
+ >
155
+ <span
156
+ className={cn(
157
+ "flex size-8 items-center justify-center rounded-lg transition-colors",
158
+ active
159
+ ? "bg-[var(--design-editor-selection-color)] text-[var(--design-editor-accent-color)]"
160
+ : "text-muted-foreground group-hover:bg-[var(--design-editor-layer-hover-color)] group-hover:text-foreground",
161
+ disabled &&
162
+ "group-hover:bg-transparent group-hover:text-muted-foreground",
163
+ )}
164
+ >
165
+ {item.icon}
166
+ </span>
167
+ <span className="max-w-full truncate leading-none">
168
+ {item.label}
169
+ </span>
170
+ </button>
171
+ </TooltipTrigger>
172
+ <TooltipContent side="right">{item.label}</TooltipContent>
173
+ </Tooltip>
174
+ </div>
175
+ );
176
+ })}
177
+ </div>
178
+ {onMotionToggle ? (
179
+ <div className="mt-4 flex w-full flex-col items-center border-t border-border/70 pt-3">
180
+ <Tooltip>
181
+ <TooltipTrigger asChild>
182
+ <button
183
+ type="button"
184
+ aria-label={"Motion" /* i18n-ignore */}
185
+ aria-disabled={motionDisabled || undefined}
186
+ aria-pressed={motionOpen || undefined}
187
+ tabIndex={motionDisabled ? -1 : undefined}
188
+ onClick={(event) => {
189
+ if (motionDisabled) {
190
+ event.preventDefault();
191
+ return;
192
+ }
193
+ onMotionToggle();
194
+ }}
195
+ className={cn(
196
+ "group flex w-12 cursor-pointer flex-col items-center justify-start gap-1 rounded-none !text-[10px] font-[450] leading-none text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
197
+ motionDisabled &&
198
+ "cursor-default opacity-35 hover:text-muted-foreground",
199
+ motionOpen && "text-foreground",
200
+ )}
201
+ >
202
+ <span
203
+ className={cn(
204
+ "flex size-8 items-center justify-center rounded-lg transition-colors",
205
+ motionOpen
206
+ ? "bg-[var(--design-editor-selection-color)] text-[var(--design-editor-accent-color)]"
207
+ : "text-muted-foreground group-hover:bg-[var(--design-editor-layer-hover-color)] group-hover:text-foreground",
208
+ motionDisabled &&
209
+ "group-hover:bg-transparent group-hover:text-muted-foreground",
210
+ )}
211
+ >
212
+ {motionOpen ? (
213
+ <IconChevronDown className="size-[15px]" />
214
+ ) : (
215
+ <IconChevronUp className="size-[15px]" />
216
+ )}
217
+ </span>
218
+ <span className="max-w-full truncate leading-none">
219
+ {"Motion" /* i18n-ignore */}
220
+ </span>
221
+ </button>
222
+ </TooltipTrigger>
223
+ <TooltipContent side="right">
224
+ {"Motion" /* i18n-ignore */}
225
+ </TooltipContent>
226
+ </Tooltip>
227
+ </div>
228
+ ) : null}
229
+ </nav>
230
+ );
231
+ }
@@ -0,0 +1,223 @@
1
+ import { withBuilderUtmTrackingParams } from "@agent-native/core/shared";
2
+ import {
3
+ IconCircleCheck,
4
+ IconExternalLink,
5
+ IconRocket,
6
+ } from "@tabler/icons-react";
7
+
8
+ import { Button } from "@/components/ui/button";
9
+ import {
10
+ Dialog,
11
+ DialogContent,
12
+ DialogDescription,
13
+ DialogFooter,
14
+ DialogHeader,
15
+ DialogTitle,
16
+ } from "@/components/ui/dialog";
17
+ import { Spinner } from "@/components/ui/spinner";
18
+
19
+ /** Result of `migrate-inline-design-to-app`, including the not-configured CTA. */
20
+ export interface DesignMigrationResult {
21
+ branchName?: string;
22
+ url?: string;
23
+ versionId?: string;
24
+ seedFileCount?: number;
25
+ status?: string;
26
+ projectId?: string;
27
+ cta?: {
28
+ kind: string;
29
+ label: string;
30
+ description?: string;
31
+ connectUrl?: string;
32
+ primaryAction: string;
33
+ };
34
+ }
35
+
36
+ export function MakeRealDialog({
37
+ open,
38
+ onOpenChange,
39
+ result,
40
+ pending,
41
+ onConfirm,
42
+ }: {
43
+ open: boolean;
44
+ onOpenChange: (open: boolean) => void;
45
+ result: DesignMigrationResult | null;
46
+ pending: boolean;
47
+ onConfirm: () => void;
48
+ }) {
49
+ return (
50
+ <Dialog
51
+ open={open}
52
+ onOpenChange={(next) => {
53
+ if (!pending) onOpenChange(next);
54
+ }}
55
+ >
56
+ <DialogContent className="sm:max-w-md">
57
+ {/* Not-configured: Builder not connected or no project ID */}
58
+ {result?.status === "not-configured" && result.cta ? (
59
+ <>
60
+ <DialogHeader>
61
+ <DialogTitle className="flex items-center gap-2">
62
+ <IconRocket className="size-5 text-muted-foreground" />
63
+ {result.cta.label}
64
+ </DialogTitle>
65
+ {result.cta.description ? (
66
+ <DialogDescription>{result.cta.description}</DialogDescription>
67
+ ) : null}
68
+ </DialogHeader>
69
+ <DialogFooter className="flex-col gap-2 sm:flex-row">
70
+ <Button
71
+ variant="outline"
72
+ onClick={() => onOpenChange(false)}
73
+ className="cursor-pointer"
74
+ >
75
+ Cancel
76
+ </Button>
77
+ {result.cta.connectUrl ? (
78
+ <Button asChild className="cursor-pointer">
79
+ <a
80
+ href={result.cta.connectUrl}
81
+ target="_blank"
82
+ rel="noopener noreferrer"
83
+ >
84
+ {result.cta.primaryAction}
85
+ <IconExternalLink className="ml-1.5 size-3.5" />
86
+ </a>
87
+ </Button>
88
+ ) : null}
89
+ </DialogFooter>
90
+ </>
91
+ ) : result?.status === "processing" ? (
92
+ /* Success: Builder accepted the migration job */
93
+ <>
94
+ <DialogHeader>
95
+ <DialogTitle className="flex items-center gap-2">
96
+ {/* guard:allow-raw-color - success checkmark; no success token exists in app/global.css */}
97
+ <IconCircleCheck className="size-5 text-green-500" />
98
+ {"Migration started" /* i18n-ignore */}
99
+ </DialogTitle>
100
+ <DialogDescription>
101
+ {
102
+ "Builder is generating a React app branch from your design. The original inline design is preserved and recoverable." /* i18n-ignore */
103
+ }
104
+ </DialogDescription>
105
+ </DialogHeader>
106
+ <div className="space-y-3 py-2">
107
+ {result.branchName && (
108
+ <div className="rounded-md border border-border bg-muted/40 px-3 py-2 text-sm">
109
+ <span className="text-muted-foreground">
110
+ {"Branch: " /* i18n-ignore */}
111
+ </span>
112
+ <span className="font-mono font-medium">
113
+ {result.branchName}
114
+ </span>
115
+ </div>
116
+ )}
117
+ {result.url && (
118
+ <a
119
+ href={withBuilderUtmTrackingParams(result.url, {
120
+ campaign: "product",
121
+ content: "design_migration",
122
+ })}
123
+ target="_blank"
124
+ rel="noopener noreferrer"
125
+ className="flex items-center gap-1.5 text-sm text-[var(--design-editor-accent-color)] hover:underline"
126
+ >
127
+ {"Open in Builder" /* i18n-ignore */}
128
+ <IconExternalLink className="size-3.5" />
129
+ </a>
130
+ )}
131
+ {result.seedFileCount !== undefined && (
132
+ <p className="text-xs text-muted-foreground">
133
+ {
134
+ `${result.seedFileCount} design file${result.seedFileCount === 1 ? "" : "s"} included in migration seed.` /* i18n-ignore */
135
+ }
136
+ </p>
137
+ )}
138
+ </div>
139
+ <DialogFooter>
140
+ <Button
141
+ onClick={() => onOpenChange(false)}
142
+ className="cursor-pointer"
143
+ >
144
+ {"Done" /* i18n-ignore */}
145
+ </Button>
146
+ </DialogFooter>
147
+ </>
148
+ ) : (
149
+ /* Idle or migrating */
150
+ <>
151
+ <DialogHeader>
152
+ <DialogTitle className="flex items-center gap-2">
153
+ <IconRocket className="size-5" />
154
+ {"Make this a real app" /* i18n-ignore */}
155
+ </DialogTitle>
156
+ <DialogDescription>
157
+ {
158
+ "Connect Builder.io (free tier available) to convert this design into a React + Tailwind app with real components, props, branches, and deploys. Your current inline design is preserved as a snapshot you can restore at any time." /* i18n-ignore */
159
+ }
160
+ </DialogDescription>
161
+ </DialogHeader>
162
+ <div className="space-y-2 py-1 text-sm text-muted-foreground">
163
+ <p>{"What happens:" /* i18n-ignore */}</p>
164
+ <ul className="list-disc pl-4 space-y-1">
165
+ <li>
166
+ {
167
+ "Your design HTML and tokens are sent to the Builder cloud agent" /* i18n-ignore */
168
+ }
169
+ </li>
170
+ <li>
171
+ {
172
+ "A React + Tailwind branch is generated in Builder" /* i18n-ignore */
173
+ }
174
+ </li>
175
+ <li>
176
+ {
177
+ "The editor switches to fusion source mode — gated panels light up" /* i18n-ignore */
178
+ }
179
+ </li>
180
+ <li>
181
+ {
182
+ "The original inline design is saved as a restorable snapshot" /* i18n-ignore */
183
+ }
184
+ </li>
185
+ </ul>
186
+ <p className="pt-1 text-xs">
187
+ {
188
+ "Requires Builder.io to be connected (free tier available) with a branch project configured." /* i18n-ignore */
189
+ }
190
+ </p>
191
+ </div>
192
+ <DialogFooter className="flex-col gap-2 sm:flex-row">
193
+ <Button
194
+ variant="outline"
195
+ onClick={() => onOpenChange(false)}
196
+ disabled={pending}
197
+ className="cursor-pointer"
198
+ >
199
+ Cancel
200
+ </Button>
201
+ <Button
202
+ onClick={() => void onConfirm()}
203
+ disabled={pending}
204
+ className="cursor-pointer"
205
+ >
206
+ {
207
+ pending ? (
208
+ <>
209
+ <Spinner className="mr-2 size-3.5" />
210
+ {"Starting migration…" /* i18n-ignore */}
211
+ </>
212
+ ) : (
213
+ "Start migration"
214
+ ) /* i18n-ignore */
215
+ }
216
+ </Button>
217
+ </DialogFooter>
218
+ </>
219
+ )}
220
+ </DialogContent>
221
+ </Dialog>
222
+ );
223
+ }
@@ -0,0 +1,58 @@
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
+ import type { DesignFile } from "@/pages/design-editor/types";
14
+
15
+ export function PendingScreenDeletionDialog({
16
+ pendingScreenDeletion,
17
+ onCancel,
18
+ onConfirm,
19
+ }: {
20
+ pendingScreenDeletion: { files: DesignFile[] } | null;
21
+ onCancel: () => void;
22
+ onConfirm: () => void;
23
+ }) {
24
+ const t = useT();
25
+ return (
26
+ <AlertDialog open={pendingScreenDeletion !== null}>
27
+ <AlertDialogContent>
28
+ <AlertDialogHeader>
29
+ <AlertDialogTitle>
30
+ {pendingScreenDeletion?.files.length === 1
31
+ ? t("designEditor.screenDeletion.titleOne")
32
+ : t("designEditor.screenDeletion.titleMany", {
33
+ count: pendingScreenDeletion?.files.length ?? 0,
34
+ })}
35
+ </AlertDialogTitle>
36
+ <AlertDialogDescription>
37
+ {pendingScreenDeletion?.files.length === 1
38
+ ? t("designEditor.screenDeletion.descriptionOne", {
39
+ filename: pendingScreenDeletion.files[0]?.filename ?? "",
40
+ })
41
+ : t("designEditor.screenDeletion.descriptionMany")}
42
+ </AlertDialogDescription>
43
+ </AlertDialogHeader>
44
+ <AlertDialogFooter>
45
+ <AlertDialogCancel onClick={onCancel}>
46
+ {t("designEditor.screenDeletion.cancel")}
47
+ </AlertDialogCancel>
48
+ <AlertDialogAction
49
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
50
+ onClick={onConfirm}
51
+ >
52
+ {t("designEditor.screenDeletion.confirm")}
53
+ </AlertDialogAction>
54
+ </AlertDialogFooter>
55
+ </AlertDialogContent>
56
+ </AlertDialog>
57
+ );
58
+ }