@gemboss/ui 0.2.0 → 0.3.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 +75 -5
- package/catalog/index.html +140 -14
- package/gemboss-ui.css +784 -11
- package/package.json +6 -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/gemboss-field.css +4 -0
- package/src/icons.tsx +1 -0
- package/src/index.ts +1 -0
- package/src/kit-plain.css +110 -0
- package/src/kit-plain.js +179 -0
- package/src/menu.tsx +64 -0
- package/src/shell.css +8 -4
- package/src/ui.css +27 -4
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
|
|
@@ -169,6 +232,11 @@ import { AppShell, BrandLockup, AccountMenu, AccountMenuItem, Icon, RailItem } f
|
|
|
169
232
|
applications that integrate with Shopify. Draw a missing one HERE, not in the surface: every rail
|
|
170
233
|
icon must keep `fill="none"` on its inner `<g>`, because `.gnav-ico svg { fill: currentColor }`
|
|
171
234
|
outranks an attribute on the `<svg>` itself and paints an outline icon solid.
|
|
235
|
+
- `Menu` / `MenuItem` / `MenuSeparator` (0.2.1): the "⋯" action menu. Same `<details>` rule as the account
|
|
236
|
+
menu (works as plain markup; with React it closes on an outside click, on Escape with focus back on the
|
|
237
|
+
trigger, and after a button row's handler). Rows share the account menu's row style. `MenuItem` is a link
|
|
238
|
+
(`href`) or a button; `disabled` on a link drops the href and sets `aria-disabled`; `className` is added
|
|
239
|
+
to the row's class.
|
|
172
240
|
|
|
173
241
|
## The catalog: every token, class and component, with its markup
|
|
174
242
|
|
|
@@ -215,6 +283,8 @@ something the base cannot express, add a token or a prop here, and every surface
|
|
|
215
283
|
|---|---|
|
|
216
284
|
| `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` |
|
|
217
285
|
| `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). |
|
|
286
|
+
| `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). |
|
|
287
|
+
| `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. |
|
|
218
288
|
| `packages/ui/src/**/*.tsx` | the components, as TypeScript source. Next: add `@gemboss/ui` to `transpilePackages`. Vite compiles it as is. |
|
|
219
289
|
|
|
220
290
|
`gemboss-ui.css` is never committed and never edited. To change a colour or a radius, change
|