figura-cli 0.8.1 → 0.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figura-cli",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Command-line client for the Figura visualization SaaS",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -28,11 +28,13 @@
28
28
  "node": ">=20"
29
29
  },
30
30
  "scripts": {
31
+ "gen-skill": "bun run scripts/gen-skill-reference.ts",
31
32
  "build": "bun run scripts/bundle-skill.mjs && bun build src/index.ts --target=node --minify --outfile=dist/index.js && bun build ../mcp/src/index.ts --target=node --minify --outfile=dist/mcp.js",
32
33
  "dev": "bun run src/index.ts",
33
34
  "prepublishOnly": "bun run build"
34
35
  },
35
36
  "devDependencies": {
37
+ "@panofm/figura-core": "workspace:*",
36
38
  "@types/node": "^20.14.0",
37
39
  "axios": "^1.7.9",
38
40
  "postgres": "^3.4.9",
@@ -5,6 +5,9 @@ argument-hint: '<what to visualize>'
5
5
  allowed-tools: 'Bash(figura *), Bash(bunx figura-cli *)'
6
6
  ---
7
7
 
8
+ <!-- GENERATED — do not edit by hand. Source of truth: packages/core/src/fig/* via
9
+ packages/cli/scripts/gen-skill-reference.ts (run `bun run gen-skill`). -->
10
+
8
11
  # Figura Fig
9
12
 
10
13
  Generate ONE self-contained, on-brand HTML fig and persist it to the user's Figura team.
@@ -77,9 +80,10 @@ bunx figura-cli brand --json
77
80
  This returns `{ name, appDescription, tokensBlock, forbidden, surfaces, isCustom }`
78
81
  (`forbidden` is a string, `surfaces` is a string[]).
79
82
 
80
- - If **`isCustom: true`** — use the returned `tokensBlock` as the ground-truth design
81
- system (palette, type, spacing, icon language) and render for `name` / `appDescription`.
82
- Never invent off-brand values.
83
+ - If **`isCustom: true`** — treat the returned `tokensBlock` as the **design system that
84
+ wins conflicts** (palette, type, spacing, icon language) and render for `name` /
85
+ `appDescription`. If anything in the request conflicts with the design system, the design
86
+ system takes priority. Never invent off-brand values.
83
87
  - If **`isCustom: false`** (no brand set) — render with **neutral defaults**: read
84
88
  `reference/brand-tokens.md` (next to this file). It is a clean, brand-neutral baseline —
85
89
  understated and professional, NOT any specific company's look.
@@ -97,7 +101,7 @@ engine's own generator does):
97
101
 
98
102
  **Pick the platform** from the request:
99
103
 
100
- - **`mobile`** (default) — an iPhone-framed app screen.
104
+ - **`mobile`** (default) — an iPhone app screen.
101
105
  - **`web`** — a browser-framed web/desktop UI (a marketing page, dashboard, SaaS screen).
102
106
  Choose this when the product is a website/web app rather than a phone screen.
103
107
 
@@ -121,15 +125,29 @@ Produce ONE complete, self-contained HTML document. Whatever the platform, it MU
121
125
  its state with CSS alone (it runs in a sandboxed iframe).
122
126
  - Use the resolved brand's tokens for all color/type/spacing — never invent off-brand values.
123
127
  - Represent every icon as **inline stroke SVG** via `<symbol>`/`<use>` (currentColor,
124
- width ~1.6) — **NEVER emoji**.
128
+ width ~1.6) — **NO emojis anywhere**.
125
129
  - Set `<title>` to a short screen name, include `<meta name="figura:context" content="…">`
126
130
  (one-line design-intent summary), and an `<h1>` naming the screen.
127
131
 
128
- **Mobile platform**render an **iPhone device frame** (`.device-port` for portrait,
129
- `.device-land` for landscape) with a dynamic island, per the reference. A `.preview` media
130
- area is OPTIONAL include it only if the design calls for a hero/media region, and size it
131
- to the design (any aspect ratio). Hand-roll controls with the pill/capsule convention; do
132
- not rely on system/OS chrome.
132
+ **Mobile platform — FRAMLESS device screen.** Render the **screen contents only, at EXACTLY
133
+ the device's logical screen size, with NO drawn phone bezel / outer border / rounded outer
134
+ frame / notch or Dynamic-Island cutout** the Figura viewer draws the physical device frame
135
+ + Dynamic Island around your output, so a fig fits a real phone 1:1.
136
+
137
+ - Portrait: render at **393×852** logical px. Landscape: **852×393**.
138
+ - Lock the body to that size and fill it edge-to-edge (no outer page padding):
139
+ `body { margin:0; width:393px; height:852px; background:#0A0A0A; overflow:hidden; }` (swap W/H for landscape).
140
+ - Reserve the OS-chrome safe areas: keep the top ~59px (status bar / Dynamic
141
+ Island) and bottom ~34px (home indicator) clear of critical controls
142
+ (landscape: top ~59px, bottom ~21px).
143
+ - **Emit `<meta name="figura:artboard" content="393x852">`** (the exact W×H you
144
+ rendered to) so the viewer can frame it. A `.preview` media area is OPTIONAL — include it
145
+ only if the design calls for a hero/media region, sized to the design (any aspect ratio).
146
+ - Hand-roll controls with the pill/capsule convention (see `reference/brand-tokens.md`); do
147
+ not rely on system/OS chrome.
148
+
149
+ (`compare` and `both` are multi-screen comparison layouts — render each screen at the size
150
+ above on a canvas that grows to fit them all, and do NOT emit a single `figura:artboard` meta.)
133
151
 
134
152
  **Web platform** — render a **browser window** instead (no phone frame):
135
153
 
@@ -141,6 +159,19 @@ not rely on system/OS chrome.
141
159
  .frame-web .viewport { /* full-bleed RESPONSIVE content — nav, hero, sections */ }
142
160
  ```
143
161
 
162
+ **Hard rules (apply to every fig):**
163
+
164
+ - NO emojis anywhere. Represent every icon as inline stroke SVG via <symbol>/<use> (currentColor, width ~1.6).
165
+ - Plain HTML + INLINE CSS ONLY (a single <style> in <head>). No external stylesheets, no fonts loaded over the network, no frameworks.
166
+ - ABSOLUTELY NO <script> tags and NO on* event handlers (onclick, onload, etc.). The fig must be fully static and render its state with CSS alone. It runs in a sandboxed iframe.
167
+
168
+ **Visual quality (intentional and crafted — never generic "AI" UI):**
169
+
170
+ - Color: 3–5 colors total, drawn from the design system — one primary, 2–3 neutrals, 1–2 accents; never exceed 5. If you override a background color, also override its text color for contrast.
171
+ - Type: at most 2 font families (headings + body). No decorative fonts for body. Clear hierarchy.
172
+ - NEVER add filler decoration: no gradient circles, blurry blobs, or abstract shapes as ornament. Avoid gradients unless the design system calls for them; prefer solid colors.
173
+ - Real, specific content (no lorem ipsum, no "Title goes here"). Generous, consistent spacing. A clear visual identity — not a bland gray/white template.
174
+
144
175
  ### 4. Persist (and optionally publish) to the Figura team
145
176
 
146
177
  **Generating the HTML is not "creating the fig" — the fig only exists once it's saved to the
@@ -1,5 +1,8 @@
1
1
  # Figura Fig — neutral default tokens (no team brand set)
2
2
 
3
+ <!-- GENERATED — do not edit by hand. Source of truth: packages/core/src/fig/* via
4
+ packages/cli/scripts/gen-skill-reference.ts (run `bun run gen-skill`). -->
5
+
3
6
  Use this baseline ONLY when `figura brand --json` returns `isCustom: false` (the team
4
7
  hasn't set its own brand). It is a clean, modern, accessible, brand-NEUTRAL default — never
5
8
  any specific company's look. The moment a team sets tokens, use those instead.
@@ -10,7 +13,7 @@ Keep it understated and professional.
10
13
 
11
14
  | Role | Value | Usage |
12
15
  |------|-------|-------|
13
- | Canvas | `#0E0E10` | Near-black base background. |
16
+ | Canvas | `#0A0A0A` | Near-black base background (the fig screen). |
14
17
  | Surface | `#161618` | Cards / raised surfaces. |
15
18
  | Text | `#F4F4F6` | Primary text. |
16
19
  | Text muted | `#9aa0ad` | Secondary text, captions. |
@@ -37,43 +40,33 @@ An 8px spacing rhythm; 12–16px corner radii; 1px hairline borders; restrained
37
40
 
38
41
  ## Icon language
39
42
 
40
- Every icon is **inline stroke SVG** via `<symbol>`/`<use>` (`stroke="currentColor"`,
41
- `stroke-width` ~1.6). **NEVER emoji.**
42
-
43
- ## iPhone device frame — CSS conventions
44
-
45
- Reuse these class names + proportions so every mobile fig reads as one house style.
43
+ Inline SVG icons define <symbol> elements in a hidden <svg> at the top of <body> and reference with <use href="#id"/>. Stroke icons (currentColor stroke, width ~1.6). NO emojis anywhere.
46
44
 
47
- **Reset + page:**
48
-
49
- ```css
50
- * { box-sizing: border-box; margin: 0; padding: 0; }
51
- body { background: #161618; color: #F4F4F6; font-family: Inter, -apple-system, BlinkMacSystemFont, system-ui, sans-serif; padding: 30px; }
52
- .page { display: flex; flex-direction: column; align-items: center; gap: 14px; }
53
- .row { display: flex; gap: 30px; flex-wrap: wrap; justify-content: center; align-items: flex-start; }
54
- .col { display: flex; flex-direction: column; align-items: center; gap: 11px; }
55
- .col-label { font-size: 11px; letter-spacing: .6px; text-transform: uppercase; color: #9aa0ad; font-weight: 600; }
56
- ```
45
+ ## Framless iPhone screen — CSS conventions
57
46
 
58
- **PORTRAIT phone (the common case):**
47
+ Mobile figs are **FRAMLESS**: render the screen contents ONLY, at the device's exact logical
48
+ screen size, with **NO drawn phone bezel / outer border / rounded outer frame / notch or
49
+ Dynamic-Island cutout** — the Figura viewer draws the physical device frame + island around
50
+ your output, so a fig fits a real phone 1:1. Lock the body to the screen size and emit a
51
+ `<meta name="figura:artboard" content="WxH">` with that size.
59
52
 
60
- ```css
61
- .device-port { position: relative; width: 300px; height: 640px; background: #000; border-radius: 44px; border: 6px solid #2a2a2a; box-shadow: 0 18px 40px rgba(0,0,0,.5), inset 0 0 0 2px #000; overflow: hidden; }
62
- .device-port .island { position: absolute; top: 9px; left: 50%; transform: translateX(-50%); width: 92px; height: 26px; background: #000; border-radius: 15px; z-index: 8; }
63
- ```
53
+ | Orientation | Logical screen | Safe area top | Safe area bottom |
54
+ |-------------|----------------|---------------|------------------|
55
+ | Portrait | 393×852 | 59px | 34px |
56
+ | Landscape | 852×393 | 59px | 21px |
64
57
 
65
- **LANDSCAPE phone (wide window, side island):**
58
+ **Reset + body (lock to the screen size, fill edge-to-edge):**
66
59
 
67
60
  ```css
68
- .device-land { position: relative; width: 660px; height: 318px; background: #000; border-radius: 44px; border: 6px solid #2a2a2a; box-shadow: 0 24px 50px rgba(0,0,0,.55), inset 0 0 0 2px #000; overflow: hidden; }
69
- .device-land .island { position: absolute; left: 9px; top: 50%; transform: translateY(-50%); width: 26px; height: 96px; background: #000; border-radius: 16px; z-index: 8; }
61
+ * { box-sizing: border-box; margin: 0; padding: 0; }
62
+ body { margin: 0; width: 393px; height: 852px; background: #0A0A0A; color: #F4F4F6; font-family: Inter, -apple-system, BlinkMacSystemFont, system-ui, sans-serif; overflow: hidden; } /* swap W/H for landscape */
70
63
  ```
71
64
 
72
- **Screen + OPTIONAL media area** (include `.preview` only if the design needs a hero/media
73
- region — size it to the design, any aspect ratio; there is no fixed film ratio):
65
+ **Screen surface + OPTIONAL media area** (include `.preview` only if the design needs a
66
+ hero/media region — size it to the design, any aspect ratio; there is no fixed film ratio):
74
67
 
75
68
  ```css
76
- .screen { position: absolute; inset: 0; background: #0E0E10; color: #F4F4F6; overflow: hidden; }
69
+ .screen { position: absolute; inset: 0; background: #0A0A0A; color: #F4F4F6; overflow: hidden; }
77
70
  .preview { position: absolute; background: #18181b; } /* optional media/hero region */
78
71
  .scrim { position: absolute; inset: 0; background: linear-gradient(to bottom, rgba(0,0,0,.45), transparent 22%, transparent 70%, rgba(0,0,0,.55)); }
79
72
  ```
@@ -88,31 +81,35 @@ region — size it to the design, any aspect ratio; there is no fixed film ratio
88
81
  .badge { min-width: 15px; height: 15px; padding: 0 4px; background: var(--accent, #4C8DFF); border-radius: 999px; font-size: 9px; font-weight: 700; color: #fff; display: flex; align-items: center; justify-content: center; } /* a quiet count, never a red alarm */
89
82
  ```
90
83
 
91
- **Annotation chips — yellow callouts pinned over the frame (label intent):**
84
+ **Annotation chips — yellow callouts pinned over the screen (label intent):**
92
85
 
93
86
  ```css
94
87
  .annot { position: absolute; font-size: 9px; font-weight: 500; color: #ffe066; background: rgba(0,0,0,.82); border: 1px solid rgba(255,224,102,.45); padding: 3px 6px; border-radius: 4px; pointer-events: none; z-index: 20; max-width: 180px; line-height: 1.3; }
95
88
  ```
96
89
 
97
- **Inline SVG icons:** define `<symbol>` elements in a hidden `<svg>` at the top of `<body>`
98
- and reference with `<use href="#id"/>`. Stroke icons (`stroke="currentColor"`,
99
- `stroke-width` ~1.6).
90
+ **Inline SVG icons:** Inline SVG icons — define <symbol> elements in a hidden <svg> at the top of <body> and reference with <use href="#id"/>. Stroke icons (currentColor stroke, width ~1.6). NO emojis anywhere.
100
91
 
101
92
  ## Hard rules (non-negotiable)
102
93
 
103
- - Lay the screen out to fill the chosen orientation; use whatever media/aspect ratios the
104
- design needs there is no fixed aspect-ratio requirement.
105
- - NO emojis anywhere. Every icon is inline stroke SVG.
94
+ - Render the screen contents at EXACTLY the chosen orientation's logical size with NO device
95
+ frame/bezel/notchthe Figura viewer draws the physical phone frame around your output.
96
+ - Emit `<meta name="figura:artboard" content="WxH">` with the exact logical screen size.
97
+ - NO emojis anywhere. Represent every icon as inline stroke SVG via <symbol>/<use> (currentColor, width ~1.6).
106
98
  - Hand-roll every control with the pill/capsule convention above — do not rely on system/OS
107
99
  chrome or platform glass effects.
108
- - Plain HTML + INLINE CSS ONLY (a single `<style>` in `<head>`). No external stylesheets,
109
- no network fonts, no frameworks.
110
- - ABSOLUTELY NO `<script>` tags and NO `on*` event handlers. Fully static; renders state
111
- with CSS alone (runs in a sandboxed iframe).
100
+ - Plain HTML + INLINE CSS ONLY (a single <style> in <head>). No external stylesheets, no fonts loaded over the network, no frameworks.
101
+ - ABSOLUTELY NO <script> tags and NO on* event handlers (onclick, onload, etc.). The fig must be fully static and render its state with CSS alone. It runs in a sandboxed iframe.
102
+
103
+ ## Visual quality (intentional and crafted never generic "AI" UI)
104
+
105
+ - Color: 3–5 colors total, drawn from the design system — one primary, 2–3 neutrals, 1–2 accents; never exceed 5. If you override a background color, also override its text color for contrast.
106
+ - Type: at most 2 font families (headings + body). No decorative fonts for body. Clear hierarchy.
107
+ - NEVER add filler decoration: no gradient circles, blurry blobs, or abstract shapes as ornament. Avoid gradients unless the design system calls for them; prefer solid colors.
108
+ - Real, specific content (no lorem ipsum, no "Title goes here"). Generous, consistent spacing. A clear visual identity — not a bland gray/white template.
112
109
 
113
110
  ## Document contract
114
111
 
115
- - Respond with a complete HTML document starting at `<!DOCTYPE html>`.
116
- - `<title>` = short screen name.
117
- - `<meta name="figura:context" content="">` = one-line design-intent summary.
112
+ - Respond with ONLY a complete HTML document. Start at <!DOCTYPE html>. No markdown code fences, no backticks, no prose before or after.
113
+ - Set <title> to a short, descriptive fig title.
114
+ - Include a meta tag summarizing the design intent: <meta name="figura:context" content="...">.
118
115
  - An `<h1>` naming the screen.
@@ -10,11 +10,13 @@ self-contained HTML document and persists it to the Figura team via `bunx figura
10
10
 
11
11
  **Resolved scope:** area `settings`, orientation `portrait`.
12
12
 
13
- **Generated document:** a `.device-port` iPhone frame with a top dynamic-island. Inside, a
14
- `.screen`: a "Settings" header + a search field, then grouped rows (Account, Notifications,
13
+ **Generated document:** a FRAMLESS portrait screen rendered at exactly 393×852 (no drawn phone
14
+ bezel the viewer frames it), with `<meta name="figura:artboard" content="393x852">`. Inside,
15
+ a `.screen`: a "Settings" header + a search field, then grouped rows (Account, Notifications,
15
16
  Privacy) — each row a label + an inline stroke-SVG glyph + a control (chevron, or a CSS-only
16
17
  toggle in the accent color). A sticky bottom save bar ("3 unsaved changes" + Save / Discard)
17
- pinned over the screen. No `.preview` media area (this screen has none).
18
+ pinned over the screen, kept clear of the ~34px bottom safe area. No `.preview` media area
19
+ (this screen has none).
18
20
 
19
21
  A `.annot` chip points at the toggles: "active = accent #4C8DFF". `<title>` = "Settings —
20
22
  account & notifications"; `<meta name="figura:context">` summarizes the unsaved-changes