@gemboss/ui 0.1.0 → 0.1.1
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/catalog/index.html +4 -4
- package/gemboss-ui.css +6 -3
- package/package.json +1 -1
- package/src/app-shell.css +4 -1
- package/src/assistant/sessions.tsx +5 -0
- package/src/index.ts +1 -1
- package/src/shell.tsx +19 -3
package/catalog/index.html
CHANGED
|
@@ -27,7 +27,7 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
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
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="#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.1.
|
|
30
|
+
<main><header class="cat-head"><h1>@gemboss/ui 0.1.1</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>c41e1cabde7b</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>
|
|
@@ -120,10 +120,10 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
120
120
|
</div>
|
|
121
121
|
</div>
|
|
122
122
|
</div></code></pre></details></figure></section>
|
|
123
|
-
<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"><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"><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">
|
|
123
|
+
<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">
|
|
124
124
|
<header class="gemboss-platform-topbar">
|
|
125
125
|
<div class="gemboss-topbar-start">
|
|
126
|
-
<button type="button" class="gemboss-mobile-menu-btn" aria-label="Mở menu" aria-expanded="false">
|
|
126
|
+
<button type="button" class="gemboss-mobile-menu-btn" aria-label="Mở menu" aria-expanded="false" aria-controls=":R0:">
|
|
127
127
|
<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true">
|
|
128
128
|
<path d="M3 5.5h14M3 10h14M3 14.5h14" stroke="currentColor" stroke-width="1.7" stroke-linecap="round">
|
|
129
129
|
</path>
|
|
@@ -162,7 +162,7 @@ body{margin:0;background:var(--gemboss-app-bg,#f1f1f1);color:var(--gemboss-text)
|
|
|
162
162
|
</details>
|
|
163
163
|
</div>
|
|
164
164
|
</header>
|
|
165
|
-
<nav class="icon-nav" aria-label="Main navigation">
|
|
165
|
+
<nav class="icon-nav" aria-label="Main navigation" id=":R0:">
|
|
166
166
|
<div class="gnav-scroll">
|
|
167
167
|
<div class="gnav-heading">Kho</div>
|
|
168
168
|
<div class="gnav-group active">
|
package/gemboss-ui.css
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* GemBoss admin design system — GENERATED — do not edit; regenerate from gemboss:
|
|
3
3
|
* node apps/admin/scripts/export-ui-css.mjs --out <path> (npm run export:ui-css)
|
|
4
|
-
* gemboss commit:
|
|
5
|
-
* sources sha256:
|
|
4
|
+
* gemboss commit: c41e1cabde7bcdfa7747fdc2c88d5afbdf632cc6
|
|
5
|
+
* sources sha256: 55d17952dd57f9e4a7fb14d4e12dc1c65236c97c26f2c7c40f86e5e04f233e1a
|
|
6
6
|
* contents: 1 allow-listed rule from apps/admin/src/app/globals.css + packages/ui/src/gemboss-tokens.css
|
|
7
7
|
* (verbatim) + packages/ui/src/ui.css (verbatim) + packages/ui/src/gemboss-field.css (verbatim)
|
|
8
8
|
* + 180 rules from packages/ui/src/shell.css (the shared shell)
|
|
@@ -1827,6 +1827,8 @@ body.gemboss-assistant-unread .gnav-item.gnav-assistant::after {
|
|
|
1827
1827
|
@media (max-width: 760px) {
|
|
1828
1828
|
/* The topbar is one flex row here: the middle slot takes what is left. */
|
|
1829
1829
|
.gemboss-topbar-center { flex: 1 1 auto; justify-content: flex-start; }
|
|
1830
|
+
/* No actions: the empty slot would still take one flex `gap` (8px) off the search at 320px. */
|
|
1831
|
+
.gemboss-topbar-end:empty { display: none; }
|
|
1830
1832
|
}
|
|
1831
1833
|
|
|
1832
1834
|
.gnav-heading {
|
|
@@ -1835,7 +1837,8 @@ body.gemboss-assistant-unread .gnav-item.gnav-assistant::after {
|
|
|
1835
1837
|
letter-spacing: 0.08em; text-transform: uppercase;
|
|
1836
1838
|
color: var(--gemboss-text-muted);
|
|
1837
1839
|
}
|
|
1838
|
-
.gnav-scroll > .gnav-heading:first-child
|
|
1840
|
+
.gnav-scroll > .gnav-heading:first-child,
|
|
1841
|
+
.gnav-scroll > .gnav-section:first-child > .gnav-heading:first-child { padding-top: 2px; }
|
|
1839
1842
|
|
|
1840
1843
|
.gnav-count {
|
|
1841
1844
|
margin-left: auto; flex: 0 0 auto; min-width: 20px; padding: 0 6px;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gemboss/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "GemBoss design system for every surface in the fleet: the admin shell (topbar + rail + drawer) as React components, and the admin stylesheet exported as one CSS file.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
package/src/app-shell.css
CHANGED
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
@media (max-width: 760px) {
|
|
21
21
|
/* The topbar is one flex row here: the middle slot takes what is left. */
|
|
22
22
|
.gemboss-topbar-center { flex: 1 1 auto; justify-content: flex-start; }
|
|
23
|
+
/* No actions: the empty slot would still take one flex `gap` (8px) off the search at 320px. */
|
|
24
|
+
.gemboss-topbar-end:empty { display: none; }
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
.gnav-heading {
|
|
@@ -28,7 +30,8 @@
|
|
|
28
30
|
letter-spacing: 0.08em; text-transform: uppercase;
|
|
29
31
|
color: var(--gemboss-text-muted);
|
|
30
32
|
}
|
|
31
|
-
.gnav-scroll > .gnav-heading:first-child
|
|
33
|
+
.gnav-scroll > .gnav-heading:first-child,
|
|
34
|
+
.gnav-scroll > .gnav-section:first-child > .gnav-heading:first-child { padding-top: 2px; }
|
|
32
35
|
|
|
33
36
|
.gnav-count {
|
|
34
37
|
margin-left: auto; flex: 0 0 auto; min-width: 20px; padding: 0 6px;
|
|
@@ -143,6 +143,7 @@ export function groupSessions(sessions: AssistantSession[], L: SessionRailLabels
|
|
|
143
143
|
export function SessionRail({
|
|
144
144
|
sessions, unlistedIds = [], activeId, pendingId, busyIds = [], collapsed, onToggleCollapsed,
|
|
145
145
|
onNewChat, newChatTitle, onOpen, onRename, onTogglePin, onDelete, onExport, footer, searchRef, labels, icons,
|
|
146
|
+
titleMaxLength = 200,
|
|
146
147
|
}: {
|
|
147
148
|
/** Pinned-first, newest-first — the order the rail shows them in. */
|
|
148
149
|
sessions: AssistantSession[];
|
|
@@ -160,6 +161,9 @@ export function SessionRail({
|
|
|
160
161
|
newChatTitle?: string;
|
|
161
162
|
onOpen: (id: string) => void;
|
|
162
163
|
onRename: (id: string, title: string) => void;
|
|
164
|
+
/** Longest title the rename box takes. Match the server's limit, or a long name is refused there
|
|
165
|
+
* and the row snaps back with no reason given (gemwatcher's store is VARCHAR(200)). */
|
|
166
|
+
titleMaxLength?: number;
|
|
163
167
|
onTogglePin?: (session: AssistantSession) => void;
|
|
164
168
|
onDelete: (id: string) => void;
|
|
165
169
|
onExport?: (id: string) => void;
|
|
@@ -254,6 +258,7 @@ export function SessionRail({
|
|
|
254
258
|
<div className="gemboss-assistant-thread-edit">
|
|
255
259
|
<input className="gemboss-field"
|
|
256
260
|
autoFocus
|
|
261
|
+
maxLength={titleMaxLength}
|
|
257
262
|
value={editValue}
|
|
258
263
|
onChange={(e) => setEditValue(e.target.value)}
|
|
259
264
|
onKeyDown={(e) => {
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export {
|
|
2
2
|
AppShell, MobileMenuButton, MobileNavBackdrop, Rail, RailBottom, RailCount, RailGroup, RailHeading,
|
|
3
|
-
RailItem, RailScroll, RailSep, RailSub, RailSubItem, Shell, ShellMain, Topbar,
|
|
3
|
+
RailItem, RailScroll, RailSection, RailSep, RailSub, RailSubItem, Shell, ShellMain, Topbar,
|
|
4
4
|
} from "./shell";
|
|
5
5
|
export { GemMark } from "./GemMark";
|
|
6
6
|
export { AuthError, AuthField, AuthFoot, AuthForm, AuthHelp, AuthLayout, PasswordField, AuthSubmit } from "./auth";
|
package/src/shell.tsx
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* No router. `RailItem`/`RailSubItem` render an `<a>` unless given `as` — the admin passes
|
|
18
18
|
* `next/link`, a hash-routed SPA passes nothing. Every other prop is forwarded untouched.
|
|
19
19
|
*/
|
|
20
|
-
import React, { useEffect, useState } from "react";
|
|
20
|
+
import React, { useEffect, useId, useState } from "react";
|
|
21
21
|
|
|
22
22
|
const cx = (...parts: Array<string | false | null | undefined>) => parts.filter(Boolean).join(" ");
|
|
23
23
|
|
|
@@ -162,6 +162,8 @@ export function AppShell({ brand, center, actions, rail, railBottom, railLabel,
|
|
|
162
162
|
children?: React.ReactNode;
|
|
163
163
|
}) {
|
|
164
164
|
const [open, setOpen] = useState(false);
|
|
165
|
+
// The hamburger names what it opens (`aria-controls`), so a screen reader can jump to the rail.
|
|
166
|
+
const railId = useId();
|
|
165
167
|
useEffect(() => {
|
|
166
168
|
if (!open || !drawerCloseEvent) return;
|
|
167
169
|
const close = () => setOpen(false);
|
|
@@ -183,13 +185,13 @@ export function AppShell({ brand, center, actions, rail, railBottom, railLabel,
|
|
|
183
185
|
<Shell mobileNavOpen={open}>
|
|
184
186
|
<Topbar>
|
|
185
187
|
<div className="gemboss-topbar-start">
|
|
186
|
-
<MobileMenuButton open={open} onToggle={() => setOpen((o) => !o)} labels={menuLabels} />
|
|
188
|
+
<MobileMenuButton open={open} onToggle={() => setOpen((o) => !o)} labels={menuLabels} aria-controls={railId} />
|
|
187
189
|
{brand}
|
|
188
190
|
</div>
|
|
189
191
|
<div className="gemboss-topbar-center">{center}</div>
|
|
190
192
|
<div className="gemboss-topbar-end">{actions}</div>
|
|
191
193
|
</Topbar>
|
|
192
|
-
<Rail label={railLabel} onClick={closeOnFollow}>
|
|
194
|
+
<Rail id={railId} label={railLabel} onClick={closeOnFollow}>
|
|
193
195
|
<RailScroll>{rail}</RailScroll>
|
|
194
196
|
{railBottom ? <RailBottom>{railBottom}</RailBottom> : null}
|
|
195
197
|
</Rail>
|
|
@@ -205,6 +207,20 @@ export function RailHeading({ id, children }: { id?: string; children?: React.Re
|
|
|
205
207
|
return <div className="gnav-heading" id={id}>{children}</div>;
|
|
206
208
|
}
|
|
207
209
|
|
|
210
|
+
/** A family of rail items under its heading, announced as a group: `role="group"` labelled by the
|
|
211
|
+
* heading, so a screen reader reads "Market, group" before its items. A bare `RailHeading` followed by
|
|
212
|
+
* items is only text to it (gemwatcher lost its groups that way moving onto this package, 29/09/2026).
|
|
213
|
+
* Adds no box of its own; app-shell.css keeps the first-heading spacing for it. */
|
|
214
|
+
export function RailSection({ heading, children }: { heading: React.ReactNode; children?: React.ReactNode }) {
|
|
215
|
+
const id = useId();
|
|
216
|
+
return (
|
|
217
|
+
<div className="gnav-section" role="group" aria-labelledby={id}>
|
|
218
|
+
<RailHeading id={id}>{heading}</RailHeading>
|
|
219
|
+
{children}
|
|
220
|
+
</div>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
208
224
|
/** A count pill at the end of a `RailItem` (unread, drafts). Caps at 99+. */
|
|
209
225
|
export function RailCount({ value, label }: { value: number; label?: string }) {
|
|
210
226
|
if (!value) return null;
|