@rogieking/figui3 8.9.9 → 8.9.11

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.
@@ -0,0 +1,184 @@
1
+ ---
2
+ name: fig-editor
3
+ description: >-
4
+ Guides FigUI3 editor bundle (`fig-editor.js` / `fig-editor.css`): fig-select,
5
+ fig-select-options, fig-select-option, fig-fill-picker, and fig-interpolation-swatch.
6
+ Use when building custom selects, grouped option lists, sticky separators, ghost
7
+ selects, fill picker dialogs, or interpolation swatches. Playground: /figui3#select
8
+ and /figui3#fill-picker with the Full editor toggle. Requires fig.js.
9
+ user-invocable: false
10
+ ---
11
+
12
+ # FigUI3 editor (`fig-editor.js`)
13
+
14
+ Custom select and full fill-picker. Not in core `fig.js`.
15
+
16
+ Canonical examples: `/figui3#select`, `/figui3#fill-picker` (enable **Full editor** in the playground theme menu). Markup: `playground/src/data/figui3Sections.ts`. Attrs: `playground/src/lib/attributeRules.ts`.
17
+
18
+ See also: `figui3` (core), `fig-lab` (`propskit-select` wraps this select).
19
+
20
+ ## Install
21
+
22
+ ```js
23
+ import "@rogieking/figui3/fig.css";
24
+ import "@rogieking/figui3/fig.js";
25
+ import "@rogieking/figui3/fig-editor.css";
26
+ import "@rogieking/figui3/fig-editor.js";
27
+ ```
28
+
29
+ `fig-editor.js` imports `fig.js` and `fig-lab.js`. Still import `fig-editor.css`. Import `fig-lab.css` if lab visuals are needed.
30
+
31
+ Registered tags:
32
+
33
+ | Tag | Role |
34
+ |---|---|
35
+ | `fig-select` | Trigger + popup listbox |
36
+ | `fig-select-options` | Scrollable panel (`slot="panel"`), overflow chevrons |
37
+ | `fig-select-option` | Option (`value`, `label`, `selected`, `disabled`) |
38
+ | `fig-fill-picker` | Full fill editor dialog (solid, gradient, image, video, webcam, custom modes) |
39
+ | `fig-interpolation-swatch` | Gradient interpolation preview swatch |
40
+
41
+ ## `fig-select`
42
+
43
+ Use this instead of `fig-dropdown` for Figma-style menus (rich options, groups, overflow).
44
+
45
+ ### Markup
46
+
47
+ Author options in `fig-select-options`, or pass `options` (comma / newline / JSON — same as `fig-options`).
48
+
49
+ ```html
50
+ <fig-select value="center" label="Align">
51
+ <fig-select-options>
52
+ <fig-select-option value="left">Left</fig-select-option>
53
+ <fig-select-option value="center">Center</fig-select-option>
54
+ <fig-select-option value="right">Right</fig-select-option>
55
+ </fig-select-options>
56
+ </fig-select>
57
+
58
+ <fig-select label="Align" value="Center" options="Left,Center,Right"></fig-select>
59
+ ```
60
+
61
+ Rich options: put extra content inside `fig-select-option` and set `label` for the closed trigger text. Use `slot="panel"` on `fig-select-options` when the panel is slotted (playground “verbose” / AI models examples).
62
+
63
+ ```html
64
+ <fig-select value="uuid" label="Version" full>
65
+ <fig-select-options slot="panel">
66
+ <fig-select-option value="uuid" label="Current (Version 6)">
67
+ <strong>Current</strong>
68
+ <span>Version 6</span>
69
+ </fig-select-option>
70
+ </fig-select-options>
71
+ </fig-select>
72
+ ```
73
+
74
+ ### Attributes
75
+
76
+ | Attr | Notes |
77
+ |---|---|
78
+ | `value` | Selected option value |
79
+ | `label` | Closed-state / aria label |
80
+ | `options` | Generated options if no authored `fig-select-option` |
81
+ | `variant` | `ghost` — no border, hover `--figma-color-bg-secondary` |
82
+ | `full` | Stretch width |
83
+ | `disabled` | Presence |
84
+ | `position` | `bottom left` (default), `bottom right`, `top left`, `top right`, `bottom center`, `top center` |
85
+ | `offset`, `closedby`, `open` | Forwarded to internal `fig-popup` |
86
+
87
+ Same `variant="ghost"` exists on core `fig-dropdown`.
88
+
89
+ ### Groups and sticky separators
90
+
91
+ ```html
92
+ <fig-select-options>
93
+ <fig-separator label="Darken" sticky></fig-separator>
94
+ <fig-select-option value="multiply">Multiply</fig-select-option>
95
+ <fig-separator label="Lighten" sticky></fig-separator>
96
+ <fig-select-option value="screen">Screen</fig-select-option>
97
+ </fig-select-options>
98
+ ```
99
+
100
+ - First separator in a panel is auto-`borderless`.
101
+ - `sticky` on `fig-separator` pins the label while scrolling.
102
+ - Overflow adds `.overflow-start` / `.overflow-end` on `fig-select-options`. Sticky `top` sits below the overflow chevron (`--fig-vertical-overflow-size`).
103
+
104
+ Playground: `#select` examples `grouped`, `many-options`; AI models list in `figui3Sections.ts` (`aiModelSelectMarkup()`). Sticky toggle is a playground inspector concern (`?_sticky=1`), not a select host attr.
105
+
106
+ ### Behavior
107
+
108
+ - Internal popup uses `popover="manual"` (top layer) so the list positions correctly inside `fig-popup variant="popover"` (CSS `filter` containing block).
109
+ - List `min-width` matches the trigger; `max-width` is `min(20rem, calc(100vw - 1rem))`.
110
+ - Overflow: top/bottom chevron buttons, not a native scrollbar.
111
+ - Keyboard: open, arrow, typeahead, Escape.
112
+ - `optionhover` fires on pointer-over with the option value in `event.detail` without changing selection.
113
+ - `input` / `change` on value commit. Do not `stopPropagation` on option click (React light-DOM handlers must run).
114
+
115
+ ### `fig-select` vs `fig-dropdown` vs `propskit-select`
116
+
117
+ - `fig-dropdown` (core): native `<select>`.
118
+ - `fig-select` (this skill): custom listbox.
119
+ - `propskit-select` (lab): labeled `fig-field` wrapping `fig-select` (falls back if select is not registered).
120
+
121
+ ## `fig-fill-picker`
122
+
123
+ Full fill editor. Core `fig-input-color` / `fig-input-fill` auto-open it when registered. Standalone:
124
+
125
+ ```html
126
+ <fig-fill-picker value='{"type":"solid","color":"#FF5733"}'>
127
+ <fig-swatch></fig-swatch>
128
+ </fig-fill-picker>
129
+ ```
130
+
131
+ Playground: `#fill-picker` — `all-modes`, `solid`, `gradient`, `image`, `video`, `webcam`.
132
+
133
+ | Attr | Notes |
134
+ |---|---|
135
+ | `value` | JSON fill object or string |
136
+ | `alpha` | `"true"` default; hide alpha with `"false"` |
137
+ | `mode` | Lock modes: `solid`, `gradient`, `image`, `video`, `webcam`, comma-separated, plus custom names |
138
+ | `disabled` | Presence |
139
+
140
+ Events: `input` / `change` with fill payload in `detail`.
141
+
142
+ Value shapes:
143
+
144
+ ```txt
145
+ solid { type, colorSpace, color, alpha, hsv }
146
+ gradient { type, colorSpace, gradient, css }
147
+ image { type, colorSpace, image }
148
+ video { type, colorSpace, video }
149
+ webcam { type, colorSpace, image: { url: snapshot, scaleMode, scale } }
150
+ custom { type: <modeName>, ...payload }
151
+ ```
152
+
153
+ `fig-input-color` expects solid data (`detail.color`, optional `detail.alpha`). Keep legacy `value` / `hex` / `rgba` on color input events.
154
+
155
+ Do not use `picker` or `picker-anchor` on `fig-input-color`. Forward picker chrome with `picker-*` (e.g. `picker-dialog-position`).
156
+
157
+ ### Custom modes (vanilla)
158
+
159
+ ```html
160
+ <fig-fill-picker mode="solid,tokens">
161
+ <fig-swatch></fig-swatch>
162
+ <div slot="mode-tokens" label="Tokens">Token UI</div>
163
+ </fig-fill-picker>
164
+ ```
165
+
166
+ Child `slot="mode-<name>"` plus `<name>` in `mode`. Custom content must dispatch `input` / `change` with `detail` so the picker stores mode data.
167
+
168
+ ### Custom modes (React)
169
+
170
+ 1. Include the mode name in `mode`.
171
+ 2. Listen for `modeready`; mount into `e.detail.container`.
172
+ 3. Do not reparent React-owned DOM after render.
173
+ 4. One React root per container; `unmount()` on host unmount; remove `modeready` listeners.
174
+
175
+ ## `fig-interpolation-swatch`
176
+
177
+ Preview for gradient interpolation (linear or polar hue arc). Playground attrs: `size="small|large"`. Used inside the fill picker gradient UI; can be used standalone with a gradient `value`.
178
+
179
+ ## Maintainer notes
180
+
181
+ - Implementation: `fig-editor.js`, styles: `fig-editor.css`
182
+ - Do not move fill-picker or select into `fig.js`
183
+ - Nested overlay positioning: keep `popover="manual"` on the select popup
184
+ - Tests: `tests/figui/component-contracts.spec.ts` (ghost variant, sticky separator vs overflow-start)
@@ -0,0 +1,46 @@
1
+ # FigUI3 editor API reference
2
+
3
+ Playground: `/figui3#select`, `/figui3#fill-picker` (Full editor on).
4
+ Rules: `playground/src/lib/attributeRules.ts`.
5
+
6
+ ## `fig-select-option`
7
+
8
+ Observed: `value`, `disabled`, `selected`, `label`.
9
+
10
+ - `value` falls back to trimmed `textContent` if the attr is omitted
11
+ - `label` is the closed-trigger string when option content is rich
12
+ - `role="option"`, `aria-selected`, `aria-disabled`
13
+
14
+ ## `fig-select-options`
15
+
16
+ - Auto `slot="panel"` if missing
17
+ - Unwraps a legacy nested `fig-chooser`
18
+ - First `fig-separator` child gets `borderless`
19
+ - Overflow nav buttons: `data-fig-select-nav`, classes `overflow-start` / `overflow-end`
20
+ - Methods: `syncOverflow()`, `scrollToOption(option, behavior)`
21
+
22
+ ## `fig-select` observed
23
+
24
+ `value`, `disabled`, `label`, `options`, `position`, `offset`, `closedby`, `open`, `variant`
25
+
26
+ Position enum (playground):
27
+
28
+ - `bottom left`, `bottom right`, `top left`, `top right`, `bottom center`, `top center`
29
+
30
+ Events: `input`, `change`, `optionhover`.
31
+
32
+ ## Fill picker modes
33
+
34
+ Built-in: `solid`, `gradient`, `image`, `video`, `webcam`.
35
+
36
+ Gradient spaces: `srgb`, `srgb-linear`, `display-p3`, `oklab`, `oklch`, `hsl`.
37
+ Hue interpolations (oklch/hsl): `shorter`, `longer`, `increasing`, `decreasing`.
38
+
39
+ `fig-interpolation-swatch` observed: `value` (gradient payload). Size via attr/CSS.
40
+
41
+ ## Color input + picker
42
+
43
+ When picker is registered, `fig-input-color` / `fig-input-fill` open it on interaction.
44
+
45
+ - Do not emit `input` from programmatic `value` writes (React loop avoidance)
46
+ - Custom mode JSON: `type` = mode name, remaining keys in payload
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: fig-lab
3
+ description: >-
4
+ Guides FigUI3 lab bundle (`fig-lab.js` / `fig-lab.css`): experimental propskit-*
5
+ property controls, fig-canvas-control, fig-input-angle, fig-reorder, and AI composer
6
+ components (fig-ai-prompt, fig-ai-context, fig-chat-message, fig-attachment). Use when
7
+ building labeled property panels, canvas handles, oscillators, reorder lists, or AI
8
+ chat UIs. Playground: /propskit/lab. APIs are unstable.
9
+ user-invocable: false
10
+ ---
11
+
12
+ # FigUI3 lab (`fig-lab.js`)
13
+
14
+ Experimental components. May change or be removed without notice.
15
+
16
+ Canonical examples: `/propskit/lab` (`playground/src/data/labSections.ts`).
17
+ Attrs: `playground/src/lib/attributeRules.ts`.
18
+ `/propskit` (raw `fig-field` rows) is the `propkit` skill, not this one.
19
+
20
+ ## Install
21
+
22
+ ```js
23
+ import "@rogieking/figui3/fig.css";
24
+ import "@rogieking/figui3/fig.js";
25
+ import "@rogieking/figui3/fig-lab.css";
26
+ import "@rogieking/figui3/fig-lab.js";
27
+ ```
28
+
29
+ `propskit-select` prefers `fig-select` (import `fig-editor.js` + `fig-editor.css`). Without editor, it falls back.
30
+
31
+ `fig-editor.js` already imports `fig-lab.js`; lab **CSS** is still required for lab visuals.
32
+
33
+ ## Catalog
34
+
35
+ Playground hashes: `/propskit/lab#{id}`.
36
+
37
+ ### Propskit (labeled property controls)
38
+
39
+ Full-surface `fig-field` wrappers. Prefer these over hand-rolled label+control rows when building lab/property UIs.
40
+
41
+ Shared:
42
+
43
+ - `label`, `direction` (`horizontal` default for most), `size` (`""` | `large`), `disabled`
44
+ - `default` — reset target (may differ from initial `value`)
45
+ - Right-click **Reset** menu; `resetToDefault()`
46
+ - `propskit-slider` also double-click resets
47
+ - Forward remaining attrs to the inner control
48
+
49
+ | Tag | Playground | Inner control | Notes |
50
+ |---|---|---|---|
51
+ | `propskit-switch` | `#propskit-switch` | `fig-switch` | `checked`, `default` boolean |
52
+ | `propskit-color` | `#propskit-color` | `fig-input-color` | `alpha` default true |
53
+ | `propskit-gradient` | `#propskit-gradient` | `fig-input-gradient` | `edit`, `mode="handle\|tip"` |
54
+ | `propskit-select` | `#propskit-select` | `fig-select` | `options` or slotted `fig-select-options` |
55
+ | `propskit-text` | `#propskit-text` | `fig-input-text` | `type`, `readonly` |
56
+ | `propskit-number` | `#propskit-number` | `fig-input-number` | `min`, `max`, `step`, `precision`, `units`, `steppers` |
57
+ | `propskit-slider` | `#propskit-slider` | `fig-slider` | `type` range/hue/delta/stepper/opacity; `elastic` default true |
58
+ | `propskit-position` | `#propskit-position` | two numbers | `x`, `y`, `units="percent"` |
59
+ | `propskit-color-point` | `#propskit-color-point` | color + position | JSON `value`; `collapsible`, `open` |
60
+ | `propskit-point-radius` | `#propskit-point-radius` | position + radius | JSON `value` |
61
+ | `propskit-point-radius-angle` | `#propskit-point-radius-angle` | + angle | JSON `value` |
62
+ | `propskit-point-point` | `#propskit-point-point` | start/end | `{ x, y, x2, y2 }` |
63
+ | `propskit-group` | `#propskit-group` | group chrome | `name`, `open`, `show-reset` |
64
+ | `propskit-oscillator` | `#oscillator` | waveform editor | JSON `waves`; `edit`, `precision`, `aspect-ratio` |
65
+
66
+ ```html
67
+ <propskit-slider
68
+ label="Opacity"
69
+ direction="horizontal"
70
+ type="opacity"
71
+ value="100"
72
+ default="100"
73
+ min="0"
74
+ max="100"
75
+ units="%"
76
+ ></propskit-slider>
77
+
78
+ <propskit-select label="Blend" value="multiply" options="Normal,Multiply,Screen"></propskit-select>
79
+ ```
80
+
81
+ Rich select (requires editor):
82
+
83
+ ```html
84
+ <propskit-select label="Space" value="oklab">
85
+ <fig-select-options slot="panel">
86
+ <fig-select-option value="srgb" label="Classic">…</fig-select-option>
87
+ </fig-select-options>
88
+ </propskit-select>
89
+ ```
90
+
91
+ ### Canvas and spatial
92
+
93
+ | Tag | Playground | Notes |
94
+ |---|---|---|
95
+ | `fig-canvas-control` | `#canvas-control` | Overlay on a positioned parent. `type`: `point` (default), `color`, `point-radius`, `point-radius-angle`, `point-point`. `value` JSON `{x,y,radius?,angle?,x2?,y2?}`. `snapping="modifier\|true\|false"`, `name`, `tooltips`, `color` |
96
+ | `fig-input-angle` | `#angle` | Dial + optional text. `text`, `dial` default true, `rotations`, `min`/`max`/`units` |
97
+ | `fig-reorder` | `#reorder` | `display:contents` wrapper; drag-reorders **direct children**. `axis="vertical\|horizontal"`, `handle` CSS selector when rows contain nested controls. Event: `reorder` `{ oldIndex, newIndex, item }` |
98
+
99
+ ```html
100
+ <div style="position:relative; aspect-ratio:1; width:100%">
101
+ <fig-canvas-control
102
+ type="point-radius-angle"
103
+ name="Position"
104
+ value='{"x":50,"y":50,"radius":60,"angle":45}'
105
+ snapping="modifier"
106
+ ></fig-canvas-control>
107
+ </div>
108
+ ```
109
+
110
+ Omit `handle` on `fig-reorder` to drag whole rows. Set `handle` when children contain sliders/handles so those stay interactive.
111
+
112
+ ### AI composer (presentation)
113
+
114
+ These are layout shells. Wire behavior yourself.
115
+
116
+ | Tag | Playground | Notes |
117
+ |---|---|---|
118
+ | `fig-ai-prompt` | `#ai-prompt` | Composer: slot `fig-input-text`, `fig-footer`, buttons, optional `fig-select` |
119
+ | `fig-ai-context` | (with prompt) | Open area above prompt for attachments/status |
120
+ | `fig-chat-message` | `#ai-chat-message` | `from="user\|agent"`. Optional `fig-avatar`, `fig-attachments` |
121
+ | `fig-attachments` / `fig-attachment` | `#ai-attachments` | `src`, `name`, `value`, `removable` default true |
122
+
123
+ ```html
124
+ <fig-chat-message from="user">
125
+ Create a settings panel.
126
+ <fig-attachments aria-label="Message attachments">
127
+ <fig-attachment value="settings" name="settings.png" src="…" removable="false"></fig-attachment>
128
+ </fig-attachments>
129
+ <fig-avatar name="Rogie King"></fig-avatar>
130
+ </fig-chat-message>
131
+ <fig-chat-message from="agent">
132
+ <fig-shimmer><span>Thinking…</span></fig-shimmer>
133
+ </fig-chat-message>
134
+ ```
135
+
136
+ ## Control choice
137
+
138
+ | Intent | Use |
139
+ |---|---|
140
+ | Labeled boolean | `propskit-switch` |
141
+ | Labeled continuous number | `propskit-slider` |
142
+ | Labeled exact number | `propskit-number` |
143
+ | Labeled text | `propskit-text` |
144
+ | Labeled discrete list | `propskit-select` (not `fig-dropdown`) |
145
+ | Labeled color / gradient | `propskit-color` / `propskit-gradient` |
146
+ | X/Y | `propskit-position` |
147
+ | Spatial on a canvas | `fig-canvas-control` |
148
+ | Angle | `fig-input-angle` |
149
+ | Section of props | `propskit-group` or core `fig-group` |
150
+ | Reorder rows | `fig-reorder` |
151
+
152
+ Raw `fig-field` + core control is still valid (see `propkit` skill / `/propskit`).
153
+
154
+ ## Maintainer notes
155
+
156
+ - Implementation: `fig-lab.js`, styles: `fig-lab.css`
157
+ - Keep lab out of `fig.js`
158
+ - Playground loads lab only on `/propskit` and `/propskit/lab`
159
+ - Update `labSections.ts` + `attributeRules.ts` with API changes
@@ -0,0 +1,67 @@
1
+ # FigUI3 lab API reference
2
+
3
+ Playground: `/propskit/lab#{id}`. Rules: `playground/src/lib/attributeRules.ts`.
4
+
5
+ ## Propskit reset
6
+
7
+ - `default` attr stores the reset value
8
+ - `resetToDefault()` on the host
9
+ - Context menu item `reset-default`
10
+ - Slider: double-click also resets
11
+ - Equality helpers treat booleans and JSON objects
12
+
13
+ ## `propskit-select`
14
+
15
+ Observed: `label`, `direction`, `aria-label`, `options`, `value`.
16
+
17
+ Uses `fig-select` when `fig-select`, `fig-select-options`, and `fig-select-option` are registered; otherwise a fallback control.
18
+
19
+ Options attr: JSON array, comma, or newline. Authored `fig-select-options slot="panel"` wins for rich menus.
20
+
21
+ ## `propskit-slider`
22
+
23
+ Playground attrs: `type` (`range`, `hue`, `delta`, `stepper`, `opacity`), `color`, `label`, `default`, `units`, `elastic` (default true), `size`, `steppers`, `disabled`.
24
+
25
+ Inner `fig-slider` still needs `min` / `max` / `step` / `value` as forwarded attrs.
26
+
27
+ ## Point JSON shapes
28
+
29
+ ```json
30
+ {"x":50,"y":50}
31
+ {"x":50,"y":50,"radius":60}
32
+ {"x":50,"y":50,"radius":60,"angle":45}
33
+ {"x":10,"y":10,"x2":90,"y2":90}
34
+ ```
35
+
36
+ Color-point combines a color payload with `x`/`y`.
37
+
38
+ Collapsible point groups: `collapsible` and `open` default true (string booleans).
39
+
40
+ ## `fig-canvas-control`
41
+
42
+ Observed: `type`, `value`, `color`, `name`, `tooltips`, `disabled`, `drag-surface`, `snapping`.
43
+
44
+ Parent must be `position: relative` (or similar) so the control can fill it. Playground wraps in an aspect-ratio box.
45
+
46
+ Types: `point`, `color`, `point-radius`, `point-radius-angle`, `point-point`.
47
+
48
+ ## `fig-input-angle`
49
+
50
+ Observed include `value`, `precision`, `text`, `min`, `max`, `units`, `dial`, plus `rotations`.
51
+
52
+ `dial` defaults true. `text="true"` shows the numeric field.
53
+
54
+ ## `fig-reorder`
55
+
56
+ Observed: `axis`, `handle`, `disabled`.
57
+
58
+ Event `reorder`: `{ oldIndex, newIndex, item }`.
59
+
60
+ Nested drag is ignored for sliders, handles, canvas controls, and most propskit spatial controls so inner gestures still work. If a row is still stolen, set `handle` to a drag-affordance selector.
61
+
62
+ ## AI shells
63
+
64
+ `fig-ai-prompt`, `fig-ai-context`, `fig-chat-message` are empty custom elements (presentation CSS only).
65
+
66
+ `fig-attachment` observed: `src`, `name`, `value`, `removable`, `disabled`.
67
+ `fig-chat-message`: `from="agent|user"`.