@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.
- 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/progress/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/settings/store.d.ts +9 -0
- package/dist/settings/store.js +19 -0
- package/dist/templates/chat/netlify.toml +2 -0
- 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
|
@@ -1274,7 +1274,7 @@ export default function ShareRoute() {
|
|
|
1274
1274
|
onReact={(emoji) => {
|
|
1275
1275
|
if (!session) {
|
|
1276
1276
|
requireSignIn("react");
|
|
1277
|
-
return;
|
|
1277
|
+
return false;
|
|
1278
1278
|
}
|
|
1279
1279
|
tracking.reportReaction(emoji);
|
|
1280
1280
|
const liveCt = isLoomEmbedBacked
|
|
@@ -1287,7 +1287,7 @@ export default function ShareRoute() {
|
|
|
1287
1287
|
liveCt < 1e7
|
|
1288
1288
|
? Math.floor(liveCt * 1000)
|
|
1289
1289
|
: currentMs;
|
|
1290
|
-
fetch(
|
|
1290
|
+
return fetch(
|
|
1291
1291
|
agentNativePath("/_agent-native/actions/react-to-recording"),
|
|
1292
1292
|
{
|
|
1293
1293
|
method: "POST",
|
|
@@ -1303,7 +1303,11 @@ export default function ShareRoute() {
|
|
|
1303
1303
|
if (!res.ok) throw new Error(`react failed: ${res.status}`);
|
|
1304
1304
|
return dataQ.refetch();
|
|
1305
1305
|
})
|
|
1306
|
-
.
|
|
1306
|
+
.then(() => true)
|
|
1307
|
+
.catch((err) => {
|
|
1308
|
+
console.warn("[clips] react failed", err);
|
|
1309
|
+
return false;
|
|
1310
|
+
});
|
|
1307
1311
|
}}
|
|
1308
1312
|
className="h-full w-full rounded-none sm:rounded-xl"
|
|
1309
1313
|
/>
|
|
@@ -1371,10 +1375,11 @@ export default function ShareRoute() {
|
|
|
1371
1375
|
) : null}
|
|
1372
1376
|
{recording.enableReactions ? (
|
|
1373
1377
|
<ReactionsTray
|
|
1378
|
+
reactions={reactions}
|
|
1374
1379
|
onReact={(emoji) => {
|
|
1375
1380
|
if (!session) {
|
|
1376
1381
|
requireSignIn("react");
|
|
1377
|
-
return;
|
|
1382
|
+
return false;
|
|
1378
1383
|
}
|
|
1379
1384
|
tracking.reportReaction(emoji);
|
|
1380
1385
|
const liveCt = isLoomEmbedBacked
|
|
@@ -1387,7 +1392,7 @@ export default function ShareRoute() {
|
|
|
1387
1392
|
liveCt < 1e7
|
|
1388
1393
|
? Math.floor(liveCt * 1000)
|
|
1389
1394
|
: currentMs;
|
|
1390
|
-
fetch(
|
|
1395
|
+
return fetch(
|
|
1391
1396
|
agentNativePath(
|
|
1392
1397
|
"/_agent-native/actions/react-to-recording",
|
|
1393
1398
|
),
|
|
@@ -1406,9 +1411,11 @@ export default function ShareRoute() {
|
|
|
1406
1411
|
throw new Error(`react failed: ${res.status}`);
|
|
1407
1412
|
return dataQ.refetch();
|
|
1408
1413
|
})
|
|
1409
|
-
.
|
|
1410
|
-
|
|
1411
|
-
|
|
1414
|
+
.then(() => true)
|
|
1415
|
+
.catch((err) => {
|
|
1416
|
+
console.warn("[clips] react failed", err);
|
|
1417
|
+
return false;
|
|
1418
|
+
});
|
|
1412
1419
|
}}
|
|
1413
1420
|
/>
|
|
1414
1421
|
) : null}
|
|
@@ -16,6 +16,8 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
|
|
|
16
16
|
|
|
17
17
|
[context.production.environment]
|
|
18
18
|
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
19
|
+
AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
|
|
20
|
+
AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
|
|
19
21
|
|
|
20
22
|
# Netlify Functions run on linux/x64 (AWS Lambda). This tells the deploy build
|
|
21
23
|
# to bundle the matching ffmpeg-static binary into the server bundle so frame
|
|
@@ -16,6 +16,11 @@ export default defineEventHandler((event) => {
|
|
|
16
16
|
return new Response(null, {
|
|
17
17
|
status: 302,
|
|
18
18
|
headers: {
|
|
19
|
+
// Keep this redirect on the same public SSR-cache path as the shell.
|
|
20
|
+
// Without a content type the deploy adapter cannot apply the canonical
|
|
21
|
+
// cache policy, so every visit to `/` reaches the origin before the
|
|
22
|
+
// already-cached `/library` shell can load.
|
|
23
|
+
"content-type": "text/html; charset=utf-8",
|
|
19
24
|
location: "/library",
|
|
20
25
|
"Permissions-Policy": MEDIA_CAPTURE_PERMISSIONS_POLICY,
|
|
21
26
|
},
|
|
@@ -16,6 +16,8 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
|
|
|
16
16
|
|
|
17
17
|
[context.production.environment]
|
|
18
18
|
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
19
|
+
AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
|
|
20
|
+
AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
|
|
19
21
|
|
|
20
22
|
# A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
|
|
21
23
|
# The framework does not impose a default agent-run timeout; if a template sets
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-editor-architecture
|
|
3
|
+
description: >-
|
|
4
|
+
Where design editor behavior lives and how to navigate DesignEditor.tsx. Use
|
|
5
|
+
before changing any editor behavior — undo/redo, paste, delete, duplicate,
|
|
6
|
+
style commit, layer move/rename, export, structure change — or before opening
|
|
7
|
+
`app/pages/DesignEditor.tsx`.
|
|
8
|
+
scope: dev
|
|
9
|
+
metadata:
|
|
10
|
+
internal: true
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Design Editor Architecture
|
|
14
|
+
|
|
15
|
+
## The routing rule
|
|
16
|
+
|
|
17
|
+
Editor **behavior** lives in `app/pages/design-editor/`, not in
|
|
18
|
+
`app/pages/DesignEditor.tsx`.
|
|
19
|
+
|
|
20
|
+
`DesignEditor.tsx` is ~20,900 lines and holds only three things: state
|
|
21
|
+
declarations, the `useCallback` wrappers that gather arguments, and the JSX.
|
|
22
|
+
Every wrapper delegates to a `run<Name>()` command module.
|
|
23
|
+
|
|
24
|
+
**To change what an editor action does, edit the command module.** Opening
|
|
25
|
+
`DesignEditor.tsx` to change behavior is almost always the wrong move — it will
|
|
26
|
+
exhaust your context before you find the code.
|
|
27
|
+
|
|
28
|
+
| Directory | Holds |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `design-editor/commands/` | 86 one-per-action modules, each exporting `run<Name>(args, …)`. Start at `commands/README.md` |
|
|
31
|
+
| `design-editor/effects/` | Subscription and autosave loops (collab text, motion autosave, agent selection mirroring) |
|
|
32
|
+
| `design-editor/derive/` | Pure derivations (`overview-screens.ts`, `design-breakpoints.ts`) |
|
|
33
|
+
| `design-editor/domains/` | Whole-domain hooks owning state + refs + effects + handlers together (`use-tweaks.ts`) |
|
|
34
|
+
| `design-editor/*.ts` | Shared helpers: `history.ts`, `selection-state.ts`, `pending-edits.ts`, `editor-state.ts`, `editor-helpers.ts`, … |
|
|
35
|
+
|
|
36
|
+
## Common task → file
|
|
37
|
+
|
|
38
|
+
| Task | File |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| Undo / redo | `commands/undo.ts`, `commands/redo.ts` |
|
|
41
|
+
| Paste | `commands/editor-paste.ts` routes; `commands/paste-selection.ts` and `commands/paste-over-selection.ts` do layers |
|
|
42
|
+
| Copy | `commands/copy-selection.ts` |
|
|
43
|
+
| Delete | `commands/delete-selection.ts` (layers), `commands/delete-files.ts` (screens) |
|
|
44
|
+
| Duplicate | `commands/duplicate-selection.ts`, `commands/duplicate-screen.ts` |
|
|
45
|
+
| Style commit | `commands/commit-visual-styles.ts` |
|
|
46
|
+
| Inspector style edit | `commands/style-change.ts` (one property), `commands/styles-change.ts` (many) |
|
|
47
|
+
| Structure change | `commands/visual-structure-change.ts`, `commands/screen-visual-structure-change.ts` |
|
|
48
|
+
| Layer move / rename | `commands/layer-move.ts`, `commands/layer-move-to-screen.ts`, `commands/layer-rename.ts` |
|
|
49
|
+
| Lock / hide a layer | `commands/toggle-layer-locked.ts`, `commands/toggle-layer-hidden.ts` |
|
|
50
|
+
| Export | `commands/render-png-blob.ts`, `download-pdf.ts`, `download-svg.ts`, `copy-as-figma-svg.ts` |
|
|
51
|
+
| Save / persistence | `commands/save-file-content.ts`, `commands/apply-file-content-update.ts` |
|
|
52
|
+
| Breakpoints | `responsive-breakpoints` skill; `derive/design-breakpoints.ts` |
|
|
53
|
+
|
|
54
|
+
A `screen-` prefix means the command is addressed by an explicit `screenId`
|
|
55
|
+
(overview canvas or board). The unprefixed twin acts on the focused screen.
|
|
56
|
+
|
|
57
|
+
Before adding a `domains/` hook, count what it would expose. Past roughly 16
|
|
58
|
+
returned values the hook stops hiding anything and just relocates the wiring —
|
|
59
|
+
measured surfaces for share/export (20), generation (18), and motion (48) are
|
|
60
|
+
all why those still live inline. Also check no input it needs is declared after
|
|
61
|
+
the point where its own outputs are first consumed; responsive-interact fails
|
|
62
|
+
that test and cannot be extracted without changing when values are read.
|
|
63
|
+
|
|
64
|
+
## Navigating DesignEditor.tsx when you must
|
|
65
|
+
|
|
66
|
+
The file carries ~82 section banners. This prints a table of contents:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
grep -n "──" app/pages/DesignEditor.tsx
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Read one region with an offset and a limit instead of opening the file. Every
|
|
73
|
+
section is under ~800 lines. Banners use `// ── Name ──` in the component body
|
|
74
|
+
and `{/* ── Render: name ── */}` inside the JSX.
|
|
75
|
+
|
|
76
|
+
When you add or move a region, add a banner for it.
|
|
77
|
+
|
|
78
|
+
## Hard constraints
|
|
79
|
+
|
|
80
|
+
**`DesignEditor.tsx` must keep exactly one runtime export — the default — and
|
|
81
|
+
zero runtime named exports.** Type-only exports are fine. Both routes
|
|
82
|
+
(`app/routes/design.$id.tsx`, `app/routes/visual-edit.$id.tsx`) import that
|
|
83
|
+
default. A named export breaks React Fast Refresh for the whole editor.
|
|
84
|
+
|
|
85
|
+
**The render-callback trio's dependency arrays are load-bearing.**
|
|
86
|
+
`DesignEditor.routeRefreshBoundary.test.ts` parses the file with the TypeScript
|
|
87
|
+
compiler and enforces:
|
|
88
|
+
|
|
89
|
+
- `renderScreenContent` deps are exactly `[renderEditableScreenContent]`
|
|
90
|
+
- `renderBreakpointContent` deps are exactly `[renderEditableScreenContent]`
|
|
91
|
+
- `renderEditableScreenContent` deps include `activeBreakpointWidthState`,
|
|
92
|
+
`motionDefaultEase`, `motionDurationMs`, `inScreenGradientEditTarget`,
|
|
93
|
+
`handleInScreenGradientEditChange`, and `statePreviewTarget`
|
|
94
|
+
|
|
95
|
+
That last one exists because cached overview canvases must invalidate on
|
|
96
|
+
preview-only state changes. Dropping a name from it renders a stale canvas.
|
|
97
|
+
|
|
98
|
+
**Many specs read source as text.** ~20 specs `readFileSync` either
|
|
99
|
+
`DesignEditor.tsx` or a command module and slice it with `indexOf` markers, then
|
|
100
|
+
assert on the source string. Moving code breaks them with a confusing failure.
|
|
101
|
+
Re-point the path and the marker in the same commit that moves the code. Prefer
|
|
102
|
+
asserting against the command module (`commandSource("undo.ts")`) over the
|
|
103
|
+
editor file.
|
|
@@ -907,7 +907,8 @@ read `references/code-layers.md`. For the VS Code-style source workbench
|
|
|
907
907
|
reserved for editor-internal state (selection chrome, editor-chrome scale
|
|
908
908
|
compensation, clipboard/surface tokens) — the editor strips any property
|
|
909
909
|
under those prefixes before persisting a cross-screen style capture
|
|
910
|
-
(`isEditorInternalCssVar` in `app/pages/
|
|
910
|
+
(`isEditorInternalCssVar` in `app/pages/design-editor/portable-style.ts`),
|
|
911
|
+
so a design that
|
|
911
912
|
defines its own theming under that namespace would silently lose those
|
|
912
913
|
values on a cross-screen move. Use `--color-*`, `--font-*`, or another
|
|
913
914
|
design-owned prefix for tokens/tweaks instead.
|
|
@@ -99,4 +99,6 @@ Read the relevant skill before deeper work in that area.
|
|
|
99
99
|
## Source Changes
|
|
100
100
|
|
|
101
101
|
Before building common workspace or agent UI, read `agent-native-toolkit`; read
|
|
102
|
-
`customizing-agent-native` before adapting shared UI.
|
|
102
|
+
`customizing-agent-native` before adapting shared UI. Editor behavior lives in
|
|
103
|
+
`app/pages/design-editor/commands/*.ts`, not `DesignEditor.tsx` — read
|
|
104
|
+
`design-editor-architecture` before changing it.
|
|
@@ -1768,14 +1768,15 @@ declare var __SELECTED_LAYER_DRAG_PRIORITY__: boolean;
|
|
|
1768
1768
|
// Editor-internal CSS custom-property prefixes — selection chrome colors,
|
|
1769
1769
|
// editor-chrome scale compensation, framework clipboard/surface tokens.
|
|
1770
1770
|
// These have no meaning outside this editor session and must never leak
|
|
1771
|
-
// into persisted user HTML/exports.
|
|
1771
|
+
// into persisted user HTML/exports. design-editor/portable-style.ts's
|
|
1772
1772
|
// applyPortableStyleSnapshotToHtml (isEditorInternalCssVar /
|
|
1773
1773
|
// EDITOR_INTERNAL_CSS_VAR_PREFIXES) already filters them back out on the
|
|
1774
1774
|
// apply side; filtering here too at COLLECTION time is pure bloat
|
|
1775
1775
|
// reduction (skips carrying them across the postMessage boundary at all)
|
|
1776
1776
|
// and changes no observable behavior on the apply side.
|
|
1777
1777
|
//
|
|
1778
|
-
// keep in sync with
|
|
1778
|
+
// keep in sync with design-editor/portable-style.ts's
|
|
1779
|
+
// EDITOR_INTERNAL_CSS_VAR_PREFIXES
|
|
1779
1780
|
var EDITOR_INTERNAL_CSS_VAR_PREFIXES = [
|
|
1780
1781
|
"--design-editor-",
|
|
1781
1782
|
"--agent-native-editor-chrome-",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function AgentNativeMenuMark({ className }: { className?: string }) {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
viewBox="-5 -5 145 88"
|
|
5
|
+
fill="none"
|
|
6
|
+
aria-hidden="true"
|
|
7
|
+
className={className}
|
|
8
|
+
>
|
|
9
|
+
<path
|
|
10
|
+
d="M29.0771 77.8838H2.5L18.9 48.9L43.2 6.6C44 5.2 45.9 5.2 46.7 6.6L69.1 44.2C69.9 45.5 69 46.7305 67.5 46.7305H48.3C47.6 46.7305 46.9 47.1 46.6 47.7L30.8 76.9C30.45 77.5 29.8 77.8838 29.0771 77.8838Z"
|
|
11
|
+
stroke="currentColor"
|
|
12
|
+
strokeWidth="10.5"
|
|
13
|
+
strokeLinecap="round"
|
|
14
|
+
strokeLinejoin="round"
|
|
15
|
+
/>
|
|
16
|
+
<path
|
|
17
|
+
d="M105.927 0H132.5C134 0 134.9 1.6 134.15 2.9L91.5 76.9C91.15 77.5 90.5 77.8853 89.8 77.8853H63.8C62.3 77.8853 61.4 76.3 62.15 75L104.2 1C104.55 0.38 105.2 0 105.927 0Z"
|
|
18
|
+
stroke="currentColor"
|
|
19
|
+
strokeWidth="10.5"
|
|
20
|
+
strokeLinecap="round"
|
|
21
|
+
strokeLinejoin="round"
|
|
22
|
+
/>
|
|
23
|
+
</svg>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import {
|
|
3
|
+
IconArrowUpRight,
|
|
4
|
+
IconBrush,
|
|
5
|
+
IconCircle,
|
|
6
|
+
IconDevices,
|
|
7
|
+
IconFrame,
|
|
8
|
+
IconHandClick,
|
|
9
|
+
IconHandStop,
|
|
10
|
+
IconLine,
|
|
11
|
+
IconMessage,
|
|
12
|
+
IconPhotoVideo,
|
|
13
|
+
IconPointer,
|
|
14
|
+
IconScale,
|
|
15
|
+
IconScribble,
|
|
16
|
+
IconSquare,
|
|
17
|
+
IconStar,
|
|
18
|
+
IconTransformPoint,
|
|
19
|
+
IconTriangle,
|
|
20
|
+
} from "@tabler/icons-react";
|
|
21
|
+
import type { ReactNode } from "react";
|
|
22
|
+
|
|
23
|
+
import type { DesignToolbarOption } from "@/components/design/editor/toolbar-controls";
|
|
24
|
+
import {
|
|
25
|
+
DesignModeTab,
|
|
26
|
+
DesignPenToolIcon,
|
|
27
|
+
DesignToolbarTool,
|
|
28
|
+
} from "@/components/design/editor/toolbar-controls";
|
|
29
|
+
import { IconText } from "@/components/design/inspector/design-icons";
|
|
30
|
+
import {
|
|
31
|
+
MOVE_GROUP_TOOL_PRESENTATIONS,
|
|
32
|
+
getMoveGroupToolPresentation,
|
|
33
|
+
} from "@/pages/design-editor/tool-state";
|
|
34
|
+
import type {
|
|
35
|
+
DesignTool,
|
|
36
|
+
EditorMode,
|
|
37
|
+
ShapeTool,
|
|
38
|
+
} from "@/pages/design-editor/types";
|
|
39
|
+
|
|
40
|
+
export function DesignBottomToolbar({
|
|
41
|
+
mode,
|
|
42
|
+
pinMode,
|
|
43
|
+
drawMode,
|
|
44
|
+
activeTool,
|
|
45
|
+
isOverview,
|
|
46
|
+
hasActiveFile,
|
|
47
|
+
onMove,
|
|
48
|
+
onFrame,
|
|
49
|
+
frameToolDraws,
|
|
50
|
+
onFrameToolDrawsChange,
|
|
51
|
+
onShape,
|
|
52
|
+
onText,
|
|
53
|
+
onPen,
|
|
54
|
+
onHand,
|
|
55
|
+
onDraw,
|
|
56
|
+
onScale,
|
|
57
|
+
onCommentPin,
|
|
58
|
+
onModeChange,
|
|
59
|
+
shortcutsPanelOpen,
|
|
60
|
+
}: {
|
|
61
|
+
mode: EditorMode;
|
|
62
|
+
pinMode: boolean;
|
|
63
|
+
drawMode: boolean;
|
|
64
|
+
activeTool: DesignTool;
|
|
65
|
+
isOverview: boolean;
|
|
66
|
+
hasActiveFile: boolean;
|
|
67
|
+
onMove: () => void;
|
|
68
|
+
onFrame: () => void;
|
|
69
|
+
frameToolDraws: "screen" | "frame";
|
|
70
|
+
onFrameToolDrawsChange: (value: "screen" | "frame") => void;
|
|
71
|
+
onShape: (tool: ShapeTool) => void;
|
|
72
|
+
onText: () => void;
|
|
73
|
+
onPen: () => void;
|
|
74
|
+
onHand: () => void;
|
|
75
|
+
onDraw: () => void;
|
|
76
|
+
onScale: () => void;
|
|
77
|
+
onCommentPin: () => void;
|
|
78
|
+
onModeChange: (mode: EditorMode) => void;
|
|
79
|
+
shortcutsPanelOpen: boolean;
|
|
80
|
+
}) {
|
|
81
|
+
const t = useT();
|
|
82
|
+
const shapeTools = new Set<DesignTool>([
|
|
83
|
+
"rect",
|
|
84
|
+
"line",
|
|
85
|
+
"arrow",
|
|
86
|
+
"ellipse",
|
|
87
|
+
"polygon",
|
|
88
|
+
"star",
|
|
89
|
+
]);
|
|
90
|
+
const activeShape = shapeTools.has(activeTool)
|
|
91
|
+
? (activeTool as ShapeTool)
|
|
92
|
+
: "rect";
|
|
93
|
+
const shapeIcon = (tool: ShapeTool, className: string) => {
|
|
94
|
+
switch (tool) {
|
|
95
|
+
case "line":
|
|
96
|
+
return <IconLine className={className} />;
|
|
97
|
+
case "arrow":
|
|
98
|
+
return <IconArrowUpRight className={className} />;
|
|
99
|
+
case "ellipse":
|
|
100
|
+
return <IconCircle className={className} />;
|
|
101
|
+
case "polygon":
|
|
102
|
+
return <IconTriangle className={className} />;
|
|
103
|
+
case "star":
|
|
104
|
+
return <IconStar className={className} />;
|
|
105
|
+
case "rect":
|
|
106
|
+
default:
|
|
107
|
+
return <IconSquare className={className} />;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const shapeOptions: DesignToolbarOption[] = [
|
|
111
|
+
{
|
|
112
|
+
key: "rect",
|
|
113
|
+
label: t("designEditor.tools.rect"),
|
|
114
|
+
icon: shapeIcon("rect", "size-4"),
|
|
115
|
+
shortcut: "R",
|
|
116
|
+
active: activeTool === "rect",
|
|
117
|
+
onSelect: () => onShape("rect"),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
key: "line",
|
|
121
|
+
label: t("designEditor.tools.line"),
|
|
122
|
+
icon: shapeIcon("line", "size-4"),
|
|
123
|
+
shortcut: "L",
|
|
124
|
+
active: activeTool === "line",
|
|
125
|
+
onSelect: () => onShape("line"),
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
key: "arrow",
|
|
129
|
+
label: t("designEditor.tools.arrow"),
|
|
130
|
+
icon: shapeIcon("arrow", "size-4"),
|
|
131
|
+
shortcut: "⇧L",
|
|
132
|
+
active: activeTool === "arrow",
|
|
133
|
+
onSelect: () => onShape("arrow"),
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
key: "ellipse",
|
|
137
|
+
label: t("designEditor.tools.ellipse"),
|
|
138
|
+
icon: shapeIcon("ellipse", "size-4"),
|
|
139
|
+
shortcut: "O",
|
|
140
|
+
active: activeTool === "ellipse",
|
|
141
|
+
onSelect: () => onShape("ellipse"),
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
key: "polygon",
|
|
145
|
+
label: t("designEditor.tools.polygon"),
|
|
146
|
+
icon: shapeIcon("polygon", "size-4"),
|
|
147
|
+
active: activeTool === "polygon",
|
|
148
|
+
onSelect: () => onShape("polygon"),
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
key: "star",
|
|
152
|
+
label: t("designEditor.tools.star"),
|
|
153
|
+
icon: shapeIcon("star", "size-4"),
|
|
154
|
+
active: activeTool === "star",
|
|
155
|
+
onSelect: () => onShape("star"),
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
key: "image-video",
|
|
159
|
+
label: t("designEditor.tools.imageVideo"),
|
|
160
|
+
icon: <IconPhotoVideo className="size-4" />,
|
|
161
|
+
disabled: true,
|
|
162
|
+
onSelect: () => {},
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
const activeShapeOption =
|
|
166
|
+
shapeOptions.find((option) => option.key === activeShape) ??
|
|
167
|
+
shapeOptions[0]!;
|
|
168
|
+
const activeMoveGroupTool = getMoveGroupToolPresentation(activeTool);
|
|
169
|
+
const handleActiveMoveGroupTool =
|
|
170
|
+
activeMoveGroupTool.tool === "hand"
|
|
171
|
+
? onHand
|
|
172
|
+
: activeMoveGroupTool.tool === "scale"
|
|
173
|
+
? onScale
|
|
174
|
+
: onMove;
|
|
175
|
+
const tools: Array<{
|
|
176
|
+
key: string;
|
|
177
|
+
active: boolean;
|
|
178
|
+
label: string;
|
|
179
|
+
icon: ReactNode;
|
|
180
|
+
onClick: () => void;
|
|
181
|
+
options: DesignToolbarOption[];
|
|
182
|
+
}> = [
|
|
183
|
+
{
|
|
184
|
+
key: "move",
|
|
185
|
+
// Parent button is active whenever any of the move-group sub-tools is
|
|
186
|
+
// selected so the toolbar visually reflects hand and scale modes too.
|
|
187
|
+
active:
|
|
188
|
+
(activeTool === "move" && mode === "edit") ||
|
|
189
|
+
activeTool === "hand" ||
|
|
190
|
+
activeTool === "scale",
|
|
191
|
+
// The parent button represents the active move-group sub-tool. Expose
|
|
192
|
+
// that same identity to assistive technology and the tooltip instead of
|
|
193
|
+
// announcing every H/K activation as the Move tool.
|
|
194
|
+
label: t(activeMoveGroupTool.labelKey),
|
|
195
|
+
// Mirror the active sub-tool icon so the parent button is always
|
|
196
|
+
// informative about the currently selected move-group tool.
|
|
197
|
+
icon:
|
|
198
|
+
activeTool === "hand" ? (
|
|
199
|
+
<IconHandStop className="size-[18px]" />
|
|
200
|
+
) : activeTool === "scale" ? (
|
|
201
|
+
<IconScale className="size-[18px]" />
|
|
202
|
+
) : (
|
|
203
|
+
<IconPointer className="size-[18px]" />
|
|
204
|
+
),
|
|
205
|
+
// Keep the primary action aligned with the icon/label it presents. A
|
|
206
|
+
// Hand or Scale button should remain Hand or Scale when clicked rather
|
|
207
|
+
// than silently switching back to Move.
|
|
208
|
+
onClick: handleActiveMoveGroupTool,
|
|
209
|
+
options: [
|
|
210
|
+
{
|
|
211
|
+
key: "move",
|
|
212
|
+
label: t("designEditor.tools.move"),
|
|
213
|
+
icon: <IconPointer className="size-4" />,
|
|
214
|
+
shortcut: MOVE_GROUP_TOOL_PRESENTATIONS.move.shortcut,
|
|
215
|
+
active: activeTool === "move" && mode === "edit",
|
|
216
|
+
onSelect: onMove,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
key: "hand",
|
|
220
|
+
label: t("designEditor.tools.hand"),
|
|
221
|
+
icon: <IconHandStop className="size-4" />,
|
|
222
|
+
shortcut: MOVE_GROUP_TOOL_PRESENTATIONS.hand.shortcut,
|
|
223
|
+
active: activeTool === "hand",
|
|
224
|
+
onSelect: onHand,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
key: "scale",
|
|
228
|
+
label: t("designEditor.tools.scale"),
|
|
229
|
+
icon: <IconScale className="size-4" />,
|
|
230
|
+
shortcut: MOVE_GROUP_TOOL_PRESENTATIONS.scale.shortcut,
|
|
231
|
+
active: activeTool === "scale",
|
|
232
|
+
onSelect: onScale,
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
key: "frame",
|
|
238
|
+
active: activeTool === "frame",
|
|
239
|
+
label:
|
|
240
|
+
frameToolDraws === "screen"
|
|
241
|
+
? t("designEditor.tools.screen")
|
|
242
|
+
: t("designEditor.tools.frame"),
|
|
243
|
+
icon:
|
|
244
|
+
frameToolDraws === "screen" ? (
|
|
245
|
+
<IconDevices className="size-[18px]" />
|
|
246
|
+
) : (
|
|
247
|
+
<IconFrame className="size-[18px]" />
|
|
248
|
+
),
|
|
249
|
+
onClick: onFrame,
|
|
250
|
+
options: [
|
|
251
|
+
{
|
|
252
|
+
key: "frame",
|
|
253
|
+
label: t("designEditor.tools.frame"),
|
|
254
|
+
icon: <IconFrame className="size-4" />,
|
|
255
|
+
shortcut: "F",
|
|
256
|
+
active: activeTool === "frame" && frameToolDraws === "frame",
|
|
257
|
+
onSelect: () => {
|
|
258
|
+
onFrameToolDrawsChange("frame");
|
|
259
|
+
onFrame();
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
key: "screen",
|
|
264
|
+
label: t("designEditor.tools.screen"),
|
|
265
|
+
icon: <IconDevices className="size-4" />,
|
|
266
|
+
active: activeTool === "frame" && frameToolDraws === "screen",
|
|
267
|
+
onSelect: () => {
|
|
268
|
+
onFrameToolDrawsChange("screen");
|
|
269
|
+
onFrame();
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
key: "shape",
|
|
276
|
+
active: shapeTools.has(activeTool),
|
|
277
|
+
label: activeShapeOption.label,
|
|
278
|
+
icon: shapeIcon(activeShape, "size-[18px]"),
|
|
279
|
+
onClick: () => onShape(activeShape),
|
|
280
|
+
options: shapeOptions,
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
key: "pen",
|
|
284
|
+
active: activeTool === "pen",
|
|
285
|
+
label: t("designEditor.tools.pen"),
|
|
286
|
+
icon: <DesignPenToolIcon className="size-[18px]" />,
|
|
287
|
+
onClick: onPen,
|
|
288
|
+
options: [
|
|
289
|
+
{
|
|
290
|
+
key: "pen",
|
|
291
|
+
label: t("designEditor.tools.pen"),
|
|
292
|
+
icon: <DesignPenToolIcon className="size-4" />,
|
|
293
|
+
shortcut: "P",
|
|
294
|
+
active: activeTool === "pen",
|
|
295
|
+
onSelect: onPen,
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
key: "draw",
|
|
299
|
+
label: t("designEditor.modes.draw"),
|
|
300
|
+
icon: <IconBrush className="size-4" />,
|
|
301
|
+
shortcut: "Y",
|
|
302
|
+
active: activeTool === "draw" && mode === "annotate" && drawMode,
|
|
303
|
+
disabled: !hasActiveFile,
|
|
304
|
+
onSelect: onDraw,
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
key: "text",
|
|
310
|
+
active: activeTool === "text",
|
|
311
|
+
label: t("designEditor.tools.text"),
|
|
312
|
+
icon: <IconText className="size-[18px]" />,
|
|
313
|
+
onClick: onText,
|
|
314
|
+
options: [
|
|
315
|
+
{
|
|
316
|
+
key: "text",
|
|
317
|
+
label: t("designEditor.tools.text"),
|
|
318
|
+
icon: <IconText className="size-4" />,
|
|
319
|
+
shortcut: "T",
|
|
320
|
+
active: activeTool === "text",
|
|
321
|
+
onSelect: onText,
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
key: "comment",
|
|
327
|
+
active: activeTool === "comment" && mode === "annotate" && pinMode,
|
|
328
|
+
label: t("designEditor.pinComment"),
|
|
329
|
+
icon: <IconMessage className="size-[18px]" />,
|
|
330
|
+
onClick: onCommentPin,
|
|
331
|
+
options: [
|
|
332
|
+
{
|
|
333
|
+
key: "comment",
|
|
334
|
+
label: t("designEditor.pinComment"),
|
|
335
|
+
icon: <IconMessage className="size-4" />,
|
|
336
|
+
shortcut: "C",
|
|
337
|
+
active: activeTool === "comment" && mode === "annotate" && pinMode,
|
|
338
|
+
disabled: !hasActiveFile || isOverview,
|
|
339
|
+
onSelect: onCommentPin,
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
];
|
|
344
|
+
|
|
345
|
+
const modes: Array<{
|
|
346
|
+
key: EditorMode;
|
|
347
|
+
active: boolean;
|
|
348
|
+
label: string;
|
|
349
|
+
icon: ReactNode;
|
|
350
|
+
onClick: () => void;
|
|
351
|
+
}> = [
|
|
352
|
+
{
|
|
353
|
+
key: "annotate",
|
|
354
|
+
active: mode === "annotate",
|
|
355
|
+
label: t("designEditor.modes.annotate"),
|
|
356
|
+
icon: <IconScribble className="size-[18px]" />,
|
|
357
|
+
onClick: () => onModeChange("annotate"),
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
key: "edit",
|
|
361
|
+
active: mode === "edit",
|
|
362
|
+
label: t("designEditor.modes.edit"),
|
|
363
|
+
icon: <IconTransformPoint className="size-[18px]" />,
|
|
364
|
+
onClick: () => onModeChange("edit"),
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
key: "interact",
|
|
368
|
+
active: mode === "interact",
|
|
369
|
+
label: t("designEditor.modes.interact"),
|
|
370
|
+
icon: <IconHandClick className="size-[18px]" />,
|
|
371
|
+
onClick: () => onModeChange("interact"),
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
return (
|
|
375
|
+
<div
|
|
376
|
+
data-design-bottom-toolbar
|
|
377
|
+
/* guard:allow-raw-color — fixed dark editor chrome, intentionally theme-independent */
|
|
378
|
+
className="fixed left-1/2 z-[70] flex max-w-[calc(100%-1rem)] -translate-x-1/2 items-center gap-1.5 overflow-x-auto rounded-xl border border-white/10 bg-[#2c2c2c]/95 p-1.5 text-neutral-100 shadow-[0_22px_55px_-24px_rgba(0,0,0,0.9),0_0_0_1px_rgba(0,0,0,0.25)] backdrop-blur transition-[bottom] duration-150 motion-reduce:transition-none md:max-w-[calc(100%-2rem)] md:overflow-visible"
|
|
379
|
+
style={{ bottom: shortcutsPanelOpen ? 257 : 16 }}
|
|
380
|
+
>
|
|
381
|
+
<div className="flex min-w-0 items-center gap-0.5">
|
|
382
|
+
{tools.map((tool) => (
|
|
383
|
+
<DesignToolbarTool
|
|
384
|
+
key={tool.key}
|
|
385
|
+
active={tool.active}
|
|
386
|
+
label={tool.label}
|
|
387
|
+
icon={tool.icon}
|
|
388
|
+
options={tool.options}
|
|
389
|
+
onPrimary={tool.onClick}
|
|
390
|
+
/>
|
|
391
|
+
))}
|
|
392
|
+
</div>
|
|
393
|
+
|
|
394
|
+
{/* guard:allow-raw-color — fixed dark editor chrome, intentionally theme-independent */}
|
|
395
|
+
<div className="h-9 w-px shrink-0 bg-white/15" />
|
|
396
|
+
|
|
397
|
+
{/* guard:allow-raw-color — fixed dark editor chrome, intentionally theme-independent */}
|
|
398
|
+
<div className="flex shrink-0 items-center gap-0.5 rounded-md bg-white/10 p-0.5">
|
|
399
|
+
{modes.map((item) => (
|
|
400
|
+
<DesignModeTab
|
|
401
|
+
key={item.key}
|
|
402
|
+
active={item.active}
|
|
403
|
+
label={item.label}
|
|
404
|
+
icon={item.icon}
|
|
405
|
+
onClick={item.onClick}
|
|
406
|
+
/>
|
|
407
|
+
))}
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
);
|
|
411
|
+
}
|