diff --git a/components.d.ts b/components.d.ts index 9a0a68f51..58774c6c7 100644 --- a/components.d.ts +++ b/components.d.ts @@ -11,7 +11,6 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { - APIKeySetup: typeof import('./src/components/chat/APIKeySetup.vue')['default'] AppearanceSection: typeof import('./src/components/properties/AppearanceSection.vue')['default'] AppMenu: typeof import('./src/components/AppMenu.vue')['default'] AppSelect: typeof import('./src/components/AppSelect.vue')['default'] @@ -35,7 +34,6 @@ declare module 'vue' { IconLucideALargeSmall: typeof import('~icons/lucide/a-large-small')['default'] IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default'] IconLucideAlignCenter: typeof import('~icons/lucide/align-center')['default'] - IconLucideAlignHorizontalDistributeCenter: typeof import('~icons/lucide/align-horizontal-distribute-center')['default'] IconLucideAlignHorizontalJustifyCenter: typeof import('~icons/lucide/align-horizontal-justify-center')['default'] IconLucideAlignHorizontalJustifyEnd: typeof import('~icons/lucide/align-horizontal-justify-end')['default'] IconLucideAlignHorizontalJustifyStart: typeof import('~icons/lucide/align-horizontal-justify-start')['default'] @@ -47,10 +45,6 @@ declare module 'vue' { IconLucideArrowDown: typeof import('~icons/lucide/arrow-down')['default'] IconLucideArrowRight: typeof import('~icons/lucide/arrow-right')['default'] IconLucideBaseline: typeof import('~icons/lucide/baseline')['default'] - IconLucideBetweenHorizontalEnd: typeof import('~icons/lucide/between-horizontal-end')['default'] - IconLucideBetweenHorizontalStart: typeof import('~icons/lucide/between-horizontal-start')['default'] - IconLucideBetweenVerticalEnd: typeof import('~icons/lucide/between-vertical-end')['default'] - IconLucideBetweenVerticalStart: typeof import('~icons/lucide/between-vertical-start')['default'] IconLucideBlend: typeof import('~icons/lucide/blend')['default'] IconLucideBold: typeof import('~icons/lucide/bold')['default'] IconLucideBot: typeof import('~icons/lucide/bot')['default'] @@ -67,9 +61,7 @@ declare module 'vue' { IconLucideFlipVertical: typeof import('~icons/lucide/flip-vertical')['default'] IconLucideFolderPlus: typeof import('~icons/lucide/folder-plus')['default'] IconLucideImage: typeof import('~icons/lucide/image')['default'] - IconLucideImageOff: typeof import('~icons/lucide/image-off')['default'] IconLucideItalic: typeof import('~icons/lucide/italic')['default'] - IconLucideKeyRound: typeof import('~icons/lucide/key-round')['default'] IconLucideLayers: typeof import('~icons/lucide/layers')['default'] IconLucideLayoutGrid: typeof import('~icons/lucide/layout-grid')['default'] IconLucideLink: typeof import('~icons/lucide/link')['default'] @@ -77,8 +69,7 @@ declare module 'vue' { IconLucideMenu: typeof import('~icons/lucide/menu')['default'] IconLucideMessageCircle: typeof import('~icons/lucide/message-circle')['default'] IconLucideMinus: typeof import('~icons/lucide/minus')['default'] - IconLucideMoveHorizontal: typeof import('~icons/lucide/move-horizontal')['default'] - IconLucideMoveVertical: typeof import('~icons/lucide/move-vertical')['default'] + IconLucidePlay: typeof import('~icons/lucide/play')['default'] IconLucidePlus: typeof import('~icons/lucide/plus')['default'] IconLucideRadius: typeof import('~icons/lucide/radius')['default'] IconLucideRedo2: typeof import('~icons/lucide/redo2')['default'] @@ -91,7 +82,6 @@ declare module 'vue' { IconLucideShare2: typeof import('~icons/lucide/share2')['default'] IconLucideSidebar: typeof import('~icons/lucide/sidebar')['default'] IconLucideSlidersHorizontal: typeof import('~icons/lucide/sliders-horizontal')['default'] - IconLucideSpace: typeof import('~icons/lucide/space')['default'] IconLucideSparkles: typeof import('~icons/lucide/sparkles')['default'] IconLucideSquare: typeof import('~icons/lucide/square')['default'] IconLucideStrikethrough: typeof import('~icons/lucide/strikethrough')['default'] diff --git a/packages/core/src/tools/create.ts b/packages/core/src/tools/create.ts index c47ed376d..b91b2760f 100644 --- a/packages/core/src/tools/create.ts +++ b/packages/core/src/tools/create.ts @@ -247,7 +247,11 @@ function createIconFrame( if (parentId) { const parent = figma.getNodeById(parentId) - if (parent) parent.appendChild(frame) + if (parent) { + parent.appendChild(frame) + frame.x = 0 + frame.y = 0 + } } for (const path of icon.paths) { @@ -297,21 +301,27 @@ export const insertIcon = defineTool({ params: { names: { type: 'string[]', - description: 'Icon names as prefix:name (e.g. ["lucide:heart"] or ["lucide:heart","lucide:home","lucide:star"])', - required: true + description: 'Icon names as prefix:name (e.g. ["lucide:heart"] or ["lucide:heart","lucide:home","lucide:star"])' + }, + name: { + type: 'string', + description: 'Single icon name (shorthand for names with one icon)' }, size: { type: 'number', description: 'Icon size in pixels (default: 24)' }, color: { type: 'color', description: 'Icon color hex (replaces currentColor). Default: #000000' }, parent_id: { type: 'string', description: 'Parent node ID for all icons' } }, execute: async (figma, args) => { + const names = args.names ?? (args.name ? [args.name] : []) + if (names.length === 0) return { error: 'Provide "names" (array) or "name" (string)' } + const size = args.size ?? 24 const color = args.color ?? '#000000' const parsedColor = parseColor(color) let icons try { - icons = await fetchIcons(args.names, size) + icons = await fetchIcons(names, size) } catch (e) { return { error: (e as Error).message } } @@ -319,7 +329,7 @@ export const insertIcon = defineTool({ const inserted: { id: string; name: string; icon: string }[] = [] const notFound: string[] = [] - for (const name of args.names) { + for (const name of names) { const icon = icons.get(name) if (!icon || icon.paths.length === 0) { notFound.push(name) @@ -329,6 +339,10 @@ export const insertIcon = defineTool({ inserted.push({ id: frame.id, name: frame.name, icon: name }) } + if (args.name && inserted.length === 1) { + return { id: inserted[0].id, name: inserted[0].name, type: 'FRAME' as const } + } + const result: Record = { inserted } if (notFound.length > 0) result.not_found = notFound return result diff --git a/src/ai/chat-debug.ts b/src/ai/chat-debug.ts index 910e1e033..178109087 100644 --- a/src/ai/chat-debug.ts +++ b/src/ai/chat-debug.ts @@ -248,7 +248,7 @@ export function serializeChatLog(messages: UIMessage[]): string { parts.push(` ${p.text as string}`) } else if (p.type === 'reasoning') { parts.push( - ` [reasoning] ${typeof p.text === 'string' ? p.text : (typeof p.content === 'string' ? p.content : '')}` + ` [reasoning] ${typeof p.text === 'string' ? p.text : typeof p.content === 'string' ? p.content : ''}` ) } else if ( p.type === 'tool-invocation' || diff --git a/src/ai/system-prompt.md b/src/ai/system-prompt.md index ebb669e32..6884bd5b8 100644 --- a/src/ai/system-prompt.md +++ b/src/ai/system-prompt.md @@ -32,6 +32,8 @@ These are ALL available props. Nothing else exists — no lineHeight, no letterS Without flex, children are positioned by x/y (absolute). With flex, x/y are ignored and children flow automatically. +⚠ **Every parent that has children using `w="fill"` or `h="fill"` MUST have `flex="col"` or `flex="row"`.** Without flex, fill sizing is ignored and children revert to fixed/hug size. This is the #1 layout bug — forgetting flex on a parent container. + justify/items require flex — always declare flex="row" or flex="col" before using alignment props. The value is "between", not "space-between". A hug parent shrinks to fit its children. A fill child stretches to its parent. These can't be circular — if the parent hugs, at least one child must have a concrete size. @@ -207,20 +209,36 @@ Three tools for Iconify icons (200k+ icons). All batch-friendly — minimize LLM **Popular icon sets:** -| Prefix | Style | Best for | -|--------|-------|----------| -| `lucide` | Outline/stroke | UI, minimal | -| `mdi` | Filled | General purpose | -| `heroicons` | Outline/stroke | Web apps | -| `tabler` | Outline/stroke | Dashboards | -| `solar` | Mixed | Modern UI | -| `mingcute` | Filled | Apps, mobile | -| `ri` | Filled | Web, editorial | -| `iconoir` | Outline/stroke | Clean, minimal | -| `ph` | Multiple weights | Versatile | +| Prefix | Style | Best for | +| ----------- | ---------------- | --------------- | +| `lucide` | Outline/stroke | UI, minimal | +| `mdi` | Filled | General purpose | +| `heroicons` | Outline/stroke | Web apps | +| `tabler` | Outline/stroke | Dashboards | +| `solar` | Mixed | Modern UI | +| `mingcute` | Filled | Apps, mobile | +| `ri` | Filled | Web, editorial | +| `iconoir` | Outline/stroke | Clean, minimal | +| `ph` | Multiple weights | Versatile | ⚠ Always set `color` on `insert_icon` — default is black, invisible on dark backgrounds. +## Emoji ⚠ + +**Do NOT use emoji (📷 🥾 ☕ 🎨 ♡ ✓ ▶ ★ etc.) in UI elements.** The canvas renders text with system fonts — emoji glyphs are missing and show as "□" (tofu). This includes: + +- Icon buttons, badges, navigation items, tags, chips +- Checkmarks, hearts, stars, arrows — use `insert_icon` instead +- Category labels like "📷 Photography" — just write "Photography" + +**Emoji ARE acceptable** only in user-generated content where they would naturally appear: + +- Chat messages, social posts, comments, reviews +- Emoji reactions (👍 ❤️ 😂) on messages +- Story/status text + +For UI iconography, always use `insert_icon` with Lucide or other Iconify sets. For checkmarks use `insert_icon(names=["lucide:check"])`, for hearts `insert_icon(names=["lucide:heart"])`, etc. + ## Shapes All visual elements (Rectangle, Ellipse, Star, Polygon, Line) have **no fill by default** — they render as invisible without `bg="#hex"` or `stroke="#hex"`.