@gemboss/ui 0.2.1 → 0.4.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/README.md +89 -6
- package/catalog/index.html +338 -10
- package/gemboss-ui.css +794 -3
- package/package.json +5 -1
- package/src/assistant/dock.ts +83 -5
- package/src/assistant/host.tsx +113 -17
- package/src/assistant/index.ts +1 -1
- package/src/assistant/provider.tsx +75 -8
- package/src/assistant.css +90 -1
- package/src/chat-composer.css +110 -0
- package/src/chat.css +427 -0
- package/src/kit-plain.css +161 -0
- package/src/kit-plain.js +179 -0
package/README.md
CHANGED
|
@@ -76,10 +76,25 @@ import { AssistantProvider, AssistantHost, useAssistant } from "@gemboss/ui/assi
|
|
|
76
76
|
</AssistantProvider>
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
screen and survives navigation, a status bar, and hidden-but-still-running.
|
|
81
|
-
thrown off screen (`assistant/dock.ts` holds that rule). The popup resizes from
|
|
82
|
-
slides along the bottom, by pointer or keyboard, and remembers both under
|
|
79
|
+
Five shapes, one mounted subtree: full screen (next to the rail), a popup that floats over every
|
|
80
|
+
screen and survives navigation, a full-height side panel, a status bar, and hidden-but-still-running.
|
|
81
|
+
A live run is never thrown off screen (`assistant/dock.ts` holds that rule). The popup resizes from
|
|
82
|
+
its top edge and slides along the bottom, by pointer or keyboard, and remembers both under
|
|
83
|
+
`storagePrefix`.
|
|
84
|
+
|
|
85
|
+
The side panel is opt-in: `<AssistantHost allowSide>` puts a "Pin to the side" button in the popup
|
|
86
|
+
header, and the panel carries "Float as a popup" back. It stands under the topbar on the right, and
|
|
87
|
+
the page moves over for it instead of being covered: the provider puts `<prefix>-side` on `<body>` and
|
|
88
|
+
the width in `--gb-assistant-side-w` (`SIDE_WIDTH_VAR`), and `.gemboss-platform-main` takes that as
|
|
89
|
+
`margin-right`. A surface with its own layout reads the same variable. Its left edge is a
|
|
90
|
+
`.ui-resizer` (drag, or ←/→, Shift for 64px, Home/End), clamped to 380–720px and never leaving the page
|
|
91
|
+
less than 480px; the width is remembered. Panel and page only fit side by side from 1100px (rail 240
|
|
92
|
+
+ 380 + 480). Between 761 and 1099 the panel covers the content area and is not offered; on a phone
|
|
93
|
+
it takes the screen. The status bar, a busy ×, leaving full screen mid-run and full
|
|
94
|
+
screen's own way back ("Back to the side panel") all return to whichever shape the person last
|
|
95
|
+
worked in (`floatShape`, remembered across reloads), so a pinned panel never pops back as a popup.
|
|
96
|
+
The host registers `allowSide` with the provider, and every way into the panel checks it, including
|
|
97
|
+
a "side" remembered in storage: a host without it gets the popup.
|
|
83
98
|
|
|
84
99
|
The rail's way in is a `RailItem` with `className="gnav-assistant"`, linking to your full-screen
|
|
85
100
|
address (the admin puts it in `RailBottom`, above Inbox). That class draws the tinted pill, and the
|
|
@@ -93,7 +108,7 @@ is a separate piece you put inside it:
|
|
|
93
108
|
```tsx
|
|
94
109
|
import { AssistantWorkspaceLayout, SessionRail, OpenChatTabs } from "@gemboss/ui/assistant";
|
|
95
110
|
|
|
96
|
-
<AssistantWorkspaceLayout dock={presentation === "dock"} rail={
|
|
111
|
+
<AssistantWorkspaceLayout dock={presentation === "dock" || presentation === "side"} rail={
|
|
97
112
|
<SessionRail sessions={sessions} activeId={active} busyIds={running}
|
|
98
113
|
collapsed={collapsed} onToggleCollapsed={toggle} onNewChat={newChat}
|
|
99
114
|
onOpen={open} onRename={rename} onTogglePin={pin} onDelete={remove} labels={VI} />
|
|
@@ -109,6 +124,54 @@ Its styles are the `gemboss-assistant-*` rules in `packages/ui/src/shell.css` (t
|
|
|
109
124
|
@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
|
|
110
125
|
through `fullActions`; a surface without those leaves them out.
|
|
111
126
|
|
|
127
|
+
## The chat body
|
|
128
|
+
|
|
129
|
+
What an answer looks like, on any surface that puts a chat somewhere: `chat.css` (inside `gemboss-ui.css`;
|
|
130
|
+
the admin `@import`s it, so it is the same file the admin's own chat is drawn with).
|
|
131
|
+
|
|
132
|
+
| Piece | Classes |
|
|
133
|
+
|---|---|
|
|
134
|
+
| Header | `.gemboss-ec-head` › `.gemboss-ec-title` + `.gemboss-ec-sub` |
|
|
135
|
+
| Message list | `.gemboss-ec-scroll` (thin visible scrollbar) |
|
|
136
|
+
| Messages | `.gemboss-ec-msg.user` / `.gemboss-ec-msg.assistant` › `.gemboss-ec-bubble` |
|
|
137
|
+
| Markdown inside a bubble | `.gemboss-md-p / -h -h1..3 / -list / -quote / -rule / -code / -codeblock / -table(wrap) / -img(link)`, links `.gemboss-ec-md-link` |
|
|
138
|
+
| Steps the assistant took | `ul.gemboss-narr` › `li.gemboss-narr-row.ok|.running|.failed|.thinking|.trace|.steer` › `.gemboss-narr-dot` + `.gemboss-narr-text` (running: `.gemboss-narr-spin`) |
|
|
139
|
+
| Working / streaming | `.gemboss-ec-pending` (`-star`, `-verb`, `-meta`), `.gemboss-ec-cursor` after the streamed text |
|
|
140
|
+
| Suggestions, choices | `.gemboss-ec-starters` › `button.gemboss-ec-starter`; `.gemboss-ec-chips` › `button.gemboss-ec-chip` |
|
|
141
|
+
| Composer | `form.gemboss-ec-input` › `textarea` + `div.gemboss-ec-toolbar` › `div.gemboss-ec-toolbar-lead` (chips; leave it EMPTY for none) + `button[type=submit]` (44px; disabled = grey). An empty lead puts the box and the button on ONE row. Stop while streaming: `button.gemboss-ec-stop` in place of submit |
|
|
142
|
+
|
|
143
|
+
The toolbar row, the one-row layout, the engine picker's layout and Stop are in a second sheet,
|
|
144
|
+
`chat-composer.css` (also inside `gemboss-ui.css`): the admin imports it from its chat components rather than its
|
|
145
|
+
layout, so screens without a chat never load it. The catalog's "Thân chat" section is the markup to copy. NOT here, on purpose: the panel's own geometry
|
|
146
|
+
(`.gemboss-ec` width, editor columns), attachments, action cards, the engine picker and work mode — those
|
|
147
|
+
are still the admin's. The one exception is the chat panel's touch / mouse target blocks: one rule per pointer
|
|
148
|
+
kind names every control of the panel, admin-only ones included (they match nothing elsewhere). The behaviour (streaming, Stop, Enter to send, IME, scroll-to-bottom) belongs to
|
|
149
|
+
the surface.
|
|
150
|
+
|
|
151
|
+
## Plain-HTML behaviour: `kit-plain.js` (0.3.0)
|
|
152
|
+
|
|
153
|
+
Surfaces without React (GemFactory, gemcare, gempress, partner-radar) render the kit's markup on the server and need
|
|
154
|
+
the few behaviours React components carry: a popup that traps Tab and closes on Escape, a toast, menus that close.
|
|
155
|
+
Each surface wrote its own; GemFactory's (reviewed into Tab trap, stacked Escape and the close-while-asking race) is
|
|
156
|
+
now this file. It is a classic script that sets `globalThis.gembossUI`, so a server-rendered page can inline it:
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
// server
|
|
160
|
+
const KIT_JS = readFileSync(require.resolve("@gemboss/ui/kit-plain.js"), "utf8");
|
|
161
|
+
html += `<script>${KIT_JS}</script>`;
|
|
162
|
+
// page
|
|
163
|
+
const { openModal, toast, initMenus } = gembossUI;
|
|
164
|
+
initMenus();
|
|
165
|
+
const m = openModal({ title: "Delete #12?", closeLabel: "Đóng", body: '<div class="ui-modal__body">…</div>',
|
|
166
|
+
foot: '<button class="ui-btn" data-no>Huỷ</button><button class="ui-btn ui-btn--primary" data-yes autofocus>Xoá</button>',
|
|
167
|
+
onClose: (v) => v && toast("Đã xoá") });
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`title`, `body` and `foot` are HTML (escape your text); toast text is text. `beforeClose` may return a promise of
|
|
171
|
+
false to keep the popup open (it gets the close value, and a guard that throws keeps it open). Its behaviour is
|
|
172
|
+
asserted in Chromium by `packages/ui/src/kit-plain.test.ts` (`npm run test:ui-browser`, run by the CI job that has
|
|
173
|
+
Chromium; `test:packages` excludes it); the catalog's "Hộp thoại" section runs it live.
|
|
174
|
+
|
|
112
175
|
## Resizable panels
|
|
113
176
|
|
|
114
177
|
`ui-resizer` is the seam a person drags between two panels; `ui-resizing` on `<body>` is the drag in
|
|
@@ -175,6 +238,25 @@ import { AppShell, BrandLockup, AccountMenu, AccountMenuItem, Icon, RailItem } f
|
|
|
175
238
|
(`href`) or a button; `disabled` on a link drops the href and sets `aria-disabled`; `className` is added
|
|
176
239
|
to the row's class.
|
|
177
240
|
|
|
241
|
+
## Report pages (0.4.0)
|
|
242
|
+
|
|
243
|
+
Plain CSS in `kit-plain.css` for a numbers page written by hand (first user: gemnews `/admin`, 03/10/2026). The
|
|
244
|
+
surface computes every number and ratio; the kit only draws. Markup for each is in the catalog's "Báo cáo" section.
|
|
245
|
+
|
|
246
|
+
- `ui-metrics--grid` + `ui-metric--lg` + `ui-metric__foot`: KPI tiles in an even grid (never 3 + 1 orphan), label on
|
|
247
|
+
top, big value, a foot line for the comparison.
|
|
248
|
+
- `ui-delta` (`--good` / `--bad`): change vs a previous period. Good or bad is the surface's call (reads up is good,
|
|
249
|
+
bounce up is bad); the arrow is direction, written in the markup. No tone = neutral or "no previous period".
|
|
250
|
+
- `ui-barlist`: horizontal bars. `--v` (0..1) is the length, `--c` the colour; label and value on one line, the bar
|
|
251
|
+
full width below it, so no fixed label column gets squeezed at 320px. `__share`, `__note`.
|
|
252
|
+
- `ui-chart`: stacked columns over time plus an optional hollow-dot series. `--v` per segment and dot, `data-max`
|
|
253
|
+
prints the axis top, gridlines at quarters. The drawing is `aria-hidden`; the numbers for a screen reader are the
|
|
254
|
+
surface's job (a table in `ui-sr-only`).
|
|
255
|
+
- `ui-legend` (`__item`, `__item--dot`), series colours `--ui-series-1..5` (1 is the brand iris; no red beside green).
|
|
256
|
+
- `ui-funnel`: steps with the conversion from the previous step (`__rate`); a row when wide, a column at ≤ 520px.
|
|
257
|
+
- `ui-grid--2` / `--3`: card grid that drops to one column; `ui-section-head`: an `h2` + note between page head and
|
|
258
|
+
card titles.
|
|
259
|
+
|
|
178
260
|
## The catalog: every token, class and component, with its markup
|
|
179
261
|
|
|
180
262
|
`npm run catalog` builds `catalog/index.html` (also shipped in the npm tarball, and built by `prepack`):
|
|
@@ -220,7 +302,8 @@ something the base cannot express, add a token or a prop here, and every surface
|
|
|
220
302
|
|---|---|
|
|
221
303
|
| `gemboss-ui.css` | built at pack time: `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` + `packages/ui/src/auth.css` + `packages/ui/src/assistant.css` |
|
|
222
304
|
| `packages/ui/src/shell.css` | the SHARED SHELL (rail, topbar, main, mobile drawer, the assistant's thread rail, popover surface, account chip). 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). |
|
|
223
|
-
| `packages/ui/src/kit-plain.css` | primitives for surfaces that render plain HTML, not React (0.2.1): menu, key/value list, icon button. Shipped inside `gemboss-ui.css`; the admin does not import it (its first-load CSS budget has ~0.1 KB headroom). |
|
|
305
|
+
| `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. |
|
|
306
|
+
| `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). A classic script: inline it or `<script src>`; see "Plain-HTML behaviour" below. |
|
|
224
307
|
| `packages/ui/src/**/*.tsx` | the components, as TypeScript source. Next: add `@gemboss/ui` to `transpilePackages`. Vite compiles it as is. |
|
|
225
308
|
|
|
226
309
|
`gemboss-ui.css` is never committed and never edited. To change a colour or a radius, change
|