@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +29 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -2
- package/corpus/templates/analytics/netlify.toml +2 -0
- package/corpus/templates/assets/netlify.toml +2 -0
- package/corpus/templates/brain/netlify.toml +2 -0
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +2 -1
- package/corpus/templates/calendar/changelog/2026-08-19-calendar-page-loads-stay-fast-after-idle-periods.md +6 -0
- package/corpus/templates/calendar/netlify.toml +2 -0
- package/corpus/templates/chat/netlify.toml +2 -0
- package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -3
- package/corpus/templates/clips/app/components/player/reaction-emojis.ts +12 -0
- package/corpus/templates/clips/app/components/player/reactions-tray.tsx +90 -9
- package/corpus/templates/clips/app/components/player/scrubber.tsx +43 -10
- package/corpus/templates/clips/app/components/player/video-player.tsx +105 -15
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -2
- package/corpus/templates/clips/app/routes/_index.tsx +10 -2
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +13 -8
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +15 -8
- package/corpus/templates/clips/netlify.toml +2 -0
- package/corpus/templates/clips/server/routes/[...page].head.ts +5 -0
- package/corpus/templates/content/netlify.toml +2 -0
- package/corpus/templates/design/.agents/skills/design-editor-architecture/SKILL.md +103 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +2 -1
- package/corpus/templates/design/AGENTS.md +3 -1
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +3 -2
- package/corpus/templates/design/app/components/design/editor/AgentNativeMenuMark.tsx +25 -0
- package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +411 -0
- package/corpus/templates/design/app/components/design/editor/DesignCollaborators.tsx +153 -0
- package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +231 -0
- package/corpus/templates/design/app/components/design/editor/MakeRealDialog.tsx +223 -0
- package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +58 -0
- package/corpus/templates/design/app/components/design/editor/PendingVisualStyleWarningDialog.tsx +56 -0
- package/corpus/templates/design/app/components/design/editor/ReadOnlyEditorPanel.tsx +23 -0
- package/corpus/templates/design/app/components/design/editor/SaveTemplateDialog.tsx +161 -0
- package/corpus/templates/design/app/components/design/editor/toolbar-controls.tsx +185 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +20913 -0
- package/corpus/templates/design/app/pages/design-editor/command-types.ts +120 -0
- package/corpus/templates/design/app/pages/design-editor/commands/README.md +193 -0
- package/corpus/templates/design/app/pages/design-editor/commands/add-auto-layout.ts +416 -0
- package/corpus/templates/design/app/pages/design-editor/commands/add-screen.ts +112 -0
- package/corpus/templates/design/app/pages/design-editor/commands/align-selection.ts +183 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +243 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +236 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +304 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +376 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-to-source.ts +132 -0
- package/corpus/templates/design/app/pages/design-editor/commands/can-move-layer.ts +117 -0
- package/corpus/templates/design/app/pages/design-editor/commands/change-selected-z-index.ts +212 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-relative-style-delta-to-selected-layers.ts +176 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-styles-to-selected-layers.ts +175 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +710 -0
- package/corpus/templates/design/app/pages/design-editor/commands/confirm-make-real.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/commands/copy-as-figma-svg.ts +95 -0
- package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +156 -0
- package/corpus/templates/design/app/pages/design-editor/commands/create-primitive.ts +291 -0
- package/corpus/templates/design/app/pages/design-editor/commands/create-screen-frame.ts +131 -0
- package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +577 -0
- package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +364 -0
- package/corpus/templates/design/app/pages/design-editor/commands/delete-selection.ts +521 -0
- package/corpus/templates/design/app/pages/design-editor/commands/detach-instance-menu-action.ts +88 -0
- package/corpus/templates/design/app/pages/design-editor/commands/distribute-selection.ts +114 -0
- package/corpus/templates/design/app/pages/design-editor/commands/download-all-screens-pdf.ts +116 -0
- package/corpus/templates/design/app/pages/design-editor/commands/download-pdf.ts +103 -0
- package/corpus/templates/design/app/pages/design-editor/commands/download-svg.ts +159 -0
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +201 -0
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +279 -0
- package/corpus/templates/design/app/pages/design-editor/commands/editor-paste.ts +81 -0
- package/corpus/templates/design/app/pages/design-editor/commands/enter-hotkey.ts +155 -0
- package/corpus/templates/design/app/pages/design-editor/commands/enter-single-screen.ts +168 -0
- package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +212 -0
- package/corpus/templates/design/app/pages/design-editor/commands/frame-selection.ts +119 -0
- package/corpus/templates/design/app/pages/design-editor/commands/geometry-commit.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +177 -0
- package/corpus/templates/design/app/pages/design-editor/commands/group-selection.ts +127 -0
- package/corpus/templates/design/app/pages/design-editor/commands/iframe-context-menu.ts +121 -0
- package/corpus/templates/design/app/pages/design-editor/commands/import-figma-clipboard-into-design.ts +119 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +163 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-move-to-screen.ts +331 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-move.ts +517 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-rename.ts +247 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +161 -0
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +122 -0
- package/corpus/templates/design/app/pages/design-editor/commands/nudge-selection.ts +213 -0
- package/corpus/templates/design/app/pages/design-editor/commands/overview-primitive-reparent.ts +318 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-copied-screens.ts +114 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-over-selection.ts +70 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +436 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-to-replace.ts +135 -0
- package/corpus/templates/design/app/pages/design-editor/commands/pasted-image-files.ts +209 -0
- package/corpus/templates/design/app/pages/design-editor/commands/persist-frame-geometry-save.ts +113 -0
- package/corpus/templates/design/app/pages/design-editor/commands/persist-tweak-save.ts +160 -0
- package/corpus/templates/design/app/pages/design-editor/commands/primitive-created.ts +168 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-layer-state-edit.ts +149 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-structure-edit.ts +200 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-text-edit.ts +152 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-visual-style-edit.ts +247 -0
- package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +1086 -0
- package/corpus/templates/design/app/pages/design-editor/commands/render-png-blob.ts +200 -0
- package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +207 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +262 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-text-content-change.ts +216 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +108 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-structure-change.ts +280 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +151 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-overview-annotations.ts +88 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +122 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +143 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +115 -0
- package/corpus/templates/design/app/pages/design-editor/commands/start-retry-generation.ts +152 -0
- package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/commands/style-change.ts +152 -0
- package/corpus/templates/design/app/pages/design-editor/commands/styles-change.ts +155 -0
- package/corpus/templates/design/app/pages/design-editor/commands/suggest-auto-layout.ts +125 -0
- package/corpus/templates/design/app/pages/design-editor/commands/text-content-change.ts +201 -0
- package/corpus/templates/design/app/pages/design-editor/commands/tidy-up.ts +123 -0
- package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-hidden.ts +167 -0
- package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-locked.ts +175 -0
- package/corpus/templates/design/app/pages/design-editor/commands/toggle-motion-keyframe.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/commands/tweak-prompt-submit.ts +84 -0
- package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +906 -0
- package/corpus/templates/design/app/pages/design-editor/commands/ungroup-selection.ts +170 -0
- package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +119 -0
- package/corpus/templates/design/app/pages/design-editor/commands/visual-structure-change.ts +274 -0
- package/corpus/templates/design/app/pages/design-editor/commands/write-frame-geometry-snapshot.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/derive/design-breakpoints.ts +56 -0
- package/corpus/templates/design/app/pages/design-editor/derive/overview-screens.ts +146 -0
- package/corpus/templates/design/app/pages/design-editor/domains/use-tweaks.ts +326 -0
- package/corpus/templates/design/app/pages/design-editor/editor-constants.ts +54 -0
- package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +183 -0
- package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +270 -0
- package/corpus/templates/design/app/pages/design-editor/effects/mirror-selection-to-agent-chat.ts +134 -0
- package/corpus/templates/design/app/pages/design-editor/effects/motion-autosave.ts +335 -0
- package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +183 -0
- package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +252 -0
- package/corpus/templates/design/app/pages/design-editor/effects/resume-pending-generation.ts +178 -0
- package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +157 -0
- package/corpus/templates/design/app/pages/design-editor/preview-html.ts +19 -0
- package/corpus/templates/design/netlify.toml +2 -0
- package/corpus/templates/design/shared/board-file.ts +2 -1
- package/corpus/templates/dispatch/netlify.toml +2 -0
- package/corpus/templates/forms/netlify.toml +2 -0
- package/corpus/templates/mail/netlify.toml +2 -0
- package/corpus/templates/plan/netlify.toml +2 -0
- package/corpus/templates/slides/netlify.toml +2 -0
- package/dist/agent/engine/builder-engine.d.ts +6 -4
- package/dist/agent/engine/builder-engine.js +91 -26
- package/dist/agent/engine/registry.js +16 -0
- package/dist/cli/code-agent-agent-tools.d.ts +2 -0
- package/dist/cli/code-agent-agent-tools.js +188 -0
- package/dist/cli/code-agent-collaboration.d.ts +34 -0
- package/dist/cli/code-agent-collaboration.js +104 -0
- package/dist/cli/code-agent-executor.js +6 -0
- package/dist/cli/code-agent-schedules.d.ts +54 -0
- package/dist/cli/code-agent-schedules.js +198 -0
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/ConnectBuilderCard.js +5 -2
- package/dist/client/EnvironmentBadge.js +6 -5
- package/dist/client/chat/run-recovery.js +3 -2
- package/dist/client/chat/tool-call-display.js +2 -3
- package/dist/client/components/CodeRequiredDialog.js +4 -2
- package/dist/client/i18n.js +5 -4
- package/dist/client/settings/SettingsPanel.js +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +6 -1
- package/dist/client/settings/useBuilderStatus.js +22 -33
- package/dist/client/transcription/BuilderTranscriptionCta.js +1 -1
- package/dist/code-agents/index.d.ts +2 -0
- package/dist/code-agents/index.js +2 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/localization/shared.d.ts +2 -0
- package/dist/localization/shared.js +5 -0
- package/dist/mcp-client/oauth-client.d.ts +1 -0
- package/dist/mcp-client/oauth-client.js +50 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/auth-marketing-locales.d.ts +7 -0
- package/dist/server/auth-marketing-locales.js +1322 -0
- package/dist/server/auth-marketing.d.ts +1 -0
- package/dist/server/auth-marketing.js +35 -6
- package/dist/server/auth.js +4 -17
- package/dist/server/builder-browser.d.ts +22 -0
- package/dist/server/builder-browser.js +77 -1
- package/dist/server/builder-oauth.d.ts +50 -0
- package/dist/server/builder-oauth.js +263 -0
- package/dist/server/core-routes-plugin.d.ts +11 -0
- package/dist/server/core-routes-plugin.js +291 -415
- package/dist/server/credential-provider.d.ts +1 -1
- package/dist/server/credential-provider.js +1 -1
- package/dist/server/csrf.js +2 -2
- package/dist/server/onboarding-html.js +52 -120
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/settings/store.d.ts +9 -0
- package/dist/settings/store.js +19 -0
- package/dist/templates/chat/netlify.toml +2 -0
- package/dist/vite/client.js +35 -37
- package/docs/content/deployment.mdx +2 -0
- package/docs/content/multi-app-workspace.mdx +1 -1
- package/package.json +2 -2
- 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
|
+
}
|