@agent-native/core 0.80.0 → 0.80.2
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 +2 -2
- package/corpus/core/CHANGELOG.md +28 -0
- package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +14 -1
- package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +15 -1
- package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +15 -1
- package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +15 -1
- package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +15 -1
- package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +16 -1
- package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +14 -1
- package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +14 -1
- package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +14 -1
- package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +14 -1
- package/corpus/core/docs/content/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/template-design.mdx +14 -1
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/application-state/handlers.ts +1 -1
- package/corpus/core/src/brand-kit/index.ts +1 -1
- package/corpus/core/src/cli/skills.ts +18 -0
- package/corpus/core/src/client/AgentPanel.tsx +1 -1
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
- package/corpus/core/src/client/extensions/portable-extension.ts +1 -1
- package/corpus/core/src/code-agents/transcript-normalizer.ts +1 -1
- package/corpus/core/src/extensions/path.ts +1 -4
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +1 -1
- package/corpus/core/src/mcp/build-server.ts +2 -2
- package/corpus/core/src/mcp/embed-app.ts +35 -1
- package/corpus/core/src/org/handlers.ts +5 -5
- package/corpus/core/src/org/plugin.ts +2 -2
- package/corpus/core/src/scripts/call-agent.ts +1 -1
- package/corpus/core/src/scripts/db/safety.ts +1 -1
- package/corpus/core/src/server/auth.ts +3 -0
- package/corpus/core/src/server/builder-design-systems.ts +308 -0
- package/corpus/core/src/server/core-routes-plugin.ts +29 -3
- package/corpus/core/src/server/index.ts +8 -0
- package/corpus/core/src/styles/agent-native.css +14 -7
- package/corpus/core/src/usage/store.ts +2 -2
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +298 -15
- package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -4
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +18 -2
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +12 -2
- package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +66 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +37 -0
- package/corpus/templates/analytics/app/i18n-data.ts +404 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +333 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +2 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +121 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +49 -26
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +137 -36
- package/corpus/templates/analytics/changelog/2026-06-29-analytics-agents-can-now-place-new-charts-into-requested-das.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-analytics-queries-now-report-failed-source-query-errors-inst.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-charts-can-now-be-dropped-between-full-dashboard-rows-to-cre.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-dashboard-email-reports-include-the-full-dashboard-image-wit.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-main-surface-border-reaches-the-top.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-session-replay-now-records-playable-sessions-in-production-a.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-set-up-session-replay-storage-from-settings-connect-builder-.md +6 -0
- package/corpus/templates/analytics/scripts/cleanup-empty-session-recordings.ts +102 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +49 -26
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +81 -20
- package/corpus/templates/analytics/server/lib/replay-storage.ts +79 -0
- package/corpus/templates/analytics/server/lib/s3-upload-provider.ts +255 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +29 -8
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
- package/corpus/templates/analytics/server/plugins/onboarding.ts +97 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +24 -0
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +105 -5
- package/corpus/templates/content/actions/set-image-alt-text.ts +1 -1
- package/corpus/templates/content/shared/document-export.ts +1 -1
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -18
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +17 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +32 -5
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +259 -0
- package/corpus/templates/design/actions/update-file.ts +9 -2
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +33 -75
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +72 -9
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +41 -39
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1029 -249
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +450 -101
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +978 -154
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -9
- package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +133 -66
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +1234 -344
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +144 -94
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +301 -72
- package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +876 -286
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +601 -0
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +242 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +30 -7
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +212 -155
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +423 -0
- package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +487 -107
- package/corpus/templates/design/app/components/design/inspector/index.ts +27 -0
- package/corpus/templates/design/app/global.css +29 -3
- package/corpus/templates/design/app/i18n/zh-TW.ts +13 -0
- package/corpus/templates/design/app/i18n-data.ts +239 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +1023 -353
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +57 -221
- package/corpus/templates/design/changelog/2026-06-29-canvas-selection-no-longer-flashes-broken-prototype-html-whe.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-command-menu-selections-are-clearer-in-dark-mode.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-element-selection-now-stays-on-the-intended-layer-after-sele.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-full-view-controls-now-sit-above-frames-inst.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-tools-can-draw-shapes-create-screens-and-kee.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-now-creates-anchor-based-bezier-vectors-with-ha.md +6 -0
- package/corpus/templates/design/server/handlers/import-figma-system.ts +38 -85
- package/corpus/templates/design/server/lib/design-snapshot.ts +9 -1
- package/corpus/templates/design/shared/code-layer.ts +209 -9
- package/corpus/templates/design/shared/html-content.ts +21 -0
- package/corpus/templates/design/shared/pen-path.ts +207 -0
- package/corpus/templates/mail/app/lib/utils.ts +2 -2
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -10
- package/corpus/templates/slides/AGENTS.md +3 -2
- package/corpus/templates/slides/actions/import-file.ts +26 -17
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +259 -0
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +35 -167
- package/corpus/templates/slides/app/components/design-system/fig-import-response.ts +9 -13
- package/corpus/templates/slides/app/context/DeckContext.tsx +1 -1
- package/corpus/templates/slides/app/i18n/ar-SA.ts +9 -37
- package/corpus/templates/slides/app/i18n/de-DE.ts +10 -25
- package/corpus/templates/slides/app/i18n/en-US.ts +9 -26
- package/corpus/templates/slides/app/i18n/es-ES.ts +10 -28
- package/corpus/templates/slides/app/i18n/fr-FR.ts +10 -28
- package/corpus/templates/slides/app/i18n/hi-IN.ts +9 -26
- package/corpus/templates/slides/app/i18n/ja-JP.ts +10 -22
- package/corpus/templates/slides/app/i18n/ko-KR.ts +9 -23
- package/corpus/templates/slides/app/i18n/pt-BR.ts +10 -28
- package/corpus/templates/slides/app/i18n/zh-CN.ts +9 -22
- package/corpus/templates/slides/app/i18n/zh-TW.ts +9 -22
- package/corpus/templates/slides/server/handlers/import-figma-system.ts +44 -12
- package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
- package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +1 -1
- package/dist/application-state/handlers.js +1 -1
- package/dist/application-state/handlers.js.map +1 -1
- package/dist/brand-kit/index.d.ts +1 -1
- package/dist/brand-kit/index.js +1 -1
- package/dist/brand-kit/index.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +18 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +1 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/extensions/portable-extension.d.ts +1 -1
- package/dist/client/extensions/portable-extension.d.ts.map +1 -1
- package/dist/client/extensions/portable-extension.js.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +1 -1
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/extensions/path.d.ts +1 -1
- package/dist/extensions/path.d.ts.map +1 -1
- package/dist/extensions/path.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/integrations/a2a-continuation-processor.js +1 -1
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/mcp/build-server.d.ts +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +1 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +35 -1
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +8 -8
- package/dist/org/handlers.js +5 -5
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/plugin.js +2 -2
- package/dist/org/plugin.js.map +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/call-agent.js +1 -1
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/db/safety.js +1 -1
- package/dist/scripts/db/safety.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +3 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-design-systems.d.ts +28 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -0
- package/dist/server/builder-design-systems.js +217 -0
- package/dist/server/builder-design-systems.js.map +1 -0
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +20 -3
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/styles/agent-native.css +14 -7
- package/dist/usage/store.js +2 -2
- package/dist/usage/store.js.map +1 -1
- package/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
- package/docs/content/locales/ar-SA/template-design.mdx +14 -1
- package/docs/content/locales/de-DE/skills-guide.mdx +5 -0
- package/docs/content/locales/de-DE/template-design.mdx +15 -1
- package/docs/content/locales/es-ES/skills-guide.mdx +5 -0
- package/docs/content/locales/es-ES/template-design.mdx +15 -1
- package/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
- package/docs/content/locales/fr-FR/template-design.mdx +15 -1
- package/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
- package/docs/content/locales/hi-IN/template-design.mdx +15 -1
- package/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
- package/docs/content/locales/ja-JP/template-design.mdx +16 -1
- package/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
- package/docs/content/locales/ko-KR/template-design.mdx +14 -1
- package/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
- package/docs/content/locales/pt-BR/template-design.mdx +14 -1
- package/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
- package/docs/content/locales/zh-CN/template-design.mdx +14 -1
- package/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
- package/docs/content/locales/zh-TW/template-design.mdx +14 -1
- package/docs/content/skills-guide.mdx +5 -0
- package/docs/content/template-design.mdx +14 -1
- package/package.json +1 -1
- package/corpus/templates/design/actions/import-figma-file.ts +0 -166
- package/corpus/templates/slides/server/lib/fig-design-system.ts +0 -110
|
@@ -2,27 +2,13 @@ import { useT } from "@agent-native/core/client";
|
|
|
2
2
|
import {
|
|
3
3
|
IconChevronDown,
|
|
4
4
|
IconChevronRight,
|
|
5
|
-
IconCode,
|
|
6
|
-
IconComponents,
|
|
7
5
|
IconEye,
|
|
8
6
|
IconEyeOff,
|
|
9
|
-
IconFile,
|
|
10
|
-
IconFileCode,
|
|
11
|
-
IconFolder,
|
|
12
|
-
IconFrame,
|
|
13
|
-
IconHierarchy,
|
|
14
|
-
IconLayersIntersect,
|
|
15
7
|
IconLock,
|
|
16
8
|
IconLockOpen,
|
|
17
9
|
IconLayoutGrid,
|
|
18
10
|
IconPlus,
|
|
19
|
-
IconPhoto,
|
|
20
|
-
IconRectangle,
|
|
21
11
|
IconSearch,
|
|
22
|
-
IconSquare,
|
|
23
|
-
IconStack2,
|
|
24
|
-
IconTypography,
|
|
25
|
-
type Icon,
|
|
26
12
|
} from "@tabler/icons-react";
|
|
27
13
|
import {
|
|
28
14
|
useCallback,
|
|
@@ -65,6 +51,14 @@ export interface LayersPanelNode {
|
|
|
65
51
|
id: string;
|
|
66
52
|
name: string;
|
|
67
53
|
type?: LayersPanelNodeType;
|
|
54
|
+
layout?: {
|
|
55
|
+
display?: string;
|
|
56
|
+
flexDirection?: string;
|
|
57
|
+
alignItems?: string;
|
|
58
|
+
justifyContent?: string;
|
|
59
|
+
isFlexContainer?: boolean;
|
|
60
|
+
isGridContainer?: boolean;
|
|
61
|
+
};
|
|
68
62
|
children?: LayersPanelNode[];
|
|
69
63
|
detail?: string;
|
|
70
64
|
badge?: string | number;
|
|
@@ -164,6 +158,7 @@ interface FlatLayerRow {
|
|
|
164
158
|
node: LayersPanelNode;
|
|
165
159
|
rowKey: string;
|
|
166
160
|
depth: number;
|
|
161
|
+
ancestorIds: string[];
|
|
167
162
|
hasChildren: boolean;
|
|
168
163
|
}
|
|
169
164
|
|
|
@@ -301,13 +296,14 @@ function flattenRows(
|
|
|
301
296
|
forceExpanded: boolean,
|
|
302
297
|
depth = 0,
|
|
303
298
|
parentKey = "root",
|
|
299
|
+
ancestorIds: string[] = [],
|
|
304
300
|
rows: FlatLayerRow[] = [],
|
|
305
301
|
) {
|
|
306
302
|
nodes.forEach((node, index) => {
|
|
307
303
|
const children = node.children ?? [];
|
|
308
304
|
const hasChildren = children.length > 0;
|
|
309
305
|
const rowKey = `${parentKey}/${node.id}:${index}`;
|
|
310
|
-
rows.push({ node, rowKey, depth, hasChildren });
|
|
306
|
+
rows.push({ node, rowKey, depth, ancestorIds, hasChildren });
|
|
311
307
|
if (hasChildren && (forceExpanded || expandedIds.has(node.id))) {
|
|
312
308
|
flattenRows(
|
|
313
309
|
children,
|
|
@@ -315,6 +311,7 @@ function flattenRows(
|
|
|
315
311
|
forceExpanded,
|
|
316
312
|
depth + 1,
|
|
317
313
|
rowKey,
|
|
314
|
+
[...ancestorIds, node.id],
|
|
318
315
|
rows,
|
|
319
316
|
);
|
|
320
317
|
}
|
|
@@ -361,36 +358,12 @@ function collectAncestorIds(
|
|
|
361
358
|
return Array.from(ancestors);
|
|
362
359
|
}
|
|
363
360
|
|
|
364
|
-
function
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
case "frame":
|
|
371
|
-
return IconFrame;
|
|
372
|
-
case "group":
|
|
373
|
-
return IconHierarchy;
|
|
374
|
-
case "component":
|
|
375
|
-
case "instance":
|
|
376
|
-
return IconComponents;
|
|
377
|
-
case "section":
|
|
378
|
-
return IconFolder;
|
|
379
|
-
case "shape":
|
|
380
|
-
return IconSquare;
|
|
381
|
-
case "rectangle":
|
|
382
|
-
return IconRectangle;
|
|
383
|
-
case "text":
|
|
384
|
-
return IconTypography;
|
|
385
|
-
case "image":
|
|
386
|
-
return IconPhoto;
|
|
387
|
-
case "code":
|
|
388
|
-
return IconFileCode;
|
|
389
|
-
case "element":
|
|
390
|
-
return IconCode;
|
|
391
|
-
default:
|
|
392
|
-
return IconLayersIntersect;
|
|
393
|
-
}
|
|
361
|
+
function layerCanShowBadge(node: LayersPanelNode) {
|
|
362
|
+
return (
|
|
363
|
+
node.type === "file" ||
|
|
364
|
+
node.type === "screen" ||
|
|
365
|
+
(node.type === "frame" && node.id.startsWith("__"))
|
|
366
|
+
);
|
|
394
367
|
}
|
|
395
368
|
|
|
396
369
|
export function LayersPanel({
|
|
@@ -429,6 +402,7 @@ export function LayersPanel({
|
|
|
429
402
|
const [renameDraft, setRenameDraft] = useState("");
|
|
430
403
|
const [dropIndicator, setDropIndicator] =
|
|
431
404
|
useState<LayersPanelMoveIntent | null>(null);
|
|
405
|
+
const [searchOpen, setSearchOpen] = useState(Boolean(searchQuery));
|
|
432
406
|
|
|
433
407
|
const roots = useMemo(
|
|
434
408
|
() =>
|
|
@@ -568,19 +542,43 @@ export function LayersPanel({
|
|
|
568
542
|
const hasAnyRows = roots.length > 0;
|
|
569
543
|
const selectedCount = selectedIds.length;
|
|
570
544
|
const screenRows = screens ?? files ?? [];
|
|
545
|
+
const openSearch = useCallback(() => {
|
|
546
|
+
setSearchOpen(true);
|
|
547
|
+
window.requestAnimationFrame(() => searchInputRef.current?.focus());
|
|
548
|
+
}, []);
|
|
549
|
+
const shouldShowSearch = searchOpen || Boolean(searchQuery.trim());
|
|
550
|
+
const collapseTargetId = useMemo(() => {
|
|
551
|
+
for (let index = selectedIds.length - 1; index >= 0; index -= 1) {
|
|
552
|
+
const selectedRow = visibleRows.find(
|
|
553
|
+
(row) => row.node.id === selectedIds[index],
|
|
554
|
+
);
|
|
555
|
+
if (!selectedRow) continue;
|
|
556
|
+
if (selectedRow.hasChildren && expandedIdSet.has(selectedRow.node.id)) {
|
|
557
|
+
return selectedRow.node.id;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return null;
|
|
561
|
+
}, [expandedIdSet, selectedIds, visibleRows]);
|
|
562
|
+
|
|
563
|
+
const collapseSelectedLayer = useCallback(() => {
|
|
564
|
+
if (!collapseTargetId) return;
|
|
565
|
+
onExpandedIdsChange(
|
|
566
|
+
expandedIds.filter((expandedId) => expandedId !== collapseTargetId),
|
|
567
|
+
);
|
|
568
|
+
}, [collapseTargetId, expandedIds, onExpandedIdsChange]);
|
|
571
569
|
|
|
572
570
|
return (
|
|
573
571
|
<aside
|
|
574
572
|
className={cn(
|
|
575
|
-
"flex h-full min-h-0 w-full flex-col overflow-hidden bg-[var(--design-editor-panel-bg)] text-[
|
|
573
|
+
"flex h-full min-h-0 w-full flex-col overflow-hidden bg-[var(--design-editor-panel-bg)] text-[12px] text-foreground",
|
|
576
574
|
className,
|
|
577
575
|
)}
|
|
578
576
|
aria-label={labels.title}
|
|
579
577
|
>
|
|
580
578
|
{screenRows.length > 0 ? (
|
|
581
|
-
<div className="shrink-0 border-b border-
|
|
582
|
-
<div className="flex h-
|
|
583
|
-
<h2 className="truncate text-[
|
|
579
|
+
<div className="shrink-0 border-b border-[var(--design-editor-panel-divider-color)] pb-2">
|
|
580
|
+
<div className="flex h-10 items-center justify-between px-3">
|
|
581
|
+
<h2 className="truncate text-[12px] font-semibold text-foreground">
|
|
584
582
|
{labels.screens}
|
|
585
583
|
</h2>
|
|
586
584
|
<div className="flex items-center gap-0.5 text-muted-foreground">
|
|
@@ -592,20 +590,20 @@ export function LayersPanel({
|
|
|
592
590
|
</IconTooltipButton>
|
|
593
591
|
<IconTooltipButton
|
|
594
592
|
label={labels.searchPlaceholder}
|
|
595
|
-
onClick={
|
|
593
|
+
onClick={openSearch}
|
|
596
594
|
>
|
|
597
|
-
<IconSearch className="size-
|
|
595
|
+
<IconSearch className="size-4" />
|
|
598
596
|
</IconTooltipButton>
|
|
599
597
|
<IconTooltipButton
|
|
600
598
|
label={labels.addScreen}
|
|
601
599
|
disabled={!onAddScreen}
|
|
602
600
|
onClick={onAddScreen}
|
|
603
601
|
>
|
|
604
|
-
<IconPlus className="size-
|
|
602
|
+
<IconPlus className="size-4" />
|
|
605
603
|
</IconTooltipButton>
|
|
606
604
|
</div>
|
|
607
605
|
</div>
|
|
608
|
-
<div className="space-y-0.5 px-
|
|
606
|
+
<div className="space-y-0.5 px-2">
|
|
609
607
|
{screenRows.map((screen) => {
|
|
610
608
|
const isActive = screen.id === activeScreenId;
|
|
611
609
|
return (
|
|
@@ -613,7 +611,7 @@ export function LayersPanel({
|
|
|
613
611
|
key={screen.id}
|
|
614
612
|
type="button"
|
|
615
613
|
className={cn(
|
|
616
|
-
"flex h-
|
|
614
|
+
"flex h-8 w-full cursor-default items-center gap-2 rounded-[5px] px-2 text-left text-[12px] font-semibold outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
|
|
617
615
|
isActive
|
|
618
616
|
? "bg-[var(--design-editor-active-row-color)] text-foreground"
|
|
619
617
|
: "text-foreground/85 hover:bg-[var(--design-editor-active-row-color)] hover:text-foreground",
|
|
@@ -621,7 +619,10 @@ export function LayersPanel({
|
|
|
621
619
|
onClick={() => onScreenSelect?.(screen.id)}
|
|
622
620
|
title={screen.filename ?? screen.name}
|
|
623
621
|
>
|
|
624
|
-
<
|
|
622
|
+
<LayerGlyph
|
|
623
|
+
node={{ ...screen, type: "file" }}
|
|
624
|
+
selected={false}
|
|
625
|
+
/>
|
|
625
626
|
<span className="min-w-0 flex-1 truncate">{screen.name}</span>
|
|
626
627
|
{screen.badge ? (
|
|
627
628
|
<span className="rounded-sm bg-muted px-1 text-[10px] font-normal text-muted-foreground">
|
|
@@ -635,9 +636,9 @@ export function LayersPanel({
|
|
|
635
636
|
</div>
|
|
636
637
|
) : null}
|
|
637
638
|
|
|
638
|
-
<div className="flex h-
|
|
639
|
+
<div className="flex h-10 shrink-0 items-center justify-between px-3">
|
|
639
640
|
<div className="min-w-0">
|
|
640
|
-
<h2 className="truncate text-[
|
|
641
|
+
<h2 className="truncate text-[12px] font-semibold text-foreground">
|
|
641
642
|
{labels.title}
|
|
642
643
|
</h2>
|
|
643
644
|
{selectedCount > 1 ? (
|
|
@@ -646,25 +647,42 @@ export function LayersPanel({
|
|
|
646
647
|
</p>
|
|
647
648
|
) : null}
|
|
648
649
|
</div>
|
|
649
|
-
<
|
|
650
|
+
<div className="flex items-center gap-0.5 text-muted-foreground">
|
|
651
|
+
<button
|
|
652
|
+
type="button"
|
|
653
|
+
className="flex size-6 items-center justify-center rounded-sm text-muted-foreground hover:bg-[var(--design-editor-layer-hover-color)] hover:text-foreground disabled:pointer-events-none disabled:opacity-35"
|
|
654
|
+
aria-label={labels.collapse}
|
|
655
|
+
disabled={!collapseTargetId}
|
|
656
|
+
onClick={collapseSelectedLayer}
|
|
657
|
+
>
|
|
658
|
+
<LayerOptionsGlyph className="size-4" />
|
|
659
|
+
</button>
|
|
660
|
+
</div>
|
|
650
661
|
</div>
|
|
651
662
|
|
|
652
|
-
|
|
653
|
-
<div className="
|
|
654
|
-
<
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
663
|
+
{shouldShowSearch ? (
|
|
664
|
+
<div className="shrink-0 p-2">
|
|
665
|
+
<div className="relative">
|
|
666
|
+
<IconSearch className="pointer-events-none absolute left-2 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" />
|
|
667
|
+
<Input
|
|
668
|
+
ref={searchInputRef}
|
|
669
|
+
value={searchQuery}
|
|
670
|
+
onChange={(event) => onSearchQueryChange(event.target.value)}
|
|
671
|
+
onKeyDown={(event) => {
|
|
672
|
+
if (event.key === "Escape" && !searchQuery.trim()) {
|
|
673
|
+
setSearchOpen(false);
|
|
674
|
+
}
|
|
675
|
+
}}
|
|
676
|
+
placeholder={labels.searchPlaceholder}
|
|
677
|
+
className="h-7 rounded-[4px] border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] pl-7 text-[12px] shadow-none placeholder:text-muted-foreground/70 focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]"
|
|
678
|
+
/>
|
|
679
|
+
</div>
|
|
662
680
|
</div>
|
|
663
|
-
|
|
681
|
+
) : null}
|
|
664
682
|
|
|
665
|
-
<div className="min-h-0 flex-1 overflow-y-auto py-
|
|
683
|
+
<div className="min-h-0 flex-1 overflow-y-auto py-2">
|
|
666
684
|
{visibleRows.length ? (
|
|
667
|
-
<div className="px-
|
|
685
|
+
<div className="px-2" role="tree" aria-label={labels.title}>
|
|
668
686
|
{visibleRows.map((row) => (
|
|
669
687
|
<LayerRow
|
|
670
688
|
key={row.rowKey}
|
|
@@ -672,6 +690,9 @@ export function LayersPanel({
|
|
|
672
690
|
labels={labels}
|
|
673
691
|
isExpanded={expandedIdSet.has(row.node.id)}
|
|
674
692
|
isSelected={selectedIdSet.has(row.node.id)}
|
|
693
|
+
isInSelectedSubtree={row.ancestorIds.some((id) =>
|
|
694
|
+
selectedIdSet.has(id),
|
|
695
|
+
)}
|
|
675
696
|
isActiveScreen={
|
|
676
697
|
row.node.id === activeScreenId &&
|
|
677
698
|
(row.node.type === "file" ||
|
|
@@ -717,6 +738,7 @@ function LayerRow({
|
|
|
717
738
|
labels,
|
|
718
739
|
isExpanded,
|
|
719
740
|
isSelected,
|
|
741
|
+
isInSelectedSubtree,
|
|
720
742
|
isActiveScreen,
|
|
721
743
|
isRenaming,
|
|
722
744
|
rowRef,
|
|
@@ -740,6 +762,7 @@ function LayerRow({
|
|
|
740
762
|
labels: LayersPanelLabels;
|
|
741
763
|
isExpanded: boolean;
|
|
742
764
|
isSelected: boolean;
|
|
765
|
+
isInSelectedSubtree: boolean;
|
|
743
766
|
isActiveScreen: boolean;
|
|
744
767
|
isRenaming: boolean;
|
|
745
768
|
rowRef: (element: HTMLDivElement | null) => void;
|
|
@@ -767,7 +790,6 @@ function LayerRow({
|
|
|
767
790
|
selectedIds: readonly string[];
|
|
768
791
|
}) {
|
|
769
792
|
const { node, depth, hasChildren } = row;
|
|
770
|
-
const Icon = layerTypeIcon(node.type);
|
|
771
793
|
const selectable = node.selectable !== false;
|
|
772
794
|
const lockable = node.lockable !== false && Boolean(onToggleLocked);
|
|
773
795
|
const hideable = node.hideable !== false && Boolean(onToggleHidden);
|
|
@@ -908,29 +930,26 @@ function LayerRow({
|
|
|
908
930
|
{activeDrop === "after" ? (
|
|
909
931
|
<span className="pointer-events-none absolute bottom-0 left-2 right-2 z-10 h-px bg-[var(--design-editor-accent-color)]" />
|
|
910
932
|
) : null}
|
|
911
|
-
{Array.from({ length: depth }).map((_, index) => (
|
|
912
|
-
<span
|
|
913
|
-
key={index}
|
|
914
|
-
className="pointer-events-none absolute bottom-0 top-0 w-px bg-border/70"
|
|
915
|
-
style={{ left: 15 + index * 12 }}
|
|
916
|
-
/>
|
|
917
|
-
))}
|
|
918
933
|
<div
|
|
919
934
|
className={cn(
|
|
920
|
-
"group flex h-
|
|
935
|
+
"group flex h-8 items-center gap-1 rounded-[5px] pr-1 text-[12px]",
|
|
921
936
|
activeDrop === "inside" &&
|
|
922
937
|
"ring-1 ring-inset ring-[var(--design-editor-accent-color)]",
|
|
923
938
|
isSelected &&
|
|
924
|
-
!isActiveScreen &&
|
|
925
939
|
"bg-[var(--design-editor-selection-color)] text-foreground",
|
|
926
|
-
|
|
940
|
+
!isSelected &&
|
|
941
|
+
isInSelectedSubtree &&
|
|
942
|
+
"bg-[var(--design-editor-selected-subtree-color)] text-foreground/95",
|
|
943
|
+
!isSelected &&
|
|
944
|
+
isActiveScreen &&
|
|
927
945
|
"bg-[var(--design-editor-active-row-color)] text-foreground hover:bg-[var(--design-editor-active-row-color)]",
|
|
928
946
|
!isSelected &&
|
|
947
|
+
!isInSelectedSubtree &&
|
|
929
948
|
!isActiveScreen &&
|
|
930
|
-
"text-foreground/90 hover:bg-
|
|
949
|
+
"text-foreground/90 hover:bg-[var(--design-editor-layer-hover-color)] hover:text-foreground",
|
|
931
950
|
node.hidden && "text-muted-foreground",
|
|
932
951
|
)}
|
|
933
|
-
style={{ paddingLeft:
|
|
952
|
+
style={{ paddingLeft: 4 + depth * 28 }}
|
|
934
953
|
>
|
|
935
954
|
{hasChildren ? (
|
|
936
955
|
<Button
|
|
@@ -945,9 +964,9 @@ function LayerRow({
|
|
|
945
964
|
}}
|
|
946
965
|
>
|
|
947
966
|
{isExpanded ? (
|
|
948
|
-
<IconChevronDown className="size-
|
|
967
|
+
<IconChevronDown className="size-4" />
|
|
949
968
|
) : (
|
|
950
|
-
<IconChevronRight className="size-
|
|
969
|
+
<IconChevronRight className="size-4 rtl:-scale-x-100" />
|
|
951
970
|
)}
|
|
952
971
|
</Button>
|
|
953
972
|
) : (
|
|
@@ -958,21 +977,26 @@ function LayerRow({
|
|
|
958
977
|
type="button"
|
|
959
978
|
disabled={!selectable}
|
|
960
979
|
className={cn(
|
|
961
|
-
"flex min-w-0 flex-1 items-center gap-
|
|
980
|
+
"flex min-w-0 flex-1 items-center gap-2 overflow-hidden rounded-sm px-0.5 py-0 text-left outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
|
|
962
981
|
selectable ? "cursor-default" : "cursor-default opacity-80",
|
|
963
982
|
)}
|
|
964
983
|
onClick={handlePointerSelect}
|
|
965
984
|
onDoubleClick={() => onStartRename(node)}
|
|
966
985
|
onKeyDown={handleKeyDown}
|
|
967
|
-
title={node.name}
|
|
968
986
|
>
|
|
969
987
|
<span
|
|
970
988
|
className={cn(
|
|
971
989
|
"shrink-0 text-muted-foreground",
|
|
972
|
-
isSelected && "text-
|
|
990
|
+
(isSelected || isInSelectedSubtree) && "text-foreground",
|
|
973
991
|
)}
|
|
974
992
|
>
|
|
975
|
-
{node.icon ??
|
|
993
|
+
{node.icon ?? (
|
|
994
|
+
<LayerGlyph
|
|
995
|
+
node={node}
|
|
996
|
+
selected={isSelected}
|
|
997
|
+
inSelectedSubtree={isInSelectedSubtree}
|
|
998
|
+
/>
|
|
999
|
+
)}
|
|
976
1000
|
</span>
|
|
977
1001
|
{isRenaming ? (
|
|
978
1002
|
<input
|
|
@@ -990,25 +1014,23 @@ function LayerRow({
|
|
|
990
1014
|
onCancelRename();
|
|
991
1015
|
}
|
|
992
1016
|
}}
|
|
993
|
-
className="h-
|
|
1017
|
+
className="h-6 min-w-0 flex-1 rounded-[4px] border border-[var(--design-editor-accent-color)] bg-[var(--design-editor-panel-bg)] px-1.5 text-[12px] text-foreground outline-none"
|
|
994
1018
|
aria-label={labels.rename}
|
|
995
1019
|
/>
|
|
996
1020
|
) : (
|
|
997
1021
|
<span
|
|
998
1022
|
className={cn(
|
|
999
|
-
"min-w-0 flex-1 truncate",
|
|
1023
|
+
"min-w-0 flex-1 truncate font-medium leading-none",
|
|
1000
1024
|
node.hidden && "line-through",
|
|
1001
1025
|
)}
|
|
1002
1026
|
>
|
|
1003
1027
|
{node.name}
|
|
1004
1028
|
</span>
|
|
1005
1029
|
)}
|
|
1006
|
-
{!isRenaming &&
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
) : null}
|
|
1011
|
-
{!isRenaming && node.badge !== null && node.badge !== undefined ? (
|
|
1030
|
+
{!isRenaming &&
|
|
1031
|
+
layerCanShowBadge(node) &&
|
|
1032
|
+
node.badge !== null &&
|
|
1033
|
+
node.badge !== undefined ? (
|
|
1012
1034
|
<span className="shrink-0 rounded-sm bg-muted px-1 text-[10px] text-muted-foreground">
|
|
1013
1035
|
{node.badge}
|
|
1014
1036
|
</span>
|
|
@@ -1023,8 +1045,9 @@ function LayerRow({
|
|
|
1023
1045
|
variant="ghost"
|
|
1024
1046
|
size="icon"
|
|
1025
1047
|
className={cn(
|
|
1026
|
-
"size-
|
|
1048
|
+
"size-5 shrink-0 rounded-sm p-0 text-muted-foreground opacity-0 hover:bg-transparent hover:text-foreground group-hover:opacity-100 focus-visible:opacity-100",
|
|
1027
1049
|
node.locked && "opacity-100",
|
|
1050
|
+
isSelected && "text-foreground",
|
|
1028
1051
|
)}
|
|
1029
1052
|
aria-label={node.locked ? labels.unlock : labels.lock}
|
|
1030
1053
|
onClick={(event) => {
|
|
@@ -1053,8 +1076,9 @@ function LayerRow({
|
|
|
1053
1076
|
variant="ghost"
|
|
1054
1077
|
size="icon"
|
|
1055
1078
|
className={cn(
|
|
1056
|
-
"size-
|
|
1079
|
+
"size-5 shrink-0 rounded-sm p-0 text-muted-foreground opacity-0 hover:bg-transparent hover:text-foreground group-hover:opacity-100 focus-visible:opacity-100",
|
|
1057
1080
|
node.hidden && "opacity-100",
|
|
1081
|
+
isSelected && "text-foreground",
|
|
1058
1082
|
)}
|
|
1059
1083
|
aria-label={node.hidden ? labels.show : labels.hide}
|
|
1060
1084
|
onClick={(event) => {
|
|
@@ -1098,7 +1122,7 @@ function IconTooltipButton({
|
|
|
1098
1122
|
type="button"
|
|
1099
1123
|
variant="ghost"
|
|
1100
1124
|
size="icon"
|
|
1101
|
-
className="size-6 rounded-sm p-0 hover:bg-
|
|
1125
|
+
className="size-6 rounded-sm p-0 text-muted-foreground hover:bg-[var(--design-editor-layer-hover-color)] hover:text-foreground"
|
|
1102
1126
|
aria-label={label}
|
|
1103
1127
|
disabled={disabled}
|
|
1104
1128
|
onClick={onClick}
|
|
@@ -1112,6 +1136,331 @@ function IconTooltipButton({
|
|
|
1112
1136
|
);
|
|
1113
1137
|
}
|
|
1114
1138
|
|
|
1139
|
+
function LayerGlyph({
|
|
1140
|
+
node,
|
|
1141
|
+
selected,
|
|
1142
|
+
inSelectedSubtree,
|
|
1143
|
+
}: {
|
|
1144
|
+
node: Pick<LayersPanelNode, "type" | "layout">;
|
|
1145
|
+
selected?: boolean;
|
|
1146
|
+
inSelectedSubtree?: boolean;
|
|
1147
|
+
}) {
|
|
1148
|
+
const common = "size-4";
|
|
1149
|
+
const componentColor =
|
|
1150
|
+
selected || inSelectedSubtree
|
|
1151
|
+
? "text-foreground"
|
|
1152
|
+
: "text-[var(--design-editor-accent-color)]";
|
|
1153
|
+
switch (node.type) {
|
|
1154
|
+
case "file":
|
|
1155
|
+
case "screen":
|
|
1156
|
+
return <PageLayerGlyph className={common} />;
|
|
1157
|
+
case "frame":
|
|
1158
|
+
return <LayoutLayerGlyph node={node} className={common} />;
|
|
1159
|
+
case "group":
|
|
1160
|
+
case "section":
|
|
1161
|
+
return <LayoutLayerGlyph node={node} className={common} />;
|
|
1162
|
+
case "component":
|
|
1163
|
+
case "instance":
|
|
1164
|
+
return <ComponentLayerGlyph className={cn(common, componentColor)} />;
|
|
1165
|
+
case "shape":
|
|
1166
|
+
case "rectangle":
|
|
1167
|
+
return <RectangleLayerGlyph className={common} />;
|
|
1168
|
+
case "text":
|
|
1169
|
+
return <TextLayerGlyph className={common} />;
|
|
1170
|
+
case "image":
|
|
1171
|
+
return <ImageLayerGlyph className={common} />;
|
|
1172
|
+
case "code":
|
|
1173
|
+
case "element":
|
|
1174
|
+
return node.layout?.isFlexContainer || node.layout?.isGridContainer ? (
|
|
1175
|
+
<LayoutLayerGlyph node={node} className={common} />
|
|
1176
|
+
) : (
|
|
1177
|
+
<ElementLayerGlyph className={common} />
|
|
1178
|
+
);
|
|
1179
|
+
default:
|
|
1180
|
+
return <FrameLayerGlyph className={common} />;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
function LayerOptionsGlyph({ className }: { className?: string }) {
|
|
1185
|
+
return (
|
|
1186
|
+
<svg
|
|
1187
|
+
viewBox="0 0 16 16"
|
|
1188
|
+
fill="none"
|
|
1189
|
+
stroke="currentColor"
|
|
1190
|
+
strokeWidth="1.4"
|
|
1191
|
+
strokeLinecap="round"
|
|
1192
|
+
strokeLinejoin="round"
|
|
1193
|
+
className={className}
|
|
1194
|
+
aria-hidden="true"
|
|
1195
|
+
>
|
|
1196
|
+
<path d="M3 4h6" />
|
|
1197
|
+
<path d="M3 8h8" />
|
|
1198
|
+
<path d="M3 12h5" />
|
|
1199
|
+
<path d="M12.5 4.5l1 1 1-1" />
|
|
1200
|
+
<path d="M12.5 11.5l1-1 1 1" />
|
|
1201
|
+
</svg>
|
|
1202
|
+
);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
function PageLayerGlyph({ className }: { className?: string }) {
|
|
1206
|
+
return (
|
|
1207
|
+
<svg
|
|
1208
|
+
viewBox="0 0 16 16"
|
|
1209
|
+
fill="none"
|
|
1210
|
+
stroke="currentColor"
|
|
1211
|
+
strokeWidth="1.45"
|
|
1212
|
+
strokeLinecap="round"
|
|
1213
|
+
strokeLinejoin="round"
|
|
1214
|
+
className={className}
|
|
1215
|
+
aria-hidden="true"
|
|
1216
|
+
>
|
|
1217
|
+
<path d="M4.5 2.5h5.1l2 2V13a.8.8 0 0 1-.8.8H4.5a.8.8 0 0 1-.8-.8V3.3a.8.8 0 0 1 .8-.8Z" />
|
|
1218
|
+
<path d="M9.6 2.6v2.1h2.1" />
|
|
1219
|
+
</svg>
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
function FrameLayerGlyph({ className }: { className?: string }) {
|
|
1224
|
+
return (
|
|
1225
|
+
<svg
|
|
1226
|
+
viewBox="0 0 16 16"
|
|
1227
|
+
fill="none"
|
|
1228
|
+
stroke="currentColor"
|
|
1229
|
+
strokeWidth="1.45"
|
|
1230
|
+
strokeLinecap="round"
|
|
1231
|
+
strokeLinejoin="round"
|
|
1232
|
+
className={className}
|
|
1233
|
+
aria-hidden="true"
|
|
1234
|
+
>
|
|
1235
|
+
<path d="M3.5 4.5h9" />
|
|
1236
|
+
<path d="M3.5 11.5h9" />
|
|
1237
|
+
<path d="M5.5 6.5h5" />
|
|
1238
|
+
<path d="M5.5 9.5h5" />
|
|
1239
|
+
</svg>
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
function LayoutLayerGlyph({
|
|
1244
|
+
node,
|
|
1245
|
+
className,
|
|
1246
|
+
}: {
|
|
1247
|
+
node: Pick<LayersPanelNode, "layout">;
|
|
1248
|
+
className?: string;
|
|
1249
|
+
}) {
|
|
1250
|
+
if (node.layout?.isGridContainer) {
|
|
1251
|
+
return (
|
|
1252
|
+
<svg
|
|
1253
|
+
viewBox="0 0 16 16"
|
|
1254
|
+
fill="none"
|
|
1255
|
+
stroke="currentColor"
|
|
1256
|
+
strokeWidth="1.35"
|
|
1257
|
+
className={className}
|
|
1258
|
+
aria-hidden="true"
|
|
1259
|
+
>
|
|
1260
|
+
<rect x="3" y="3" width="3.2" height="3.2" rx=".5" />
|
|
1261
|
+
<rect x="9.8" y="3" width="3.2" height="3.2" rx=".5" />
|
|
1262
|
+
<rect x="3" y="9.8" width="3.2" height="3.2" rx=".5" />
|
|
1263
|
+
<rect x="9.8" y="9.8" width="3.2" height="3.2" rx=".5" />
|
|
1264
|
+
</svg>
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
if (node.layout?.isFlexContainer) {
|
|
1268
|
+
const isRow = node.layout.flexDirection?.startsWith("row");
|
|
1269
|
+
const align = node.layout.alignItems ?? "stretch";
|
|
1270
|
+
const justify = node.layout.justifyContent ?? "flex-start";
|
|
1271
|
+
return isRow ? (
|
|
1272
|
+
<HorizontalAutoLayoutGlyph
|
|
1273
|
+
align={align}
|
|
1274
|
+
justify={justify}
|
|
1275
|
+
className={className}
|
|
1276
|
+
/>
|
|
1277
|
+
) : (
|
|
1278
|
+
<VerticalAutoLayoutGlyph
|
|
1279
|
+
align={align}
|
|
1280
|
+
justify={justify}
|
|
1281
|
+
className={className}
|
|
1282
|
+
/>
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
return <FrameLayerGlyph className={className} />;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
function normalizedAlignment(value: string | undefined) {
|
|
1289
|
+
if (!value) return "start";
|
|
1290
|
+
if (value === "center") return "center";
|
|
1291
|
+
if (value === "flex-end" || value === "end") return "end";
|
|
1292
|
+
if (value === "space-between") return "space-between";
|
|
1293
|
+
if (value === "space-around" || value === "space-evenly")
|
|
1294
|
+
return "space-around";
|
|
1295
|
+
if (value === "stretch") return "stretch";
|
|
1296
|
+
return "start";
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
function crossAxisOffset(align: string | undefined, axis: "x" | "y") {
|
|
1300
|
+
const normalized = normalizedAlignment(align);
|
|
1301
|
+
if (normalized === "center") return axis === "x" ? 5 : 5.5;
|
|
1302
|
+
if (normalized === "end") return axis === "x" ? 7 : 8;
|
|
1303
|
+
return axis === "x" ? 3 : 3;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
function mainAxisPositions(justify: string | undefined, axis: "x" | "y") {
|
|
1307
|
+
const normalized = normalizedAlignment(justify);
|
|
1308
|
+
if (axis === "x") {
|
|
1309
|
+
if (normalized === "center") return [3.6, 7.1, 10.6];
|
|
1310
|
+
if (normalized === "end") return [4.4, 7.8, 11.2];
|
|
1311
|
+
if (normalized === "space-between") return [2.6, 7.1, 11.6];
|
|
1312
|
+
if (normalized === "space-around") return [3.1, 7.1, 11.1];
|
|
1313
|
+
return [3, 6.6, 10.2];
|
|
1314
|
+
}
|
|
1315
|
+
if (normalized === "center") return [3.5, 7.1, 10.7];
|
|
1316
|
+
if (normalized === "end") return [4.2, 7.8, 11.4];
|
|
1317
|
+
if (normalized === "space-between") return [2.8, 7.1, 11.4];
|
|
1318
|
+
if (normalized === "space-around") return [3.2, 7.1, 11];
|
|
1319
|
+
return [3, 6.6, 10.2];
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
function VerticalAutoLayoutGlyph({
|
|
1323
|
+
align,
|
|
1324
|
+
justify,
|
|
1325
|
+
className,
|
|
1326
|
+
}: {
|
|
1327
|
+
align?: string;
|
|
1328
|
+
justify?: string;
|
|
1329
|
+
className?: string;
|
|
1330
|
+
}) {
|
|
1331
|
+
const x = crossAxisOffset(align, "x");
|
|
1332
|
+
const yPositions = mainAxisPositions(justify, "y");
|
|
1333
|
+
return (
|
|
1334
|
+
<svg
|
|
1335
|
+
viewBox="0 0 16 16"
|
|
1336
|
+
fill="none"
|
|
1337
|
+
stroke="currentColor"
|
|
1338
|
+
strokeWidth="1.45"
|
|
1339
|
+
strokeLinejoin="round"
|
|
1340
|
+
className={className}
|
|
1341
|
+
aria-hidden="true"
|
|
1342
|
+
>
|
|
1343
|
+
<rect x={x} y={yPositions[0]} width="6" height="1.55" rx=".45" />
|
|
1344
|
+
<rect x={x} y={yPositions[1]} width="6" height="1.55" rx=".45" />
|
|
1345
|
+
<rect x={x} y={yPositions[2]} width="6" height="1.55" rx=".45" />
|
|
1346
|
+
</svg>
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
function HorizontalAutoLayoutGlyph({
|
|
1351
|
+
align,
|
|
1352
|
+
justify,
|
|
1353
|
+
className,
|
|
1354
|
+
}: {
|
|
1355
|
+
align?: string;
|
|
1356
|
+
justify?: string;
|
|
1357
|
+
className?: string;
|
|
1358
|
+
}) {
|
|
1359
|
+
const y = crossAxisOffset(align, "y");
|
|
1360
|
+
const xPositions = mainAxisPositions(justify, "x");
|
|
1361
|
+
return (
|
|
1362
|
+
<svg
|
|
1363
|
+
viewBox="0 0 16 16"
|
|
1364
|
+
fill="none"
|
|
1365
|
+
stroke="currentColor"
|
|
1366
|
+
strokeWidth="1.45"
|
|
1367
|
+
strokeLinejoin="round"
|
|
1368
|
+
className={className}
|
|
1369
|
+
aria-hidden="true"
|
|
1370
|
+
>
|
|
1371
|
+
<rect x={xPositions[0]} y={y} width="1.55" height="5" rx=".45" />
|
|
1372
|
+
<rect x={xPositions[1]} y={y} width="1.55" height="5" rx=".45" />
|
|
1373
|
+
<rect x={xPositions[2]} y={y} width="1.55" height="5" rx=".45" />
|
|
1374
|
+
</svg>
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
function ComponentLayerGlyph({ className }: { className?: string }) {
|
|
1379
|
+
return (
|
|
1380
|
+
<svg
|
|
1381
|
+
viewBox="0 0 16 16"
|
|
1382
|
+
fill="none"
|
|
1383
|
+
stroke="currentColor"
|
|
1384
|
+
strokeWidth="1.45"
|
|
1385
|
+
strokeLinejoin="round"
|
|
1386
|
+
className={className}
|
|
1387
|
+
aria-hidden="true"
|
|
1388
|
+
>
|
|
1389
|
+
<path d="m8 2.6 5.4 5.4L8 13.4 2.6 8 8 2.6Z" />
|
|
1390
|
+
</svg>
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
function RectangleLayerGlyph({ className }: { className?: string }) {
|
|
1395
|
+
return (
|
|
1396
|
+
<svg
|
|
1397
|
+
viewBox="0 0 16 16"
|
|
1398
|
+
fill="none"
|
|
1399
|
+
stroke="currentColor"
|
|
1400
|
+
strokeWidth="1.45"
|
|
1401
|
+
className={className}
|
|
1402
|
+
aria-hidden="true"
|
|
1403
|
+
>
|
|
1404
|
+
<rect x="3.2" y="4" width="9.6" height="8" rx="1" />
|
|
1405
|
+
</svg>
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
function TextLayerGlyph({ className }: { className?: string }) {
|
|
1410
|
+
return (
|
|
1411
|
+
<svg
|
|
1412
|
+
viewBox="0 0 16 16"
|
|
1413
|
+
fill="none"
|
|
1414
|
+
stroke="currentColor"
|
|
1415
|
+
strokeWidth="1.45"
|
|
1416
|
+
strokeLinecap="round"
|
|
1417
|
+
strokeLinejoin="round"
|
|
1418
|
+
className={className}
|
|
1419
|
+
aria-hidden="true"
|
|
1420
|
+
>
|
|
1421
|
+
<path d="M3.2 4h9.6" />
|
|
1422
|
+
<path d="M8 4v8.4" />
|
|
1423
|
+
</svg>
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
function ImageLayerGlyph({ className }: { className?: string }) {
|
|
1428
|
+
return (
|
|
1429
|
+
<svg
|
|
1430
|
+
viewBox="0 0 16 16"
|
|
1431
|
+
fill="none"
|
|
1432
|
+
stroke="currentColor"
|
|
1433
|
+
strokeWidth="1.35"
|
|
1434
|
+
strokeLinecap="round"
|
|
1435
|
+
strokeLinejoin="round"
|
|
1436
|
+
className={className}
|
|
1437
|
+
aria-hidden="true"
|
|
1438
|
+
>
|
|
1439
|
+
<rect x="3" y="3" width="10" height="10" rx="1.2" />
|
|
1440
|
+
<circle cx="6" cy="6" r="1" />
|
|
1441
|
+
<path d="m4.2 12 3.2-3.3 1.8 1.8 1.3-1.4 1.3 1.4" />
|
|
1442
|
+
</svg>
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
function ElementLayerGlyph({ className }: { className?: string }) {
|
|
1447
|
+
return (
|
|
1448
|
+
<svg
|
|
1449
|
+
viewBox="0 0 16 16"
|
|
1450
|
+
fill="none"
|
|
1451
|
+
stroke="currentColor"
|
|
1452
|
+
strokeWidth="1.45"
|
|
1453
|
+
strokeLinecap="round"
|
|
1454
|
+
strokeLinejoin="round"
|
|
1455
|
+
className={className}
|
|
1456
|
+
aria-hidden="true"
|
|
1457
|
+
>
|
|
1458
|
+
<path d="m6.4 4.2-3.2 3.8 3.2 3.8" />
|
|
1459
|
+
<path d="m9.6 4.2 3.2 3.8-3.2 3.8" />
|
|
1460
|
+
</svg>
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1115
1464
|
function dropPlacementForEvent(
|
|
1116
1465
|
event: DragEvent<HTMLDivElement>,
|
|
1117
1466
|
canDropInside: boolean,
|