@gemboss/ui 0.46.0 → 0.48.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 +90 -0
- package/README.md +59 -8
- package/catalog/editor.html +1 -1
- package/catalog/index.html +44 -40
- package/gemboss-ui.css +4 -4
- package/package.json +6 -1
- package/parts/components.css +2 -2
- package/parts/core.css +1 -1
- package/parts/shell.css +2 -2
- package/src/assistant-plain.js +1 -1
- package/src/assistant.css +1 -1
- package/src/index.ts +2 -0
- package/src/kit-plain.css +1 -1
- package/src/language-switcher-plain.js +1 -1
- package/src/language-switcher.css +1 -1
- package/src/logo-sheet.css +1 -0
- package/src/logo-sheet.tsx +63 -0
- package/src/portal.tsx +19 -4
- package/src/question-card.css +1 -1
- package/src/question-card.tsx +19 -4
- package/src/session-rail-plain.js +1 -0
- package/src/shell.css +1 -1
- package/src/topnav-fold.css +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,96 @@
|
|
|
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
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 0.48.0 (21/10/2026)
|
|
8
|
+
|
|
9
|
+
**LanguageSwitcher: what its first three surfaces had to work around** (21/10/2026; GemCommunity #333, GemNews #2,
|
|
10
|
+
GemFeedback #82).
|
|
11
|
+
|
|
12
|
+
Added
|
|
13
|
+
- `gembossUI.languageSwitcher()` (plain JS) returns `update({ value, labels, suggested, languages })` and
|
|
14
|
+
`setLabels(labels)`: the switcher changes where it is, no new element. The button's name and label, the menu's name and
|
|
15
|
+
the suggested hint change in place; the rows are drawn again only when `languages` or `suggested` change, and the row
|
|
16
|
+
that had focus has it again. An open menu stays open (placed again), focus stays where it was. `labels` given replace
|
|
17
|
+
those words only; `suggested: null` takes the row away. GemNews's `paintLang()` rebuilt the switcher (destroy + new,
|
|
18
|
+
focus carried by hand) every time its language changed. `set()`, `value()`, `open()`, `close()`, `destroy()` unchanged.
|
|
19
|
+
- `TopbarNav` `foot`: the app's own section at the foot of the FOLDED tab list, under a line (`.gb-topnav-foot`), and the
|
|
20
|
+
class `gb-topnav-unfolded` for its copy in the topbar's end, shown only while the tabs are unfolded. A portal topbar
|
|
21
|
+
(brand, tab menu, search, theme, Sign in) has no room for a 55px language button at 320–390: GemFeedback hid it and
|
|
22
|
+
printed links at the foot of the page. Now `foot={<LanguageSwitcher variant="inline" end={false} …/>}` plus
|
|
23
|
+
`<LanguageSwitcher className="gb-topnav-unfolded" …/>` in the end slot: one language control in the bar at every width.
|
|
24
|
+
- `TopbarNav` `foldAt={760}`: the tabs fold over the whole phone topbar (≤760px) instead of under 600px, for a bar whose end
|
|
25
|
+
holds more (GemFeedback's three tabs ran under the switcher at 601–687). Default 600, unchanged.
|
|
26
|
+
- `topnav-fold.css` (`@gemboss/ui/topnav-fold.css`), a new entry `TopbarNav` imports after `portal.css`: the rules for `foot`,
|
|
27
|
+
`foldAt`, `gb-topnav-unfolded` and the ellipsis below. `portal.css` is in the frozen `gemboss-ui.css`, which had 24 bytes
|
|
28
|
+
left under its ceiling. 0.3 KB gzip minified (ceiling 789 in `weight-budget.json`).
|
|
29
|
+
|
|
30
|
+
Changed
|
|
31
|
+
- The LanguageSwitcher's rows, hint and separator are one class (0,1,0) instead of zero specificity (`:where`), with the
|
|
32
|
+
core's values: an app's `button { font: inherit }` (0,0,1) beat the 13px rows (GemCommunity 14 / 16px, GemNews 16px; both
|
|
33
|
+
restated 13px). An app restyles them on purpose with one class of its own. The surface and the box stay `:where`.
|
|
34
|
+
- Every `--gemboss-*` token `language-switcher.css` reads has its light value as a fallback: a page with neither the core nor
|
|
35
|
+
`gemboss-tokens.css` draws the switcher right (light).
|
|
36
|
+
- `TopbarNav`'s folded button: the open tab's name sits in `span.gb-topnav-fold-label` and gives way with an ellipsis before
|
|
37
|
+
it runs under the topbar's end ("Changelog" at 320 overlapped the search button by 40px, with or without a switcher).
|
|
38
|
+
|
|
39
|
+
Fixed
|
|
40
|
+
- The switcher's tick touched the language's name on a page with app-shell.css and no core (GemCommunity's /welcome, for the
|
|
41
|
+
brand): app-shell.css's `.ui-menu__item` (0,1,0) reads `--ui-space-2` / `--ui-radius-sm`, unset there, and beat the row's
|
|
42
|
+
gap. The row's gap and corners are at two classes (`.gb-lang__item.ui-menu__item`, 8px / 6px). app-shell.css itself is
|
|
43
|
+
unchanged: the frozen gemboss-ui.css has 5 bytes left under its ceiling, and two fallbacks there cost 3.
|
|
44
|
+
- README: "the switcher fits the phone bar at 320–390" is AppShell's bar; a portal Topbar + TopbarNav puts it in the tab
|
|
45
|
+
menu (`foot`).
|
|
46
|
+
|
|
47
|
+
## 0.47.0 (21/10/2026)
|
|
48
|
+
|
|
49
|
+
**The kit's menu rows at the admin's density** (Chris 21/10/2026, of a menu drawn 44px tall: "nó đang to hơn bình thường";
|
|
50
|
+
0.43.0 did this for the editor's menus).
|
|
51
|
+
|
|
52
|
+
Changed
|
|
53
|
+
- `.ui-menu__item` (`Menu` / `MenuItem`, kit-plain menus, the assistant header's ⋯) and `.gemboss-account-menu-item`
|
|
54
|
+
(`AccountMenu`, the admin's account corner included) under a mouse: a Polaris ActionList item, 32px tall with 4px padding
|
|
55
|
+
above and below (were 44px with 12px, for every pointer). A kit menu's rows sit 6px in from a list padded 6
|
|
56
|
+
(`.ui-menu__list`, `.gb-dock-menu-list`: were 4), as an ActionList item in its section; the account menu's rows keep 16px at
|
|
57
|
+
the sides, under its head. Under a finger or on a screen 767px or narrower: 44px with 12px 16px, as before. The row states
|
|
58
|
+
`box-sizing: border-box` itself. `language-switcher.css` repeats the core's row for a page without the core (gemboss.ai), so it
|
|
59
|
+
follows: list padded 6, rows 32 / 4px 6px under a mouse, 44 / 12px 16px under a finger. The density gate measures both (`menuItem`, `menuSurface` in polaris-reference.json), red
|
|
60
|
+
on 0.46's rows. Measured on the admin (local): the account menu's four rows 176px → 128px, the assistant's ⋯ rows 44 → 32.
|
|
61
|
+
|
|
62
|
+
**A choice made by looking, and a logo sheet** (GemBoss Brand › Logo, 21/10/2026: the seller picks one of three AI logos from
|
|
63
|
+
GemMedia, sees each the way it will be used, and confirms one).
|
|
64
|
+
|
|
65
|
+
Added
|
|
66
|
+
- `QuestionCard`: a choice's `media` (a picture across the top of the choice, before its dot and words), `layout="tiles"`
|
|
67
|
+
(the choices side by side, as many as fit at 200px: three on a card, one on a phone) and a choice's `disabled` (shown greyed,
|
|
68
|
+
its radio off, never the confirmed value: the confirm stays off while a disabled choice is the value). Rows stay the default.
|
|
69
|
+
`question-card.css` +3 rules (`.ui-qcard__media`, `.ui-qcard--tiles …`), within its ceiling.
|
|
70
|
+
- `LogoSheet` (`packages/ui/src/logo-sheet.tsx`, `@gemboss/ui/logo-sheet.css`, its own entry): the mark on a checkerboard, the
|
|
71
|
+
name lockup on a light and on a dark header, the favicon at its real 32px beside the name. Spans and images only, so it sits
|
|
72
|
+
inside a choice. 0.5 KB gzip minified (ceiling 1,057 in `weight-budget.json`).
|
|
73
|
+
- `logo-sheet.test.tsx`, and `question-card.test.tsx` for tiles and a disabled choice.
|
|
74
|
+
|
|
75
|
+
**The thread rail for a page with no React** (GemFactory's full-screen Factory Assistant, 21/10/2026: the plain frame had no
|
|
76
|
+
way back to an earlier conversation).
|
|
77
|
+
|
|
78
|
+
Added
|
|
79
|
+
- `session-rail-plain.js`, `gembossUI.sessionRail(opts)` → `{ el, update, focusSearch, destroy }`: `SessionRail` in ES5, the
|
|
80
|
+
same markup and classes, the same search (marks folded), recency groups, inline rename, ⋯ menu and two-step delete; the
|
|
81
|
+
helpers as `gembossUI.assistantSessions` (`relativeTime`, `normalizeForSearch`, `groupSessions`). With no
|
|
82
|
+
`onToggleCollapsed` it keeps its own collapse, in localStorage under `storageKey`. Its own entry: 4.5 KB gzip minified
|
|
83
|
+
(ceiling 5,115 in `weight-budget.json`).
|
|
84
|
+
- `assistantFrame({ rail })`: the admin's `AssistantWorkspaceLayout` around the body (the rail in full screen only,
|
|
85
|
+
`is-dock` in every other shape, the body never moved) and `frame.setSwitching(on)`. Without `rail` the frame's markup is
|
|
86
|
+
as before. `assistant-plain.js` stays inside its ceiling.
|
|
87
|
+
- `gembossUI.fullChromeRoom(chatEl)` (`assistant-plain.js`): `chat.tsx`'s `useFullChromeRoom` for a page with no React. In full
|
|
88
|
+
screen the frame's floated buttons sat over the first message of a chat column (GemFactory `/assistant` with the rail, 1440);
|
|
89
|
+
how far they reach into the `.gb-chat` is written as `--gb-chat-chrome-w` / `-h` + `data-chrome`, which assistant-chat.css
|
|
90
|
+
already pads by. Cleared outside full screen and when they do not overlap.
|
|
91
|
+
- `assistant-plain-browser.test.ts`: the helpers against `sessions.tsx`; in Chromium the plain rail against `SessionRail`
|
|
92
|
+
over 20 steps at 1440 and 900 (the tree, the focus, the menu's place and every callback after each), the framed layout
|
|
93
|
+
against `AssistantHost` + `AssistantWorkspaceLayout` over 10 shapes, and two negative controls.
|
|
94
|
+
|
|
5
95
|
## 0.46.0 (21/10/2026)
|
|
6
96
|
|
|
7
97
|
**`.gemboss-page-section-content` is gone** (gemboss one panel P2b, 21/10/2026).
|
package/README.md
CHANGED
|
@@ -59,6 +59,7 @@ its CSS out of the build with it (`sideEffects`). gzip of the minified file, as
|
|
|
59
59
|
| auth | `@gemboss/ui/auth.css` | `packages/ui/src/auth.css` | the sign-in screen (+ the grid backdrop, 0.30.0) | 2.8 KB | `AuthLayout` & co. import it |
|
|
60
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
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
|
+
| topnav-fold | `@gemboss/ui/topnav-fold.css` | `packages/ui/src/topnav-fold.css` | `TopbarNav`'s folded menu after the frozen bundle (unreleased): `foot`, `foldAt={760}`, `gb-topnav-unfolded`, the open tab's ellipsis | 0.3 KB | `TopbarNav` imports it (after portal.css) |
|
|
62
63
|
| site | `@gemboss/ui/site.css` | `packages/ui/src/site.css` | the public site layer (0.40.0): the bar, the lockup at the site size, links, buttons, the footer | 1.0 KB | `SiteHeader` / `SiteBrand` import it; a plain-HTML page links it |
|
|
63
64
|
| 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 |
|
|
64
65
|
| collections | `@gemboss/ui/collections.css` | `packages/ui/src/collections.css` | tables, lists, grids, boards | 4.6 KB | `IndexTable`, `ResourceList`, `Board`… import it |
|
|
@@ -80,6 +81,7 @@ its CSS out of the build with it (`sideEffects`). gzip of the minified file, as
|
|
|
80
81
|
| range-bar | `@gemboss/ui/range-bar.css` | `packages/ui/src/range-bar.css` | `RangeBar`: a value against a market's spread on one axis (0.43.0) | 0.6 KB | `RangeBar` imports it |
|
|
81
82
|
| progress-ring | `@gemboss/ui/progress-ring.css` | `packages/ui/src/progress-ring.css` | `ProgressRing`: "3 of 5" as a ring (0.43.0) | 0.2 KB | `ProgressRing` imports it |
|
|
82
83
|
| spot-art | `@gemboss/ui/spot-art.css` | `packages/ui/src/spot-art.css` | `SpotArt`: six brand pictures for pauses and empty states (0.43.0) | 0.4 KB | `SpotArt` imports it |
|
|
84
|
+
| logo-sheet | `@gemboss/ui/logo-sheet.css` | `packages/ui/src/logo-sheet.css` | `LogoSheet`: one logo as a person judges it, the mark on a checkerboard, the lockup on a light and a dark header, the favicon at its real 32px (unreleased) | 0.5 KB | `LogoSheet` imports it |
|
|
83
85
|
| bundle | `@gemboss/ui/gemboss-ui.css` | `gemboss-ui.css` | core + components + shell + auth + assistant + chat + portal + theme-dark | 21.9 KB | compatibility only (see CHANGELOG 0.23.0) |
|
|
84
86
|
|
|
85
87
|
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).
|
|
@@ -333,7 +335,27 @@ The page reloads on every click, so the frame keeps the shape on screen for the
|
|
|
333
335
|
`<prefix>.presentation`, `keepAcrossPages: false` to turn it off): open on one page, open on the next. A run does not
|
|
334
336
|
survive the reload, and leaving the full-screen page leaves the status bar, as `leave-full` does. The full-screen page
|
|
335
337
|
is yours (`fullHref`); the frame enters full there by itself. `launchers` are hidden while the frame is up and open it.
|
|
336
|
-
Not in the plain frame yet: `secondTab
|
|
338
|
+
Not in the plain frame yet: `secondTab`, page facts and the popup's open-chat tabs (`OpenChatTabs`).
|
|
339
|
+
|
|
340
|
+
**The thread rail, with no React (0.47.0): `session-rail-plain.js`.** `SessionRail` for the same pages: the same
|
|
341
|
+
markup and classes (shell.css draws both), the search box (case and Vietnamese marks folded), the recency groups, inline
|
|
342
|
+
rename, the ⋯ menu and its two-step delete. The sessions, and what opening, renaming, pinning or deleting one does, stay
|
|
343
|
+
the page's. Hand it to the frame as `rail` and the frame builds the admin's `AssistantWorkspaceLayout` around the body:
|
|
344
|
+
the rail in full screen only, `is-dock` in the popup and the panel, the body never moved. `frame.setSwitching(true)` dims
|
|
345
|
+
the page while the next thread loads; `gembossUI.fullChromeRoom(chatEl)` keeps the first message out from under the
|
|
346
|
+
full-screen buttons (as `useFullChromeRoom` does for `AssistantChat`). The rail is its own file and its own ceiling (4.5 KB gzip), so a page with no full-screen
|
|
347
|
+
assistant does not pay for it. `assistant-plain-browser.test.ts` holds it to `SessionRail` over 20 steps (typing, Escape,
|
|
348
|
+
the menu, rename, pin, delete, collapse, an empty list) and the framed layout to `AssistantHost` + `AssistantWorkspaceLayout`.
|
|
349
|
+
|
|
350
|
+
```html
|
|
351
|
+
<script src="kit-plain.js"></script><script src="session-rail-plain.js"></script><script src="assistant-plain.js"></script>
|
|
352
|
+
<script>
|
|
353
|
+
const rail = gembossUI.sessionRail({ sessions, activeId, onNewChat, onOpen: openThread, onRename: rename,
|
|
354
|
+
onDelete: remove, titleMaxLength: 120, storageKey: "gemfactory.assistant.railCollapsed" });
|
|
355
|
+
const frame = gembossUI.assistantFrame({ title: "Factory Assistant", body: chatElement, rail, fullHref: "/assistant" });
|
|
356
|
+
rail.update({ sessions: fresh, activeId: id }); // after a turn, a rename, a delete
|
|
357
|
+
</script>
|
|
358
|
+
```
|
|
337
359
|
|
|
338
360
|
## The engine picker (0.25.0): `EnginePicker`, `BridgeNotice`
|
|
339
361
|
|
|
@@ -1442,8 +1464,8 @@ import { FLEET_LANGUAGES, LanguageSwitcher, matchLanguage } from "@gemboss/ui";
|
|
|
1442
1464
|
| The menu | the kit's, not a new one: `details.ui-menu` (it opens with no JavaScript), its list `ui-menu__list gemboss-popover-surface` floated in the top layer by `./menu-layer` as `Menu`'s is, its rows `ui-menu__item`, so it looks like the account menu. `role="menu"` named `trigger`; each row a `menuitemradio` with `aria-checked`, `data-lang`, a check column (`.gb-lang__check`, shown on the checked row only) and the name in its own `lang` (`span.gb-lang__name lang="zh"`), so a screen reader says "中文" in Chinese. The suggested row carries `ui-menu__hint`, then `hr.ui-menu__sep` |
|
|
1443
1465
|
| Keys | an opening (a click, Enter / Space, ↓ / ↑ on the button) puts focus on the checked row; ↑ ↓ wrap, Home / End; Escape closes it, focus back on the button; Tab closes it and focus goes on from the button, forward or back; a click outside closes it; a row closes it, focus on the button, and calls `onChange` when it is not the current one. Rows are out of the Tab order (`tabindex="-1"`), as a menu's are |
|
|
1444
1466
|
| Helpers | `matchLanguage(languages, wanted)`: the first of `navigator.languages`, an Accept-Language header or one tag that the list has, exactly or by its base language ("zh-CN" → "zh"), else undefined; pure, so a server reads the header with it. `languageShort(language)` |
|
|
1445
|
-
| CSS | `language-switcher.css`, its own entry, 1.1 KB gzip, imported by the component. The menu's base rules (the core's `ui-menu` ones) are repeated there at zero specificity (`:where`)
|
|
1446
|
-
| The topbar | at 320 / 360 / 390 the
|
|
1467
|
+
| CSS | `language-switcher.css`, its own entry, 1.1 KB gzip, imported by the component. The menu's base rules (the core's `ui-menu` ones) are repeated there with the core's values, so a page that links only this file (gemboss.ai, whose pages the core would reset; GemNews's extension) draws the same menu. The surface and the box at zero specificity (`:where`); the rows, the hint and the separator at ONE class (0,1,0, since 22/10/2026), the core's own weight: an app's `button { font: inherit }` (0,0,1) cannot make them 16px, and one class of the app's (`.gb-lang__item { … }` after it, `.my-bar .gb-lang__item`) restyles them on purpose (the gap and corners take two: `.gb-lang__item.ui-menu__item`, so app-shell.css's unset tokens on a page without the core cannot zero the gap). Tokens, so `data-theme="dark"` follows, each with its light value as the fallback: no core, no `gemboss-tokens.css`, still right (light) |
|
|
1468
|
+
| The topbar | AppShell's phone bar (☰, brand, search, toggle, switcher, account chip) fits it at 320 / 360 / 390, between ThemeToggle and the chip, no horizontal scroll. A portal `Topbar` with `TopbarNav` does NOT have that room (GemFeedback: brand, tab menu, search, theme, Sign in, no room for a 55px button at 320–390, and at 601–687 the tabs ran under it): there the switcher goes in the folded tab menu, `TopbarNav foot`, and its end-slot copy wears `gb-topnav-unfolded` (README "Public portal pieces" › TopbarNav) |
|
|
1447
1469
|
|
|
1448
1470
|
### Plain HTML: `gembossUI.languageSwitcher()` (21/10/2026)
|
|
1449
1471
|
|
|
@@ -1474,7 +1496,8 @@ the page has it; it holds no closing script tag, so a server can inline it.
|
|
|
1474
1496
|
| | |
|
|
1475
1497
|
| --- | --- |
|
|
1476
1498
|
| Options | the component's props, same names and defaults (`value`, `languages`, `onChange`, `suggested`, `labels`, `variant`, `end`, `className`), plus `mount`: an element the switcher REPLACES (a placeholder the server wrote) |
|
|
1477
|
-
| Returns | `{ el, value(), set(code), open(), close(), destroy() }`. A chosen row updates the switcher itself before `onChange` (the component waits for its `value` prop); `set` is for a change from elsewhere; `destroy` removes it and its two document listeners |
|
|
1499
|
+
| Returns | `{ el, value(), set(code), update(props), setLabels(labels), open(), close(), destroy() }`. A chosen row updates the switcher itself before `onChange` (the component waits for its `value` prop); `set` is for a change from elsewhere; `destroy` removes it and its two document listeners |
|
|
1500
|
+
| `update` / `setLabels` (22/10/2026) | the page's language changed, or its list: `sw.setLabels({ trigger: T("menu.lang"), suggested: T("menu.lang_suggested") })`, `sw.update({ value, labels, suggested, languages })` (any of them). In place, as the component re-renders: the button's name and label, the menu's name, the hint; the rows drawn again only when `languages` or `suggested` change, the row that had focus focused again. An open menu stays open, placed again; the same `el`. `labels` given replace those words only; `suggested: null` takes the row away. No more destroy + new + carrying the focus by hand (GemNews's `paintLang`) |
|
|
1478
1501
|
| `gembossUI` | `languageSwitcher`, `FLEET_LANGUAGES`, `LANGUAGE_SWITCHER_LABELS`, `matchLanguage`, `languageShort`. With `kit-plain.js` the open list floats in the top layer (`gembossUI.floatMenu`) and `initMenus` closes it like any kit menu; without it the list hangs under the button (`position: absolute`), as every kit menu does where there is no popover API |
|
|
1479
1502
|
| A Chrome extension | a popup or options page loads both files by `src` (no inline script: an extension's CSP forbids it) and calls it from its own script; `onChange` writes `chrome.storage` |
|
|
1480
1503
|
|
|
@@ -1484,10 +1507,18 @@ two DOMs node for node, attribute for attribute, with focus, open state and the
|
|
|
1484
1507
|
script: closed, opened by a click, ↓ ↓ Home End ↑, Escape, ↓ on the button, Home + Enter, the current row, a click outside,
|
|
1485
1508
|
Tab; with the defaults and with Vietnamese labels, a suggested row, the inline variant, left-aligned, links. NEGATIVE
|
|
1486
1509
|
CONTROL: one `lang` changed on the plain page and the comparison sees it. Then: the short label per language, the list
|
|
1487
|
-
under the button's right edge, one tick; 36 / 44px and
|
|
1510
|
+
under the button's right edge, one tick; 36 / 44px and rows of 44 under a finger; the phone bar at 390 / 360 / 320; contrast in both
|
|
1488
1511
|
themes (label ≥ 4.5:1, tick ≥ 3:1); the standalone page's menu equal to the core's (negative control: without the file,
|
|
1489
|
-
the browser's popover box). `
|
|
1490
|
-
|
|
1512
|
+
the browser's popover box). 22/10/2026: `update` / `setLabels` with the menu open and a row focused (the plain DOM equal to
|
|
1513
|
+
the component's re-render, focus and open state kept); an app's `button { font: inherit; font-size: 16px; padding: 0 }`,
|
|
1514
|
+
`a {}` and `hr {}` before or after the kit, and a page with only this file (+ app-shell.css): 13px rows at the core's
|
|
1515
|
+
padding (4px 6px under a mouse, 12px 16px under a finger), the tick 8px from the name (negative controls: the rows back at
|
|
1516
|
+
`:where` → 16px; without the two-class gap rule, app-shell.css after it → 0px gap);
|
|
1517
|
+
GemFeedback's portal bar with the switcher in `TopbarNav foot`, `foldAt={760}`: one language control at 320–1024, nothing
|
|
1518
|
+
overlapping or cut (negative control: at 320 without the foot the tab menu is squeezed out).
|
|
1519
|
+
`LANGUAGE_SWITCHER_SHOTS=<dir>` writes the screenshots (closed / open, both variants, both themes, vi / en / zh).
|
|
1520
|
+
`language-switcher.test.tsx` holds the markup, the labels, the helpers, the plain file's defaults and the CSS contract (the
|
|
1521
|
+
rows at one class, every token with its fallback).
|
|
1491
1522
|
|
|
1492
1523
|
## Repeater rows (16/10/2026): `RepeaterList`, `useKeyedRows`
|
|
1493
1524
|
|
|
@@ -1675,7 +1706,25 @@ import { Avatar, Dialog, Leaderboard, Podium, SearchDialog, ThemeToggle, themeSc
|
|
|
1675
1706
|
- `TopbarNav`: section tabs in the topbar after the brand, the active one underlined on the topbar's
|
|
1676
1707
|
edge. A topbar holding tabs drops the centre search column (`:has(.gb-topnav)`), so the tabs get the
|
|
1677
1708
|
room. 36px under a mouse, 44px under a finger. Under 600px the tabs fold into one button naming the
|
|
1678
|
-
open tab, with the tabs in a list under it (a row that scrolls sideways hid the third tab at 390).
|
|
1709
|
+
open tab, with the tabs in a list under it (a row that scrolls sideways hid the third tab at 390). The open tab's name
|
|
1710
|
+
gives way with an ellipsis before it runs under the topbar's end (22/10/2026: "Changelog" at 320).
|
|
1711
|
+
- `foot` (22/10/2026): the app's own section at the foot of the folded list, under a line; shown only folded. What the
|
|
1712
|
+
topbar's end has no room for once the tabs fold, the language first. Its copy in the end slot wears
|
|
1713
|
+
`gb-topnav-unfolded`, hidden wherever the tabs fold, so the bar has one at every width, one at a time:
|
|
1714
|
+
|
|
1715
|
+
```tsx
|
|
1716
|
+
const Lang = (p: Partial<LanguageSwitcherProps>) => <LanguageSwitcher value={lang} languages={withHrefs} {...p} />;
|
|
1717
|
+
<div className="gemboss-topbar-start"><BrandLockup name="GemFeedback" href="/" />
|
|
1718
|
+
<TopbarNav as={Link} items={tabs} foldAt={760} foot={<Lang variant="inline" end={false} />} /></div>
|
|
1719
|
+
<div className="gemboss-topbar-center" />
|
|
1720
|
+
<div className="gemboss-topbar-end"><SearchDialog … /><ThemeToggle /><Lang className="gb-topnav-unfolded" /><SignInButton … /></div>
|
|
1721
|
+
```
|
|
1722
|
+
- `foldAt={760}`: fold over the whole phone topbar (≤760px) instead of under 600px, for a bar whose end holds more
|
|
1723
|
+
(GemFeedback signed out: search, theme, language, Sign in; its three tabs ran under the switcher at 601–687). Measured
|
|
1724
|
+
in Chromium: brand + folded tabs + search + theme + Sign in fit from 320 with the language in the foot, and from 761 the
|
|
1725
|
+
tabs + all four controls.
|
|
1726
|
+
- These three and the ellipsis are `topnav-fold.css`, an entry of their own that `TopbarNav` imports after `portal.css`
|
|
1727
|
+
(which is in the frozen `gemboss-ui.css`). A page that renders TopbarNav's markup without the component links it.
|
|
1679
1728
|
- `SearchDialog`: the topbar search button opening a 640px popup (Featurebase's, the admin's ⌘K).
|
|
1680
1729
|
`initial` before typing, `search(q)` while typing (150 ms debounce, latest answer wins); ↑ ↓ Enter
|
|
1681
1730
|
Esc; ⌘K / Ctrl+K and "/" open it. Results are links; without JavaScript the button goes to
|
|
@@ -2456,6 +2505,8 @@ entry, each presentational and server-renderable (no state, no effect):
|
|
|
2456
2505
|
| `RangeBar` | a value against a market's spread on one axis: `band` (where half of it sits), `mid`, `you`, `ticks`. `size="sm"` is the shape for a metric tile; `size="lg"` adds the labels. The surface passes the words and the numbers (`labels`, `format`): the bar does no money maths. `rangeBarGeometry` is the pure part, for a plain-HTML page. |
|
|
2457
2506
|
| `ProgressRing` | "3 of 5" as a ring with the count inside. `progressRingGeometry` is the pure arc. A `max` that is not a positive number is an empty ring, never NaN. |
|
|
2458
2507
|
| `SpotArt` | six pictures in the brand's gem drawing style (`gem-radar`, `orbit`, `people`, `page`, `plug`, `price`) for the pauses and empty states a text-only screen lacks. Painted by tokens, decorative by default, an image only when given a `title`. |
|
|
2508
|
+
| `QuestionCard` choices made by looking (unreleased) | a choice's `media` is a picture across the top of it (spans and images: the choice is a `<label>`), `layout="tiles"` puts the choices side by side (three on a card, one column on a phone), and a choice's `disabled` shows it greyed with its radio off (an option that failed its checks: seen, not choosable); the confirm stays off while a disabled choice is the value. GemBoss Brand › Logo is the first surface: three AI logos, one confirmed. |
|
|
2509
|
+
| `LogoSheet` (unreleased) | one logo the way it has to be judged before it becomes a store's: the mark on a checkerboard (a white box around it shows), the name lockup on a light header and on a dark one (`lockup`, `lockupDark`), and the favicon at 32 × 32 CSS px beside the name, as a tab shows it. Every picture is a file the surface already has; nothing is drawn or scaled. Spans only, so it can be a `QuestionCard` choice's `media`. |
|
|
2459
2510
|
| `question-card.css`, `briefing-card.css`, `range-bar.css`, `progress-ring.css`, `spot-art.css` | one entry each, loaded by its component; a plain-HTML surface links `@gemboss/ui/<name>.css` and writes the classes listed at the top of each file |
|
|
2460
2511
|
|
|
2461
2512
|
Three lighter steps of iris came with them, because a wash, a band and an art stroke need more than `--gemboss-accent-soft`:
|
package/catalog/editor.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
.cat-code summary{padding:10px 16px;font-size:13px;cursor:pointer;color:var(--gemboss-text-muted);border-top:1px solid var(--gemboss-divider);min-height:24px}
|
|
9
9
|
.cat-code pre,.cat-api{margin:0;padding:12px 16px;overflow:auto;background:#0f1012;color:#e8e8e6;font-size:12px;line-height:1.5;border-radius:0}
|
|
10
10
|
.cat-api{border-radius:12px;margin:0 0 16px}</style></head><body><div class="cat">
|
|
11
|
-
<h1>@gemboss/ui 0.
|
|
11
|
+
<h1>@gemboss/ui 0.48.0 · Editor</h1>
|
|
12
12
|
<p>Khung của mọi editor: <code>EditorFrame</code> (cột trái · khe kéo · preview · cột phải dock trên mép phải preview) và <code>EditorTopbar</code> (← · tiêu đề · cụm giữa · cụm phải · dòng trạng thái). Là đúng khung của theme / page / section editor trong admin. Gói chỉ vẽ KHUNG; chat, Configure, canvas, page picker, menu, Publish là của editor, đi vào bằng slot. Trang HTML thuần nạp <code>editor.css</code> + <code>editor-plain.js</code> rồi gọi <code>gembossUI.initEditorFrame()</code>: kéo khe (chuột, chạm, phím mũi tên / Home / End), đổi thiết bị, mở ⋯ và Publish ▾, ẩn cột trái, Chat | Preview trên điện thoại. <a href="index.html">← Catalog</a></p>
|
|
13
13
|
<h2>React</h2><pre class="cat-api"><code>import "@gemboss/ui/editor.css";
|
|
14
14
|
import { EditorFrame, EditorTopbar, EditorBack, EditorChatToggle, EditorPaneTabs, EditorSaveState, DeviceToggle } from "@gemboss/ui/editor";
|