@gemboss/ui 0.21.0 → 0.25.0
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/CHANGELOG.md +150 -0
- package/README.md +177 -6
- package/catalog/editor.html +426 -10
- package/catalog/index.html +184 -32
- package/gemboss-ui.css +3 -3
- package/package.json +11 -3
- package/parts/components.css +5 -0
- package/parts/core.css +5 -0
- package/parts/shell.css +5 -0
- package/src/app-shell.css +1 -1
- package/src/assistant/engine.tsx +607 -0
- package/src/assistant/host.tsx +1 -0
- package/src/assistant/index.ts +1 -0
- package/src/assistant-engine.css +1 -0
- package/src/auth.tsx +1 -0
- package/src/collections.tsx +1 -0
- package/src/dialog.tsx +3 -135
- package/src/editor/controls.tsx +1 -0
- package/src/editor/frame.tsx +1 -0
- package/src/editor/index.ts +9 -0
- package/src/editor/layers.tsx +136 -0
- package/src/editor/panel.tsx +1 -0
- package/src/editor/panes.tsx +4 -3
- package/src/editor/sortable.tsx +79 -0
- package/src/editor/tree.tsx +61 -0
- package/src/editor-layers-plain.js +1 -0
- package/src/editor.css +1 -1
- package/src/index-table.tsx +1 -0
- package/src/index.ts +4 -2
- package/src/kit-plain.css +1 -1
- package/src/launcher.tsx +1 -0
- package/src/menu-layer.ts +9 -2
- package/src/portal.tsx +1 -0
- package/src/reactions.tsx +1 -0
- package/src/resource-list-select.tsx +1 -0
- package/src/search-dialog.tsx +146 -0
- package/src/selection.tsx +1 -0
- package/src/theme-script.ts +10 -0
- package/src/theme.tsx +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,151 @@
|
|
|
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.25.0 (07/10/2026)
|
|
6
|
+
|
|
7
|
+
**The assistant's engine picker** (Chris 07/10/2026): a chip in the composer row, as in the admin, that opens a popup
|
|
8
|
+
with Engine · Setup · Status tabs. Engine, model, effort, the bridge's status and its install command leave the chat.
|
|
9
|
+
|
|
10
|
+
Added
|
|
11
|
+
- `EnginePicker`, `EnginePanel`, `BridgeNotice`, `useEnginePicker` (+ types `EngineChoice`, `Choice`, `BridgeStatus`,
|
|
12
|
+
`BridgeSetup`, `BridgeState`, `EnginePickerTab`) in `@gemboss/ui/assistant`. README "The engine picker".
|
|
13
|
+
- `assistant-engine.css`, a new entry (1.9 KB gzip), imported by the component; not in `gemboss-ui.css`.
|
|
14
|
+
- `placeFloating(layer, anchor, end)` in `menu-layer.ts`: the menus' placement, now shared with the picker's panel
|
|
15
|
+
(`floatMenu` calls it; same geometry).
|
|
16
|
+
|
|
17
|
+
## 0.24.0 (07/10/2026)
|
|
18
|
+
|
|
19
|
+
**The Studio canvas in the editors' frame** (gemboss #3856): the pieces the admin's Studio needed from the editor kit.
|
|
20
|
+
|
|
21
|
+
Added
|
|
22
|
+
- `EditorChatToggle` takes a `name` ("Hide <name>"; default "chat panel"): a column that is not a chat says so.
|
|
23
|
+
|
|
24
|
+
Changed
|
|
25
|
+
- A folded editor column (`.is-chat-collapsed`) is `visibility: hidden`: at width 0 its fields stayed in the Tab order.
|
|
26
|
+
|
|
27
|
+
Removed
|
|
28
|
+
- `.gemboss-pageedit-status` and `.gemboss-pageedit-kind` looks: the page editor draws Polaris' Badge, as the funnel beside it does
|
|
29
|
+
(`.gemboss-pageedit-status` stays on a wrapper as a hook, `data-status`, with no look).
|
|
30
|
+
|
|
31
|
+
## 0.23.0 (07/10/2026)
|
|
32
|
+
|
|
33
|
+
**The CSS comes in parts: a core every surface loads, and entries that come with the components that draw them.**
|
|
34
|
+
README "Load only what you use" has the table and the imports for Next and for plain HTML.
|
|
35
|
+
|
|
36
|
+
Added
|
|
37
|
+
- `@gemboss/ui/core.css` (reset + tokens, 1.0 KB gzip), `@gemboss/ui/components.css` (the kit, the field look,
|
|
38
|
+
kit-plain.css; 8.2 KB), `@gemboss/ui/shell.css` (the shell + app-shell.css; 5.5 KB): built into `parts/` from the
|
|
39
|
+
same sections as `gemboss-ui.css`, each opening with a header that names the gemboss commit (kept through minify).
|
|
40
|
+
The core is 14.7 KB against 21.7 for the bundle. Like `gemboss-ui.css`, the parts exist after `build-css.mjs` (the
|
|
41
|
+
tarball has them; in this repo, build first).
|
|
42
|
+
- Components import their own entry: `AuthLayout` & co. → `auth.css`; `AssistantHost`, the launcher → `assistant.css`;
|
|
43
|
+
`TopbarNav`, `SearchDialog`, `Avatar`, `Leaderboard`, `Podium`, `ThemeToggle` → `portal.css`; `ThemeToggle` /
|
|
44
|
+
`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`.
|
|
45
|
+
A component a surface never imports takes its CSS out of the build with it (`sideEffects`).
|
|
46
|
+
- The editor kit's step 4 (#3850: `LayerList`, `SortableLayerList`, `LayerTree` in `layers.tsx`, `sortable.tsx`, `tree.tsx`)
|
|
47
|
+
imports `editor.css` like the rest of the editor; `editor-layers-plain.js` is minified and weighed like the other plain
|
|
48
|
+
scripts (1.8 KB gzip).
|
|
49
|
+
- `Reactions` (#3861, born an entry) imports `reactions.css` itself too; the parts gate found it unclassified on the
|
|
50
|
+
rebase, which is the gate doing its job.
|
|
51
|
+
|
|
52
|
+
Changed
|
|
53
|
+
- `@gemboss/ui/shell.css` is the built shell PART (was `packages/ui/src/shell.css`, which only the admin imported). The source is
|
|
54
|
+
`@gemboss/ui/shell-source.css`; the admin's `gemboss-shell.css` imports that, same file as before.
|
|
55
|
+
- `SearchDialog` moved to its own module (`search-dialog.tsx`, same export from `@gemboss/ui`), so a surface that only
|
|
56
|
+
opens a `Dialog` does not get `portal.css`.
|
|
57
|
+
- kit-plain.css → app-shell.css: `.ui-menu__item { display: flex … }` and the thread rail's 44px buttons at phone width
|
|
58
|
+
(`.gemboss-assistant-collapse`, `-newchat`). Both override a shell.css rule by coming after it; kit-plain.css sat
|
|
59
|
+
after shell.css in `gemboss-ui.css` but is in `components.css` now, which loads before it. Same rules, same weight.
|
|
60
|
+
- `gemboss-ui.css` holds the same parts as 0.19.0, in the parts' order (kit-plain.css now sits before the shell).
|
|
61
|
+
Computed style of every element of both catalogs at 1280 and 390, 0.19.0 bundle vs 0.23.0 bundle: 0 differences.
|
|
62
|
+
|
|
63
|
+
Measured on surfaces (tarball installed locally, nothing committed there), gzip of the package's CSS a page loads:
|
|
64
|
+
- 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 →
|
|
65
|
+
36.9. Keeping `gemboss-ui.css` on 0.23.0 instead costs +1.9 KB (`/v2` 40.4): the launcher brings assistant.css twice.
|
|
66
|
+
- taskdesk (plain HTML): 22.2 KB (the minified bundle) → 15.0 with the core; every element's computed style the same and
|
|
67
|
+
screenshots byte-identical at 1280 and 390.
|
|
68
|
+
- GemFactory sign-in (plain HTML): 22.2 KB → 17.7 with the core + auth.css; screenshots byte-identical at 1280 and 390.
|
|
69
|
+
|
|
70
|
+
Upgrading
|
|
71
|
+
- A surface that bundles the package with esbuild needs a CSS loader (`loader: { ".css": "empty" }` if it links the CSS
|
|
72
|
+
itself): esbuild stops at a component's own `import "./auth.css"` without one. GemFactory's gbui.mjs has it already.
|
|
73
|
+
|
|
74
|
+
The compatibility bundle, and the road out
|
|
75
|
+
- `gemboss-ui.css` stays, minified, FROZEN at the 0.19.0 set: a surface that links it does not break, and a new
|
|
76
|
+
component never goes into it (`parts-gate.mjs`, CI).
|
|
77
|
+
- 1. Now: every surface moves to the core + entries on its next bump (Next and Vite: three imports in the root layout and
|
|
78
|
+
drop `gemboss-ui.css` in the same bump; plain HTML: link the core and the entries each page draws).
|
|
79
|
+
2. A search across the fleet's repos for `gemboss-ui.css` shows who still loads it (06/10/2026: content-radar,
|
|
80
|
+
partner-radar, search-radar, talent-radar, GemCommunity admin, GemFeedback, gemwatcher, GemFactory, taskdesk).
|
|
81
|
+
3. When nobody does, it is removed in its own release, with this note pointing at it. Not before.
|
|
82
|
+
|
|
83
|
+
Checks
|
|
84
|
+
- `packages/ui/scripts/parts-gate.mjs` (CI, with `--selftest`): the core and the bundle frozen; every stylesheet in `src/`
|
|
85
|
+
classified; every entry exported; a component that draws an entry's class imports it; the built bundle holds exactly
|
|
86
|
+
the frozen sections.
|
|
87
|
+
- `parts-cascade.test.ts` (`test:ui-browser`): the catalog with the parts (the core first, then the entries in order and
|
|
88
|
+
reversed, collections.css and editor.css included) paints what the bundle paints, element for element, at 1280 and
|
|
89
|
+
390; two planted entries that only agree by order turn it red. The core loads first by contract (README "Order"):
|
|
90
|
+
an entry before it loses its modifiers (`.ui-empty--no-match`), measured.
|
|
91
|
+
- Ceilings for the three parts in `weight-budget.json`.
|
|
92
|
+
|
|
93
|
+
## 0.22.0 (07/10/2026)
|
|
94
|
+
|
|
95
|
+
**The layers, the element outline and the drawer as components (editor kit, step 4 of 5).** Nothing changes on screen
|
|
96
|
+
or in behaviour in the admin: its page editor's Layers, Page settings and Edit content drawers and its element
|
|
97
|
+
inspector's Elements outline now render these, measured equal on a production build (below). README "Layers and
|
|
98
|
+
drawer".
|
|
99
|
+
|
|
100
|
+
Added
|
|
101
|
+
- `@gemboss/ui/editor`, three more modules behind the entry: `layers` (`LayerList`, `EditorDrawer`), `sortable`
|
|
102
|
+
("use client": `SortableLayerList`) and `tree` (`LayerTree`).
|
|
103
|
+
- `LayerList`: the page's sections as rows (34px, 50 with a subtitle); select (`aria-current`), the caller's actions,
|
|
104
|
+
an empty line; off until asked: an eye to hide / show (`onToggleHidden`, `aria-pressed`), indent per `depth`.
|
|
105
|
+
- `SortableLayerList`: the same rows, reordered by drag (a grip, the dragged row faded, a drop line) and Alt+↑ / Alt+↓
|
|
106
|
+
(the focus kept on the moved row, also when `onMove` answers after a server write); the drag is kept by id. Its own
|
|
107
|
+
module, so the admin's Layers (no reordering) do not carry it. For GemFactory's Builder (step 5).
|
|
108
|
+
- `LayerTree`: an indented outline, `role="tree"` of `treeitem`s with level, position and selection.
|
|
109
|
+
- `EditorDrawer`: backdrop + a side panel (`right`: `.gemboss-settings`, `left`: `.gemboss-layers`), head with ✕,
|
|
110
|
+
`before`, body, `footer`.
|
|
111
|
+
- `editor-layers-plain.js` (its own file and its own weight ceiling, 2,315 B gzip; editor-plain.js is unchanged):
|
|
112
|
+
`gembossUI.initEditorLayers()`: select, hide, drag and Alt+↑/↓ (the row moves in the document),
|
|
113
|
+
outline select / clear, drawers opened by `data-gb-drawer` and closed by ✕ and the backdrop; an event for each.
|
|
114
|
+
- editor.css: the optional pieces (`.gemboss-layer-grip`, `.is-dragging`, `[data-drop]`, `.is-hidden`,
|
|
115
|
+
`.gemboss-layer-eye`) and `[hidden]` for a drawer a plain page keeps in the document.
|
|
116
|
+
- `catalog/editor.html`: Layers (drag, hide, select, indent), Page settings and the outline, running, at 1280 and 390.
|
|
117
|
+
|
|
118
|
+
Measured in the admin (two production builds, before = main f23a60729 with the #3848 fixes (0.20.1) and after, pinned SENTRY_RELEASE /
|
|
119
|
+
GIT_COMMIT_SHA / GEMBOSS_BUILD_STAMP, served by `next start` on the same local data; every write the harness could cause
|
|
120
|
+
was held)
|
|
121
|
+
- DOM and computed style (+ ::before / ::after, box, control values) of every element, and :hover / :focus-visible /
|
|
122
|
+
:active forced on every control of the editor's columns and drawers, for 13 states (page editor: Layers open, a layer
|
|
123
|
+
selected, focus on a row's actions, closed with ✕, closed from the backdrop, ↓ pressed with the write held; Edit
|
|
124
|
+
content drawer; Page settings drawer, typing in it; theme editor inspector, Elements: the outline, a node picked, "Add
|
|
125
|
+
inside" open, a node picked and cleared) at 320, 390, 768 (touch), 1280, 1366, 1920: 78 captures, 22,888 element
|
|
126
|
+
renders, 10,620 forced-state checks. 0 differences in computed style or box. In markup, the intended ones only (720):
|
|
127
|
+
`data-id` on each layer row and outline node, `type="button"` on the buttons the components draw (no form around
|
|
128
|
+
them), `aria-current="true"` on the selected layer's button. Not the change: inline styles serialised by the server or
|
|
129
|
+
by the client (computed equal), the logo's per-port URL, the canvas's loading spinner (four states re-captured with
|
|
130
|
+
the canvas settled: equal), and the forced states of the chat column's work-mode select, which differ between two
|
|
131
|
+
captures of the SAME base build too. Negative controls (one declaration laid over the after build: the layer button's
|
|
132
|
+
padding, the outline name's, the drawer head's) are reported in 3 of 3 states.
|
|
133
|
+
- 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,
|
|
134
|
+
7,612 element renders, 3,540 forced-state checks: again 0 differences in computed style or box, markup as above.
|
|
135
|
+
- First load (brotli), against main with 0.20.1: the shell 239,674 → 239,655 B (−19); page editor +693 (LayerList and
|
|
136
|
+
EditorDrawer; SortableLayerList is not in it), campaign editor +327, theme editor +314, product studio +198, section
|
|
137
|
+
editor +125, plugin editor −12. Package weight gate: editor.css 8.1 / 8.4 KB gzip, editor-plain.js unchanged,
|
|
138
|
+
editor-layers-plain.js 1.8 / 2.3 KB.
|
|
139
|
+
- Behaviour the admin does not use (drag, Alt+↑/↓, the eye, indent) and the plain-HTML side are asserted in Chromium:
|
|
140
|
+
editor-layers-browser.test.tsx (React and `initEditorLayers()`, with negative controls).
|
|
141
|
+
|
|
142
|
+
Known, kept as it was (this step moves markup, not behaviour)
|
|
143
|
+
- The admin's Layers do not drag and have no eye: they move with ↑ / ↓ buttons, which also work on touch (HTML drag and
|
|
144
|
+
drop does not start from a touch). Using `onMove` there is a change of its own.
|
|
145
|
+
- The drawers have no Escape, and do not move the focus into themselves or back to their opener.
|
|
146
|
+
- `DesignControls` (the inspector's Design tab body) is not moved: moving it changes how it looks.
|
|
147
|
+
- The theme editor's "Sections on this page" list (ThemeSectionList) keeps its own bordered rows (inline styles); drawn
|
|
148
|
+
as a LayerList it would look different.
|
|
149
|
+
|
|
5
150
|
## 0.21.0 (07/10/2026)
|
|
6
151
|
|
|
7
152
|
**Reactions: one emoji set and one chip for the fleet.** GemCare's team chat reactions, moved into the kit so GemFeedback
|
|
@@ -68,8 +213,13 @@ Fixed
|
|
|
68
213
|
- `.ui-seg svg` filled a stroke icon solid (`fill: currentColor` over `fill="none"`): the monitor and phone glyphs drew
|
|
69
214
|
as blocks. A `fill="none"` svg keeps its outline.
|
|
70
215
|
|
|
216
|
+
- A folded editor column (`.is-chat-collapsed`) is `visibility: hidden`: at width 0 its fields stayed in the Tab order.
|
|
217
|
+
- `EditorChatToggle` takes a `name` ("Hide <name>"; default "chat panel"): a column that is not a chat says so.
|
|
218
|
+
|
|
71
219
|
Removed
|
|
72
220
|
- The `.gemboss-ei-seg` look from `editor.css`.
|
|
221
|
+
- `.gemboss-pageedit-status` and `.gemboss-pageedit-kind` (the page editor draws Polaris' Badge, as the funnel beside it does).
|
|
222
|
+
|
|
73
223
|
## 0.19.0 (06/10/2026)
|
|
74
224
|
|
|
75
225
|
**The published CSS and plain scripts are minified, and each file has a wire-weight ceiling.** README "Minified on
|
package/README.md
CHANGED
|
@@ -11,7 +11,10 @@ npm install @gemboss/ui
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
```tsx
|
|
14
|
-
|
|
14
|
+
// The core, once, in the root layout, FIRST and in this order (0.23.0; "Load only what you use" below):
|
|
15
|
+
import "@gemboss/ui/core.css";
|
|
16
|
+
import "@gemboss/ui/components.css";
|
|
17
|
+
import "@gemboss/ui/shell.css";
|
|
15
18
|
import { AppShell, RailItem, RailGroup, RailHeading, BrandLockup } from "@gemboss/ui";
|
|
16
19
|
|
|
17
20
|
<AppShell
|
|
@@ -40,6 +43,88 @@ Surfaces with no React (gemcare, gempress) use the CSS alone and write the same
|
|
|
40
43
|
`nav.icon-nav > .gnav-scroll > .gnav-group > a.gnav-item > span.gnav-ico + span.gnav-label`.
|
|
41
44
|
`packages/ui/src/shell.tsx` is the reference.
|
|
42
45
|
|
|
46
|
+
## Load only what you use: the core and the entries (0.23.0)
|
|
47
|
+
|
|
48
|
+
Every surface used to load `gemboss-ui.css` whole and use 14–21% of it (measured 06/10/2026 on twelve surfaces). Since
|
|
49
|
+
0.23.0 the CSS comes in parts (`packages/ui/scripts/parts.mjs` is the list): a CORE every surface loads once, and ENTRIES that
|
|
50
|
+
come with the component that draws them. A React component imports its own entry (`auth.tsx` imports `auth.css`), so
|
|
51
|
+
a Next or Vite surface downloads an entry only on the routes that render it, and a component it never imports takes
|
|
52
|
+
its CSS out of the build with it (`sideEffects`). gzip of the minified file, as published:
|
|
53
|
+
|
|
54
|
+
| Part | Import (Node / bundler) | File in the tarball | What | gzip | Who loads it |
|
|
55
|
+
|---|---|---|---|---:|---|
|
|
56
|
+
| core | `@gemboss/ui/core.css` | `parts/core.css` | the box-sizing reset + the tokens | 1.0 KB | every surface, first |
|
|
57
|
+
| components | `@gemboss/ui/components.css` | `parts/components.css` | the kit (`ui.css`), the field look, the plain-HTML primitives (`kit-plain.css`: menu, chips, report pieces, popup body) | 8.2 KB | every surface, second |
|
|
58
|
+
| shell | `@gemboss/ui/shell.css` | `parts/shell.css` | rail, topbar, main, mobile drawer, the thread rail, topbar slots, base text, page widths (`app-shell.css`) | 5.5 KB | every surface, third |
|
|
59
|
+
| auth | `@gemboss/ui/auth.css` | `packages/ui/src/auth.css` | the sign-in screen | 2.6 KB | `AuthLayout` & co. import it |
|
|
60
|
+
| assistant | `@gemboss/ui/assistant.css` | `packages/ui/src/assistant.css` | the dock and the launcher | 2.2 KB | `AssistantHost`, the launcher import it |
|
|
61
|
+
| portal | `@gemboss/ui/portal.css` | `packages/ui/src/portal.css` | `TopbarNav`, `SearchDialog`, `Avatar`, `Leaderboard`, `Podium`, the `ThemeToggle` button | 2.1 KB | those components import it |
|
|
62
|
+
| theme-dark | `@gemboss/ui/theme-dark.css` | `packages/ui/src/theme-dark.css` | the dark palette | 0.6 KB | `ThemeToggle` and `themeScript` (`theme-script.ts`, without portal.css) import it |
|
|
63
|
+
| collections | `@gemboss/ui/collections.css` | `packages/ui/src/collections.css` | tables, lists, grids, boards | 4.6 KB | `IndexTable`, `ResourceList`, `Board`… import it |
|
|
64
|
+
| editor | `@gemboss/ui/editor.css` | `packages/ui/src/editor.css` | the editors | 7.9 KB | the `@gemboss/ui/editor` components import it |
|
|
65
|
+
| reactions | `@gemboss/ui/reactions.css` | `packages/ui/src/reactions.css` | the emoji reactions and their picker | 0.8 KB | `Reactions` / `ReactionPicker` import it |
|
|
66
|
+
| assistant-engine | `@gemboss/ui/assistant-engine.css` | `packages/ui/src/assistant-engine.css` | the engine picker (chip + Engine/Setup/Status popup) and the bridge notice | 1.9 KB | `EnginePicker` / `EnginePanel` / `BridgeNotice` import it |
|
|
67
|
+
| chat | `@gemboss/ui/chat.css` + `chat-composer.css` | `packages/ui/src/chat.css`, `packages/ui/src/chat-composer.css` | the chat body, the composer | 3.0 + 0.9 KB | the surface (no package component draws it) |
|
|
68
|
+
| bundle | `@gemboss/ui/gemboss-ui.css` | `gemboss-ui.css` | core + components + shell + auth + assistant + chat + portal + theme-dark | 21.7 KB | compatibility only (see CHANGELOG 0.23.0) |
|
|
69
|
+
|
|
70
|
+
The core alone is 14.7 KB against 21.7 for the bundle (each part opens with a short header naming the gemboss commit, kept through minify, so a surface serving the parts can still say which commit its CSS is); a list page adds collections (4.6), a sign-in page auth (2.6).
|
|
71
|
+
|
|
72
|
+
**Next.js / Vite.** The core's three imports go at the top of the root layout, above any component import, in the
|
|
73
|
+
order above. Entries need nothing: the components bring them. A page that writes chat markup itself imports
|
|
74
|
+
`@gemboss/ui/chat.css`. Drop `gemboss-ui.css` IN THE SAME BUMP: kept beside the parts, the entries a component brings
|
|
75
|
+
load a second time, after your own CSS, and a second copy of a rule you override wins again.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
// app/layout.tsx
|
|
79
|
+
import "@gemboss/ui/core.css";
|
|
80
|
+
import "@gemboss/ui/components.css";
|
|
81
|
+
import "@gemboss/ui/shell.css";
|
|
82
|
+
import "./globals.css"; // your own CSS after the core
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Add `"@gemboss/ui"` to `experimental.optimizePackageImports` in `next.config` (the admin does, 0.23.0): a SERVER
|
|
86
|
+
component that imports from the index otherwise collects the CSS of every module the index re-exports (the admin's
|
|
87
|
+
`/login`, which imports `AuthHelp` in its server page, picked up portal.css and collections.css under webpack until it did).
|
|
88
|
+
|
|
89
|
+
Which bundler drops what (measured 06/10/2026): Next 16 (Turbopack) leaves out the entry of a component a route does
|
|
90
|
+
not render. content-radar on 0.23.0 with the core: `/v2` 38.5 → 32.3 KB gzip of CSS, `/login` 31.2 → 25.4 (auth.css
|
|
91
|
+
only there, portal.css nowhere). esbuild keeps the CSS of every module the index reaches even when it drops their JS, so
|
|
92
|
+
through `@gemboss/ui` it ships every entry the index re-exports; and esbuild with no CSS loader now stops at a
|
|
93
|
+
component's `import "./x.css"`: give it `loader: { ".css": "empty" }` (you link the CSS yourself, as GemFactory's
|
|
94
|
+
gbui.mjs and this package's tests do) or `"css"`.
|
|
95
|
+
|
|
96
|
+
**Plain HTML** (a server that serves the files out of `node_modules`): link the core, then only the entries the page
|
|
97
|
+
draws.
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<link rel="stylesheet" href="/ui/core.css"> <!-- require.resolve("@gemboss/ui/core.css") -->
|
|
101
|
+
<link rel="stylesheet" href="/ui/components.css"> <!-- require.resolve("@gemboss/ui/components.css") -->
|
|
102
|
+
<link rel="stylesheet" href="/ui/shell.css"> <!-- require.resolve("@gemboss/ui/shell.css") -->
|
|
103
|
+
<link rel="stylesheet" href="/ui/auth.css"> <!-- the sign-in page only -->
|
|
104
|
+
<link rel="stylesheet" href="/ui/collections.css"> <!-- list pages only -->
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Order.** The core loads FIRST, and its three parts in that order: the top lines of the root layout, or the first three
|
|
108
|
+
`<link>`s. `shell.css` restyles the field look at phone width, and entries restyle the kit with BEM modifiers at the
|
|
109
|
+
kit's own weight (`.ui-empty--no-match` after `.ui-empty`): both are written to follow what they modify, which keeps
|
|
110
|
+
every rule at the weight your own CSS can override. After the core, the entries may land in any order:
|
|
111
|
+
`packages/ui/scripts/cascade-check.mjs` loads the catalog with the entries (collections.css and editor.css included) after the core,
|
|
112
|
+
in order and reversed, and holds every element's computed style to the bundle's at 1280 and 390 (`parts-cascade.test.ts`,
|
|
113
|
+
CI; negative control: two entries that only agree by order). An entry loaded BEFORE the core is not supported: measured,
|
|
114
|
+
`.ui-empty--no-match` loses its 32px padding to `.ui-empty`. Two kit-plain.css rules that overrode shell.css by coming
|
|
115
|
+
after it (the menu row's flex, the thread rail's 44px buttons at phone width) moved to the end of app-shell.css, same
|
|
116
|
+
weight, so they still follow it.
|
|
117
|
+
|
|
118
|
+
**A new component** gets its own entry: a file in `src/`, listed in `ENTRIES` in `packages/ui/scripts/parts.mjs`, exported,
|
|
119
|
+
imported by the component that draws it, with a ceiling in `weight-budget.json`. It never goes into the core or into
|
|
120
|
+
`gemboss-ui.css`: `packages/ui/scripts/parts-gate.mjs` (CI) holds both frozen, fails on a stylesheet nobody classified, and on a
|
|
121
|
+
component that draws an entry's class without importing it.
|
|
122
|
+
|
|
123
|
+
**The admin** is unchanged: it has Polaris and its own order, so it imports the sources one by one
|
|
124
|
+
(`gemboss-tokens.css`, `ui.css`, and `shell-source.css` from its `gemboss-shell.css`; `@gemboss/ui/shell.css` is the
|
|
125
|
+
built part now). Its editor, assistant and sign-in modules already imported the same entry files the components now
|
|
126
|
+
import, so nothing moves.
|
|
127
|
+
|
|
43
128
|
## Page width (0.8.1)
|
|
44
129
|
|
|
45
130
|
Chris, 04/10/2026: the rail on the left, and on the right a content column with the same max width on every
|
|
@@ -187,6 +272,44 @@ Its styles are the `gemboss-assistant-*` rules in `packages/ui/src/shell.css` (t
|
|
|
187
272
|
@imports too), and they reach you through `gemboss-ui.css` together with `gemboss-field.css`. The admin passes a second tab ("Talk to a person", gemcare) through `secondTab` and its button
|
|
188
273
|
through `fullActions`; a surface without those leaves them out.
|
|
189
274
|
|
|
275
|
+
## The engine picker (0.25.0): `EnginePicker`, `BridgeNotice`
|
|
276
|
+
|
|
277
|
+
Which engine answers the assistant, and the bridge on the user's machine, without spending the chat's space on it
|
|
278
|
+
(Chris 07/10/2026: content-radar's assistant spent close to half its frame on an ENGINE / MODEL / EFFORT row, an
|
|
279
|
+
"outdated bridge" block and the install command). The admin's shape: a chip in the composer row opens a popup.
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
import { EnginePicker, BridgeNotice, useEnginePicker } from "@gemboss/ui/assistant"; // brings assistant-engine.css
|
|
283
|
+
|
|
284
|
+
const picker = useEnginePicker(); // open + tab, shared with the notice
|
|
285
|
+
<EnginePicker {...picker.props}
|
|
286
|
+
engines={[{ id: "claude", label: "Claude Code", note: "Your Claude subscription" }, { id: "server", label: "Server" }]}
|
|
287
|
+
engine={engine} onEngineChange={setEngine}
|
|
288
|
+
models={[{ id: "", label: "Default" }, { id: "opus", label: "Opus" }]} model={model} onModelChange={setModel}
|
|
289
|
+
efforts={[{ id: "", label: "Default" }, { id: "high", label: "High" }]} effort={effort} onEffortChange={setEffort}
|
|
290
|
+
bridge={{ state: "outdated", version: "8", latest: "10", autostart: true, detail: "Codex cannot see the radar tools." }}
|
|
291
|
+
setup={{ command: installCommand, pairingCode, note: "The panel reconnects in a few seconds." }}
|
|
292
|
+
statusActions={<button className="ui-btn" onClick={recheck}>Check again</button>} />
|
|
293
|
+
|
|
294
|
+
// Only when a turn FAILED because of the bridge, one line in the stream:
|
|
295
|
+
<BridgeNotice onOpenSetup={() => picker.openTab("setup")}>The bridge on your machine is not responding.</BridgeNotice>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
- **Chip**: engine name, the model when it is not the default (`id: ""`), a dot for `bridge.state`: green `running`,
|
|
299
|
+
amber `outdated`, red `offline` / `unpaired`, hollow `checking`. Its accessible name says the state in words.
|
|
300
|
+
- **Popup**: a non-modal dialog in the top layer (placed like the kit's menus, `placeFloating`), so the assistant's frame
|
|
301
|
+
cannot cut it. Tabs: Engine always; Setup when `setup` is given; Status when `bridge` is given. ←/→/Home/End move
|
|
302
|
+
between tabs; ↑/↓ move between engines (one Tab stop). Escape with focus in the panel, or a model / effort pick from
|
|
303
|
+
the buttons, closes it and gives focus back to the chip; an outside click closes. An engine pick and a pick from a
|
|
304
|
+
select (more than six models, or grouped ones) keep it open. Closing goes back to the Engine tab. 44px targets under a coarse pointer and at phone width.
|
|
305
|
+
- **Not in the chat**: no standing warning, no command in the conversation. The amber dot on the chip and on the Setup
|
|
306
|
+
tab is the warning. `BridgeNotice` is for a turn that actually failed, once.
|
|
307
|
+
- The package knows no bridge: the surface polls its own, computes `outdated`, builds the command, and owns the
|
|
308
|
+
behaviour of every callback. `EnginePanel` is the same tabs without the chip (catalog, a settings page).
|
|
309
|
+
|
|
310
|
+
Tests: `packages/ui/src/assistant/engine.test.tsx` (markup, styles, its own entry), `packages/ui/src/assistant/engine-browser.test.tsx`
|
|
311
|
+
(Chromium: top layer through a clipping frame, keys, focus, copy, Open setup, 44px; with negative controls).
|
|
312
|
+
|
|
190
313
|
## The chat body
|
|
191
314
|
|
|
192
315
|
What an answer looks like, on any surface that puts a chat somewhere: `chat.css` (inside `gemboss-ui.css`;
|
|
@@ -243,8 +366,9 @@ import "@gemboss/ui/editor.css"; // only on the screens that show an editor
|
|
|
243
366
|
Not here, on purpose: the body modes that hide the admin's own topbar and rail while an editor is open
|
|
244
367
|
(`gemboss-editor-fullscreen`, `-just-exited`, `-layer-open`) and admin-only extensions such as the product studio's
|
|
245
368
|
resizer. The frame and its top bar are React components since 0.14.0 ("Editor frame" below), with their behaviour for
|
|
246
|
-
plain HTML in `editor-plain.js`; the panel and its controls are components since 0.17.0 ("Settings panel" below, step 3);
|
|
247
|
-
|
|
369
|
+
plain HTML in `editor-plain.js`; the panel and its controls are components since 0.17.0 ("Settings panel" below, step 3); the
|
|
370
|
+
layers, the element outline and the drawer since 0.22.0 ("Layers and drawer" below, step 4). Step 5 moves GemFactory's
|
|
371
|
+
Builder onto them.
|
|
248
372
|
|
|
249
373
|
Changes since the verbatim move, in 0.10.1 (05/10/2026, step 1b, the only ones that change what the admin shows; CHANGELOG):
|
|
250
374
|
|
|
@@ -391,6 +515,51 @@ named (it bubbles):
|
|
|
391
515
|
| Edges: Enter blurs; a box that loses focus sends all four | `.gemboss-ei-edgebox` | `gemboss:editor-edges` `{ t, r, b, l }` |
|
|
392
516
|
| Image: commits on blur / Enter when changed; the thumbnail follows; a chip with `data-gb-image-clear` empties it | `.gemboss-ei-img` | `gemboss:editor-image` `{ value }` |
|
|
393
517
|
|
|
518
|
+
## Layers and drawer (0.22.0): `LayerList`, `SortableLayerList`, `LayerTree`, `EditorDrawer`
|
|
519
|
+
|
|
520
|
+
The page editor's Layers, Page settings and Edit content drawers and the element inspector's Elements outline, as
|
|
521
|
+
components (`@gemboss/ui/editor`). The admin draws those four with them since 0.22.0, measured before and after on a
|
|
522
|
+
production build: the same computed style and box, the markup differing only in `data-id` on each row, `type="button"`
|
|
523
|
+
on the buttons the components draw, and `aria-current` on the selected layer (CHANGELOG has the numbers).
|
|
524
|
+
|
|
525
|
+
```tsx
|
|
526
|
+
import { EditorDrawer, LayerList, LayerTree, SortableLayerList } from "@gemboss/ui/editor";
|
|
527
|
+
|
|
528
|
+
{layersOpen ? (
|
|
529
|
+
<EditorDrawer side="left" label="Page layers" title="Layers" onClose={() => setLayersOpen(false)}>
|
|
530
|
+
<SortableLayerList items={sections.map((s) => ({ id: s.id, title: s.type, subtitle: s.heading, hidden: s.hidden }))}
|
|
531
|
+
selectedId={picked} onSelect={(it) => setPicked(it.id)}
|
|
532
|
+
onMove={(it, to) => moveSection(it.id, to)} onToggleHidden={(it, hidden) => setHidden(it.id, hidden)}
|
|
533
|
+
actions={(it) => <button aria-label="Delete" onClick={() => remove(it.id)}>✕</button>} empty="No sections yet" />
|
|
534
|
+
</EditorDrawer>
|
|
535
|
+
) : null}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
| Component | Markup | Behaviour |
|
|
539
|
+
|---|---|---|
|
|
540
|
+
| `LayerList` | `.gemboss-layers-list` › `.gemboss-layer[data-id]` (`.is-sel`) › `button.gemboss-layer-main` (`.gemboss-layer-type` · `.gemboss-layer-head`) · `.gemboss-layer-acts` | A click selects (`onSelect`; the selected button says `aria-current`). Rows are 34px, 50px with a subtitle. `empty` shows in `.gemboss-picker-empty`. `onToggleHidden`: an eye first among the actions (named "Hide <name>", `aria-pressed` = hidden, the row faded). `depth` per item indents it (`indent` px per level, default 14). Stateless. |
|
|
541
|
+
| `SortableLayerList` | the same, each row `draggable` with a `.gemboss-layer-grip` | `onMove(item, to)` (`to` = the index after the move): drag a row (faded while dragged, a 2px line where it lands), or Alt+↑ / Alt+↓ on its button (the focus stays on the moved row, also when `onMove` answers later). The drag is kept by id. Its own module (`"use client"`): a list that does not reorder does not carry it. |
|
|
542
|
+
| `LayerTree` | `ul.gemboss-ei-tree[role=tree]` › `li.gemboss-ei-tnode[role=treeitem][data-id]` (`.on`, level / position / selection) › `button.gemboss-ei-tname` (+ `small`) · `.gemboss-ei-tacts` · `below` | A click on the name calls `onSelect`; toggling it off is the caller's. Actions show on hover, on the selected row and on focus. 27px rows, 14px per level. |
|
|
543
|
+
| `EditorDrawer` | `.gemboss-picker-backdrop` + `aside.gemboss-settings` (`side="right"`, min(380px, 100%)) or `aside.gemboss-layers` (`side="left"`, 300px) › `before` · `.gemboss-picker-head` (title, ✕) · children · `.gemboss-settings-foot` (`footer`) | ✕ and the backdrop call `onClose`. Mounted while open. The body is the caller's: `.gemboss-settings-body` › `label.gemboss-settings-field` (`span`, control, `small`). |
|
|
544
|
+
|
|
545
|
+
Drag to reorder is pointer only (HTML drag and drop does not start from touch); a list that must reorder on a phone
|
|
546
|
+
keeps ↑ / ↓ buttons in its `actions`, as the admin's does. Kept as the admin had them: the drawer has no Escape and
|
|
547
|
+
does not move the focus into itself or back; the admin's Layers do not drag and have no eye (they move with ↑ / ↓).
|
|
548
|
+
|
|
549
|
+
### Plain HTML: `initEditorLayers()`
|
|
550
|
+
|
|
551
|
+
The same markup (copy it from `catalog/editor.html`), then `gembossUI.initEditorLayers()` (`editor-layers-plain.js`, its own file:
|
|
552
|
+
load it only where layers or drawers are, after `kit-plain.js`; the whole document, or `initEditorLayers(element)`). A drawer stays in the document while closed:
|
|
553
|
+
`hidden` on the aside and on the backdrop before it, an `id` on the aside.
|
|
554
|
+
|
|
555
|
+
| Wires | Markup | Event |
|
|
556
|
+
|---|---|---|
|
|
557
|
+
| Select a layer (`.is-sel`, `aria-current`) | `.gemboss-layer-main` | `gemboss:editor-layer` `{ id, index }` on the row |
|
|
558
|
+
| Hide / show (`aria-pressed`, `.is-hidden`, the eye's slash) | `.gemboss-layer-eye` | `gemboss:editor-layer-hidden` `{ id, hidden }` |
|
|
559
|
+
| Reorder: drag a row, or Alt+↑ / Alt+↓ on its button; the row moves in the document and keeps the focus | `.gemboss-layer[draggable=true]` | `gemboss:editor-layer-move` `{ id, from, to }` |
|
|
560
|
+
| Select an outline node; a second click clears it | `.gemboss-ei-tree .gemboss-ei-tname` | `gemboss:editor-tree` `{ id }` (`null` when cleared) |
|
|
561
|
+
| Open a drawer (closing any other open one) / close it (✕ in its head, `data-gb-drawer-close`, the backdrop) | `[data-gb-drawer="<aside id>"]` | `gemboss:editor-drawer` `{ id, open }` on the aside |
|
|
562
|
+
|
|
394
563
|
## Plain-HTML behaviour: `kit-plain.js` (0.3.0)
|
|
395
564
|
|
|
396
565
|
Surfaces without React (GemFactory, gemcare, gempress, partner-radar) render the kit's markup on the server and need
|
|
@@ -1086,13 +1255,15 @@ something the base cannot express, add a token or a prop here, and every surface
|
|
|
1086
1255
|
|
|
1087
1256
|
| Path | What |
|
|
1088
1257
|
|---|---|
|
|
1089
|
-
| `
|
|
1090
|
-
| `
|
|
1258
|
+
| `parts/core.css`, `parts/components.css`, `parts/shell.css` | 0.23.0: the core, built by `packages/ui/scripts/build-css.mjs` from the same sections as `gemboss-ui.css` (see "Load only what you use"). |
|
|
1259
|
+
| `gemboss-ui.css` | built at stage time and shipped minified (0.19.0); since 0.23.0 the COMPATIBILITY BUNDLE, frozen: `apps/admin/scripts/export-ui-css.mjs` (tokens + kit + field + `packages/ui/src/shell.css`, with Polaris `--p-*` resolved to their fallbacks) + `packages/ui/src/app-shell.css` + `auth.css` + `assistant.css` + `kit-plain.css` + `chat.css` + `chat-composer.css` (`packages/ui/scripts/build-css.mjs`) |
|
|
1260
|
+
| `packages/ui/src/shell.css` | the SHARED SHELL (rail, topbar, main, mobile drawer, the assistant's thread rail, popover surface, account chip). Exported as `@gemboss/ui/shell-source.css` since 0.23.0 (`@gemboss/ui/shell.css` is the built part). The admin's `gemboss-shell.css` @imports it on line 2; the export refuses a shared-shell rule in the admin's sheet or an admin-only rule here (one owner). |
|
|
1091
1261
|
| `packages/ui/src/kit-plain.css` | primitives for surfaces that render plain HTML, not React (0.2.1): menu, key/value list, icon button; 0.3.0 adds `ui-link` / `ui-prose`, the media card (`ui-card--media`, `__media`, `__body`, `__foot`) and the popup's `ui-modal__body` / `__foot`. Shipped inside `gemboss-ui.css`; the admin does not import it (its first-load CSS budget has ~0.1 KB headroom).; (0.4.0) report pages: delta, large KPI grid, bar list, column chart, legend, funnel, card grid, section head. |
|
|
1092
1262
|
| `packages/ui/src/kit-plain.js` | 0.3.0: the behaviour those surfaces each wrote by hand: `gembossUI.openModal` (Escape, backdrop, Tab trap, stacking, ask-before-close, focus back), `toast` (text, error tone), `initMenus` (outside click, item, blur, Escape closes the menu before the popup); 0.5.0: `confirm`, `initShell`, `initPasswordFields` / `resetPasswordFields`. A classic script: inline it or `<script src>`; see "Plain-HTML behaviour" above. |
|
|
1093
1263
|
| `packages/ui/src/collections.css` | 0.13.0: tables, lists, grids, boards and their states (see "Collections"). A separate entry, NOT inside `gemboss-ui.css`; the admin does not load it. |
|
|
1094
|
-
| `packages/ui/src/editor/` | 0.14.0, `@gemboss/ui/editor`: `EditorFrame`, `EditorPaneTabs`, `EditorTopbar`, `EditorBack`, `EditorChatToggle`, `EditorSaveState`, `DeviceToggle`. See "Editor frame". 0.17.0: `PanelTabs`, `PanelHeader`, `PanelAction`, `InspectorGroup`, `Field`, `Seg`, `ColorControl`, `RangeControl`, `EdgeBox`, `ImageControl`, `ChipButton`, `swatchState` (`./editor/swatch` too); 0.20.1 `cssUrl`. See "Settings panel". |
|
|
1264
|
+
| `packages/ui/src/editor/` | 0.14.0, `@gemboss/ui/editor`: `EditorFrame`, `EditorPaneTabs`, `EditorTopbar`, `EditorBack`, `EditorChatToggle`, `EditorSaveState`, `DeviceToggle`. See "Editor frame". 0.17.0: `PanelTabs`, `PanelHeader`, `PanelAction`, `InspectorGroup`, `Field`, `Seg`, `ColorControl`, `RangeControl`, `EdgeBox`, `ImageControl`, `ChipButton`, `swatchState` (`./editor/swatch` too); 0.20.1 `cssUrl`. See "Settings panel". 0.22.0: `LayerList`, `SortableLayerList`, `LayerTree`, `EditorDrawer`. See "Layers and drawer". |
|
|
1095
1265
|
| `packages/ui/src/editor-plain.js` | 0.14.0: `gembossUI.initEditorFrame()`, the frame's behaviour for plain HTML; 0.17.0: `gembossUI.initEditorPanel()`, the settings panel's. Load after `kit-plain.js`. |
|
|
1266
|
+
| `packages/ui/src/editor-layers-plain.js` | 0.22.0: `gembossUI.initEditorLayers()`, the layers', the outline's and the drawers' behaviour for plain HTML. Load after `kit-plain.js`. |
|
|
1096
1267
|
| `catalog/editor.html` | 0.14.0: the frame rendered from the components and running (`packages/ui/scripts/build-catalog-editor.mjs`), linked from `catalog/index.html`. |
|
|
1097
1268
|
| `packages/ui/src/editor.css` | 0.9.0: the editors (frame, top bar, preview, Configure dock, inspector and controls, Layers, settings drawer, page picker, menus). A separate entry, NOT inside `gemboss-ui.css`; the admin imports it from the editor components, never from a layout. See "Editor CSS" above. |
|
|
1098
1269
|
| `packages/ui/src/chat.css`, `chat-composer.css`, `assistant.css`, `auth.css`, `app-shell.css` | the chat body, the composer, the assistant frame, the sign-in screen, the topbar slots + base text + the page width tiers (`ui-page`, 0.8.1). All inside `gemboss-ui.css`; the admin @imports `chat.css` and `chat-composer.css` itself. |
|