@gemboss/ui 0.21.0 → 0.30.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 (57) hide show
  1. package/CHANGELOG.md +328 -0
  2. package/README.md +433 -24
  3. package/catalog/editor.html +426 -10
  4. package/catalog/index.html +907 -33
  5. package/gemboss-ui.css +3 -3
  6. package/package.json +23 -3
  7. package/parts/components.css +5 -0
  8. package/parts/core.css +5 -0
  9. package/parts/shell.css +5 -0
  10. package/src/action-card.css +1 -0
  11. package/src/action-card.tsx +149 -0
  12. package/src/app-shell.css +1 -1
  13. package/src/assistant/chat-markdown.ts +279 -0
  14. package/src/assistant/chat-turns.ts +120 -0
  15. package/src/assistant/chat.tsx +871 -0
  16. package/src/assistant/engine-mark-art.ts +45 -0
  17. package/src/assistant/engine-mark.tsx +39 -0
  18. package/src/assistant/engine.tsx +697 -0
  19. package/src/assistant/host.tsx +3 -1
  20. package/src/assistant/index.ts +6 -0
  21. package/src/assistant-chat.css +1 -0
  22. package/src/assistant-engine.css +1 -0
  23. package/src/auth.css +1 -1
  24. package/src/auth.tsx +18 -2
  25. package/src/chat-composer.css +1 -1
  26. package/src/chat-plain.js +6 -0
  27. package/src/chat.css +1 -1
  28. package/src/collections.tsx +1 -0
  29. package/src/dialog.tsx +3 -135
  30. package/src/editor/controls.tsx +1 -0
  31. package/src/editor/frame.tsx +12 -4
  32. package/src/editor/index.ts +10 -1
  33. package/src/editor/layers.tsx +136 -0
  34. package/src/editor/panel.tsx +1 -0
  35. package/src/editor/panes.tsx +4 -3
  36. package/src/editor/sortable.tsx +79 -0
  37. package/src/editor/tree.tsx +61 -0
  38. package/src/editor-layers-plain.js +1 -0
  39. package/src/editor.css +1 -1
  40. package/src/engine-plain.js +1 -0
  41. package/src/index-table.tsx +1 -0
  42. package/src/index.ts +8 -3
  43. package/src/kit-plain.css +1 -1
  44. package/src/kit-plain.js +1 -1
  45. package/src/launcher.tsx +1 -0
  46. package/src/menu-layer.ts +9 -2
  47. package/src/minichart.css +1 -0
  48. package/src/minichart.tsx +165 -0
  49. package/src/portal.tsx +1 -0
  50. package/src/reactions.tsx +1 -0
  51. package/src/resource-list-select.tsx +1 -0
  52. package/src/search-dialog.tsx +146 -0
  53. package/src/selection.tsx +1 -0
  54. package/src/setup-guide.css +1 -0
  55. package/src/setup-guide.tsx +116 -0
  56. package/src/theme-script.ts +10 -0
  57. package/src/theme.tsx +5 -5
package/CHANGELOG.md CHANGED
@@ -2,6 +2,329 @@
2
2
 
3
3
  Versions up to 0.9.0 are described in the README, each in the section that introduced it.
4
4
 
5
+ ## 0.30.2 (09/10/2026)
6
+
7
+ The admin's chat moves onto the package's composer and engine picker (gemboss admin, assistant sync).
8
+
9
+ - `ChatComposer` `canSend`: overrides the empty-box rule for the action. The admin sends a reference picture with no words,
10
+ and holds send while an upload runs, without disabling the text box. React only: `chat-plain.js` sits at its weight
11
+ ceiling (8698 B gzip) and no plain-HTML surface needs it yet.
12
+ - `ChatComposer` `stopDisabled`: Stop stays in the slot, disabled, while a stop is on its way (the admin's turn can take
13
+ seconds to abort; a second press was a silent no-op).
14
+ - chat-composer.css / chat.css: the admin's own engine chip rules are deleted (`.gemboss-ec-enginepick`, `-enginepick-btn`,
15
+ `-chiplabel`, `-chipsub`, `-chipmark` and their density and 44px-target rules). The admin now draws `EnginePicker` like
16
+ every surface (`gb-engine-*`, assistant-engine.css); nothing in the fleet wears those classes any more.
17
+
18
+ ## 0.30.1 (09/10/2026)
19
+
20
+ - `LayerList` / `SortableLayerList` under a finger (`pointer: coarse`): the row's action buttons are 44x44 (were 26x26,
21
+ the mouse size) and the row's pick button is at least 44px tall. Where the name and the buttons do not fit side by side
22
+ (the 300px Layers drawer), the row wraps and the buttons take a line of their own, so a name is never drawn under them.
23
+ Measured on the funnel canvas's section list at 768 with touch. Mouse sizes are unchanged.
24
+
25
+ ## 0.30.0 (09/10/2026)
26
+
27
+ **What gemform had and the package did not** (Chris 08/10/2026: "cái page login của gemform cái background bên dưới logo
28
+ cũng đẹp… trong gemform có cái gì mà gemboss ui không có thì chuẩn hoá đăng ký vào gemboss ui"). gemform
29
+ (gemlabs `toolkits/gemform`) was inventoried screen by screen; three things in it were shared patterns the package lacked.
30
+ Everything else it draws (shell, KPIs, cards, tables with bulk actions, filters, pager, switches, banners, key-value
31
+ lists, skeletons) the package already has, and gemform moves onto those. README "The sign-in screen", "Mini charts",
32
+ "Setup guide".
33
+
34
+ Added
35
+ - `AuthLayout backdrop`: `"facet"` (default, the markup and pixels of 0.29.2), `"grid"` (gemform's ground: a 34px iris
36
+ grid fading out from the panel's top-left corner, on an ink gradient), `"none"`. Tokens `--gb-panel-grid-bg`,
37
+ `--gb-panel-grid-line`, `--gb-panel-grid-size`, `--gb-panel-grid-fade` on `.gb-auth`; classes `gb-auth-panel--grid` /
38
+ `--none` for a page that writes the markup. Drawn only where the panel is (900 wide AND 520 tall).
39
+ - `Sparkline` + `sparklinePaths` and `ShareBar`, a new entry `minichart.css` (0.5 KB gzip). Six hand-drawn sparklines in
40
+ the fleet (admin x3, talent-radar, search-radar, gemwatcher, gemform); the baseline is 0 for counts unless fixed.
41
+ - `SetupGuide`, a new entry `setup-guide.css` (0.9 KB gzip): the admin's Setup guide (StoreChecklistCard,
42
+ `gemboss-setup-*`) with the kit's tokens, all native `<details>` (no script for plain HTML). The admin keeps its own
43
+ until it moves onto this one in a PR of its own.
44
+ - The catalog draws the sign-in screens at 1280 (at the catalog's column width the panel never showed), the grid beside
45
+ the facet, the mini charts in "Báo cáo", and a "Hướng dẫn thiết lập" section.
46
+
47
+ Neither new entry is in `gemboss-ui.css` or the core (parts gate); the admin imports none of them. The grid's rules are
48
+ in auth.css (+0.2 KB gzip there and in the bundle). `apps/admin/scripts/check-token-fallbacks-agree.mjs` now reads
49
+ minichart.css and setup-guide.css too.
50
+
51
+ Review #3925 (in this version): class names are written out (no `ui-sharebar__seg--${tone}` stem: the admin's orphan-rule
52
+ gate went red on it); ShareBar grows by share, so fractions fill the track; a sparse array's holes are gaps; SetupGuide
53
+ decides the open step once, draws a step without a body as a plain row, fills a `<progress>` instead of a styled width,
54
+ renders nothing for no steps, remembers a fold (`rememberKey`), takes a `group` name; the reduced-motion block names the
55
+ head's chevron with its own selector (`setup-guide-browser.test.tsx` reads the computed transition, with the weak
56
+ selector as negative control); the specificity test computes specificity instead of matching strings.
57
+
58
+ ## 0.29.2 (08/10/2026)
59
+
60
+ Fixes the rollout to search-radar (gemlabs #943) and content-radar (#944) found in the chat body. `chat-plain.js` does the
61
+ same as the components, held to them by chat-plain.test.ts.
62
+
63
+ - `ChatMarkdown` / `parseInline`: a bare `https://…` (and `<https://…>`) is a link again, as the surfaces' own renderers
64
+ drew it. GitHub's reading of where it ends: trailing `. , : ; ! ? _ ~ ' "` and a `)` or `]` with no partner inside the
65
+ URL go back to the sentence (linear time: a URL followed by 50 000 `)` parses in milliseconds). It also ends at `*`,
66
+ CJK text and full-width / typographic punctuation, and one glued to a word (`foohttps://…`) is not a link. Same
67
+ allowlist as a written link (`linkTarget`, then `new URL`: http(s) with a host), so `https://.`, `ftp:`, `javascript:`
68
+ stay text. New export `bareUrl`.
69
+ - `ChatMarkdown` / `parseBlocks`: `####` to `######` are headings at the bubble's lowest step (`level: 3`, an `h5` with
70
+ `gemboss-md-h3`) instead of four hashes in front of a sentence. `#######` and `#tag` stay text; a heading line with no
71
+ words (`#### `) is dropped instead of drawing an empty heading.
72
+ - `ImageHost.host` is optional: an entry with no host is the page's OWN origin (`imageHosts={[{ path: "/api/img/" }]}`),
73
+ for a root-relative picture (content-radar's `/api/img/…`). Checked after the browser's normalisation: the resolved
74
+ path must start with `path`; `../`, `%2e%2e`, `\`, an encoded slash or backslash, a double encoding (`%25`), a `;` path
75
+ parameter (`..;/`), `//host` and `/\host` are refused, and the src drawn is the resolved path. `path` must be a
76
+ directory below the root (`/x/`; `/` and `/x` are ignored). An entry WITH a host never opens relative paths.
77
+ - chat.css: the send circle is `.gemboss-ec-toolbar > button[type="submit"]` (the toolbar's own child, same specificity).
78
+ `.gemboss-ec-input button[type="submit"]` also reached the engine popup, a DOM descendant of the composer even in the
79
+ top layer, and drew a surface's Pair button there as the 44px filled circle (five surfaces wrote `type="button"` to
80
+ dodge it). Test of the forbidden combination in chat.test.tsx, and the popup measured in Chromium against the same
81
+ button outside the composer (chat-browser.test.tsx, chat-plain.test.ts), each with the old selector as its negative.
82
+
83
+ ## 0.29.1 (08/10/2026)
84
+
85
+ - `ChatThread` and plain `chat.thread`: keep following when a native scroll event from their own write-to-end arrives
86
+ after new content. The increased content gap no longer masquerades as the reader scrolling up. Wheel, key, touch and
87
+ direct programmatic moves up still unpin the thread.
88
+
89
+ ## 0.29.0 (08/10/2026)
90
+
91
+ **The assistant's chat body as components** (Chris 08/10/2026: "đồng bộ hết giao diện của AI assistant… mọi service có
92
+ panel này đồng bộ style + code dùng gemboss ui"). Until now the package drew the frame, the conversation list and the
93
+ engine picker; every surface wrote the bubbles, steps, starters and composer by hand on chat.css's classes, with its own
94
+ markdown renderer (five of them). The surface now hands in data and actions. README "The chat body".
95
+
96
+ Added
97
+ - `@gemboss/ui/assistant`: `AssistantChat` (thread + empty state + composer, from `messages`), and its pieces
98
+ `ChatThread` (role=log, follows the end only while the reader is there, aria-busy), `ChatMessage`, `ChatBubble`,
99
+ `ChatMarkdown` (the admin's parser and renderer: tables, headings demoted, fenced code that folds, safe links, pictures
100
+ only from fleet hosts, no raw HTML; `ownLink` / `renderLink` for a surface's own scheme), `ChatSteps` (rows print and
101
+ stop; kinds step / thinking / trace / steer), `ChatPending` (the one heartbeat; says its label once to a screen
102
+ reader), `ChatStarters` (they wrap), `ChatMeta` (Copy · engine · seconds · cost), `ChatError` (a fix: sentence,
103
+ command, Copy, the original folded), `ChatNote`, `ChatComposer` (a DIV, not a form; Enter sends, Shift+Enter and an IME's
104
+ Enter do not; one 44px slot that is send or stop; grows to 84px; `data-density` full → compact → icon → tight, measured),
105
+ `ChatAttach`, `PaperclipIcon`, `useComposerDensity`, `ChatLabelsProvider`, `CHAT_EN`.
106
+ - `useChatTurns()`: the conversation's state with an id per message; a turn is written BY ITS ID (`start`, `write`,
107
+ `step`, `finish`, `fail`, `note`), so a note landing mid-turn or the next turn opening cannot take its text (the
108
+ "patch the last element" bug of partner-radar #929 / content-radar #937). `bridgeTurnError(e)` + `atBridge(e, status)`:
109
+ only a failure AT the bridge (its call threw, or it answered 401: pairing code rejected) is the bridge's; its 400 / 503
110
+ and failures on the way to the server are the turn's.
111
+ - `parseBlocks`, `parseInline`, `linkTarget`, `imageSource`, `FLEET_IMAGE_HOSTS`: the markdown, testable without React.
112
+ - `EngineMark`, `ENGINE_MARK_ART`, `isEngineMarkId`: each engine's mark (from the admin).
113
+ - `EnginePicker`: `EngineChoice.mark` (the chip shows the engine's mark instead of its name when the composer is too
114
+ narrow), `EngineChoice.models` + `onPick(engine, model)` (every engine with its own models, one click picks both: the
115
+ admin's popup), `problem` (a red line on the Engine tab), `disabled` (the chip itself, while a switch is in flight).
116
+ - `assistant-chat.css`, a new entry (0.9 KB gzip), imported by the chat components; not in `gemboss-ui.css`.
117
+ chat.tsx imports chat.css and chat-composer.css too (they were "the surface links it").
118
+ - `chat-plain.js` (after `kit-plain.js`, and `engine-plain.js` when the page has the picker): `gembossUI.chat` with
119
+ `markdown`, `message`, `render`, `thread`, `steps`, `pending`, `starters`, `meta`, `error`, `note`, `composer`, `attach`,
120
+ `turnError`, `atBridge`, and `gembossUI.engineMark`. Same markup as the components, element by element (chat-plain.test.ts).
121
+ - `AssistantChat` `title` / `subtitle`: the chat's own heading where the frame has no title bar (full screen).
122
+ - `useFullChromeRoom(ref)`: how far full screen's floated buttons reach into an element (`--gb-chat-chrome-w` / `-h`,
123
+ `data-chrome`), for a surface arranging the pieces itself; `AssistantChat` uses it.
124
+ - Exports `./assistant/markdown` (the parser) and `./assistant/engine-mark-art` (the artwork): no JSX, no CSS, so a node
125
+ test or a server module imports them. The admin's `components/chat/markdown-blocks.ts` and `lib/engine-mark-art.ts`
126
+ now re-export these (one owner; the admin's tests read the package's code). A fence with a tag like `c#`, `c++`,
127
+ `objective-c` is a fence (it opened nothing, and its closing fence swallowed the rest of the answer as code).
128
+
129
+ Fixed
130
+ - Full screen: the frame's floated buttons (Back to the popup and a surface's `fullActions`) covered the first message of
131
+ a chat with no heading (content-radar /v2/assistant at 1280). `AssistantChat` measures how far they reach into it and
132
+ makes room (`data-gb-dock-chrome` on the host's button row is what it measures).
133
+ - On a phone, a `fullActions` button with no icon lost its words and showed an empty circle (assistant-chat.css).
134
+ - `BridgeNotice`'s "Open setup" was a 16px box with a 24px overlay, which a measuring tool reads as 16: its own box is now
135
+ 24px with a mouse and 44px under touch / at phone width (negative margins keep the line 16px). A long sentence no longer
136
+ wraps away from its dot (dot | sentence, the action under the sentence).
137
+ - `engine-plain.js`: the chip carries the engine's mark (`mark`) when chat-plain.js is on the page.
138
+
139
+ Review #3910 (all in this version): the log unpins at the reader's first move up (wheel, finger, keys), not 24px later, and
140
+ goes back to the end on a send or another conversation (`ChatThread pinKey`; plain: `log.toEnd()` / composer `thread`);
141
+ `turns.step` keeps a field an update leaves out; a bridge `TimeoutError` is the bridge's; clicking the pick already in
142
+ force changes nothing; a step for an engine with no mark keeps its ✓ / ✕; message keys never mix ids and positions;
143
+ plain `render()` keeps a message's element when its look did not change, the heartbeat's clock, and the folds the reader
144
+ opened; the full-screen room is measured again when the buttons themselves change size.
145
+
146
+ ## 0.28.0 (08/10/2026)
147
+
148
+ **Action card: an assistant proposes, a person applies.** The look of GemFeedback's merge and board-action cards (Chris
149
+ 08/10/2026, "follow theo gemboss ui"), so every assistant in the fleet asks before a change the same way. README "Action card".
150
+
151
+ Added
152
+ - `ActionCard`, `ActionCardSection`, `ActionCardItem`, `ActionChange` (React), `action-card.css` (its own entry
153
+ `./action-card.css`, loaded by the component; `gemboss-ui.css` unchanged).
154
+ - Catalog section "Thẻ hành động".
155
+ - `packages/ui/weight-budget.json`: a ceiling for `packages/ui/src/action-card.css`.
156
+ ## 0.27.0 (08/10/2026)
157
+
158
+ **The engine picker for plain HTML**, for gemcare's two assistants (inbox, Reports), which have no React.
159
+
160
+ Added
161
+ - `engine-plain.js` (new entry, after `kit-plain.js`): `gembossUI.enginePicker(host, options)` → `{ update, openTab, open,
162
+ close, isOpen, destroy, element, panel }` and `gembossUI.bridgeNotice(options)`. Same markup as `EnginePicker` (one
163
+ stylesheet, `assistant-engine.css`), same behaviour; a redraw patches in place. README "Plain HTML:
164
+ `gembossUI.enginePicker()`". 5.3 KB gzip minified, its own ceiling.
165
+ - `gembossUI.placeFloating(layer, anchor, end)` in kit-plain.js: the menus' placement, one plain copy (floatMenu and
166
+ engine-plain.js use it).
167
+
168
+ Changed
169
+ - `EnginePicker`: an Escape that closes the panel is marked handled (`preventDefault`) also when focus is outside the
170
+ panel. A surface whose Escape hides the assistant and checks `defaultPrevented` waits for the next one; the
171
+ window-capture markers the surfaces added (gemlabs #922, gemfeedback #66) are no longer needed. Not an Escape it does
172
+ not own: an IME composition (keyCode 229), or one in a modal opened from the panel; and focus entering such a modal
173
+ closes the panel, which would otherwise sit above it in the top layer.
174
+
175
+ ## 0.26.0 (08/10/2026)
176
+
177
+ **A tablet glyph to draw with** (the funnel canvas's "All sizes": each page at desktop, tablet and phone widths).
178
+
179
+ Added
180
+ - `TabletGlyph` in `@gemboss/ui/editor`, beside `MonitorGlyph` and `PhoneGlyph`. `DeviceToggle` draws it for its Tablet
181
+ button (it carried the same SVG inline), so there is one tablet.
182
+
183
+ ## 0.25.0 (07/10/2026)
184
+
185
+ **The assistant's engine picker** (Chris 07/10/2026): a chip in the composer row, as in the admin, that opens a popup
186
+ with Engine · Setup · Status tabs. Engine, model, effort, the bridge's status and its install command leave the chat.
187
+
188
+ Added
189
+ - `EnginePicker`, `EnginePanel`, `BridgeNotice`, `useEnginePicker` (+ types `EngineChoice`, `Choice`, `BridgeStatus`,
190
+ `BridgeSetup`, `BridgeState`, `EnginePickerTab`) in `@gemboss/ui/assistant`. README "The engine picker".
191
+ - `assistant-engine.css`, a new entry (1.9 KB gzip), imported by the component; not in `gemboss-ui.css`.
192
+ - `placeFloating(layer, anchor, end)` in `menu-layer.ts`: the menus' placement, now shared with the picker's panel
193
+ (`floatMenu` calls it; same geometry).
194
+
195
+ ## 0.24.0 (07/10/2026)
196
+
197
+ **The Studio canvas in the editors' frame** (gemboss #3856): the pieces the admin's Studio needed from the editor kit.
198
+
199
+ Added
200
+ - `EditorChatToggle` takes a `name` ("Hide <name>"; default "chat panel"): a column that is not a chat says so.
201
+
202
+ Changed
203
+ - A folded editor column (`.is-chat-collapsed`) is `visibility: hidden`: at width 0 its fields stayed in the Tab order.
204
+
205
+ Removed
206
+ - `.gemboss-pageedit-status` and `.gemboss-pageedit-kind` looks: the page editor draws Polaris' Badge, as the funnel beside it does
207
+ (`.gemboss-pageedit-status` stays on a wrapper as a hook, `data-status`, with no look).
208
+
209
+ ## 0.23.0 (07/10/2026)
210
+
211
+ **The CSS comes in parts: a core every surface loads, and entries that come with the components that draw them.**
212
+ README "Load only what you use" has the table and the imports for Next and for plain HTML.
213
+
214
+ Added
215
+ - `@gemboss/ui/core.css` (reset + tokens, 1.0 KB gzip), `@gemboss/ui/components.css` (the kit, the field look,
216
+ kit-plain.css; 8.2 KB), `@gemboss/ui/shell.css` (the shell + app-shell.css; 5.5 KB): built into `parts/` from the
217
+ same sections as `gemboss-ui.css`, each opening with a header that names the gemboss commit (kept through minify).
218
+ The core is 14.7 KB against 21.7 for the bundle. Like `gemboss-ui.css`, the parts exist after `build-css.mjs` (the
219
+ tarball has them; in this repo, build first).
220
+ - Components import their own entry: `AuthLayout` & co. → `auth.css`; `AssistantHost`, the launcher → `assistant.css`;
221
+ `TopbarNav`, `SearchDialog`, `Avatar`, `Leaderboard`, `Podium`, `ThemeToggle` → `portal.css`; `ThemeToggle` /
222
+ `themeScript` (now in `theme-script.ts`, so a root layout with only the script does not take portal.css) → `theme-dark.css`; the collections components → `collections.css`; the editor components → `editor.css`.
223
+ A component a surface never imports takes its CSS out of the build with it (`sideEffects`).
224
+ - The editor kit's step 4 (#3850: `LayerList`, `SortableLayerList`, `LayerTree` in `layers.tsx`, `sortable.tsx`, `tree.tsx`)
225
+ imports `editor.css` like the rest of the editor; `editor-layers-plain.js` is minified and weighed like the other plain
226
+ scripts (1.8 KB gzip).
227
+ - `Reactions` (#3861, born an entry) imports `reactions.css` itself too; the parts gate found it unclassified on the
228
+ rebase, which is the gate doing its job.
229
+
230
+ Changed
231
+ - `@gemboss/ui/shell.css` is the built shell PART (was `packages/ui/src/shell.css`, which only the admin imported). The source is
232
+ `@gemboss/ui/shell-source.css`; the admin's `gemboss-shell.css` imports that, same file as before.
233
+ - `SearchDialog` moved to its own module (`search-dialog.tsx`, same export from `@gemboss/ui`), so a surface that only
234
+ opens a `Dialog` does not get `portal.css`.
235
+ - kit-plain.css → app-shell.css: `.ui-menu__item { display: flex … }` and the thread rail's 44px buttons at phone width
236
+ (`.gemboss-assistant-collapse`, `-newchat`). Both override a shell.css rule by coming after it; kit-plain.css sat
237
+ after shell.css in `gemboss-ui.css` but is in `components.css` now, which loads before it. Same rules, same weight.
238
+ - `gemboss-ui.css` holds the same parts as 0.19.0, in the parts' order (kit-plain.css now sits before the shell).
239
+ Computed style of every element of both catalogs at 1280 and 390, 0.19.0 bundle vs 0.23.0 bundle: 0 differences.
240
+
241
+ Measured on surfaces (tarball installed locally, nothing committed there), gzip of the package's CSS a page loads:
242
+ - content-radar (Next 16): `/v2` 38.5 KB on 0.19.0 → 32.3 with the core; `/login` 31.2 → 25.4; `/v2/accounts` 43.1 →
243
+ 36.9. Keeping `gemboss-ui.css` on 0.23.0 instead costs +1.9 KB (`/v2` 40.4): the launcher brings assistant.css twice.
244
+ - taskdesk (plain HTML): 22.2 KB (the minified bundle) → 15.0 with the core; every element's computed style the same and
245
+ screenshots byte-identical at 1280 and 390.
246
+ - GemFactory sign-in (plain HTML): 22.2 KB → 17.7 with the core + auth.css; screenshots byte-identical at 1280 and 390.
247
+
248
+ Upgrading
249
+ - A surface that bundles the package with esbuild needs a CSS loader (`loader: { ".css": "empty" }` if it links the CSS
250
+ itself): esbuild stops at a component's own `import "./auth.css"` without one. GemFactory's gbui.mjs has it already.
251
+
252
+ The compatibility bundle, and the road out
253
+ - `gemboss-ui.css` stays, minified, FROZEN at the 0.19.0 set: a surface that links it does not break, and a new
254
+ component never goes into it (`parts-gate.mjs`, CI).
255
+ - 1. Now: every surface moves to the core + entries on its next bump (Next and Vite: three imports in the root layout and
256
+ drop `gemboss-ui.css` in the same bump; plain HTML: link the core and the entries each page draws).
257
+ 2. A search across the fleet's repos for `gemboss-ui.css` shows who still loads it (06/10/2026: content-radar,
258
+ partner-radar, search-radar, talent-radar, GemCommunity admin, GemFeedback, gemwatcher, GemFactory, taskdesk).
259
+ 3. When nobody does, it is removed in its own release, with this note pointing at it. Not before.
260
+
261
+ Checks
262
+ - `packages/ui/scripts/parts-gate.mjs` (CI, with `--selftest`): the core and the bundle frozen; every stylesheet in `src/`
263
+ classified; every entry exported; a component that draws an entry's class imports it; the built bundle holds exactly
264
+ the frozen sections.
265
+ - `parts-cascade.test.ts` (`test:ui-browser`): the catalog with the parts (the core first, then the entries in order and
266
+ reversed, collections.css and editor.css included) paints what the bundle paints, element for element, at 1280 and
267
+ 390; two planted entries that only agree by order turn it red. The core loads first by contract (README "Order"):
268
+ an entry before it loses its modifiers (`.ui-empty--no-match`), measured.
269
+ - Ceilings for the three parts in `weight-budget.json`.
270
+
271
+ ## 0.22.0 (07/10/2026)
272
+
273
+ **The layers, the element outline and the drawer as components (editor kit, step 4 of 5).** Nothing changes on screen
274
+ or in behaviour in the admin: its page editor's Layers, Page settings and Edit content drawers and its element
275
+ inspector's Elements outline now render these, measured equal on a production build (below). README "Layers and
276
+ drawer".
277
+
278
+ Added
279
+ - `@gemboss/ui/editor`, three more modules behind the entry: `layers` (`LayerList`, `EditorDrawer`), `sortable`
280
+ ("use client": `SortableLayerList`) and `tree` (`LayerTree`).
281
+ - `LayerList`: the page's sections as rows (34px, 50 with a subtitle); select (`aria-current`), the caller's actions,
282
+ an empty line; off until asked: an eye to hide / show (`onToggleHidden`, `aria-pressed`), indent per `depth`.
283
+ - `SortableLayerList`: the same rows, reordered by drag (a grip, the dragged row faded, a drop line) and Alt+↑ / Alt+↓
284
+ (the focus kept on the moved row, also when `onMove` answers after a server write); the drag is kept by id. Its own
285
+ module, so the admin's Layers (no reordering) do not carry it. For GemFactory's Builder (step 5).
286
+ - `LayerTree`: an indented outline, `role="tree"` of `treeitem`s with level, position and selection.
287
+ - `EditorDrawer`: backdrop + a side panel (`right`: `.gemboss-settings`, `left`: `.gemboss-layers`), head with ✕,
288
+ `before`, body, `footer`.
289
+ - `editor-layers-plain.js` (its own file and its own weight ceiling, 2,315 B gzip; editor-plain.js is unchanged):
290
+ `gembossUI.initEditorLayers()`: select, hide, drag and Alt+↑/↓ (the row moves in the document),
291
+ outline select / clear, drawers opened by `data-gb-drawer` and closed by ✕ and the backdrop; an event for each.
292
+ - editor.css: the optional pieces (`.gemboss-layer-grip`, `.is-dragging`, `[data-drop]`, `.is-hidden`,
293
+ `.gemboss-layer-eye`) and `[hidden]` for a drawer a plain page keeps in the document.
294
+ - `catalog/editor.html`: Layers (drag, hide, select, indent), Page settings and the outline, running, at 1280 and 390.
295
+
296
+ Measured in the admin (two production builds, before = main f23a60729 with the #3848 fixes (0.20.1) and after, pinned SENTRY_RELEASE /
297
+ GIT_COMMIT_SHA / GEMBOSS_BUILD_STAMP, served by `next start` on the same local data; every write the harness could cause
298
+ was held)
299
+ - DOM and computed style (+ ::before / ::after, box, control values) of every element, and :hover / :focus-visible /
300
+ :active forced on every control of the editor's columns and drawers, for 13 states (page editor: Layers open, a layer
301
+ selected, focus on a row's actions, closed with ✕, closed from the backdrop, ↓ pressed with the write held; Edit
302
+ content drawer; Page settings drawer, typing in it; theme editor inspector, Elements: the outline, a node picked, "Add
303
+ inside" open, a node picked and cleared) at 320, 390, 768 (touch), 1280, 1366, 1920: 78 captures, 22,888 element
304
+ renders, 10,620 forced-state checks. 0 differences in computed style or box. In markup, the intended ones only (720):
305
+ `data-id` on each layer row and outline node, `type="button"` on the buttons the components draw (no form around
306
+ them), `aria-current="true"` on the selected layer's button. Not the change: inline styles serialised by the server or
307
+ by the client (computed equal), the logo's per-port URL, the canvas's loading spinner (four states re-captured with
308
+ the canvas settled: equal), and the forced states of the chat column's work-mode select, which differ between two
309
+ captures of the SAME base build too. Negative controls (one declaration laid over the after build: the layer button's
310
+ padding, the outline name's, the drawer head's) are reported in 3 of 3 states.
311
+ - Re-checked after rebasing onto main with 0.20.0 (#3846) and 0.20.1 (#3848): the 13 states at 1280 and 390, 26 captures,
312
+ 7,612 element renders, 3,540 forced-state checks: again 0 differences in computed style or box, markup as above.
313
+ - First load (brotli), against main with 0.20.1: the shell 239,674 → 239,655 B (−19); page editor +693 (LayerList and
314
+ EditorDrawer; SortableLayerList is not in it), campaign editor +327, theme editor +314, product studio +198, section
315
+ editor +125, plugin editor −12. Package weight gate: editor.css 8.1 / 8.4 KB gzip, editor-plain.js unchanged,
316
+ editor-layers-plain.js 1.8 / 2.3 KB.
317
+ - Behaviour the admin does not use (drag, Alt+↑/↓, the eye, indent) and the plain-HTML side are asserted in Chromium:
318
+ editor-layers-browser.test.tsx (React and `initEditorLayers()`, with negative controls).
319
+
320
+ Known, kept as it was (this step moves markup, not behaviour)
321
+ - The admin's Layers do not drag and have no eye: they move with ↑ / ↓ buttons, which also work on touch (HTML drag and
322
+ drop does not start from a touch). Using `onMove` there is a change of its own.
323
+ - The drawers have no Escape, and do not move the focus into themselves or back to their opener.
324
+ - `DesignControls` (the inspector's Design tab body) is not moved: moving it changes how it looks.
325
+ - The theme editor's "Sections on this page" list (ThemeSectionList) keeps its own bordered rows (inline styles); drawn
326
+ as a LayerList it would look different.
327
+
5
328
  ## 0.21.0 (07/10/2026)
6
329
 
7
330
  **Reactions: one emoji set and one chip for the fleet.** GemCare's team chat reactions, moved into the kit so GemFeedback
@@ -68,8 +391,13 @@ Fixed
68
391
  - `.ui-seg svg` filled a stroke icon solid (`fill: currentColor` over `fill="none"`): the monitor and phone glyphs drew
69
392
  as blocks. A `fill="none"` svg keeps its outline.
70
393
 
394
+ - A folded editor column (`.is-chat-collapsed`) is `visibility: hidden`: at width 0 its fields stayed in the Tab order.
395
+ - `EditorChatToggle` takes a `name` ("Hide <name>"; default "chat panel"): a column that is not a chat says so.
396
+
71
397
  Removed
72
398
  - The `.gemboss-ei-seg` look from `editor.css`.
399
+ - `.gemboss-pageedit-status` and `.gemboss-pageedit-kind` (the page editor draws Polaris' Badge, as the funnel beside it does).
400
+
73
401
  ## 0.19.0 (06/10/2026)
74
402
 
75
403
  **The published CSS and plain scripts are minified, and each file has a wire-weight ceiling.** README "Minified on