@gemboss/ui 0.2.1 → 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 +70 -6
- package/catalog/index.html +91 -10
- package/gemboss-ui.css +688 -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 +55 -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
|
|
@@ -220,7 +283,8 @@ something the base cannot express, add a token or a prop here, and every surface
|
|
|
220
283
|
|---|---|
|
|
221
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` |
|
|
222
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). |
|
|
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). |
|
|
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. |
|
|
224
288
|
| `packages/ui/src/**/*.tsx` | the components, as TypeScript source. Next: add `@gemboss/ui` to `transpilePackages`. Vite compiles it as is. |
|
|
225
289
|
|
|
226
290
|
`gemboss-ui.css` is never committed and never edited. To change a colour or a radius, change
|
package/catalog/index.html
CHANGED
|
@@ -26,8 +26,8 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
26
26
|
|
|
27
27
|
@media (max-width:760px){.cat{grid-template-columns:1fr;padding:16px}.cat-nav{position:static;flex-direction:row;flex-wrap:wrap}}
|
|
28
28
|
</style></head><body>
|
|
29
|
-
<div class="cat"><nav class="cat-nav" aria-label="Mục"><a href="#tokens">Token</a><a href="#buttons">Nút</a><a href="#fields">Ô nhập</a><a href="#choice">Tab, nhóm nút, thẻ</a><a href="#surfaces">Thẻ, số liệu, bảng, trạng thái trống</a><a href="#resizer">Thanh kéo giữa hai cột</a><a href="#modal">Hộp thoại</a><a href="#shell">Khung app</a><a href="#auth">Đăng nhập</a><a href="#assistant">Trợ lý: cột phiên</a><a href="#icons">Icon</a></nav>
|
|
30
|
-
<main><header class="cat-head"><h1>@gemboss/ui 0.
|
|
29
|
+
<div class="cat"><nav class="cat-nav" aria-label="Mục"><a href="#tokens">Token</a><a href="#buttons">Nút</a><a href="#fields">Ô nhập</a><a href="#choice">Tab, nhóm nút, thẻ</a><a href="#surfaces">Thẻ, số liệu, bảng, trạng thái trống</a><a href="#resizer">Thanh kéo giữa hai cột</a><a href="#modal">Hộp thoại, toast, menu (JS thuần)</a><a href="#shell">Khung app</a><a href="#auth">Đăng nhập</a><a href="#chat">Thân chat</a><a href="#assistant">Trợ lý: cột phiên</a><a href="#icons">Icon</a></nav>
|
|
30
|
+
<main><header class="cat-head"><h1>@gemboss/ui 0.3.0</h1><p>Ngôn ngữ thiết kế chung của các công cụ GemBoss. Markup dưới mỗi ví dụ là đúng thứ package render ra; bề mặt không dùng React (HTML tĩnh, Python) chép markup này và nạp <code>gemboss-ui.css</code>. Dựng từ gemboss <code>7d7968ae7238</code>.</p></header>
|
|
31
31
|
<section id="tokens"><h2>Token</h2><p>Đổi giao diện một bề mặt bằng cách đặt lại token, không viết đè class. Tên và giá trị đọc thẳng từ gemboss-ui.css.</p><div class="cat-swatches"><div class="cat-swatch"><i style="background:var(--gemboss-primary)"></i><code>--gemboss-primary</code><span>#1b1c1e</span></div><div class="cat-swatch"><i style="background:var(--gemboss-primary-dark)"></i><code>--gemboss-primary-dark</code><span>#000000</span></div><div class="cat-swatch"><i style="background:var(--gemboss-primary-soft)"></i><code>--gemboss-primary-soft</code><span>#f2f3f5</span></div><div class="cat-swatch"><i style="background:var(--gemboss-accent)"></i><code>--gemboss-accent</code><span>#5661f5</span></div><div class="cat-swatch"><i style="background:var(--gemboss-accent-strong)"></i><code>--gemboss-accent-strong</code><span>#3a44de</span></div><div class="cat-swatch"><i style="background:var(--gemboss-accent-soft)"></i><code>--gemboss-accent-soft</code><span>#eef0fe</span></div><div class="cat-swatch"><i style="background:var(--gemboss-text-strong)"></i><code>--gemboss-text-strong</code><span>#1b1c1e</span></div><div class="cat-swatch"><i style="background:var(--gemboss-text)"></i><code>--gemboss-text</code><span>#1b1c1e</span></div><div class="cat-swatch"><i style="background:var(--gemboss-text-muted)"></i><code>--gemboss-text-muted</code><span>#686b71</span></div><div class="cat-swatch"><i style="background:var(--gemboss-surface)"></i><code>--gemboss-surface</code><span>#ffffff</span></div><div class="cat-swatch"><i style="background:var(--gemboss-canvas)"></i><code>--gemboss-canvas</code><span>#ffffff</span></div><div class="cat-swatch"><i style="background:var(--gemboss-preview-canvas)"></i><code>--gemboss-preview-canvas</code><span>#eceef1</span></div><div class="cat-swatch"><i style="background:var(--gemboss-rail-bg)"></i><code>--gemboss-rail-bg</code><span>#ffffff</span></div><div class="cat-swatch"><i style="background:var(--gemboss-panel-bg)"></i><code>--gemboss-panel-bg</code><span>#ffffff</span></div><div class="cat-swatch"><i style="background:var(--gemboss-divider)"></i><code>--gemboss-divider</code><span>#e5e7ea</span></div><div class="cat-swatch"><i style="background:var(--gemboss-divider-soft)"></i><code>--gemboss-divider-soft</code><span>#edeff2</span></div><div class="cat-swatch"><i style="background:var(--gemboss-hover)"></i><code>--gemboss-hover</code><span>#eceef2</span></div><div class="cat-swatch"><i style="background:var(--gemboss-active-bg)"></i><code>--gemboss-active-bg</code><span>#f0f1f4</span></div><div class="cat-swatch"><i style="background:var(--gemboss-active-fg)"></i><code>--gemboss-active-fg</code><span>#1b1c1e</span></div><div class="cat-swatch"><i style="background:var(--gemboss-danger)"></i><code>--gemboss-danger</code><span>#d72c0d</span></div><div class="cat-swatch"><i style="background:var(--gemboss-danger-strong)"></i><code>--gemboss-danger-strong</code><span>#b42318</span></div><div class="cat-swatch"><i style="background:var(--gemboss-danger-soft)"></i><code>--gemboss-danger-soft</code><span>#fff0e9</span></div><div class="cat-swatch"><i style="background:var(--gemboss-danger-border)"></i><code>--gemboss-danger-border</code><span>#f5c9bc</span></div><div class="cat-swatch"><i style="background:var(--gemboss-success)"></i><code>--gemboss-success</code><span>#007f5f</span></div><div class="cat-swatch"><i style="background:var(--gemboss-success-strong)"></i><code>--gemboss-success-strong</code><span>#0a6a4c</span></div><div class="cat-swatch"><i style="background:var(--gemboss-success-soft)"></i><code>--gemboss-success-soft</code><span>#e3f1df</span></div><div class="cat-swatch"><i style="background:var(--gemboss-success-border)"></i><code>--gemboss-success-border</code><span>#b7dfcd</span></div><div class="cat-swatch"><i style="background:var(--gemboss-warning)"></i><code>--gemboss-warning</code><span>#9a6700</span></div><div class="cat-swatch"><i style="background:var(--gemboss-warning-soft)"></i><code>--gemboss-warning-soft</code><span>#fff8e0</span></div><div class="cat-swatch"><i style="background:var(--gemboss-warning-border)"></i><code>--gemboss-warning-border</code><span>#f5d569</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-bg)"></i><code>--gemboss-dark-bg</code><span>#0a0a0c</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-rail)"></i><code>--gemboss-dark-rail</code><span>#0e0e11</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-surface)"></i><code>--gemboss-dark-surface</code><span>#16161a</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-surface-2)"></i><code>--gemboss-dark-surface-2</code><span>#1e1e24</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-divider)"></i><code>--gemboss-dark-divider</code><span>#2a2a31</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-text)"></i><code>--gemboss-dark-text</code><span>#ededf0</span></div><div class="cat-swatch"><i style="background:var(--gemboss-dark-muted)"></i><code>--gemboss-dark-muted</code><span>#9a9aa4</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-bg)"></i><code>--gemboss-field-bg</code><span>#fdfdfd</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-bg-hover)"></i><code>--gemboss-field-bg-hover</code><span>#fafafa</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-ink)"></i><code>--gemboss-field-ink</code><span>#303030</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-placeholder)"></i><code>--gemboss-field-placeholder</code><span>#616161</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-line)"></i><code>--gemboss-field-line</code><span>#8a8a8a</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-line-hover)"></i><code>--gemboss-field-line-hover</code><span>#616161</span></div><div class="cat-swatch"><i style="background:var(--gemboss-field-line-active)"></i><code>--gemboss-field-line-active</code><span>#1a1a1a</span></div><div class="cat-swatch"><i style="background:var(--gemboss-chip-ink)"></i><code>--gemboss-chip-ink</code><span>#303633</span></div><div class="cat-swatch"><i style="background:var(--gemboss-chip-line)"></i><code>--gemboss-chip-line</code><span>#cfd6ce</span></div><div class="cat-swatch"><i style="background:var(--gemboss-chip-line-hover)"></i><code>--gemboss-chip-line-hover</code><span>#aab4ad</span></div><div class="cat-swatch"><i style="background:var(--gemboss-chip-bg-hover)"></i><code>--gemboss-chip-bg-hover</code><span>#f6f7f5</span></div><div class="cat-swatch"><i style="background:var(--gemboss-on-primary)"></i><code>--gemboss-on-primary</code><span>#ffffff</span></div><div class="cat-swatch"><i style="background:var(--gemboss-app-bg)"></i><code>--gemboss-app-bg</code><span>#ffffff</span></div></div><table class="ui-table cat-scale"><thead><tr><th>Token</th><th>Giá trị</th><th>Mẫu</th></tr></thead><tbody><tr><td><code>--gemboss-fs-micro</code></td><td>11px</td><td style="font-size:var(--gemboss-fs-micro)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-caption</code></td><td>12px</td><td style="font-size:var(--gemboss-fs-caption)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-body</code></td><td>14px</td><td style="font-size:var(--gemboss-fs-body)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-body-lg</code></td><td>16px</td><td style="font-size:var(--gemboss-fs-body-lg)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-h3</code></td><td>16px</td><td style="font-size:var(--gemboss-fs-h3)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-h2</code></td><td>20px</td><td style="font-size:var(--gemboss-fs-h2)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-h1</code></td><td>24px</td><td style="font-size:var(--gemboss-fs-h1)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-fs-display</code></td><td>30px</td><td style="font-size:var(--gemboss-fs-display)">Ngôn ngữ thiết kế chung</td></tr><tr><td><code>--gemboss-radius-xs</code></td><td>4px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-xs)"></i></td></tr><tr><td><code>--gemboss-radius-sm</code></td><td>6px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-sm)"></i></td></tr><tr><td><code>--gemboss-radius-md</code></td><td>8px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-md)"></i></td></tr><tr><td><code>--gemboss-radius-lg</code></td><td>12px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-lg)"></i></td></tr><tr><td><code>--gemboss-radius-xl</code></td><td>16px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-xl)"></i></td></tr><tr><td><code>--gemboss-radius-2xl</code></td><td>20px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-2xl)"></i></td></tr><tr><td><code>--gemboss-radius-full</code></td><td>999px</td><td><i class="cat-radius" style="border-radius:var(--gemboss-radius-full)"></i></td></tr><tr><td><code>--gemboss-shadow-resting</code></td><td class="cat-small">0 1px 2px rgba(31, 30, 29, 0.04), 0 2px 6px rgba(31, 30, 29, 0.06)</td><td><i class="cat-radius" style="box-shadow:var(--gemboss-shadow-resting);border:0"></i></td></tr><tr><td><code>--gemboss-shadow-raised</code></td><td class="cat-small">0 4px 16px rgba(31, 30, 29, 0.10)</td><td><i class="cat-radius" style="box-shadow:var(--gemboss-shadow-raised);border:0"></i></td></tr><tr><td><code>--gemboss-shadow-overlay</code></td><td class="cat-small">0 16px 48px rgba(31, 30, 29, 0.18)</td><td><i class="cat-radius" style="box-shadow:var(--gemboss-shadow-overlay);border:0"></i></td></tr></tbody></table></section>
|
|
32
32
|
<section id="buttons"><h2>Nút</h2><p>Một lớp <code>ui-btn</code>, ba biến thể, một cỡ nhỏ. Dùng được trên <code><button></code> lẫn <code><a></code> (căn giữa sẵn).</p><figure class="cat-demo"><figcaption>Mặc định · chính · ghost · nhỏ · tắt</figcaption><div class="cat-live"><div class="cat-row"><button type="button" class="ui-btn">Lưu nháp</button><button type="button" class="ui-btn ui-btn--primary">Xuất bản</button><button type="button" class="ui-btn ui-btn--ghost">Huỷ</button><button type="button" class="ui-btn ui-btn--sm">Nhỏ</button><button type="button" class="ui-btn ui-btn--primary" disabled>Đang lưu…</button><a class="ui-btn" href="#buttons">Link dạng nút</a></div></div><details class="cat-code"><summary>Markup</summary><pre><code><div class="cat-row">
|
|
33
33
|
<button type="button" class="ui-btn">Lưu nháp</button>
|
|
@@ -185,7 +185,24 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
185
185
|
<span aria-current="page">2 / 9</span>
|
|
186
186
|
<a href="#" rel="next">Sau <span aria-hidden="true">›</span>
|
|
187
187
|
</a>
|
|
188
|
-
</nav></code></pre></details></figure><figure class="cat-demo"><figcaption>
|
|
188
|
+
</nav></code></pre></details></figure><figure class="cat-demo"><figcaption>Link (<code>ui-link</code>) · HTML sinh ra (<code>ui-prose</code>: mọi <code><a></code> không class bên trong)</figcaption><div class="cat-live"><p>Xem <a class="ui-link" href="#">báo cáo đối thủ</a> trước khi gửi.</p><div class="ui-prose"><p>Đoạn markdown: nguồn ở <a href="#">trang tích hợp của Replo</a>.</p></div></div><details class="cat-code"><summary>Markup</summary><pre><code><p>Xem <a class="ui-link" href="#">báo cáo đối thủ</a> trước khi gửi.</p>
|
|
189
|
+
<div class="ui-prose">
|
|
190
|
+
<p>Đoạn markdown: nguồn ở <a href="#">trang tích hợp của Replo</a>.</p>
|
|
191
|
+
</div></code></pre></details></figure><figure class="cat-demo"><figcaption>Thẻ có ảnh (<code>ui-card--media</code>: <code>__media</code> 16:10 · <code>__body</code> · <code>__foot</code>)</figcaption><div class="cat-live"><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(min(240px,100%),1fr));gap:12px"><article class="ui-card ui-card--media"><button type="button" class="ui-card__media" aria-label="Xem lớn"><img alt="" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 10'%3E%3Crect width='16' height='10' fill='%23e9e7f7'/%3E%3Crect x='2' y='2' width='7' height='1.2' rx='.4' fill='%235661f5'/%3E%3Crect x='2' y='4.2' width='10' height='.8' rx='.3' fill='%23b9bdf0'/%3E%3C/svg%3E"></button><div class="ui-card__body"><h3 class="ui-card__title" style="margin:0">#392 Thẻ sản phẩm so sánh</h3><span class="ui-tag">comparison-table</span></div><div class="ui-card__foot"><span>Minh Anh · v5</span><span>2 giờ trước</span></div></article></div></div><details class="cat-code"><summary>Markup</summary><pre><code><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(min(240px,100%),1fr));gap:12px">
|
|
192
|
+
<article class="ui-card ui-card--media">
|
|
193
|
+
<button type="button" class="ui-card__media" aria-label="Xem lớn">
|
|
194
|
+
<img alt="" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 10'%3E%3Crect width='16' height='10' fill='%23e9e7f7'/%3E%3Crect x='2' y='2' width='7' height='1.2' rx='.4' fill='%235661f5'/%3E%3Crect x='2' y='4.2' width='10' height='.8' rx='.3' fill='%23b9bdf0'/%3E%3C/svg%3E">
|
|
195
|
+
</button>
|
|
196
|
+
<div class="ui-card__body">
|
|
197
|
+
<h3 class="ui-card__title" style="margin:0">#392 Thẻ sản phẩm so sánh</h3>
|
|
198
|
+
<span class="ui-tag">comparison-table</span>
|
|
199
|
+
</div>
|
|
200
|
+
<div class="ui-card__foot">
|
|
201
|
+
<span>Minh Anh · v5</span>
|
|
202
|
+
<span>2 giờ trước</span>
|
|
203
|
+
</div>
|
|
204
|
+
</article>
|
|
205
|
+
</div></code></pre></details></figure><figure class="cat-demo"><figcaption>Card</figcaption><div class="cat-live"><div class="ui-card" style="padding:16px">Một khối nội dung. Nền, viền, bo góc và bóng lấy từ token.</div></div><details class="cat-code"><summary>Markup</summary><pre><code><div class="ui-card" style="padding:16px">Một khối nội dung. Nền, viền, bo góc và bóng lấy từ token.</div></code></pre></details></figure><figure class="cat-demo"><figcaption>Đầu thẻ có nút (<code>ui-card__head</code>)</figcaption><div class="cat-live"><div class="ui-card"><div class="ui-card__head"><h3 class="ui-card__title">Hạng đối tác</h3><div><button type="button" class="ui-btn ui-btn--primary">Đổi hạng</button><button type="button" class="ui-btn">Kết thúc</button></div></div><p class="ui-muted" style="margin:0">Partner từ 01/08/2026.</p></div></div><details class="cat-code"><summary>Markup</summary><pre><code><div class="ui-card">
|
|
189
206
|
<div class="ui-card__head">
|
|
190
207
|
<h3 class="ui-card__title">Hạng đối tác</h3>
|
|
191
208
|
<div>
|
|
@@ -233,7 +250,11 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
233
250
|
</div>
|
|
234
251
|
<div style="padding:12px;background:var(--gemboss-surface)">Nội dung</div>
|
|
235
252
|
</div></code></pre></details></figure></section>
|
|
236
|
-
<section id="modal"><h2>Hộp thoại</h2><p>Lớp phủ <code>ui-overlay</code> + hộp <code>ui-modal</code>
|
|
253
|
+
<section id="modal"><h2>Hộp thoại, toast, menu (JS thuần)</h2><p>Lớp phủ <code>ui-overlay</code> + hộp <code>ui-modal</code>, thân <code>ui-modal__body</code>, chân <code>ui-modal__foot</code>. Bề mặt không dùng React nạp <code>@gemboss/ui/kit-plain.js</code> (script thường, chèn thẳng vào trang được) và gọi <code>gembossUI.openModal</code> / <code>toast</code> / <code>initMenus</code>: Escape, chạm nền, giữ Tab trong hộp, hộp chồng nhau, hỏi trước khi đóng. Bấm thử các nút dưới đây.</p><figure class="cat-demo"><figcaption>Bấm thử (<code>gembossUI</code>) <span>chạy kit-plain.js thật của gói</span></figcaption><div class="cat-live"><div class="cat-row"><button type="button" class="ui-btn ui-btn--primary" data-cat-js="modal">Mở hộp thoại</button><button type="button" class="ui-btn" data-cat-js="toast">Toast</button><button type="button" class="ui-btn" data-cat-js="toast-error">Toast lỗi</button></div></div><details class="cat-code"><summary>Markup</summary><pre><code><div class="cat-row">
|
|
254
|
+
<button type="button" class="ui-btn ui-btn--primary" data-cat-js="modal">Mở hộp thoại</button>
|
|
255
|
+
<button type="button" class="ui-btn" data-cat-js="toast">Toast</button>
|
|
256
|
+
<button type="button" class="ui-btn" data-cat-js="toast-error">Toast lỗi</button>
|
|
257
|
+
</div></code></pre></details></figure><figure class="cat-demo"><figcaption>Hộp thoại (<code>ui-modal__head</code> có nút đóng 44px)</figcaption><div class="cat-live"><div class="ui-modal" role="dialog" aria-modal="true" aria-labelledby="cat-m"><div class="ui-modal__head"><div class="ui-modal__title" id="cat-m">Xoá phiên này?</div><button type="button" class="ui-modal__close" aria-label="Đóng"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5l10 10M15 5 5 15"></path></g></svg></button></div><div class="ui-modal__body">Lịch sử vẫn còn trong sổ; phiên chỉ ẩn khỏi danh sách.</div><div class="ui-modal__foot"><button type="button" class="ui-btn ui-btn--ghost">Huỷ</button><button type="button" class="ui-btn ui-btn--primary">Xoá</button></div></div></div><details class="cat-code"><summary>Markup</summary><pre><code><div class="ui-modal" role="dialog" aria-modal="true" aria-labelledby="cat-m">
|
|
237
258
|
<div class="ui-modal__head">
|
|
238
259
|
<div class="ui-modal__title" id="cat-m">Xoá phiên này?</div>
|
|
239
260
|
<button type="button" class="ui-modal__close" aria-label="Đóng">
|
|
@@ -245,10 +266,10 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
245
266
|
</svg>
|
|
246
267
|
</button>
|
|
247
268
|
</div>
|
|
248
|
-
<div
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
</
|
|
269
|
+
<div class="ui-modal__body">Lịch sử vẫn còn trong sổ; phiên chỉ ẩn khỏi danh sách.</div>
|
|
270
|
+
<div class="ui-modal__foot">
|
|
271
|
+
<button type="button" class="ui-btn ui-btn--ghost">Huỷ</button>
|
|
272
|
+
<button type="button" class="ui-btn ui-btn--primary">Xoá</button>
|
|
252
273
|
</div>
|
|
253
274
|
</div></code></pre></details></figure></section>
|
|
254
275
|
<section id="shell"><h2>Khung app</h2><p>Topbar 56px, rail 240px, ≤760px thành ngăn kéo. Logo <code>BrandLockup</code>, tài khoản <code>AccountMenu</code> (bấm chữ viết tắt), mục Trợ lý ở chân rail có class <code>gnav-assistant</code>.</p><figure class="cat-demo cat-screen"><figcaption>AppShell + BrandLockup + AccountMenu + Icon</figcaption><iframe title="AppShell + BrandLockup + AccountMenu + Icon" style="height:460px" srcdoc="<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="../gemboss-ui.css"></head><body><div class="gemboss-platform-shell"><header class="gemboss-platform-topbar"><div class="gemboss-topbar-start"><button type="button" class="gemboss-mobile-menu-btn" aria-label="Mở menu" aria-expanded="false" aria-controls=":R0:"><svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><path d="M3 5.5h14M3 10h14M3 14.5h14" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"></path></svg></button><a class="gb-brand" href="#" aria-label="GemFactory home"><svg class="gb-brand-gem" viewBox="0 0 32 40" width="20.8" height="26" aria-hidden="true" focusable="false"><g transform="translate(0,0.8) scale(0.31)"><polygon points="26,14 74,14 66,50 34,50" fill="#5661F5" fill-opacity="0.92"></polygon><path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="currentColor" stroke-width="8.75" stroke-linejoin="round"></path><path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="currentColor" stroke-width="5.45" stroke-linejoin="round"></path></g></svg><span class="gb-brand-name">GemFactory</span></a></div><div class="gemboss-topbar-center"></div><div class="gemboss-topbar-end"><details class="gb-account"><summary class="gemboss-account-chip" aria-label="Account · Minh Anh" title="Minh Anh">MA</summary><div role="menu" class="gemboss-popover-surface gemboss-account-menu"><div class="gb-account-head"><div class="gb-account-name">Minh Anh</div><div class="gb-account-email">ma@gemboss.ai</div><div class="gb-account-role">Designer</div></div><a role="menuitem" class="gemboss-account-menu-item" href="#">Cài đặt</a><form method="post" action="#" class="gb-account-form"><button role="menuitem" type="submit" class="gemboss-account-menu-item danger">Đăng xuất</button></form></div></details></div></header><nav class="icon-nav" aria-label="Main navigation" id=":R0:"><div class="gnav-scroll"><div class="gnav-heading">Kho</div><div class="gnav-group active"><a class="gnav-item active" aria-current="page" href="#"><span class="gnav-ico"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3.5" width="4" height="13" rx="1.2"></rect><rect x="8" y="3.5" width="4" height="9" rx="1.2"></rect><rect x="13" y="3.5" width="4" height="6" rx="1.2"></rect></g></svg></span><span class="gnav-label">Board</span></a></div><div class="gnav-group"><a class="gnav-item" href="#"><span class="gnav-ico"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3.5" y="3" width="13" height="3.5" rx="1"></rect><rect x="3.5" y="8.25" width="13" height="3.5" rx="1"></rect><rect x="3.5" y="13.5" width="13" height="3.5" rx="1"></rect></g></svg></span><span class="gnav-label">Ghép trang</span></a></div><div class="gnav-group"><a class="gnav-item" href="#"><span class="gnav-ico"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 3.2 17.3 16H2.7z"></path><path d="M10 8.2v3.6M10 13.9v.1"></path></g></svg></span><span class="gnav-label">Framework thiếu</span><span class="gnav-count" aria-label="3 mục đang mở">3</span></a></div></div><div class="gnav-bottom"><div class="gnav-sep"></div><a class="gnav-item gnav-assistant" href="#"><span class="gnav-ico"><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 4.5h14v9H8.5L5 16.5v-3H3z"></path></g></svg></span><span class="gnav-label">Trợ lý</span></a></div></nav><button type="button" class="gemboss-mobile-nav-backdrop" aria-label="Đóng menu" tabindex="-1"></button><main class="gemboss-platform-main"><div style="padding:24px"><h1 style="margin:0">Nội dung màn</h1></div></main></div></body></html>"></iframe><details class="cat-code"><summary>Markup</summary><pre><code><div class="gemboss-platform-shell">
|
|
@@ -421,7 +442,55 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
421
442
|
</aside>
|
|
422
443
|
</div>
|
|
423
444
|
</main></code></pre></details></figure></section>
|
|
424
|
-
<section id="
|
|
445
|
+
<section id="chat"><h2>Thân chat</h2><p>Một câu trả lời trông thế nào, ở bất kỳ bề mặt nào đặt một ô chat: tin người dùng / trợ lý, markdown, các bước đang làm (narration), chỉ báo "đang làm", câu hỏi gợi ý, chip lựa chọn, ô soạn. CSS là <code>chat.css</code> của gói (admin <code>@import</code> nó). Khung bao ngoài (cột, ngăn, popup) và hành vi (stream, Dừng, phím) là của bề mặt.</p><figure class="cat-demo"><figcaption>Hội thoại: người dùng, trợ lý (markdown), các bước, đang làm</figcaption><div class="cat-live"><div style="display:flex;flex-direction:column;max-width:520px;border:1px solid var(--gemboss-divider);border-radius:12px;background:var(--gemboss-panel-bg)"><div class="gemboss-ec-head"><div class="gemboss-ec-title">Trợ lý</div><div class="gemboss-ec-sub">Hội thoại #1042</div></div><div class="gemboss-ec-scroll" style="max-height:none"><div class="gemboss-ec-msg user"><div class="gemboss-ec-bubble">Khách nói trang checkout trắng sau khi publish.</div></div><div class="gemboss-ec-msg assistant"><ul class="gemboss-narr"><li class="gemboss-narr-row ok"><span class="gemboss-narr-dot">✓</span><span class="gemboss-narr-text">Đọc hội thoại</span></li><li class="gemboss-narr-row running"><span class="gemboss-narr-dot"><span class="gemboss-narr-spin"></span></span><span class="gemboss-narr-text">Tìm bài help liên quan</span></li></ul><div class="gemboss-ec-bubble"><p class="gemboss-md-p">Gợi ý trả lời: vào <a class="gemboss-ec-md-link" href="#chat">Settings › Checkout</a>, tắt <code class="gemboss-md-code">legacy script</code> rồi publish lại.</p><ul class="gemboss-md-list"><li>Nếu vẫn trắng: xoá cache trình duyệt.</li></ul><span class="gemboss-ec-cursor"></span></div></div><div class="gemboss-ec-pending"><span class="gemboss-ec-pending-star">✳</span><span class="gemboss-ec-pending-verb">Đang nghĩ</span><span class="gemboss-ec-pending-meta">4s</span></div><div class="gemboss-ec-starters"><button type="button" class="gemboss-ec-starter">Nên trả lời thế nào?</button><button type="button" class="gemboss-ec-starter">Trước đây giải quyết sao?</button></div></div><form class="gemboss-ec-input" onsubmit="return false"><textarea rows="1" aria-label="Hỏi trợ lý" placeholder="Hỏi về hội thoại này…"></textarea><button type="submit" aria-label="Gửi">↑</button></form></div></div><details class="cat-code"><summary>Markup</summary><pre><code><div style="display:flex;flex-direction:column;max-width:520px;border:1px solid var(--gemboss-divider);border-radius:12px;background:var(--gemboss-panel-bg)">
|
|
446
|
+
<div class="gemboss-ec-head">
|
|
447
|
+
<div class="gemboss-ec-title">Trợ lý</div>
|
|
448
|
+
<div class="gemboss-ec-sub">Hội thoại #1042</div>
|
|
449
|
+
</div>
|
|
450
|
+
<div class="gemboss-ec-scroll" style="max-height:none">
|
|
451
|
+
<div class="gemboss-ec-msg user">
|
|
452
|
+
<div class="gemboss-ec-bubble">Khách nói trang checkout trắng sau khi publish.</div>
|
|
453
|
+
</div>
|
|
454
|
+
<div class="gemboss-ec-msg assistant">
|
|
455
|
+
<ul class="gemboss-narr">
|
|
456
|
+
<li class="gemboss-narr-row ok">
|
|
457
|
+
<span class="gemboss-narr-dot">✓</span>
|
|
458
|
+
<span class="gemboss-narr-text">Đọc hội thoại</span>
|
|
459
|
+
</li>
|
|
460
|
+
<li class="gemboss-narr-row running">
|
|
461
|
+
<span class="gemboss-narr-dot">
|
|
462
|
+
<span class="gemboss-narr-spin">
|
|
463
|
+
</span>
|
|
464
|
+
</span>
|
|
465
|
+
<span class="gemboss-narr-text">Tìm bài help liên quan</span>
|
|
466
|
+
</li>
|
|
467
|
+
</ul>
|
|
468
|
+
<div class="gemboss-ec-bubble">
|
|
469
|
+
<p class="gemboss-md-p">Gợi ý trả lời: vào <a class="gemboss-ec-md-link" href="#chat">Settings › Checkout</a>, tắt <code class="gemboss-md-code">legacy script</code> rồi publish lại.</p>
|
|
470
|
+
<ul class="gemboss-md-list">
|
|
471
|
+
<li>Nếu vẫn trắng: xoá cache trình duyệt.</li>
|
|
472
|
+
</ul>
|
|
473
|
+
<span class="gemboss-ec-cursor">
|
|
474
|
+
</span>
|
|
475
|
+
</div>
|
|
476
|
+
</div>
|
|
477
|
+
<div class="gemboss-ec-pending">
|
|
478
|
+
<span class="gemboss-ec-pending-star">✳</span>
|
|
479
|
+
<span class="gemboss-ec-pending-verb">Đang nghĩ</span>
|
|
480
|
+
<span class="gemboss-ec-pending-meta">4s</span>
|
|
481
|
+
</div>
|
|
482
|
+
<div class="gemboss-ec-starters">
|
|
483
|
+
<button type="button" class="gemboss-ec-starter">Nên trả lời thế nào?</button>
|
|
484
|
+
<button type="button" class="gemboss-ec-starter">Trước đây giải quyết sao?</button>
|
|
485
|
+
</div>
|
|
486
|
+
</div>
|
|
487
|
+
<form class="gemboss-ec-input" onsubmit="return false">
|
|
488
|
+
<textarea rows="1" aria-label="Hỏi trợ lý" placeholder="Hỏi về hội thoại này…">
|
|
489
|
+
</textarea>
|
|
490
|
+
<button type="submit" aria-label="Gửi">↑</button>
|
|
491
|
+
</form>
|
|
492
|
+
</div></code></pre></details></figure></section>
|
|
493
|
+
<section id="assistant"><h2>Trợ lý: cột phiên</h2><p><code>SessionRail</code> (React). Khung toàn màn hình / cửa sổ nổi / cột phải cao hết màn (<code>allowSide</code>) / thanh là <code>AssistantHost</code>, cần React vì có trạng thái.</p><figure class="cat-demo"><figcaption>SessionRail</figcaption><div class="cat-live"><div style="height:360px;display:flex"><aside class="gemboss-assistant-rail"><div class="gemboss-assistant-rail-top"><button type="button" class="gemboss-assistant-newchat" title="Phiên mới"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H5.5A1.5 1.5 0 0 0 4 5.5v9A1.5 1.5 0 0 0 5.5 16h9a1.5 1.5 0 0 0 1.5-1.5V9"></path><path d="M14.5 3.5l2 2L10 12H8v-2z"></path></g></svg><span>Phiên mới</span></button><button type="button" class="gemboss-assistant-collapse" aria-label="Collapse sidebar" title="Collapse"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5l-5 5 5 5"></path></g></svg></button></div><div class="gemboss-assistant-search gemboss-field gemboss-field--group"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="5"></circle><path d="M13 13l3.5 3.5"></path></g></svg><input class="gemboss-field__bare" placeholder="Tìm phiên…" aria-label="Search history" value=""/><kbd class="gemboss-assistant-kbd">/</kbd></div><div class="gemboss-assistant-threads"><div class="gemboss-assistant-group"><div class="gemboss-assistant-group-label">Đã ghim</div><div class="gemboss-assistant-thread"><button type="button" class="gemboss-assistant-thread-main" title="So sánh advertorial với listicle"><span class="gemboss-assistant-thread-titlerow"><svg viewBox="0 0 20 20" class="gemboss-assistant-pin-dot" aria-hidden="true" focusable="false"><path d="M8 3.5h4l-.5 5 2.5 2.5H6l2.5-2.5z" fill="currentColor"></path><path d="M10 11v5.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"></path></svg><span class="gemboss-assistant-thread-title">So sánh advertorial với listicle</span></span><span class="gemboss-assistant-thread-meta">23/8/2026</span></button><div class="gemboss-assistant-thread-actions"><button type="button" class="gemboss-assistant-thread-act" aria-label="Options"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="5" cy="10" r=".8"></circle><circle cx="10" cy="10" r=".8"></circle><circle cx="15" cy="10" r=".8"></circle></g></svg></button></div></div></div><div class="gemboss-assistant-group"><div class="gemboss-assistant-group-label">Hôm nay</div><div class="gemboss-assistant-thread active"><button type="button" class="gemboss-assistant-thread-main" title="Store nào đang dựng loạt landing?"><span class="gemboss-assistant-thread-titlerow"><span class="gemboss-assistant-thread-title">Store nào đang dựng loạt landing?</span></span><span class="gemboss-assistant-thread-meta">3 giờ trước</span></button><div class="gemboss-assistant-thread-actions"><button type="button" class="gemboss-assistant-thread-act" aria-label="Options"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="5" cy="10" r=".8"></circle><circle cx="10" cy="10" r=".8"></circle><circle cx="15" cy="10" r=".8"></circle></g></svg></button></div></div></div><div class="gemboss-assistant-group"><div class="gemboss-assistant-group-label">Hôm qua</div><div class="gemboss-assistant-thread"><button type="button" class="gemboss-assistant-thread-main" title="Landing điểm cao nhất của corpus"><span class="gemboss-assistant-thread-titlerow"><span class="gemboss-assistant-thread-title">Landing điểm cao nhất của corpus</span></span><span class="gemboss-assistant-thread-meta">1 ngày trước</span></button><div class="gemboss-assistant-thread-actions"><button type="button" class="gemboss-assistant-thread-act" aria-label="Options"><svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="5" cy="10" r=".8"></circle><circle cx="10" cy="10" r=".8"></circle><circle cx="15" cy="10" r=".8"></circle></g></svg></button></div></div></div></div></aside></div></div><details class="cat-code"><summary>Markup</summary><pre><code><div style="height:360px;display:flex">
|
|
425
494
|
<aside class="gemboss-assistant-rail">
|
|
426
495
|
<div class="gemboss-assistant-rail-top">
|
|
427
496
|
<button type="button" class="gemboss-assistant-newchat" title="Phiên mới">
|
|
@@ -551,4 +620,16 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
551
620
|
</rect>
|
|
552
621
|
</g>
|
|
553
622
|
</svg></code></pre></details></figure></section>
|
|
554
|
-
</main></div></
|
|
623
|
+
</main></div><script src="../src/kit-plain.js"></script><script>gembossUI.initMenus();
|
|
624
|
+
document.addEventListener("click", function (e) {
|
|
625
|
+
var b = e.target.closest("[data-cat-js]"); if (!b) return;
|
|
626
|
+
var k = b.dataset.catJs;
|
|
627
|
+
if (k === "toast") gembossUI.toast("Đã lưu");
|
|
628
|
+
else if (k === "toast-error") gembossUI.toast("Không lưu được: mất mạng", { error: true });
|
|
629
|
+
else {
|
|
630
|
+
var m = gembossUI.openModal({ title: "Xoá phiên này?", closeLabel: "Đóng", body: '<div class="ui-modal__body">Lịch sử vẫn còn trong sổ; phiên chỉ ẩn khỏi danh sách.</div>',
|
|
631
|
+
foot: '<button type="button" class="ui-btn ui-btn--ghost" data-v="0">Huỷ</button><button type="button" class="ui-btn ui-btn--primary" data-v="1" autofocus>Xoá</button>',
|
|
632
|
+
onClose: function (v) { if (v) gembossUI.toast("Đã xoá (thử)"); } });
|
|
633
|
+
m.panel.querySelectorAll("[data-v]").forEach(function (x) { x.onclick = function () { m.close(x.dataset.v === "1"); }; });
|
|
634
|
+
}
|
|
635
|
+
});</script></body></html>
|