@gemboss/ui 0.45.0 → 0.47.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 +111 -0
- package/README.md +129 -1
- package/catalog/editor.html +1 -1
- package/catalog/index.html +229 -86
- package/gemboss-ui.css +2 -2
- package/package.json +10 -2
- package/parts/components.css +1 -1
- package/parts/core.css +1 -1
- package/parts/shell.css +1 -1
- package/src/assistant-plain.js +1 -1
- package/src/editor/layers.tsx +1 -1
- package/src/editor.css +1 -1
- package/src/icons.tsx +2 -0
- package/src/index.ts +8 -1
- package/src/language-switcher-plain.js +1 -0
- package/src/language-switcher.css +1 -0
- package/src/language-switcher.tsx +206 -0
- package/src/logo-sheet.css +1 -0
- package/src/logo-sheet.tsx +63 -0
- package/src/question-card.css +1 -1
- package/src/question-card.tsx +19 -4
- package/src/session-rail-plain.js +1 -0
- package/src/setup-guide.css +1 -1
- package/src/setup-guide.tsx +92 -22
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,117 @@
|
|
|
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.47.0 (21/10/2026)
|
|
6
|
+
|
|
7
|
+
**A choice made by looking, and a logo sheet** (GemBoss Brand › Logo, 21/10/2026: the seller picks one of three AI logos from
|
|
8
|
+
GemMedia, sees each the way it will be used, and confirms one).
|
|
9
|
+
|
|
10
|
+
Added
|
|
11
|
+
- `QuestionCard`: a choice's `media` (a picture across the top of the choice, before its dot and words), `layout="tiles"`
|
|
12
|
+
(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,
|
|
13
|
+
its radio off, never the confirmed value: the confirm stays off while a disabled choice is the value). Rows stay the default.
|
|
14
|
+
`question-card.css` +3 rules (`.ui-qcard__media`, `.ui-qcard--tiles …`), within its ceiling.
|
|
15
|
+
- `LogoSheet` (`packages/ui/src/logo-sheet.tsx`, `@gemboss/ui/logo-sheet.css`, its own entry): the mark on a checkerboard, the
|
|
16
|
+
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
|
|
17
|
+
inside a choice. 0.5 KB gzip minified (ceiling 1,057 in `weight-budget.json`).
|
|
18
|
+
- `logo-sheet.test.tsx`, and `question-card.test.tsx` for tiles and a disabled choice.
|
|
19
|
+
|
|
20
|
+
**The thread rail for a page with no React** (GemFactory's full-screen Factory Assistant, 21/10/2026: the plain frame had no
|
|
21
|
+
way back to an earlier conversation).
|
|
22
|
+
|
|
23
|
+
Added
|
|
24
|
+
- `session-rail-plain.js`, `gembossUI.sessionRail(opts)` → `{ el, update, focusSearch, destroy }`: `SessionRail` in ES5, the
|
|
25
|
+
same markup and classes, the same search (marks folded), recency groups, inline rename, ⋯ menu and two-step delete; the
|
|
26
|
+
helpers as `gembossUI.assistantSessions` (`relativeTime`, `normalizeForSearch`, `groupSessions`). With no
|
|
27
|
+
`onToggleCollapsed` it keeps its own collapse, in localStorage under `storageKey`. Its own entry: 4.5 KB gzip minified
|
|
28
|
+
(ceiling 5,115 in `weight-budget.json`).
|
|
29
|
+
- `assistantFrame({ rail })`: the admin's `AssistantWorkspaceLayout` around the body (the rail in full screen only,
|
|
30
|
+
`is-dock` in every other shape, the body never moved) and `frame.setSwitching(on)`. Without `rail` the frame's markup is
|
|
31
|
+
as before. `assistant-plain.js` stays inside its ceiling.
|
|
32
|
+
- `gembossUI.fullChromeRoom(chatEl)` (`assistant-plain.js`): `chat.tsx`'s `useFullChromeRoom` for a page with no React. In full
|
|
33
|
+
screen the frame's floated buttons sat over the first message of a chat column (GemFactory `/assistant` with the rail, 1440);
|
|
34
|
+
how far they reach into the `.gb-chat` is written as `--gb-chat-chrome-w` / `-h` + `data-chrome`, which assistant-chat.css
|
|
35
|
+
already pads by. Cleared outside full screen and when they do not overlap.
|
|
36
|
+
- `assistant-plain-browser.test.ts`: the helpers against `sessions.tsx`; in Chromium the plain rail against `SessionRail`
|
|
37
|
+
over 20 steps at 1440 and 900 (the tree, the focus, the menu's place and every callback after each), the framed layout
|
|
38
|
+
against `AssistantHost` + `AssistantWorkspaceLayout` over 10 shapes, and two negative controls.
|
|
39
|
+
|
|
40
|
+
## 0.46.0 (21/10/2026)
|
|
41
|
+
|
|
42
|
+
**`.gemboss-page-section-content` is gone** (gemboss one panel P2b, 21/10/2026).
|
|
43
|
+
|
|
44
|
+
Removed
|
|
45
|
+
- The rule that made every button, input and textarea 44px inside `.gemboss-page-section-content`. That class was the admin
|
|
46
|
+
page editor's "Edit section content" drawer, which P2b replaced with the section's column on the Settings tab (the Canvas's
|
|
47
|
+
own). No element carries it any more, so the rule matched nothing (gemboss `a-tap-target-rule-outlived-its-markup`). The
|
|
48
|
+
page editor's dock rule (`.gemboss-pageedit .gemboss-ec-dock-body`) still sizes its controls.
|
|
49
|
+
|
|
50
|
+
**One language button for the fleet: `LanguageSwitcher`, and the button says the language** (Chris 21/10/2026: "bộ switch
|
|
51
|
+
ngôn ngữ ở gemcommunity, gemnews … chuẩn hoá ở gemboss ui để khi chọn một ngôn ngữ nó hiển thị lên trên cái icon luôn ví dụ
|
|
52
|
+
là VI, EN hay chữ Zhong của trung quốc"). Each surface drew its own; GemCommunity's topbar was a globe alone.
|
|
53
|
+
|
|
54
|
+
Added
|
|
55
|
+
- `LanguageSwitcher` (language-switcher.tsx, exported from the root): the kit's `details.ui-menu`, its button the `globe`
|
|
56
|
+
and the current language's short label ("EN", "VI", "中"), named "Language: Tiếng Việt"; the menu lists each language
|
|
57
|
+
by its native name in its own `lang`, the current one checked (`menuitemradio`), an optional `suggested` row first.
|
|
58
|
+
Props `value`, `languages`, `onChange(code)` (the app stores it), `suggested`, `labels` (`trigger`, `suggested`),
|
|
59
|
+
`variant` (`topbar`: ThemeToggle's 36 / 44px ghost; `inline`: a pill with a caret), `end`, `className`. A language with
|
|
60
|
+
an `href` is a link (`hreflang`). Keys: an opening focuses the checked row, ↑ ↓ wrap, Home / End, Escape back to the
|
|
61
|
+
button, Tab leaves; a click outside closes it. The list floats in the top layer through `./menu-layer`, as `Menu`'s.
|
|
62
|
+
- `FLEET_LANGUAGES` (en English EN, vi Tiếng Việt VI, zh 中文 中), `LANGUAGE_SWITCHER_LABELS`, `matchLanguage(languages,
|
|
63
|
+
navigator.languages | Accept-Language)`, `languageShort(language)`; types `Language`, `LanguageSwitcherLabels`,
|
|
64
|
+
`LanguageSwitcherProps`.
|
|
65
|
+
- `language-switcher.css` (`@gemboss/ui/language-switcher.css`), its own entry, 1.1 KB gzip (ceiling 1,593): the button,
|
|
66
|
+
both variants, the check column; the core's menu rules repeated at zero specificity, so a page with only the tokens and
|
|
67
|
+
this file (gemboss.ai) draws the same menu, and where the core is loaded nothing changes.
|
|
68
|
+
- `language-switcher-plain.js` (`@gemboss/ui/language-switcher-plain.js`), its own file, 2.3 KB gzip (ceiling 2,902):
|
|
69
|
+
`gembossUI.languageSwitcher(opts)` draws the component's DOM for a page with no React (`mount` replaces a placeholder)
|
|
70
|
+
and returns `{ el, value(), set(code), open(), close(), destroy() }`; with `FLEET_LANGUAGES`, `LANGUAGE_SWITCHER_LABELS`,
|
|
71
|
+
`matchLanguage`, `languageShort`.
|
|
72
|
+
- Icon `chevronDown` (the inline variant's caret).
|
|
73
|
+
|
|
74
|
+
Tests
|
|
75
|
+
- `language-switcher.test.tsx`: the markup and ARIA, the three short labels ("中", not "ZH"), translated labels, the
|
|
76
|
+
suggested row and separator, links, an unknown value, strings as text, the helpers, the plain file's defaults against
|
|
77
|
+
the TypeScript, no closing script tag in it, the CSS's classes, 44px rule and tokens only.
|
|
78
|
+
- `language-switcher-browser.test.ts` (`test:ui-browser`): in Chromium, the component and the plain switcher on the same
|
|
79
|
+
topbar and site header, DOM, focus, open state and `onChange` compared after each of 14 steps (keys, Escape, Enter, the
|
|
80
|
+
current row, a click outside, Tab), with defaults and with labels / suggested / inline / left / links; negative control
|
|
81
|
+
one `lang` changed. The label per language and the list's place; 36 / 44px, 44px rows; the phone bar at 390 / 360 / 320;
|
|
82
|
+
contrast in both themes; the standalone menu equal to the core's (negative control without the file).
|
|
83
|
+
|
|
84
|
+
**One onboarding checklist for GemBoss and GemCommunity** (21/10/2026, Chris: "cái onboarding checklist của gemcommunity
|
|
85
|
+
với của gemboss nút tick màu xanh progress bar nên đồng bộ nhé để dùng cùng một gemboss ui kit"). The admin's "Set up your
|
|
86
|
+
store" drew grey ✓ glyphs and a near-black bar in Polaris; GemCommunity's "Start here" drew filled green ticks, an iris bar
|
|
87
|
+
and a "✓ 2 completed" fold in its own markup. Both now render `SetupGuide` (README "Setup guide").
|
|
88
|
+
|
|
89
|
+
Changed
|
|
90
|
+
- The tick is an 18px ring; a done step fills it with `--gemboss-success` and a white check drawn by two borders (no
|
|
91
|
+
glyph, so it is the same tick in every app); `unknown` is a dashed ring with its `?`. The glyphs stay in the markup for a
|
|
92
|
+
page without the sheet.
|
|
93
|
+
- The bar fills in the same green from the first step (it was the accent, green only at the end); the
|
|
94
|
+
`ui-setup__bar--done` class is no longer rendered.
|
|
95
|
+
- The steps done when the guide first renders fold behind one "✓ N completed" row (`<details class="ui-setup__done">`, not
|
|
96
|
+
in the steps' `name` group, so opening it never shuts the open step). Decided once, like the open step: a step finished
|
|
97
|
+
on screen stays in the list. `foldDone={false}` lists them as before.
|
|
98
|
+
- The count's amber/green tones ship in `setup-guide.css` (scoped to `.ui-setup`): the admin loads `ui.css` without
|
|
99
|
+
`kit-plain.css`, where the badge tones live, and drew a grey count.
|
|
100
|
+
- The open step's body is indented 30px (18px mark + 12px gap).
|
|
101
|
+
|
|
102
|
+
Added
|
|
103
|
+
- `optional` (steps under a hairline and an "Optional" heading, never counted), `onDismiss` (a × at the end of the head,
|
|
104
|
+
44px under a thumb; a dismissible guide does not also fold), `SetupStep.action` (the step's control at the end of a
|
|
105
|
+
plain row, or under its body), `SetupStep.className`, `labels.completed` / `labels.optional` / `labels.dismiss`, and the
|
|
106
|
+
`SetupGuideLabels` type. Under a thumb a step's buttons are 44px too (the admin's rule, now the kit's).
|
|
107
|
+
|
|
108
|
+
Tests
|
|
109
|
+
- `setup-guide.test.tsx`: the fold (order, count, outside the group, `foldDone={false}`, nothing done → no fold), the
|
|
110
|
+
optional group never counted, `action` never inside a `<summary>`, `onDismiss` markup, one green token for tick and bar,
|
|
111
|
+
the × in the coarse block (with its negative control). `setup-guide-browser.test.tsx`: computed colours of the done tick,
|
|
112
|
+
its check and the bar, the todo ring; a step finished on screen stays out of the fold; opening the fold keeps the open
|
|
113
|
+
step open; the × dismisses; the ×, the rows and the fold are 44px at 390 (negative control: without the rule the × is 28).
|
|
114
|
+
- Catalog "Hướng dẫn thiết lập": the admin's and GemCommunity's guides as demos.
|
|
115
|
+
|
|
5
116
|
## 0.45.0 (21/10/2026)
|
|
6
117
|
|
|
7
118
|
**The reaction picker speaks the app's language** (21/10/2026, seen on GemCommunity's Vietnamese chat with 0.43.0: the
|
package/README.md
CHANGED
|
@@ -73,12 +73,14 @@ its CSS out of the build with it (`sideEffects`). gzip of the minified file, as
|
|
|
73
73
|
| setup-guide | `@gemboss/ui/setup-guide.css` | `packages/ui/src/setup-guide.css` | `SetupGuide` (0.30.0) | 0.9 KB | `SetupGuide` imports it |
|
|
74
74
|
| reader-dialog | `@gemboss/ui/reader-dialog.css` | `packages/ui/src/reader-dialog.css` | `ReaderDialog`: the popup sized for reading a document (0.41.0) | 0.4 KB | `ReaderDialog` imports it |
|
|
75
75
|
| service-switcher | `@gemboss/ui/service-switcher.css` | `packages/ui/src/service-switcher.css` | `ServiceSwitcher` and `Switcher`: the button after the brand and its panel (0.36.0; any list, illustrated tiles 0.40.0) | 2.2 KB | `ServiceSwitcher` imports it, and `BrandLockup` through it (README "ServiceSwitcher") |
|
|
76
|
+
| language-switcher | `@gemboss/ui/language-switcher.css` | `packages/ui/src/language-switcher.css` | `LanguageSwitcher`: the globe + "EN" / "VI" / "中" and the menu of languages (21/10/2026; works without the core) | 1.1 KB | `LanguageSwitcher` imports it; a plain-HTML page links it with `language-switcher-plain.js` (README "LanguageSwitcher") |
|
|
76
77
|
| team-chat | `@gemboss/ui/team-chat.css` | `packages/ui/src/team-chat.css` | the team chat layer (20/10/2026): rail, messages, composer, thread and people panels, the screen's layout | 3.6 KB | `TeamChat` and its pieces import it (with `Avatar`'s portal.css and `Reactions`' reactions.css); a plain-HTML page links it (README "The team chat layer") |
|
|
77
78
|
| question-card | `@gemboss/ui/question-card.css` | `packages/ui/src/question-card.css` | `QuestionCard`: the assistant's one question with one to three answers, the pick chosen (0.43.0) | 0.9 KB | `QuestionCard` imports it |
|
|
78
79
|
| briefing-card | `@gemboss/ui/briefing-card.css` | `packages/ui/src/briefing-card.css` | `BriefingCard`: one proposal at the top of a screen, on an iris wash (0.43.0) | 0.5 KB | `BriefingCard` imports it |
|
|
79
80
|
| 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 |
|
|
80
81
|
| 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 |
|
|
81
82
|
| 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 |
|
|
83
|
+
| 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 |
|
|
82
84
|
| 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) |
|
|
83
85
|
|
|
84
86
|
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).
|
|
@@ -332,7 +334,27 @@ The page reloads on every click, so the frame keeps the shape on screen for the
|
|
|
332
334
|
`<prefix>.presentation`, `keepAcrossPages: false` to turn it off): open on one page, open on the next. A run does not
|
|
333
335
|
survive the reload, and leaving the full-screen page leaves the status bar, as `leave-full` does. The full-screen page
|
|
334
336
|
is yours (`fullHref`); the frame enters full there by itself. `launchers` are hidden while the frame is up and open it.
|
|
335
|
-
Not in the plain frame yet: `secondTab
|
|
337
|
+
Not in the plain frame yet: `secondTab`, page facts and the popup's open-chat tabs (`OpenChatTabs`).
|
|
338
|
+
|
|
339
|
+
**The thread rail, with no React (0.47.0): `session-rail-plain.js`.** `SessionRail` for the same pages: the same
|
|
340
|
+
markup and classes (shell.css draws both), the search box (case and Vietnamese marks folded), the recency groups, inline
|
|
341
|
+
rename, the ⋯ menu and its two-step delete. The sessions, and what opening, renaming, pinning or deleting one does, stay
|
|
342
|
+
the page's. Hand it to the frame as `rail` and the frame builds the admin's `AssistantWorkspaceLayout` around the body:
|
|
343
|
+
the rail in full screen only, `is-dock` in the popup and the panel, the body never moved. `frame.setSwitching(true)` dims
|
|
344
|
+
the page while the next thread loads; `gembossUI.fullChromeRoom(chatEl)` keeps the first message out from under the
|
|
345
|
+
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
|
|
346
|
+
assistant does not pay for it. `assistant-plain-browser.test.ts` holds it to `SessionRail` over 20 steps (typing, Escape,
|
|
347
|
+
the menu, rename, pin, delete, collapse, an empty list) and the framed layout to `AssistantHost` + `AssistantWorkspaceLayout`.
|
|
348
|
+
|
|
349
|
+
```html
|
|
350
|
+
<script src="kit-plain.js"></script><script src="session-rail-plain.js"></script><script src="assistant-plain.js"></script>
|
|
351
|
+
<script>
|
|
352
|
+
const rail = gembossUI.sessionRail({ sessions, activeId, onNewChat, onOpen: openThread, onRename: rename,
|
|
353
|
+
onDelete: remove, titleMaxLength: 120, storageKey: "gemfactory.assistant.railCollapsed" });
|
|
354
|
+
const frame = gembossUI.assistantFrame({ title: "Factory Assistant", body: chatElement, rail, fullHref: "/assistant" });
|
|
355
|
+
rail.update({ sessions: fresh, activeId: id }); // after a turn, a rename, a delete
|
|
356
|
+
</script>
|
|
357
|
+
```
|
|
336
358
|
|
|
337
359
|
## The engine picker (0.25.0): `EnginePicker`, `BridgeNotice`
|
|
338
360
|
|
|
@@ -1412,6 +1434,82 @@ its negative control changes one attribute and the comparison sees it. Then the
|
|
|
1412
1434
|
beside it, idempotence, `<head>`, alone, the wrong order). Catalog section "Chuyển dịch vụ", last frame: the plain page,
|
|
1413
1435
|
live.
|
|
1414
1436
|
|
|
1437
|
+
## LanguageSwitcher (21/10/2026): the button says which language the page is in
|
|
1438
|
+
|
|
1439
|
+
Chris 21/10/2026: "bộ switch ngôn ngữ ở gemcommunity, gemnews và xem những chỗ nào khác nên chuẩn hoá ở gemboss ui để khi
|
|
1440
|
+
chọn một ngôn ngữ nó hiển thị lên trên cái icon luôn ví dụ là VI, EN hay chữ Zhong của trung quốc ntn đó để dễ hiểu hơn là
|
|
1441
|
+
icon quả địa cầu như hiện tại". Each surface drew its own (GemCommunity's topbar a globe alone, its sign-in pages
|
|
1442
|
+
"🌐 EN ⌄", GemNews its own): one component now, whose button shows the globe AND the current language's short label,
|
|
1443
|
+
"EN", "VI", "中" (the character, not "ZH": the button is read by people).
|
|
1444
|
+
|
|
1445
|
+
```tsx
|
|
1446
|
+
import { FLEET_LANGUAGES, LanguageSwitcher, matchLanguage } from "@gemboss/ui";
|
|
1447
|
+
|
|
1448
|
+
// The topbar's end slot, beside ThemeToggle and the account menu. The app keeps the choice (cookie, storage, profile).
|
|
1449
|
+
<AppShell actions={<><ThemeToggle /><LanguageSwitcher value={lang} onChange={saveLang} labels={{ trigger: t("Language") }} /><AccountMenu … /></>} … />
|
|
1450
|
+
|
|
1451
|
+
// A sign-in page, a site header, a footer: the pill with a caret; the browser's language suggested first.
|
|
1452
|
+
<LanguageSwitcher variant="inline" value={lang} onChange={saveLang}
|
|
1453
|
+
suggested={matchLanguage(FLEET_LANGUAGES, navigator.languages)} labels={{ trigger: "Ngôn ngữ", suggested: "Gợi ý" }} />
|
|
1454
|
+
|
|
1455
|
+
// A site whose languages are paths: each row is a link (hreflang), so it switches with no script.
|
|
1456
|
+
<LanguageSwitcher value="vi" languages={FLEET_LANGUAGES.map((l) => ({ ...l, href: l.code === "en" ? "/" : `/${l.code}/` }))} />
|
|
1457
|
+
```
|
|
1458
|
+
|
|
1459
|
+
| | |
|
|
1460
|
+
| --- | --- |
|
|
1461
|
+
| Props | `value` (the current code, required; one not in the list shows its code upper case), `languages` (default `FLEET_LANGUAGES`: `{ code, label, short }` for en "English" EN, vi "Tiếng Việt" VI, zh "中文" 中; `label` is the NATIVE name, `short` what the button shows, default the code's first part upper case; `href` makes the row a link), `onChange(code)` (a row other than the current one; the app stores it and passes the new `value`), `suggested` (a code: that row first, with the hint, then a separator; one the list does not have changes nothing), `labels` (`trigger` "Language", `suggested` "Suggested": `LANGUAGE_SWITCHER_LABELS`), `variant` (`topbar` default, `inline`), `end` (default true: the menu's right edge on the button's), `className` (on the `<details>`) |
|
|
1462
|
+
| The button | `summary.gb-lang__btn`: the kit's `globe` at 18px, then `span.gb-lang__short` ("VI", in its own `lang`, so "中" takes a Chinese glyph), in the text colour (the globe muted), at least 2ch wide so EN / VI / 中 do not move the bar. Its name and tooltip: `<trigger>: <the current language's label>` ("Language: Tiếng Việt", "Ngôn ngữ: 中文"); `aria-haspopup="menu"`. `topbar`: ThemeToggle's ghost, 36px; `inline`: a pill on the surface with the chips' line and the `chevronDown` caret (new in the set), turned while open. 44px under a finger or at phone width (≤767px), both |
|
|
1463
|
+
| 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` |
|
|
1464
|
+
| 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 |
|
|
1465
|
+
| 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)` |
|
|
1466
|
+
| 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`) with the core's values: where the core is loaded it wins and nothing changes; a page that links only the tokens and this file (gemboss.ai, whose pages the core would reset) draws the same menu. Tokens only, so `data-theme="dark"` follows |
|
|
1467
|
+
| The topbar | at 320 / 360 / 390 the kit's phone bar still fits with the switcher between ThemeToggle and the account chip (☰, brand, search, toggle, switcher, chip; no horizontal scroll) |
|
|
1468
|
+
|
|
1469
|
+
### Plain HTML: `gembossUI.languageSwitcher()` (21/10/2026)
|
|
1470
|
+
|
|
1471
|
+
For a page with no React (gemboss.ai, GemNews's extension popup, GemFactory, GemCare, GemPress): `language-switcher-plain.js`
|
|
1472
|
+
draws the component's DOM (classes, ARIA, order) and runs its keys, so `language-switcher.css` draws it unchanged. Its own
|
|
1473
|
+
file, 2.3 KB gzip, loaded only where the switcher is. A classic script that ADDS to `gembossUI`, after `kit-plain.js` when
|
|
1474
|
+
the page has it; it holds no closing script tag, so a server can inline it.
|
|
1475
|
+
|
|
1476
|
+
```html
|
|
1477
|
+
<link rel="stylesheet" href="/ui/language-switcher.css"> <!-- require.resolve("@gemboss/ui/language-switcher.css") -->
|
|
1478
|
+
…
|
|
1479
|
+
<span data-gb-language-switcher hidden></span> <!-- where the button goes -->
|
|
1480
|
+
…
|
|
1481
|
+
<script src="/ui/kit-plain.js"></script> <!-- if the page has it: FIRST (the menu then floats) -->
|
|
1482
|
+
<script src="/ui/language-switcher-plain.js"></script> <!-- @gemboss/ui/language-switcher-plain.js -->
|
|
1483
|
+
<script>
|
|
1484
|
+
var sw = gembossUI.languageSwitcher({
|
|
1485
|
+
value: document.documentElement.lang || "en",
|
|
1486
|
+
onChange: function (code) { document.cookie = "lang=" + code + "; path=/; max-age=31536000"; location.reload(); },
|
|
1487
|
+
suggested: gembossUI.matchLanguage(gembossUI.FLEET_LANGUAGES, navigator.languages),
|
|
1488
|
+
labels: { trigger: "Ngôn ngữ", suggested: "Gợi ý" },
|
|
1489
|
+
variant: "inline",
|
|
1490
|
+
mount: document.querySelector("[data-gb-language-switcher]"), // replaced by the switcher; none: append sw.el
|
|
1491
|
+
});
|
|
1492
|
+
</script>
|
|
1493
|
+
```
|
|
1494
|
+
|
|
1495
|
+
| | |
|
|
1496
|
+
| --- | --- |
|
|
1497
|
+
| 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) |
|
|
1498
|
+
| 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
|
+
| `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 |
|
|
1500
|
+
| 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` |
|
|
1501
|
+
|
|
1502
|
+
`packages/ui/src/language-switcher-browser.test.ts` (Chromium, `test:ui-browser`) renders the component and the plain
|
|
1503
|
+
switcher on the same shells (the kit's topbar with the `topbar` variant, the site header with `inline`) and compares the
|
|
1504
|
+
two DOMs node for node, attribute for attribute, with focus, open state and the `onChange` calls, after each step of one
|
|
1505
|
+
script: closed, opened by a click, ↓ ↓ Home End ↑, Escape, ↓ on the button, Home + Enter, the current row, a click outside,
|
|
1506
|
+
Tab; with the defaults and with Vietnamese labels, a suggested row, the inline variant, left-aligned, links. NEGATIVE
|
|
1507
|
+
CONTROL: one `lang` changed on the plain page and the comparison sees it. Then: the short label per language, the list
|
|
1508
|
+
under the button's right edge, one tick; 36 / 44px and 44px rows; the phone bar at 390 / 360 / 320; contrast in both
|
|
1509
|
+
themes (label ≥ 4.5:1, tick ≥ 3:1); the standalone page's menu equal to the core's (negative control: without the file,
|
|
1510
|
+
the browser's popover box). `LANGUAGE_SWITCHER_SHOTS=<dir>` writes the screenshots (closed / open, both variants, both
|
|
1511
|
+
themes, vi / en / zh). `language-switcher.test.tsx` holds the markup, the labels, the helpers and the plain file's defaults.
|
|
1512
|
+
|
|
1415
1513
|
## Repeater rows (16/10/2026): `RepeaterList`, `useKeyedRows`
|
|
1416
1514
|
|
|
1417
1515
|
A setting that is a list (an accordion's items, a pricing table's plans, choice cards) is the element's content. The admin's
|
|
@@ -2379,6 +2477,8 @@ entry, each presentational and server-renderable (no state, no effect):
|
|
|
2379
2477
|
| `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. |
|
|
2380
2478
|
| `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. |
|
|
2381
2479
|
| `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`. |
|
|
2480
|
+
| `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. |
|
|
2481
|
+
| `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`. |
|
|
2382
2482
|
| `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 |
|
|
2383
2483
|
|
|
2384
2484
|
Three lighter steps of iris came with them, because a wash, a band and an art stroke need more than `--gemboss-accent-soft`:
|
|
@@ -2447,6 +2547,33 @@ visits (localStorage); `group` names the steps' `name` when two guides render in
|
|
|
2447
2547
|
]} />
|
|
2448
2548
|
```
|
|
2449
2549
|
|
|
2550
|
+
**One checklist for GemBoss and GemCommunity (21/10/2026).** Chris: "cái onboarding checklist của gemcommunity với của
|
|
2551
|
+
gemboss nút tick màu xanh progress bar nên đồng bộ nhé để dùng cùng một gemboss ui kit". The admin's "Set up your store"
|
|
2552
|
+
(StoreChecklistCard) and GemCommunity's feed "Start here" both render this guide; the guide took what each did better:
|
|
2553
|
+
|
|
2554
|
+
| | Before (GemBoss admin / GemCommunity) | Now |
|
|
2555
|
+
|---|---|---|
|
|
2556
|
+
| Tick | grey ✓ glyph / 18px green circle, white ✓ | 18px ring; done fills `--gemboss-success` with a white check drawn in CSS (same in every app and font); unknown is a dashed ring with `?` |
|
|
2557
|
+
| Bar | 6px near-black / 6px iris gradient | 6px, the ticks' green from the first step (it counts them); `ui-setup__bar--done` is no longer drawn |
|
|
2558
|
+
| Count | Polaris badge / "2/5 steps done" text | the kit badge, amber then green; its tones ship in `setup-guide.css` (the admin loads `ui.css`, not `kit-plain.css`) |
|
|
2559
|
+
| Done steps | listed / folded behind "✓ 2 completed" | folded behind one "✓ N completed" row (`foldDone`, default on; `labels.completed`) |
|
|
2560
|
+
| Extra steps | "Optional" group / none | `optional`: under a hairline and an "Optional" heading (`labels.optional`), never counted |
|
|
2561
|
+
| Row control | buttons in the open step / an "Open" button at the row's end | `body` for a step that explains; `action` at the end of a plain row (under the body when there is one) |
|
|
2562
|
+
| Put away | fold, remembered / × | fold with `rememberKey`, or `onDismiss`: a × (`labels.dismiss`) and no fold |
|
|
2563
|
+
|
|
2564
|
+
The fold is decided once, like the open step: a step finished while the guide is on screen stays in the list and turns
|
|
2565
|
+
green; the next render that mounts the guide folds it. `className` on a step is for the surface's own rule (GemCommunity
|
|
2566
|
+
hides its Telegram step where its rail asks for Telegram). The ×, the rows, the head and a step's buttons are 44px under a
|
|
2567
|
+
thumb and at phone width.
|
|
2568
|
+
|
|
2569
|
+
```tsx
|
|
2570
|
+
<SetupGuide title="Start here" description="A few steps to get into the loop." onDismiss={hide}
|
|
2571
|
+
labels={{ completed: (n) => `${n} completed`, dismiss: "Hide" }} steps={[
|
|
2572
|
+
{ id: "profile", title: "Fill your profile", state: "done" },
|
|
2573
|
+
{ id: "post", title: "Write your first post", action: <Link className="ui-btn" href="/">Open</Link> },
|
|
2574
|
+
]} />
|
|
2575
|
+
```
|
|
2576
|
+
|
|
2450
2577
|
## Collections (0.13.0): tables, lists, grids, boards
|
|
2451
2578
|
|
|
2452
2579
|
Chris settled these on 05/10/2026 after an inventory of 136 hand-made collections on 11 surfaces (86 tables scrolling
|
|
@@ -2672,6 +2799,7 @@ something the base cannot express, add a token or a prop here, and every surface
|
|
|
2672
2799
|
| `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`. |
|
|
2673
2800
|
| `packages/ui/src/service-switcher-plain.js` | 0.36.0: `gembossUI.initServiceSwitcher()`, the ServiceSwitcher drawn and driven for plain HTML (`@gemboss/ui/service-switcher-plain.js`). After `kit-plain.js` when the page has it; works alone. See "ServiceSwitcher". |
|
|
2674
2801
|
| `packages/ui/src/team-chat-plain.js` | 20/10/2026: `gembossUI.teamChat`, builders that return the team chat's rows (messages, day separators, "New messages", jump, receipt, rail rows, people rows, reactions and the picker) with the components' markup, for plain HTML (`@gemboss/ui/team-chat-plain.js`). After `kit-plain.js` when the page has it; works alone. See "The team chat layer". |
|
|
2802
|
+
| `packages/ui/src/language-switcher-plain.js` | 21/10/2026: `gembossUI.languageSwitcher()`, the LanguageSwitcher drawn and driven for plain HTML (`@gemboss/ui/language-switcher-plain.js`), with `FLEET_LANGUAGES`, `matchLanguage`, `languageShort`. After `kit-plain.js` when the page has it; works alone. See "LanguageSwitcher". |
|
|
2675
2803
|
| `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`. |
|
|
2676
2804
|
| `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. |
|
|
2677
2805
|
| `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. |
|
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.47.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";
|