@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.
Files changed (228) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +28 -0
  3. package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  4. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +14 -1
  5. package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  6. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +15 -1
  7. package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  8. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +15 -1
  9. package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  10. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +15 -1
  11. package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  12. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +15 -1
  13. package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  14. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +16 -1
  15. package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  16. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +14 -1
  17. package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  18. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +14 -1
  19. package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  20. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +14 -1
  21. package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  22. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +14 -1
  23. package/corpus/core/docs/content/skills-guide.mdx +5 -0
  24. package/corpus/core/docs/content/template-design.mdx +14 -1
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/application-state/handlers.ts +1 -1
  27. package/corpus/core/src/brand-kit/index.ts +1 -1
  28. package/corpus/core/src/cli/skills.ts +18 -0
  29. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  30. package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
  31. package/corpus/core/src/client/extensions/portable-extension.ts +1 -1
  32. package/corpus/core/src/code-agents/transcript-normalizer.ts +1 -1
  33. package/corpus/core/src/extensions/path.ts +1 -4
  34. package/corpus/core/src/integrations/a2a-continuation-processor.ts +1 -1
  35. package/corpus/core/src/mcp/build-server.ts +2 -2
  36. package/corpus/core/src/mcp/embed-app.ts +35 -1
  37. package/corpus/core/src/org/handlers.ts +5 -5
  38. package/corpus/core/src/org/plugin.ts +2 -2
  39. package/corpus/core/src/scripts/call-agent.ts +1 -1
  40. package/corpus/core/src/scripts/db/safety.ts +1 -1
  41. package/corpus/core/src/server/auth.ts +3 -0
  42. package/corpus/core/src/server/builder-design-systems.ts +308 -0
  43. package/corpus/core/src/server/core-routes-plugin.ts +29 -3
  44. package/corpus/core/src/server/index.ts +8 -0
  45. package/corpus/core/src/styles/agent-native.css +14 -7
  46. package/corpus/core/src/usage/store.ts +2 -2
  47. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +298 -15
  48. package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -4
  49. package/corpus/templates/analytics/actions/mutate-dashboard.ts +18 -2
  50. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +12 -2
  51. package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +66 -0
  52. package/corpus/templates/analytics/app/i18n/zh-TW.ts +37 -0
  53. package/corpus/templates/analytics/app/i18n-data.ts +404 -0
  54. package/corpus/templates/analytics/app/pages/Settings.tsx +333 -0
  55. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +2 -1
  56. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +121 -3
  57. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +49 -26
  58. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +137 -36
  59. package/corpus/templates/analytics/changelog/2026-06-29-analytics-agents-can-now-place-new-charts-into-requested-das.md +6 -0
  60. package/corpus/templates/analytics/changelog/2026-06-29-analytics-queries-now-report-failed-source-query-errors-inst.md +6 -0
  61. package/corpus/templates/analytics/changelog/2026-06-29-charts-can-now-be-dropped-between-full-dashboard-rows-to-cre.md +6 -0
  62. package/corpus/templates/analytics/changelog/2026-06-29-dashboard-email-reports-include-the-full-dashboard-image-wit.md +6 -0
  63. package/corpus/templates/analytics/changelog/2026-06-29-main-surface-border-reaches-the-top.md +6 -0
  64. package/corpus/templates/analytics/changelog/2026-06-29-session-replay-now-records-playable-sessions-in-production-a.md +6 -0
  65. package/corpus/templates/analytics/changelog/2026-06-29-set-up-session-replay-storage-from-settings-connect-builder-.md +6 -0
  66. package/corpus/templates/analytics/scripts/cleanup-empty-session-recordings.ts +102 -0
  67. package/corpus/templates/analytics/server/lib/dashboard-report.ts +49 -26
  68. package/corpus/templates/analytics/server/lib/real-data-actions.ts +81 -20
  69. package/corpus/templates/analytics/server/lib/replay-storage.ts +79 -0
  70. package/corpus/templates/analytics/server/lib/s3-upload-provider.ts +255 -0
  71. package/corpus/templates/analytics/server/lib/session-replay.ts +29 -8
  72. package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
  73. package/corpus/templates/analytics/server/plugins/onboarding.ts +97 -0
  74. package/corpus/templates/clips/desktop/src/lib/recorder.ts +24 -0
  75. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +105 -5
  76. package/corpus/templates/content/actions/set-image-alt-text.ts +1 -1
  77. package/corpus/templates/content/shared/document-export.ts +1 -1
  78. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -18
  79. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +17 -0
  80. package/corpus/templates/design/actions/add-localhost-screens.ts +32 -5
  81. package/corpus/templates/design/actions/index-design-system-with-builder.ts +259 -0
  82. package/corpus/templates/design/actions/update-file.ts +9 -2
  83. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +33 -75
  84. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +72 -9
  85. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +41 -39
  86. package/corpus/templates/design/app/components/design/EditPanel.tsx +1029 -249
  87. package/corpus/templates/design/app/components/design/LayersPanel.tsx +450 -101
  88. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +978 -154
  89. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -9
  90. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +133 -66
  91. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +1234 -344
  92. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +144 -94
  93. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +301 -72
  94. package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +876 -286
  95. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +601 -0
  96. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +242 -0
  97. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +30 -7
  98. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +212 -155
  99. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +423 -0
  100. package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +487 -107
  101. package/corpus/templates/design/app/components/design/inspector/index.ts +27 -0
  102. package/corpus/templates/design/app/global.css +29 -3
  103. package/corpus/templates/design/app/i18n/zh-TW.ts +13 -0
  104. package/corpus/templates/design/app/i18n-data.ts +239 -1
  105. package/corpus/templates/design/app/pages/DesignEditor.tsx +1023 -353
  106. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +57 -221
  107. package/corpus/templates/design/changelog/2026-06-29-canvas-selection-no-longer-flashes-broken-prototype-html-whe.md +6 -0
  108. package/corpus/templates/design/changelog/2026-06-29-command-menu-selections-are-clearer-in-dark-mode.md +6 -0
  109. package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +6 -0
  110. package/corpus/templates/design/changelog/2026-06-29-element-selection-now-stays-on-the-intended-layer-after-sele.md +6 -0
  111. package/corpus/templates/design/changelog/2026-06-29-screen-overview-full-view-controls-now-sit-above-frames-inst.md +6 -0
  112. package/corpus/templates/design/changelog/2026-06-29-screen-overview-tools-can-draw-shapes-create-screens-and-kee.md +6 -0
  113. package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-now-creates-anchor-based-bezier-vectors-with-ha.md +6 -0
  114. package/corpus/templates/design/server/handlers/import-figma-system.ts +38 -85
  115. package/corpus/templates/design/server/lib/design-snapshot.ts +9 -1
  116. package/corpus/templates/design/shared/code-layer.ts +209 -9
  117. package/corpus/templates/design/shared/html-content.ts +21 -0
  118. package/corpus/templates/design/shared/pen-path.ts +207 -0
  119. package/corpus/templates/mail/app/lib/utils.ts +2 -2
  120. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -10
  121. package/corpus/templates/slides/AGENTS.md +3 -2
  122. package/corpus/templates/slides/actions/import-file.ts +26 -17
  123. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +259 -0
  124. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +35 -167
  125. package/corpus/templates/slides/app/components/design-system/fig-import-response.ts +9 -13
  126. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -1
  127. package/corpus/templates/slides/app/i18n/ar-SA.ts +9 -37
  128. package/corpus/templates/slides/app/i18n/de-DE.ts +10 -25
  129. package/corpus/templates/slides/app/i18n/en-US.ts +9 -26
  130. package/corpus/templates/slides/app/i18n/es-ES.ts +10 -28
  131. package/corpus/templates/slides/app/i18n/fr-FR.ts +10 -28
  132. package/corpus/templates/slides/app/i18n/hi-IN.ts +9 -26
  133. package/corpus/templates/slides/app/i18n/ja-JP.ts +10 -22
  134. package/corpus/templates/slides/app/i18n/ko-KR.ts +9 -23
  135. package/corpus/templates/slides/app/i18n/pt-BR.ts +10 -28
  136. package/corpus/templates/slides/app/i18n/zh-CN.ts +9 -22
  137. package/corpus/templates/slides/app/i18n/zh-TW.ts +9 -22
  138. package/corpus/templates/slides/server/handlers/import-figma-system.ts +44 -12
  139. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  140. package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +1 -1
  141. package/dist/application-state/handlers.js +1 -1
  142. package/dist/application-state/handlers.js.map +1 -1
  143. package/dist/brand-kit/index.d.ts +1 -1
  144. package/dist/brand-kit/index.js +1 -1
  145. package/dist/brand-kit/index.js.map +1 -1
  146. package/dist/cli/skills.d.ts.map +1 -1
  147. package/dist/cli/skills.js +18 -0
  148. package/dist/cli/skills.js.map +1 -1
  149. package/dist/client/AgentPanel.js +1 -1
  150. package/dist/client/AgentPanel.js.map +1 -1
  151. package/dist/client/MultiTabAssistantChat.js +1 -1
  152. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  153. package/dist/client/extensions/portable-extension.d.ts +1 -1
  154. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  155. package/dist/client/extensions/portable-extension.js.map +1 -1
  156. package/dist/code-agents/transcript-normalizer.js +1 -1
  157. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  158. package/dist/collab/awareness.d.ts +2 -2
  159. package/dist/collab/awareness.d.ts.map +1 -1
  160. package/dist/collab/routes.d.ts +1 -1
  161. package/dist/extensions/path.d.ts +1 -1
  162. package/dist/extensions/path.d.ts.map +1 -1
  163. package/dist/extensions/path.js.map +1 -1
  164. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  165. package/dist/integrations/a2a-continuation-processor.js +1 -1
  166. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  167. package/dist/mcp/build-server.d.ts +1 -1
  168. package/dist/mcp/build-server.d.ts.map +1 -1
  169. package/dist/mcp/build-server.js +1 -1
  170. package/dist/mcp/build-server.js.map +1 -1
  171. package/dist/mcp/embed-app.d.ts.map +1 -1
  172. package/dist/mcp/embed-app.js +35 -1
  173. package/dist/mcp/embed-app.js.map +1 -1
  174. package/dist/notifications/routes.d.ts +3 -3
  175. package/dist/observability/routes.d.ts +8 -8
  176. package/dist/org/handlers.js +5 -5
  177. package/dist/org/handlers.js.map +1 -1
  178. package/dist/org/plugin.js +2 -2
  179. package/dist/org/plugin.js.map +1 -1
  180. package/dist/resources/handlers.d.ts +1 -1
  181. package/dist/scripts/call-agent.js +1 -1
  182. package/dist/scripts/call-agent.js.map +1 -1
  183. package/dist/scripts/db/safety.js +1 -1
  184. package/dist/scripts/db/safety.js.map +1 -1
  185. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  186. package/dist/server/auth.d.ts.map +1 -1
  187. package/dist/server/auth.js +3 -0
  188. package/dist/server/auth.js.map +1 -1
  189. package/dist/server/builder-design-systems.d.ts +28 -0
  190. package/dist/server/builder-design-systems.d.ts.map +1 -0
  191. package/dist/server/builder-design-systems.js +217 -0
  192. package/dist/server/builder-design-systems.js.map +1 -0
  193. package/dist/server/core-routes-plugin.d.ts +1 -0
  194. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  195. package/dist/server/core-routes-plugin.js +20 -3
  196. package/dist/server/core-routes-plugin.js.map +1 -1
  197. package/dist/server/index.d.ts +1 -0
  198. package/dist/server/index.d.ts.map +1 -1
  199. package/dist/server/index.js +1 -0
  200. package/dist/server/index.js.map +1 -1
  201. package/dist/styles/agent-native.css +14 -7
  202. package/dist/usage/store.js +2 -2
  203. package/dist/usage/store.js.map +1 -1
  204. package/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  205. package/docs/content/locales/ar-SA/template-design.mdx +14 -1
  206. package/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  207. package/docs/content/locales/de-DE/template-design.mdx +15 -1
  208. package/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  209. package/docs/content/locales/es-ES/template-design.mdx +15 -1
  210. package/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  211. package/docs/content/locales/fr-FR/template-design.mdx +15 -1
  212. package/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  213. package/docs/content/locales/hi-IN/template-design.mdx +15 -1
  214. package/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  215. package/docs/content/locales/ja-JP/template-design.mdx +16 -1
  216. package/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  217. package/docs/content/locales/ko-KR/template-design.mdx +14 -1
  218. package/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  219. package/docs/content/locales/pt-BR/template-design.mdx +14 -1
  220. package/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  221. package/docs/content/locales/zh-CN/template-design.mdx +14 -1
  222. package/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  223. package/docs/content/locales/zh-TW/template-design.mdx +14 -1
  224. package/docs/content/skills-guide.mdx +5 -0
  225. package/docs/content/template-design.mdx +14 -1
  226. package/package.json +1 -1
  227. package/corpus/templates/design/actions/import-figma-file.ts +0 -166
  228. 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 layerTypeIcon(type: LayersPanelNodeType | undefined): Icon {
365
- switch (type) {
366
- case "file":
367
- return IconFile;
368
- case "screen":
369
- return IconFrame;
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-[11px] text-foreground",
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-border pb-2">
582
- <div className="flex h-9 items-center justify-between px-2.5">
583
- <h2 className="truncate text-[11px] font-semibold text-foreground">
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={() => searchInputRef.current?.focus()}
593
+ onClick={openSearch}
596
594
  >
597
- <IconSearch className="size-3.5" />
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-3.5" />
602
+ <IconPlus className="size-4" />
605
603
  </IconTooltipButton>
606
604
  </div>
607
605
  </div>
608
- <div className="space-y-0.5 px-1">
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-7 w-full cursor-default items-center gap-2 rounded-sm px-2 text-left text-[12px] font-medium outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
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
- <IconFile className="size-3.5 shrink-0 text-muted-foreground" />
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-9 shrink-0 items-center justify-between border-b border-border px-2.5">
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-[11px] font-semibold text-foreground">
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
- <IconStack2 className="size-3.5 shrink-0 text-muted-foreground" />
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
- <div className="shrink-0 border-b border-border p-1.5">
653
- <div className="relative">
654
- <IconSearch className="pointer-events-none absolute left-2 top-1/2 size-3 -translate-y-1/2 text-muted-foreground" />
655
- <Input
656
- ref={searchInputRef}
657
- value={searchQuery}
658
- onChange={(event) => onSearchQueryChange(event.target.value)}
659
- placeholder={labels.searchPlaceholder}
660
- className="h-7 rounded-sm border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] pl-6 text-[11px] shadow-none placeholder:text-muted-foreground/70 focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]"
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
- </div>
681
+ ) : null}
664
682
 
665
- <div className="min-h-0 flex-1 overflow-y-auto py-1">
683
+ <div className="min-h-0 flex-1 overflow-y-auto py-2">
666
684
  {visibleRows.length ? (
667
- <div className="px-1" role="tree" aria-label={labels.title}>
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-6 items-center gap-0.5 rounded-sm pr-0.5 text-[11px]",
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
- isActiveScreen &&
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-accent/70 hover:text-foreground",
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: 2 + depth * 12 }}
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-3" />
967
+ <IconChevronDown className="size-4" />
949
968
  ) : (
950
- <IconChevronRight className="size-3 rtl:-scale-x-100" />
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-1.5 overflow-hidden rounded-sm px-1 py-0.5 text-left outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
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-[var(--design-editor-accent-strong-color)]",
990
+ (isSelected || isInSelectedSubtree) && "text-foreground",
973
991
  )}
974
992
  >
975
- {node.icon ?? <Icon className="size-3.5" />}
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-5 min-w-0 flex-1 rounded-sm border border-[var(--design-editor-accent-color)] bg-[var(--design-editor-panel-bg)] px-1 text-[11px] text-foreground outline-none"
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 && node.detail ? (
1007
- <span className="hidden max-w-[72px] shrink truncate text-[10px] text-muted-foreground group-hover:inline">
1008
- {node.detail}
1009
- </span>
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-4 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",
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-4 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",
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-accent hover:text-foreground"
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,