@anyknown/ui 0.6.0 → 0.7.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 CHANGED
@@ -5,8 +5,8 @@ AnyKnown 全產品線共用的 design system,以 [StyleX](https://stylexjs.com)
5
5
  ## 結構
6
6
 
7
7
  - `src/tokens.stylex.ts` — semantic tokens(color / font / text / space / radius / motion / shadow)。light 為預設,dark 跟隨 OS。**唯一真相**。
8
- - `src/tokens.css` — 同一組值的純 CSS variables(`--ak-*`),給非 StyleX 的使用端(desktop 的 Tailwind v4 `@theme` 直接引用)。import 路徑:`@anyknown/ui/tokens.css`。
9
- - `src/themes.stylex.ts` — `light` / `dark` theme,給有手動切換主題的 app:`<div {...stylex.props(...light)}>`。**由 `pnpm gen:themes` 從 tokens 生成**,不要手改(`themes.test.ts` 會擋住不同步)。
8
+ - `src/tokens.css` — 同一組值的純 CSS variables(`--ak-*`),給非 StyleX 的使用端(desktop 的 Tailwind v4 `@theme` 直接引用)。import 路徑:`@anyknown/ui/tokens.css`。palette 掛在 `<html>` 的 `data-palette="neutral"`,亮暗照樣跟系統,`data-theme` 才鎖。
9
+ - `src/themes.stylex.ts` — `light` / `dark` theme,給有手動切換主題的 app:`<div {...stylex.props(...light)}>`。另一套 palette 是 `neutral`(冷灰;`neutral` 跟系統亮暗,`neutralLight` / `neutralDark` 鎖死),值在 `scripts/neutral.mjs`。**由 `pnpm gen:themes` 從 tokens 生成**,不要手改(`themes.test.ts` 會擋住不同步)。
10
10
  - `src/brand.css` — 給沒有 bundler 的一次性頁面(報告、提案、benchmark)的有界詞彙,二十來個 `ak-*` class。build 時內聯 tokens、接 Google Fonts、把按鈕織體預渲染成 SVG data URI(`scripts/brand-css.mjs`),出 `dist/brand.css` 與 <https://ui.anyknown.com/brand.css>。判斷寫在根目錄的 [DESIGN.md](./DESIGN.md),`pnpm check` 會擋文件與詞彙不同步。
11
11
  - `src/scrollbar.css` — 客製捲軸(全域套用)。StyleX 做不了 `::-webkit-scrollbar` 偽元素,所以獨立成 css 檔。
12
12
  - `src/components/` — 34 個元件,每個一個 folder(`<Name>.tsx` + `<Name>.test.tsx`)。清單與共同決策見 [components/README.md](./src/components/README.md),定案理由與走過的彎路見 [components/COMPONENTS.md](./src/components/COMPONENTS.md)。
package/dist/brand.css CHANGED
@@ -43,6 +43,12 @@ html {
43
43
  --ak-success-hl: #c6e0c6;
44
44
  --ak-danger-hl: #efcec3;
45
45
 
46
+ --ak-layer1: #efece3;
47
+ --ak-layer2: #fafaf6;
48
+ --ak-layer3: #f1efe7;
49
+ --ak-layer4: #e8e5da;
50
+ --ak-layer5: #dedacd;
51
+
46
52
  --ak-shadow-popover: 0 8px 24px rgba(35, 33, 29, 0.1);
47
53
  --ak-shadow-raised: 0 1px 2px rgba(35, 33, 29, 0.08);
48
54
 
@@ -91,12 +97,23 @@ html {
91
97
  --ak-success-hl: #2f4a2e;
92
98
  --ak-danger-hl: #573328;
93
99
 
100
+ --ak-layer1: #0e0c09;
101
+ --ak-layer2: #181613;
102
+ --ak-layer3: #211e19;
103
+ --ak-layer4: #2b2721;
104
+ --ak-layer5: #35302a;
105
+
94
106
  --ak-shadow-popover: 0 8px 24px rgba(0, 0, 0, 0.4);
95
107
  --ak-shadow-raised: 0 1px 2px rgba(0, 0, 0, 0.3);
96
108
  }
97
109
  }
98
110
 
99
111
  [data-theme="dark"] {
112
+ --ak-layer1: #0e0c09;
113
+ --ak-layer2: #181613;
114
+ --ak-layer3: #211e19;
115
+ --ak-layer4: #2b2721;
116
+ --ak-layer5: #35302a;
100
117
  --ak-bone: #2a2620;
101
118
  --ak-sheen: #35302a;
102
119
  --ak-success-hl: #2f4a2e;
@@ -124,6 +141,79 @@ html {
124
141
  --ak-info-subtle: #1e2c38;
125
142
  --ak-focus-ring: #4fa184;
126
143
  }
144
+
145
+ /* neutral palette:紙與墨換一套,語意色(danger / success / info)不換。
146
+ * 掛在 <html> 上:data-palette="neutral";亮暗照樣跟系統,data-theme 才鎖。 */
147
+ [data-palette="neutral"] {
148
+ --ak-bg: #ffffff;
149
+ --ak-surface: #f5f5f7;
150
+ --ak-surface-raised: #ffffff;
151
+ --ak-border: #e5e5ea;
152
+ --ak-border-strong: #c7c7cc;
153
+ --ak-text: #1d1d1f;
154
+ --ak-text-muted: #6e6e73;
155
+ --ak-text-faint: #86868b;
156
+ --ak-accent: #1d1d1f;
157
+ --ak-accent-text: #ffffff;
158
+ --ak-accent-subtle: #e8e8ed;
159
+ --ak-warning: #b25000;
160
+ --ak-focus-ring: #1d1d1f;
161
+ --ak-bone: #e6e6ea;
162
+ --ak-sheen: #f2f2f5;
163
+ --ak-layer1: #efeff2;
164
+ --ak-layer2: #ffffff;
165
+ --ak-layer3: #f2f2f5;
166
+ --ak-layer4: #e6e6ea;
167
+ --ak-layer5: #d9d9de;
168
+ }
169
+
170
+ @media (prefers-color-scheme: dark) {
171
+ [data-palette="neutral"]:not([data-theme="light"]) {
172
+ --ak-bg: #000000;
173
+ --ak-surface: #1c1c1e;
174
+ --ak-surface-raised: #2c2c2e;
175
+ --ak-border: #38383a;
176
+ --ak-border-strong: #48484a;
177
+ --ak-text: #f5f5f7;
178
+ --ak-text-muted: #98989d;
179
+ --ak-text-faint: #8e8e93;
180
+ --ak-accent: #f5f5f7;
181
+ --ak-accent-text: #000000;
182
+ --ak-accent-subtle: #2c2c2e;
183
+ --ak-warning: #ffb340;
184
+ --ak-focus-ring: #f5f5f7;
185
+ --ak-bone: #2c2c2e;
186
+ --ak-sheen: #3f3f42;
187
+ --ak-layer1: #000000;
188
+ --ak-layer2: #161618;
189
+ --ak-layer3: #242426;
190
+ --ak-layer4: #323234;
191
+ --ak-layer5: #3f3f42;
192
+ }
193
+ }
194
+
195
+ [data-palette="neutral"][data-theme="dark"] {
196
+ --ak-bg: #000000;
197
+ --ak-surface: #1c1c1e;
198
+ --ak-surface-raised: #2c2c2e;
199
+ --ak-border: #38383a;
200
+ --ak-border-strong: #48484a;
201
+ --ak-text: #f5f5f7;
202
+ --ak-text-muted: #98989d;
203
+ --ak-text-faint: #8e8e93;
204
+ --ak-accent: #f5f5f7;
205
+ --ak-accent-text: #000000;
206
+ --ak-accent-subtle: #2c2c2e;
207
+ --ak-warning: #ffb340;
208
+ --ak-focus-ring: #f5f5f7;
209
+ --ak-bone: #2c2c2e;
210
+ --ak-sheen: #3f3f42;
211
+ --ak-layer1: #000000;
212
+ --ak-layer2: #161618;
213
+ --ak-layer3: #242426;
214
+ --ak-layer4: #323234;
215
+ --ak-layer5: #3f3f42;
216
+ }
127
217
  /* 織體與手動主題:由 scripts/brand-css.mjs 生成 */
128
218
  :root {
129
219
  --ak-weave-primary-light: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 36' preserveAspectRatio='none'%3e%3crect width='120' height='36' fill='%2311362C'/%3e%3cg fill='none' stroke-linecap='round'%3e%3cg stroke='%23123C31' opacity='.5'%3e%3cpath d='M-4.0,0.8L0.0,0.9L4.0,1.0L8.0,1.0L12.0,1.1L16.0,1.2L20.0,1.3L24.0,1.4L28.0,1.5L32.0,1.5L36.0,1.6L40.0,1.7L44.0,1.7L48.0,1.8L52.0,1.8L56.0,1.8L60.0,1.8L64.0,1.8L68.0,1.8L72.0,1.7L76.0,1.7L80.0,1.6L84.0,1.5L88.0,1.5L92.0,1.4L96.0,1.3L100.0,1.2L104.0,1.2L108.0,1.1L112.0,1.1L116.0,1.0L120.0,1.0L124.0,1.0' stroke-width='0.78'/%3e%3cpath d='M-4.0,3.3L0.0,3.4L4.0,3.4L8.0,3.5L12.0,3.6L16.0,3.7L20.0,3.8L24.0,3.9L28.0,4.0L32.0,4.0L36.0,4.1L40.0,4.1L44.0,4.2L48.0,4.2L52.0,4.2L56.0,4.2L60.0,4.2L64.0,4.2L68.0,4.1L72.0,4.1L76.0,4.0L80.0,3.9L84.0,3.9L88.0,3.8L92.0,3.7L96.0,3.6L100.0,3.6L104.0,3.5L108.0,3.4L112.0,3.4L116.0,3.4L120.0,3.4L124.0,3.4' stroke-width='0.59'/%3e%3cpath d='M-4.0,5.8L0.0,5.8L4.0,5.9L8.0,6.0L12.0,6.1L16.0,6.2L20.0,6.3L24.0,6.4L28.0,6.4L32.0,6.5L36.0,6.5L40.0,6.6L44.0,6.6L48.0,6.6L52.0,6.6L56.0,6.6L60.0,6.6L64.0,6.5L68.0,6.5L72.0,6.4L76.0,6.3L80.0,6.3L84.0,6.2L88.0,6.1L92.0,6.0L96.0,5.9L100.0,5.9L104.0,5.8L108.0,5.8L112.0,5.7L116.0,5.7L120.0,5.7L124.0,5.8' stroke-width='0.55'/%3e%3cpath d='M-4.0,8.3L0.0,8.3L4.0,8.4L8.0,8.5L12.0,8.6L16.0,8.7L20.0,8.8L24.0,8.8L28.0,8.9L32.0,8.9L36.0,9.0L40.0,9.0L44.0,9.0L48.0,9.0L52.0,9.0L56.0,9.0L60.0,8.9L64.0,8.9L68.0,8.8L72.0,8.7L76.0,8.6L80.0,8.6L84.0,8.5L88.0,8.4L92.0,8.3L96.0,8.3L100.0,8.2L104.0,8.2L108.0,8.1L112.0,8.1L116.0,8.1L120.0,8.1L124.0,8.2' stroke-width='0.78'/%3e%3cpath d='M-4.0,10.7L0.0,10.8L4.0,10.9L8.0,11.0L12.0,11.1L16.0,11.2L20.0,11.2L24.0,11.3L28.0,11.3L32.0,11.4L36.0,11.4L40.0,11.4L44.0,11.4L48.0,11.4L52.0,11.4L56.0,11.3L60.0,11.3L64.0,11.2L68.0,11.1L72.0,11.0L76.0,11.0L80.0,10.9L84.0,10.8L88.0,10.7L92.0,10.6L96.0,10.6L100.0,10.5L104.0,10.5L108.0,10.5L112.0,10.5L116.0,10.5L120.0,10.6L124.0,10.6' stroke-width='0.78'/%3e%3cpath d='M-4.0,13.2L0.0,13.3L4.0,13.4L8.0,13.5L12.0,13.6L16.0,13.6L20.0,13.7L24.0,13.7L28.0,13.8L32.0,13.8L36.0,13.8L40.0,13.8L44.0,13.8L48.0,13.8L52.0,13.7L56.0,13.6L60.0,13.6L64.0,13.5L68.0,13.4L72.0,13.3L76.0,13.3L80.0,13.2L84.0,13.1L88.0,13.0L92.0,13.0L96.0,12.9L100.0,12.9L104.0,12.9L108.0,12.9L112.0,12.9L116.0,12.9L120.0,13.0L124.0,13.1' stroke-width='0.67'/%3e%3cpath d='M-4.0,15.7L0.0,15.8L4.0,15.9L8.0,16.0L12.0,16.0L16.0,16.1L20.0,16.1L24.0,16.2L28.0,16.2L32.0,16.2L36.0,16.2L40.0,16.2L44.0,16.2L48.0,16.1L52.0,16.0L56.0,16.0L60.0,15.9L64.0,15.8L68.0,15.7L72.0,15.6L76.0,15.6L80.0,15.5L84.0,15.4L88.0,15.4L92.0,15.3L96.0,15.3L100.0,15.3L104.0,15.3L108.0,15.3L112.0,15.3L116.0,15.4L120.0,15.4L124.0,15.5' stroke-width='0.79'/%3e%3cpath d='M-4.0,18.2L0.0,18.3L4.0,18.4L8.0,18.4L12.0,18.5L16.0,18.6L20.0,18.6L24.0,18.6L28.0,18.6L32.0,18.6L36.0,18.6L40.0,18.5L44.0,18.5L48.0,18.4L52.0,18.4L56.0,18.3L60.0,18.2L64.0,18.1L68.0,18.0L72.0,18.0L76.0,17.9L80.0,17.8L84.0,17.7L88.0,17.7L92.0,17.7L96.0,17.6L100.0,17.6L104.0,17.7L108.0,17.7L112.0,17.8L116.0,17.8L120.0,17.9L124.0,18.0' stroke-width='0.61'/%3e%3cpath d='M-4.0,20.7L0.0,20.8L4.0,20.9L8.0,20.9L12.0,21.0L16.0,21.0L20.0,21.0L24.0,21.0L28.0,21.0L32.0,21.0L36.0,20.9L40.0,20.9L44.0,20.8L48.0,20.8L52.0,20.7L56.0,20.6L60.0,20.5L64.0,20.4L68.0,20.3L72.0,20.3L76.0,20.2L80.0,20.1L84.0,20.1L88.0,20.0L92.0,20.0L96.0,20.0L100.0,20.0L104.0,20.1L108.0,20.1L112.0,20.2L116.0,20.3L120.0,20.4L124.0,20.5' stroke-width='0.60'/%3e%3cpath d='M-4.0,23.2L0.0,23.3L4.0,23.3L8.0,23.4L12.0,23.4L16.0,23.4L20.0,23.4L24.0,23.4L28.0,23.4L32.0,23.3L36.0,23.3L40.0,23.2L44.0,23.2L48.0,23.1L52.0,23.0L56.0,22.9L60.0,22.8L64.0,22.7L68.0,22.7L72.0,22.6L76.0,22.5L80.0,22.5L84.0,22.4L88.0,22.4L92.0,22.4L96.0,22.4L100.0,22.5L104.0,22.5L108.0,22.6L112.0,22.7L116.0,22.8L120.0,22.9L124.0,23.0' stroke-width='0.80'/%3e%3cpath d='M-4.0,25.7L0.0,25.7L4.0,25.8L8.0,25.8L12.0,25.8L16.0,25.8L20.0,25.8L24.0,25.8L28.0,25.7L32.0,25.7L36.0,25.6L40.0,25.6L44.0,25.5L48.0,25.4L52.0,25.3L56.0,25.2L60.0,25.1L64.0,25.0L68.0,25.0L72.0,24.9L76.0,24.8L80.0,24.8L84.0,24.8L88.0,24.8L92.0,24.8L96.0,24.8L100.0,24.9L104.0,25.0L108.0,25.0L112.0,25.2L116.0,25.3L120.0,25.4L124.0,25.6' stroke-width='0.58'/%3e%3cpath d='M-4.0,28.1L0.0,28.2L4.0,28.2L8.0,28.2L12.0,28.2L16.0,28.2L20.0,28.2L24.0,28.1L28.0,28.1L32.0,28.0L36.0,28.0L40.0,27.9L44.0,27.8L48.0,27.7L52.0,27.6L56.0,27.5L60.0,27.4L64.0,27.4L68.0,27.3L72.0,27.2L76.0,27.2L80.0,27.2L84.0,27.2L88.0,27.2L92.0,27.2L96.0,27.3L100.0,27.3L104.0,27.4L108.0,27.5L112.0,27.7L116.0,27.8L120.0,27.9L124.0,28.1' stroke-width='0.70'/%3e%3cpath d='M-4.0,30.6L0.0,30.6L4.0,30.6L8.0,30.6L12.0,30.6L16.0,30.6L20.0,30.6L24.0,30.5L28.0,30.4L32.0,30.4L36.0,30.3L40.0,30.2L44.0,30.1L48.0,30.0L52.0,29.9L56.0,29.8L60.0,29.7L64.0,29.7L68.0,29.6L72.0,29.6L76.0,29.5L80.0,29.5L84.0,29.6L88.0,29.6L92.0,29.6L96.0,29.7L100.0,29.8L104.0,29.9L108.0,30.0L112.0,30.2L116.0,30.3L120.0,30.5L124.0,30.6' stroke-width='0.73'/%3e%3cpath d='M-4.0,33.0L0.0,33.0L4.0,33.0L8.0,33.0L12.0,33.0L16.0,33.0L20.0,32.9L24.0,32.8L28.0,32.8L32.0,32.7L36.0,32.6L40.0,32.5L44.0,32.4L48.0,32.3L52.0,32.2L56.0,32.1L60.0,32.1L64.0,32.0L68.0,32.0L72.0,31.9L76.0,31.9L80.0,31.9L84.0,32.0L88.0,32.0L92.0,32.1L96.0,32.2L100.0,32.3L104.0,32.4L108.0,32.5L112.0,32.7L116.0,32.8L120.0,33.0L124.0,33.2' stroke-width='0.70'/%3e%3cpath d='M-4.0,35.4L0.0,35.4L4.0,35.4L8.0,35.4L12.0,35.4L16.0,35.3L20.0,35.2L24.0,35.2L28.0,35.1L32.0,35.0L36.0,34.9L40.0,34.8L44.0,34.7L48.0,34.6L52.0,34.5L56.0,34.4L60.0,34.4L64.0,34.3L68.0,34.3L72.0,34.3L76.0,34.3L80.0,34.3L84.0,34.4L88.0,34.5L92.0,34.5L96.0,34.7L100.0,34.8L104.0,34.9L108.0,35.1L112.0,35.2L116.0,35.4L120.0,35.6L124.0,35.7' stroke-width='0.53'/%3e%3c/g%3e%3cg stroke='%231E6250'%3e%3c/g%3e%3cg stroke='%23216A55'%3e%3cpath d='M-4.0,26.5L0.0,26.6L4.0,26.6L8.0,26.6L12.0,26.6L16.0,26.6L20.0,26.6L24.0,26.6L28.0,26.6L32.0,26.5L36.0,26.4L40.0,26.4L44.0,26.3L48.0,26.2L52.0,26.1L56.0,26.0L60.0,25.9L64.0,25.8L68.0,25.8L72.0,25.7L76.0,25.6L80.0,25.6L84.0,25.6L88.0,25.6L92.0,25.6L96.0,25.7L100.0,25.7L104.0,25.8L108.0,25.9L112.0,26.0L116.0,26.1L120.0,26.3L124.0,26.4' stroke-width='0.76'/%3e%3cpath d='M-4.0,31.6L0.0,31.6L4.0,31.7L8.0,31.7L12.0,31.6L16.0,31.6L20.0,31.6L24.0,31.5L28.0,31.4L32.0,31.3L36.0,31.3L40.0,31.2L44.0,31.1L48.0,31.0L52.0,30.9L56.0,30.8L60.0,30.7L64.0,30.7L68.0,30.6L72.0,30.6L76.0,30.6L80.0,30.6L84.0,30.6L88.0,30.6L92.0,30.7L96.0,30.8L100.0,30.9L104.0,31.0L108.0,31.1L112.0,31.2L116.0,31.4L120.0,31.6L124.0,31.7' stroke-width='0.87'/%3e%3cpath d='M-4.0,34.0L0.0,34.0L4.0,34.0L8.0,34.0L12.0,33.9L16.0,33.9L20.0,33.8L24.0,33.7L28.0,33.7L32.0,33.6L36.0,33.5L40.0,33.4L44.0,33.3L48.0,33.2L52.0,33.1L56.0,33.0L60.0,33.0L64.0,32.9L68.0,32.9L72.0,32.9L76.0,32.8L80.0,32.9L84.0,32.9L88.0,33.0L92.0,33.0L96.0,33.1L100.0,33.2L104.0,33.4L108.0,33.5L112.0,33.7L116.0,33.8L120.0,34.0L124.0,34.2' stroke-width='0.86'/%3e%3cpath d='M-4.0,36.4L0.0,36.4L4.0,36.3L8.0,36.3L12.0,36.3L16.0,36.2L20.0,36.1L24.0,36.0L28.0,36.0L32.0,35.9L36.0,35.8L40.0,35.7L44.0,35.6L48.0,35.5L52.0,35.4L56.0,35.3L60.0,35.3L64.0,35.2L68.0,35.2L72.0,35.2L76.0,35.2L80.0,35.3L84.0,35.3L88.0,35.4L92.0,35.5L96.0,35.6L100.0,35.7L104.0,35.9L108.0,36.0L112.0,36.2L116.0,36.4L120.0,36.5L124.0,36.7' stroke-width='0.86'/%3e%3c/g%3e%3cg stroke='%2323705A'%3e%3cpath d='M-4.0,16.4L0.0,16.5L4.0,16.5L8.0,16.6L12.0,16.7L16.0,16.7L20.0,16.8L24.0,16.8L28.0,16.8L32.0,16.8L36.0,16.8L40.0,16.8L44.0,16.8L48.0,16.7L52.0,16.6L56.0,16.6L60.0,16.5L64.0,16.4L68.0,16.3L72.0,16.2L76.0,16.2L80.0,16.1L84.0,16.0L88.0,16.0L92.0,15.9L96.0,15.9L100.0,15.9L104.0,15.9L108.0,15.9L112.0,15.9L116.0,16.0L120.0,16.1L124.0,16.2' stroke-width='0.62'/%3e%3cpath d='M-4.0,17.7L0.0,17.8L4.0,17.8L8.0,17.9L12.0,18.0L16.0,18.0L20.0,18.1L24.0,18.1L28.0,18.1L32.0,18.1L36.0,18.1L40.0,18.0L44.0,18.0L48.0,17.9L52.0,17.9L56.0,17.8L60.0,17.7L64.0,17.6L68.0,17.5L72.0,17.5L76.0,17.4L80.0,17.3L84.0,17.2L88.0,17.2L92.0,17.1L96.0,17.1L100.0,17.1L104.0,17.1L108.0,17.2L112.0,17.2L116.0,17.3L120.0,17.4L124.0,17.5' stroke-width='0.66'/%3e%3cpath d='M-4.0,19.0L0.0,19.1L4.0,19.1L8.0,19.2L12.0,19.3L16.0,19.3L20.0,19.3L24.0,19.3L28.0,19.4L32.0,19.3L36.0,19.3L40.0,19.3L44.0,19.2L48.0,19.2L52.0,19.1L56.0,19.0L60.0,18.9L64.0,18.8L68.0,18.7L72.0,18.7L76.0,18.6L80.0,18.5L84.0,18.5L88.0,18.4L92.0,18.4L96.0,18.4L100.0,18.4L104.0,18.4L108.0,18.4L112.0,18.5L116.0,18.6L120.0,18.7L124.0,18.8' stroke-width='0.79'/%3e%3cpath d='M-4.0,22.8L0.0,22.8L4.0,22.9L8.0,22.9L12.0,23.0L16.0,23.0L20.0,23.0L24.0,23.0L28.0,23.0L32.0,22.9L36.0,22.9L40.0,22.8L44.0,22.8L48.0,22.7L52.0,22.6L56.0,22.5L60.0,22.4L64.0,22.3L68.0,22.3L72.0,22.2L76.0,22.1L80.0,22.1L84.0,22.0L88.0,22.0L92.0,22.0L96.0,22.0L100.0,22.0L104.0,22.1L108.0,22.2L112.0,22.2L116.0,22.3L120.0,22.5L124.0,22.6' stroke-width='1.18'/%3e%3cpath d='M-4.0,23.9L0.0,23.9L4.0,24.0L8.0,24.0L12.0,24.1L16.0,24.1L20.0,24.1L24.0,24.1L28.0,24.0L32.0,24.0L36.0,23.9L40.0,23.9L44.0,23.8L48.0,23.7L52.0,23.6L56.0,23.6L60.0,23.5L64.0,23.4L68.0,23.3L72.0,23.2L76.0,23.2L80.0,23.1L84.0,23.1L88.0,23.1L92.0,23.1L96.0,23.1L100.0,23.1L104.0,23.2L108.0,23.3L112.0,23.4L116.0,23.5L120.0,23.6L124.0,23.7' stroke-width='0.84'/%3e%3cpath d='M-4.0,27.9L0.0,27.9L4.0,28.0L8.0,28.0L12.0,28.0L16.0,28.0L20.0,28.0L24.0,27.9L28.0,27.9L32.0,27.8L36.0,27.7L40.0,27.6L44.0,27.6L48.0,27.5L52.0,27.4L56.0,27.3L60.0,27.2L64.0,27.1L68.0,27.1L72.0,27.0L76.0,27.0L80.0,26.9L84.0,26.9L88.0,26.9L92.0,27.0L96.0,27.0L100.0,27.1L104.0,27.2L108.0,27.3L112.0,27.4L116.0,27.5L120.0,27.7L124.0,27.8' stroke-width='1.31'/%3e%3cpath d='M-4.0,29.2L0.0,29.2L4.0,29.2L8.0,29.2L12.0,29.2L16.0,29.2L20.0,29.2L24.0,29.1L28.0,29.1L32.0,29.0L36.0,28.9L40.0,28.8L44.0,28.7L48.0,28.7L52.0,28.6L56.0,28.5L60.0,28.4L64.0,28.3L68.0,28.3L72.0,28.2L76.0,28.2L80.0,28.2L84.0,28.2L88.0,28.2L92.0,28.2L96.0,28.3L100.0,28.4L104.0,28.5L108.0,28.6L112.0,28.7L116.0,28.8L120.0,29.0L124.0,29.1' stroke-width='1.29'/%3e%3cpath d='M-4.0,30.5L0.0,30.5L4.0,30.5L8.0,30.5L12.0,30.5L16.0,30.5L20.0,30.5L24.0,30.4L28.0,30.3L32.0,30.3L36.0,30.2L40.0,30.1L44.0,30.0L48.0,29.9L52.0,29.8L56.0,29.7L60.0,29.7L64.0,29.6L68.0,29.5L72.0,29.5L76.0,29.5L80.0,29.5L84.0,29.5L88.0,29.5L92.0,29.5L96.0,29.6L100.0,29.7L104.0,29.8L108.0,29.9L112.0,30.1L116.0,30.2L120.0,30.4L124.0,30.5' stroke-width='0.89'/%3e%3cpath d='M-4.0,32.7L0.0,32.7L4.0,32.7L8.0,32.7L12.0,32.7L16.0,32.7L20.0,32.6L24.0,32.6L28.0,32.5L32.0,32.4L36.0,32.3L40.0,32.2L44.0,32.1L48.0,32.0L52.0,31.9L56.0,31.9L60.0,31.8L64.0,31.7L68.0,31.7L72.0,31.6L76.0,31.6L80.0,31.6L84.0,31.7L88.0,31.7L92.0,31.8L96.0,31.9L100.0,32.0L104.0,32.1L108.0,32.2L112.0,32.4L116.0,32.5L120.0,32.7L124.0,32.9' stroke-width='0.66'/%3e%3cpath d='M-4.0,35.2L0.0,35.2L4.0,35.2L8.0,35.1L12.0,35.1L16.0,35.0L20.0,35.0L24.0,34.9L28.0,34.8L32.0,34.7L36.0,34.6L40.0,34.5L44.0,34.4L48.0,34.3L52.0,34.3L56.0,34.2L60.0,34.1L64.0,34.1L68.0,34.0L72.0,34.0L76.0,34.0L80.0,34.1L84.0,34.1L88.0,34.2L92.0,34.3L96.0,34.4L100.0,34.5L104.0,34.6L108.0,34.8L112.0,34.9L116.0,35.1L120.0,35.3L124.0,35.4' stroke-width='0.70'/%3e%3c/g%3e%3cg stroke='%2328795F'%3e%3cpath d='M-4.0,2.2L0.0,2.3L4.0,2.3L8.0,2.4L12.0,2.5L16.0,2.6L20.0,2.7L24.0,2.8L28.0,2.8L32.0,2.9L36.0,3.0L40.0,3.0L44.0,3.1L48.0,3.1L52.0,3.1L56.0,3.1L60.0,3.1L64.0,3.1L68.0,3.1L72.0,3.0L76.0,3.0L80.0,2.9L84.0,2.8L88.0,2.7L92.0,2.7L96.0,2.6L100.0,2.5L104.0,2.4L108.0,2.4L112.0,2.3L116.0,2.3L120.0,2.3L124.0,2.3' stroke-width='0.91'/%3e%3cpath d='M-4.0,9.8L0.0,9.8L4.0,9.9L8.0,10.0L12.0,10.1L16.0,10.2L20.0,10.3L24.0,10.3L28.0,10.4L32.0,10.4L36.0,10.5L40.0,10.5L44.0,10.5L48.0,10.5L52.0,10.4L56.0,10.4L60.0,10.3L64.0,10.3L68.0,10.2L72.0,10.1L76.0,10.0L80.0,10.0L84.0,9.9L88.0,9.8L92.0,9.7L96.0,9.7L100.0,9.6L104.0,9.6L108.0,9.6L112.0,9.6L116.0,9.6L120.0,9.6L124.0,9.7' stroke-width='1.37'/%3e%3cpath d='M-4.0,13.8L0.0,13.9L4.0,13.9L8.0,14.0L12.0,14.1L16.0,14.2L20.0,14.2L24.0,14.3L28.0,14.3L32.0,14.3L36.0,14.3L40.0,14.3L44.0,14.3L48.0,14.3L52.0,14.2L56.0,14.2L60.0,14.1L64.0,14.0L68.0,13.9L72.0,13.8L76.0,13.8L80.0,13.7L84.0,13.6L88.0,13.5L92.0,13.5L96.0,13.4L100.0,13.4L104.0,13.4L108.0,13.4L112.0,13.4L116.0,13.5L120.0,13.5L124.0,13.6' stroke-width='0.73'/%3e%3cpath d='M-4.0,15.1L0.0,15.2L4.0,15.3L8.0,15.4L12.0,15.5L16.0,15.5L20.0,15.6L24.0,15.6L28.0,15.6L32.0,15.6L36.0,15.6L40.0,15.6L44.0,15.6L48.0,15.6L52.0,15.5L56.0,15.4L60.0,15.4L64.0,15.3L68.0,15.2L72.0,15.1L76.0,15.0L80.0,14.9L84.0,14.9L88.0,14.8L92.0,14.8L96.0,14.7L100.0,14.7L104.0,14.7L108.0,14.7L112.0,14.7L116.0,14.8L120.0,14.9L124.0,15.0' stroke-width='0.85'/%3e%3cpath d='M-4.0,20.3L0.0,20.4L4.0,20.5L8.0,20.5L12.0,20.6L16.0,20.6L20.0,20.7L24.0,20.7L28.0,20.7L32.0,20.6L36.0,20.6L40.0,20.5L44.0,20.5L48.0,20.4L52.0,20.3L56.0,20.3L60.0,20.2L64.0,20.1L68.0,20.0L72.0,19.9L76.0,19.8L80.0,19.8L84.0,19.7L88.0,19.7L92.0,19.7L96.0,19.7L100.0,19.7L104.0,19.7L108.0,19.8L112.0,19.8L116.0,19.9L120.0,20.0L124.0,20.1' stroke-width='1.00'/%3e%3cpath d='M-4.0,21.5L0.0,21.6L4.0,21.7L8.0,21.7L12.0,21.8L16.0,21.8L20.0,21.8L24.0,21.8L28.0,21.8L32.0,21.8L36.0,21.7L40.0,21.7L44.0,21.6L48.0,21.5L52.0,21.5L56.0,21.4L60.0,21.3L64.0,21.2L68.0,21.1L72.0,21.0L76.0,21.0L80.0,20.9L84.0,20.9L88.0,20.8L92.0,20.8L96.0,20.8L100.0,20.8L104.0,20.9L108.0,20.9L112.0,21.0L116.0,21.1L120.0,21.2L124.0,21.4' stroke-width='1.29'/%3e%3cpath d='M-4.0,25.2L0.0,25.3L4.0,25.3L8.0,25.4L12.0,25.4L16.0,25.4L20.0,25.4L24.0,25.4L28.0,25.3L32.0,25.3L36.0,25.2L40.0,25.1L44.0,25.1L48.0,25.0L52.0,24.9L56.0,24.8L60.0,24.7L64.0,24.6L68.0,24.6L72.0,24.5L76.0,24.4L80.0,24.4L84.0,24.4L88.0,24.4L92.0,24.4L96.0,24.4L100.0,24.4L104.0,24.5L108.0,24.6L112.0,24.7L116.0,24.8L120.0,25.0L124.0,25.1' stroke-width='0.62'/%3e%3c/g%3e%3cg stroke='%232E8266'%3e%3cpath d='M-4.0,-0.1L0.0,-0.1L4.0,-0.0L8.0,0.0L12.0,0.1L16.0,0.2L20.0,0.2L24.0,0.3L28.0,0.4L32.0,0.5L36.0,0.6L40.0,0.7L44.0,0.7L48.0,0.8L52.0,0.8L56.0,0.8L60.0,0.8L64.0,0.8L68.0,0.8L72.0,0.8L76.0,0.7L80.0,0.6L84.0,0.6L88.0,0.5L92.0,0.4L96.0,0.4L100.0,0.3L104.0,0.2L108.0,0.1L112.0,0.1L116.0,0.0L120.0,0.0L124.0,0.0' stroke-width='0.94'/%3e%3cpath d='M-4.0,1.0L0.0,1.1L4.0,1.1L8.0,1.2L12.0,1.3L16.0,1.4L20.0,1.5L24.0,1.6L28.0,1.7L32.0,1.7L36.0,1.8L40.0,1.9L44.0,1.9L48.0,2.0L52.0,2.0L56.0,2.0L60.0,2.0L64.0,2.0L68.0,2.0L72.0,1.9L76.0,1.9L80.0,1.8L84.0,1.7L88.0,1.7L92.0,1.6L96.0,1.5L100.0,1.4L104.0,1.4L108.0,1.3L112.0,1.2L116.0,1.2L120.0,1.2L124.0,1.2' stroke-width='1.31'/%3e%3cpath d='M-4.0,3.5L0.0,3.6L4.0,3.7L8.0,3.8L12.0,3.9L16.0,3.9L20.0,4.0L24.0,4.1L28.0,4.2L32.0,4.3L36.0,4.3L40.0,4.4L44.0,4.4L48.0,4.5L52.0,4.5L56.0,4.5L60.0,4.4L64.0,4.4L68.0,4.4L72.0,4.3L76.0,4.2L80.0,4.2L84.0,4.1L88.0,4.0L92.0,3.9L96.0,3.9L100.0,3.8L104.0,3.7L108.0,3.7L112.0,3.6L116.0,3.6L120.0,3.6L124.0,3.6' stroke-width='0.90'/%3e%3cpath d='M-4.0,4.7L0.0,4.8L4.0,4.9L8.0,5.0L12.0,5.1L16.0,5.1L20.0,5.2L24.0,5.3L28.0,5.4L32.0,5.5L36.0,5.5L40.0,5.6L44.0,5.6L48.0,5.6L52.0,5.6L56.0,5.6L60.0,5.6L64.0,5.5L68.0,5.5L72.0,5.4L76.0,5.4L80.0,5.3L84.0,5.2L88.0,5.1L92.0,5.0L96.0,5.0L100.0,4.9L104.0,4.8L108.0,4.8L112.0,4.8L116.0,4.7L120.0,4.7L124.0,4.7' stroke-width='1.03'/%3e%3cpath d='M-4.0,6.1L0.0,6.1L4.0,6.2L8.0,6.3L12.0,6.4L16.0,6.5L20.0,6.6L24.0,6.7L28.0,6.7L32.0,6.8L36.0,6.8L40.0,6.9L44.0,6.9L48.0,6.9L52.0,6.9L56.0,6.9L60.0,6.8L64.0,6.8L68.0,6.7L72.0,6.7L76.0,6.6L80.0,6.5L84.0,6.4L88.0,6.4L92.0,6.3L96.0,6.2L100.0,6.2L104.0,6.1L108.0,6.1L112.0,6.0L116.0,6.0L120.0,6.0L124.0,6.1' stroke-width='1.00'/%3e%3cpath d='M-4.0,7.3L0.0,7.3L4.0,7.4L8.0,7.5L12.0,7.6L16.0,7.7L20.0,7.8L24.0,7.8L28.0,7.9L32.0,8.0L36.0,8.0L40.0,8.0L44.0,8.0L48.0,8.0L52.0,8.0L56.0,8.0L60.0,8.0L64.0,7.9L68.0,7.9L72.0,7.8L76.0,7.7L80.0,7.6L84.0,7.5L88.0,7.5L92.0,7.4L96.0,7.3L100.0,7.3L104.0,7.2L108.0,7.2L112.0,7.2L116.0,7.2L120.0,7.2L124.0,7.2' stroke-width='1.05'/%3e%3cpath d='M-4.0,8.5L0.0,8.5L4.0,8.6L8.0,8.7L12.0,8.8L16.0,8.9L20.0,8.9L24.0,9.0L28.0,9.1L32.0,9.1L36.0,9.2L40.0,9.2L44.0,9.2L48.0,9.2L52.0,9.2L56.0,9.1L60.0,9.1L64.0,9.0L68.0,9.0L72.0,8.9L76.0,8.8L80.0,8.7L84.0,8.7L88.0,8.6L92.0,8.5L96.0,8.4L100.0,8.4L104.0,8.3L108.0,8.3L112.0,8.3L116.0,8.3L120.0,8.3L124.0,8.4' stroke-width='0.79'/%3e%3cpath d='M-4.0,11.0L0.0,11.1L4.0,11.2L8.0,11.2L12.0,11.3L16.0,11.4L20.0,11.5L24.0,11.5L28.0,11.6L32.0,11.6L36.0,11.6L40.0,11.6L44.0,11.6L48.0,11.6L52.0,11.6L56.0,11.5L60.0,11.5L64.0,11.4L68.0,11.3L72.0,11.3L76.0,11.2L80.0,11.1L84.0,11.0L88.0,10.9L92.0,10.9L96.0,10.8L100.0,10.8L104.0,10.7L108.0,10.7L112.0,10.7L116.0,10.8L120.0,10.8L124.0,10.9' stroke-width='1.36'/%3e%3cpath d='M-4.0,12.4L0.0,12.5L4.0,12.6L8.0,12.6L12.0,12.7L16.0,12.8L20.0,12.9L24.0,12.9L28.0,12.9L32.0,13.0L36.0,13.0L40.0,13.0L44.0,13.0L48.0,12.9L52.0,12.9L56.0,12.8L60.0,12.8L64.0,12.7L68.0,12.6L72.0,12.6L76.0,12.5L80.0,12.4L84.0,12.3L88.0,12.2L92.0,12.2L96.0,12.1L100.0,12.1L104.0,12.1L108.0,12.1L112.0,12.1L116.0,12.1L120.0,12.2L124.0,12.2' stroke-width='0.73'/%3e%3c/g%3e%3cg stroke='%235CAA89' opacity='.45'%3e%3cpath d='M-4.0,15.8L0.0,15.9L4.0,15.9L8.0,16.0L12.0,16.1L16.0,16.1L20.0,16.2L24.0,16.2L28.0,16.2L32.0,16.2L36.0,16.2L40.0,16.2L44.0,16.2L48.0,16.1L52.0,16.1L56.0,16.0L60.0,15.9L64.0,15.9L68.0,15.8L72.0,15.7L76.0,15.6L80.0,15.5L84.0,15.5L88.0,15.4L92.0,15.3L96.0,15.3L100.0,15.3L104.0,15.3L108.0,15.3L112.0,15.4L116.0,15.4L120.0,15.5L124.0,15.6' stroke-width='0.69'/%3e%3cpath d='M-4.0,14.5L0.0,14.6L4.0,14.7L8.0,14.8L12.0,14.8L16.0,14.9L20.0,15.0L24.0,15.0L28.0,15.0L32.0,15.0L36.0,15.0L40.0,15.0L44.0,15.0L48.0,15.0L52.0,14.9L56.0,14.8L60.0,14.8L64.0,14.7L68.0,14.6L72.0,14.5L76.0,14.4L80.0,14.4L84.0,14.3L88.0,14.2L92.0,14.2L96.0,14.1L100.0,14.1L104.0,14.1L108.0,14.1L112.0,14.1L116.0,14.2L120.0,14.3L124.0,14.3' stroke-width='0.60'/%3e%3cpath d='M-4.0,15.4L0.0,15.5L4.0,15.5L8.0,15.6L12.0,15.7L16.0,15.7L20.0,15.8L24.0,15.8L28.0,15.9L32.0,15.9L36.0,15.9L40.0,15.8L44.0,15.8L48.0,15.8L52.0,15.7L56.0,15.6L60.0,15.6L64.0,15.5L68.0,15.4L72.0,15.3L76.0,15.2L80.0,15.1L84.0,15.1L88.0,15.0L92.0,15.0L96.0,14.9L100.0,14.9L104.0,14.9L108.0,14.9L112.0,15.0L116.0,15.0L120.0,15.1L124.0,15.2' stroke-width='0.47'/%3e%3cpath d='M-4.0,11.3L0.0,11.4L4.0,11.5L8.0,11.6L12.0,11.7L16.0,11.7L20.0,11.8L24.0,11.9L28.0,11.9L32.0,11.9L36.0,12.0L40.0,12.0L44.0,12.0L48.0,11.9L52.0,11.9L56.0,11.9L60.0,11.8L64.0,11.7L68.0,11.6L72.0,11.6L76.0,11.5L80.0,11.4L84.0,11.3L88.0,11.3L92.0,11.2L96.0,11.1L100.0,11.1L104.0,11.1L108.0,11.1L112.0,11.1L116.0,11.1L120.0,11.1L124.0,11.2' stroke-width='0.45'/%3e%3c/g%3e%3c/g%3e%3c/svg%3e");
@@ -176,6 +266,11 @@ html {
176
266
  --ak-sheen: #f6f4ec;
177
267
  --ak-success-hl: #c6e0c6;
178
268
  --ak-danger-hl: #efcec3;
269
+ --ak-layer1: #efece3;
270
+ --ak-layer2: #fafaf6;
271
+ --ak-layer3: #f1efe7;
272
+ --ak-layer4: #e8e5da;
273
+ --ak-layer5: #dedacd;
179
274
  --ak-shadow-popover: 0 8px 24px rgba(35, 33, 29, 0.1);
180
275
  --ak-shadow-raised: 0 1px 2px rgba(35, 33, 29, 0.08);
181
276
  --ak-font-display: "Newsreader Variable", Georgia, "Times New Roman", serif;
@@ -199,6 +294,11 @@ html {
199
294
  /* 手動鎖定暗色。 */
200
295
  :root.ak-theme-dark,
201
296
  .ak-theme-dark {
297
+ --ak-layer1: #0e0c09;
298
+ --ak-layer2: #181613;
299
+ --ak-layer3: #211e19;
300
+ --ak-layer4: #2b2721;
301
+ --ak-layer5: #35302a;
202
302
  --ak-bone: #2a2620;
203
303
  --ak-sheen: #35302a;
204
304
  --ak-success-hl: #2f4a2e;
@@ -22,9 +22,17 @@ export type BadgeProps = Omit<ComponentProps<"span">, "color"> & {
22
22
  sx?: StyleArg;
23
23
  };
24
24
  export declare function Badge({ variant, dot, count, children, sx, ...props }: BadgeProps): import("react").JSX.Element;
25
- export type ChipProps = BadgeProps & {
26
- onRemove?: () => void;
25
+ /** removeLabel 只有在真的有 × 的時候才必填 —— 沒有按鈕就沒有要唸的東西。 */
26
+ export type ChipProps = BadgeProps & ({
27
+ onRemove: () => void;
27
28
  removeLabel: string;
29
+ } | {
30
+ onRemove?: undefined;
31
+ removeLabel?: string;
32
+ }) & {
33
+ /** 給了就是可按的一枚(真的 `<button aria-pressed>`),沒給就是一枚標籤。 */
34
+ onClick?: () => void;
35
+ pressed?: boolean;
28
36
  };
29
- export declare function Chip({ onRemove, removeLabel, variant, children, ...props }: ChipProps): import("react").JSX.Element;
37
+ export declare function Chip({ onRemove, removeLabel, onClick, pressed, variant, children, sx, ref, ...props }: ChipProps): import("react").JSX.Element;
30
38
  export {};
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import * as stylex from "@stylexjs/stylex";
3
3
  import { reset, styled } from "../../lib/styled.js";
4
4
  import { color, font, radius, space, text } from "../../tokens.stylex.js";
@@ -42,6 +42,20 @@ const styles = stylex.create({
42
42
  dotDanger: { color: color.danger },
43
43
  dotWarning: { color: color.warning },
44
44
  count: { fontFamily: font.mono, fontSize: "0.7rem", fontWeight: 600, lineHeight: 1 },
45
+ pressable: {
46
+ cursor: "pointer",
47
+ backgroundColor: {
48
+ default: "transparent",
49
+ ":hover": "color-mix(in srgb, currentColor 10%, transparent)",
50
+ },
51
+ outline: { default: "none", ":focus-visible": `2px solid ${color.focusRing}` },
52
+ outlineOffset: 2,
53
+ },
54
+ pressed: {
55
+ backgroundColor: { default: color.text, ":hover": color.text },
56
+ borderColor: color.text,
57
+ color: color.bg,
58
+ },
45
59
  remove: {
46
60
  position: "relative",
47
61
  display: "grid",
@@ -74,6 +88,10 @@ const DOT_TONES = {
74
88
  export function Badge({ variant = "neutral", dot, count, children, sx, ...props }) {
75
89
  return (_jsxs("span", { ...props, ...styled(props, styles.base, styles[variant], sx), children: [dot != null && _jsx("span", { "aria-hidden": "true", ...stylex.props(styles.dot, DOT_TONES[dot]) }), children, count != null && _jsx("span", { ...stylex.props(styles.count), children: count })] }));
76
90
  }
77
- export function Chip({ onRemove, removeLabel, variant = "outline", children, ...props }) {
78
- return (_jsxs(Badge, { variant: variant, ...props, children: [children, onRemove != null && (_jsx("button", { type: "button", "aria-label": removeLabel, onClick: onRemove, ...stylex.props(reset.control, styles.remove), children: _jsx("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", "aria-hidden": "true", children: _jsx("path", { d: "m1 1 6 6M7 1 1 7" }) }) }))] }));
91
+ export function Chip({ onRemove, removeLabel, onClick, pressed, variant = "outline", children, sx, ref, ...props }) {
92
+ const body = (_jsxs(_Fragment, { children: [children, onRemove != null && (_jsx("button", { type: "button", "aria-label": removeLabel, onClick: onRemove, ...stylex.props(reset.control, styles.remove), children: _jsx("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", "aria-hidden": "true", children: _jsx("path", { d: "m1 1 6 6M7 1 1 7" }) }) }))] }));
93
+ if (onClick == null) {
94
+ return (_jsx(Badge, { variant: variant, sx: sx, ref: ref, ...props, children: body }));
95
+ }
96
+ return (_jsx("button", { type: "button", "aria-pressed": pressed, onClick: onClick, ...props, ...styled(props, reset.control, styles.base, styles.pressable, styles[variant], pressed === true && styles.pressed, sx), children: body }));
79
97
  }
@@ -1,4 +1,5 @@
1
1
  import { type ReactNode } from "react";
2
+ import { type StyleArg } from "../../lib/styled.js";
2
3
  export type SortState = {
3
4
  col: string;
4
5
  dir: "asc" | "desc";
@@ -34,5 +35,10 @@ export type DataTableProps<Row> = {
34
35
  selectAllLabel?: string;
35
36
  editLabel?: (column: string, key: string) => string;
36
37
  clearLabel?: string;
38
+ /** 捲動區的高度上限。 @default "20rem" */
39
+ maxHeight?: string | number;
40
+ /** 跟著列一起捲的結尾 —— 「載入更多」要待在清單裡,不是待在清單外。 */
41
+ footer?: ReactNode;
42
+ sx?: StyleArg;
37
43
  };
38
- export declare function DataTable<Row>({ rows, total, rowKey, columns, label, filter, onFilterChange, filterPlaceholder, sort, onSortChange, selected, onSelectedChange, emptyState, onClearFilter, countLabel, selectLabel, selectAllLabel, editLabel, clearLabel, }: DataTableProps<Row>): import("react").JSX.Element;
44
+ export declare function DataTable<Row>({ rows, total, rowKey, columns, label, filter, onFilterChange, filterPlaceholder, sort, onSortChange, selected, onSelectedChange, emptyState, onClearFilter, countLabel, selectLabel, selectAllLabel, editLabel, clearLabel, maxHeight, footer, sx, }: DataTableProps<Row>): import("react").JSX.Element;
@@ -46,10 +46,10 @@ const styles = stylex.create({
46
46
  borderStyle: "solid",
47
47
  borderColor: color.border,
48
48
  borderRadius: radius.lg,
49
- maxHeight: "20rem",
50
49
  overflow: "auto",
51
50
  marginTop: space.xs,
52
51
  },
52
+ capped: (maxHeight) => ({ maxHeight }),
53
53
  table: { width: "100%", borderCollapse: "collapse", fontFamily: font.body, fontSize: text.sm },
54
54
  th: {
55
55
  position: "sticky",
@@ -161,7 +161,7 @@ function focusOnMount(node) {
161
161
  function cellValue(column, row) {
162
162
  return column.value?.(row) ?? "";
163
163
  }
164
- export function DataTable({ rows, total, rowKey, columns, label, filter = "", onFilterChange, filterPlaceholder = "過濾…", sort = null, onSortChange, selected, onSelectedChange, emptyState, onClearFilter, countLabel = (shown, total) => `${shown} / ${total}`, selectLabel = (key) => `選取 ${key}`, selectAllLabel = "全選", editLabel = (column, key) => `編輯 ${key} 的 ${column}`, clearLabel = "清除過濾", }) {
164
+ export function DataTable({ rows, total, rowKey, columns, label, filter = "", onFilterChange, filterPlaceholder = "過濾…", sort = null, onSortChange, selected, onSelectedChange, emptyState, onClearFilter, countLabel = (shown, total) => `${shown} / ${total}`, selectLabel = (key) => `選取 ${key}`, selectAllLabel = "全選", editLabel = (column, key) => `編輯 ${key} 的 ${column}`, clearLabel = "清除過濾", maxHeight = "20rem", footer, sx, }) {
165
165
  const [editing, setEditing] = useState(null);
166
166
  const cancelling = useRef(false);
167
167
  const [draft, setDraft] = useState("");
@@ -219,38 +219,38 @@ export function DataTable({ rows, total, rowKey, columns, label, filter = "", on
219
219
  }
220
220
  onSelectedChange?.(next);
221
221
  }
222
- return (_jsxs("div", { children: [onFilterChange != null && (_jsxs("div", { ...stylex.props(styles.toolbar), children: [_jsxs("div", { ...stylex.props(styles.filter), children: [_jsx("span", { ...stylex.props(styles.filterIcon), children: _jsx(SearchIcon, {}) }), _jsx("input", { type: "search", "aria-label": filterPlaceholder, placeholder: filterPlaceholder, value: filter, onChange: (event) => onFilterChange(event.currentTarget.value), ...stylex.props(styles.filterInput) })] }), _jsx("span", { "aria-live": "polite", ...stylex.props(styles.count), children: countLabel(visible.length, total ?? rows.length) })] })), _jsx("div", { tabIndex: 0, role: "region", "aria-label": label, ...stylex.props(styles.wrap), children: _jsxs("table", { "aria-label": label, ...stylex.props(styles.table), children: [_jsx("thead", { children: _jsxs("tr", { children: [selected != null && (_jsx("th", { scope: "col", ...stylex.props(styles.th, styles.thCheck), children: _jsx("input", { ref: selectAll, type: "checkbox", checked: allSelected, "aria-label": selectAllLabel, onChange: (event) => toggleAll(event.currentTarget.checked), ...stylex.props(styles.checkbox) }) })), columns.map((column) => (_jsx("th", { scope: "col", "aria-sort": sort?.col === column.id ? (sort.dir === "asc" ? "ascending" : "descending") : undefined, ...stylex.props(styles.th), children: column.sortable ? (_jsxs("button", { type: "button", onClick: () => toggleSort(column), ...stylex.props(reset.control, styles.sort), children: [column.header, sort?.col === column.id && (_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.arrow), children: sort.dir === "asc" ? "▲" : "▼" }))] })) : (_jsx("span", { ...stylex.props(styles.plainHeader), children: column.header })) }, column.id)))] }) }), _jsx("tbody", { children: visible.length === 0 ? (_jsx("tr", { children: _jsxs("td", { colSpan: columns.length + (selected != null ? 1 : 0), ...stylex.props(styles.emptyRow), children: [emptyState?.(filter) ?? `找不到符合「${filter}」的資料。`, onClearFilter != null && (_jsxs(_Fragment, { children: [" ", _jsx("button", { type: "button", onClick: onClearFilter, ...stylex.props(reset.control, styles.clear), children: clearLabel })] }))] }) })) : (visible.map((row) => {
223
- const key = rowKey(row);
224
- const isSelected = selected?.has(key) ?? false;
225
- return (_jsxs("tr", { ...stylex.props(styles.row, isSelected && styles.rowSelected), children: [selected != null && (_jsx("td", { ...stylex.props(styles.td, styles.tdCheck), children: _jsx("input", { type: "checkbox", checked: isSelected, "aria-label": selectLabel(key), onChange: (event) => {
226
- const next = new Set(selected);
227
- if (event.currentTarget.checked)
228
- next.add(key);
229
- else
230
- next.delete(key);
231
- onSelectedChange?.(next);
232
- }, ...stylex.props(styles.checkbox) }) })), columns.map((column) => {
233
- const isEditing = editing?.key === key && editing.col === column.id;
234
- const value = cellValue(column, row);
235
- return (_jsx("td", { tabIndex: column.editable ? 0 : undefined, onDoubleClick: column.editable ? () => startEdit(key, column.id, value) : undefined, onKeyDown: column.editable && !isEditing
236
- ? (event) => {
237
- if (event.key !== "Enter" && event.key !== "F2")
238
- return;
239
- event.preventDefault();
240
- startEdit(key, column.id, value);
241
- }
242
- : undefined, ...stylex.props(styles.td, column.mono && styles.mono, column.editable && styles.editable), children: isEditing ? (_jsx("input", { ref: focusOnMount, "aria-label": editLabel(column.header, key), value: draft, onChange: (event) => setDraft(event.currentTarget.value), onBlur: (event) => commit(column, row, draft, event.currentTarget.closest("td")), onKeyDown: (event) => {
243
- const cell = event.currentTarget.closest("td");
244
- if (event.key === "Enter") {
222
+ return (_jsxs("div", { ...stylex.props(sx), children: [onFilterChange != null && (_jsxs("div", { ...stylex.props(styles.toolbar), children: [_jsxs("div", { ...stylex.props(styles.filter), children: [_jsx("span", { ...stylex.props(styles.filterIcon), children: _jsx(SearchIcon, {}) }), _jsx("input", { type: "search", "aria-label": filterPlaceholder, placeholder: filterPlaceholder, value: filter, onChange: (event) => onFilterChange(event.currentTarget.value), ...stylex.props(styles.filterInput) })] }), _jsx("span", { "aria-live": "polite", ...stylex.props(styles.count), children: countLabel(visible.length, total ?? rows.length) })] })), _jsxs("div", { tabIndex: 0, role: "region", "aria-label": label, ...stylex.props(styles.wrap, styles.capped(maxHeight)), children: [_jsxs("table", { "aria-label": label, ...stylex.props(styles.table), children: [_jsx("thead", { children: _jsxs("tr", { children: [selected != null && (_jsx("th", { scope: "col", ...stylex.props(styles.th, styles.thCheck), children: _jsx("input", { ref: selectAll, type: "checkbox", checked: allSelected, "aria-label": selectAllLabel, onChange: (event) => toggleAll(event.currentTarget.checked), ...stylex.props(styles.checkbox) }) })), columns.map((column) => (_jsx("th", { scope: "col", "aria-sort": sort?.col === column.id ? (sort.dir === "asc" ? "ascending" : "descending") : undefined, ...stylex.props(styles.th), children: column.sortable ? (_jsxs("button", { type: "button", onClick: () => toggleSort(column), ...stylex.props(reset.control, styles.sort), children: [column.header, sort?.col === column.id && (_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.arrow), children: sort.dir === "asc" ? "▲" : "▼" }))] })) : (_jsx("span", { ...stylex.props(styles.plainHeader), children: column.header })) }, column.id)))] }) }), _jsx("tbody", { children: visible.length === 0 ? (_jsx("tr", { children: _jsxs("td", { colSpan: columns.length + (selected != null ? 1 : 0), ...stylex.props(styles.emptyRow), children: [emptyState?.(filter) ?? `找不到符合「${filter}」的資料。`, onClearFilter != null && (_jsxs(_Fragment, { children: [" ", _jsx("button", { type: "button", onClick: onClearFilter, ...stylex.props(reset.control, styles.clear), children: clearLabel })] }))] }) })) : (visible.map((row) => {
223
+ const key = rowKey(row);
224
+ const isSelected = selected?.has(key) ?? false;
225
+ return (_jsxs("tr", { ...stylex.props(styles.row, isSelected && styles.rowSelected), children: [selected != null && (_jsx("td", { ...stylex.props(styles.td, styles.tdCheck), children: _jsx("input", { type: "checkbox", checked: isSelected, "aria-label": selectLabel(key), onChange: (event) => {
226
+ const next = new Set(selected);
227
+ if (event.currentTarget.checked)
228
+ next.add(key);
229
+ else
230
+ next.delete(key);
231
+ onSelectedChange?.(next);
232
+ }, ...stylex.props(styles.checkbox) }) })), columns.map((column) => {
233
+ const isEditing = editing?.key === key && editing.col === column.id;
234
+ const value = cellValue(column, row);
235
+ return (_jsx("td", { tabIndex: column.editable ? 0 : undefined, onDoubleClick: column.editable ? () => startEdit(key, column.id, value) : undefined, onKeyDown: column.editable && !isEditing
236
+ ? (event) => {
237
+ if (event.key !== "Enter" && event.key !== "F2")
238
+ return;
245
239
  event.preventDefault();
246
- commit(column, row, draft, cell);
240
+ startEdit(key, column.id, value);
247
241
  }
248
- else if (event.key === "Escape") {
249
- event.preventDefault();
250
- leaveEdit(cell);
251
- }
252
- }, ...stylex.props(reset.control, styles.cellInput) })) : ((column.cell?.(row) ??
253
- (value === "" ? _jsx("span", { ...stylex.props(styles.empty), children: "\u2014" }) : value))) }, column.id));
254
- })] }, key));
255
- })) })] }) })] }));
242
+ : undefined, ...stylex.props(styles.td, column.mono && styles.mono, column.editable && styles.editable), children: isEditing ? (_jsx("input", { ref: focusOnMount, "aria-label": editLabel(column.header, key), value: draft, onChange: (event) => setDraft(event.currentTarget.value), onBlur: (event) => commit(column, row, draft, event.currentTarget.closest("td")), onKeyDown: (event) => {
243
+ const cell = event.currentTarget.closest("td");
244
+ if (event.key === "Enter") {
245
+ event.preventDefault();
246
+ commit(column, row, draft, cell);
247
+ }
248
+ else if (event.key === "Escape") {
249
+ event.preventDefault();
250
+ leaveEdit(cell);
251
+ }
252
+ }, ...stylex.props(reset.control, styles.cellInput) })) : ((column.cell?.(row) ??
253
+ (value === "" ? _jsx("span", { ...stylex.props(styles.empty), children: "\u2014" }) : value))) }, column.id));
254
+ })] }, key));
255
+ })) })] }), footer] })] }));
256
256
  }
@@ -1,4 +1,5 @@
1
1
  import { type ReactElement, type ReactNode } from "react";
2
+ import type { StyleArg } from "../../lib/styled.js";
2
3
  export type DialogProps = {
3
4
  open?: boolean;
4
5
  defaultOpen?: boolean;
@@ -19,8 +20,15 @@ export type DialogContentProps = {
19
20
  title: ReactNode;
20
21
  description?: ReactNode;
21
22
  children?: ReactNode;
23
+ /** 寬度階;`full` 是沉浸式(整個視窗那麼高)。 @default "sm" */
24
+ size?: "sm" | "md" | "full";
25
+ /** 會捲的那一段 —— 標頭與 children 釘住,只有這裡捲。 */
26
+ body?: ReactNode;
27
+ bodySx?: StyleArg;
28
+ /** 寬高是 popup 自己的事,但要蓋得掉 —— 三欄選擇器那種得自己給 width / maxHeight */
29
+ sx?: StyleArg;
22
30
  };
23
- export declare function DialogContent({ title, description, children }: DialogContentProps): import("react").JSX.Element;
31
+ export declare function DialogContent({ title, description, children, size, body, bodySx, sx, }: DialogContentProps): import("react").JSX.Element;
24
32
  export type ConfirmDialogProps = {
25
33
  open?: boolean;
26
34
  defaultOpen?: boolean;
@@ -44,6 +44,14 @@ const styles = stylex.create({
44
44
  outline: { default: "none", ":focus-visible": `2px solid ${color.focusRing}` },
45
45
  outlineOffset: -2,
46
46
  },
47
+ md: { width: "min(40rem, calc(100vw - 2rem))" },
48
+ full: {
49
+ width: "min(64rem, calc(100vw - 2rem))",
50
+ height: "min(46rem, calc(100vh - 2rem))",
51
+ },
52
+ // 有 body 就換成「標頭釘住、只有 body 捲」:popup 自己不再捲
53
+ split: { display: "flex", flexDirection: "column", overflowY: "hidden" },
54
+ body: { flexGrow: 1, minHeight: 0, overflowY: "auto" },
47
55
  title: {
48
56
  fontFamily: font.display,
49
57
  fontSize: text.lg,
@@ -67,8 +75,8 @@ export function DialogClose({ children }) {
67
75
  export function DialogActions({ children }) {
68
76
  return _jsx("div", { ...stylex.props(styles.actions), children: children });
69
77
  }
70
- export function DialogContent({ title, description, children }) {
71
- return (_jsxs(BaseDialog.Portal, { children: [_jsx(BaseDialog.Backdrop, { ...stylex.props(layerStyles.dialogBackdrop, styles.backdrop) }), _jsx(BaseDialog.Viewport, { ...stylex.props(layerStyles.dialog, styles.viewport), children: _jsxs(BaseDialog.Popup, { ...stylex.props(styles.popup), children: [_jsx(BaseDialog.Title, { ...stylex.props(styles.title), children: title }), description != null && (_jsx(BaseDialog.Description, { ...stylex.props(styles.description), children: description })), children] }) })] }));
78
+ export function DialogContent({ title, description, children, size = "sm", body, bodySx, sx, }) {
79
+ return (_jsxs(BaseDialog.Portal, { children: [_jsx(BaseDialog.Backdrop, { ...stylex.props(layerStyles.dialogBackdrop, styles.backdrop) }), _jsx(BaseDialog.Viewport, { ...stylex.props(layerStyles.dialog, styles.viewport), children: _jsxs(BaseDialog.Popup, { ...stylex.props(styles.popup, size === "md" && styles.md, size === "full" && styles.full, body != null && styles.split, sx), children: [_jsx(BaseDialog.Title, { ...stylex.props(styles.title), children: title }), description != null && (_jsx(BaseDialog.Description, { ...stylex.props(styles.description), children: description })), children, body != null && _jsx("div", { ...stylex.props(styles.body, bodySx), children: body })] }) })] }));
72
80
  }
73
81
  export function ConfirmDialog({ trigger, title, description, danger = false, confirmLabel = "確認", cancelLabel = "取消", onConfirm, ...props }) {
74
82
  const cancelRef = useRef(null);
@@ -0,0 +1,7 @@
1
+ import type { StyleArg } from "../../lib/styled.js";
2
+ export type LiveDotProps = {
3
+ /** 有 label 就變成 `role="status"`,讀屏聽得到「還在跑」。沒有就只是一顆裝飾。 */
4
+ label?: string;
5
+ sx?: StyleArg;
6
+ };
7
+ export declare function LiveDot({ label, sx }: LiveDotProps): import("react").JSX.Element;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as stylex from "@stylexjs/stylex";
3
+ import { color, radius, space } from "../../tokens.stylex.js";
4
+ const REDUCED = "@media (prefers-reduced-motion: reduce)";
5
+ // 呼吸只到 0.35 就回來:淡出到底會變成閃爍,那是警報不是「還在跑」。
6
+ const breathe = stylex.keyframes({ "50%": { opacity: 0.35 } });
7
+ const styles = stylex.create({
8
+ wrap: { display: "inline-flex", alignItems: "center", gap: space.xxs },
9
+ dot: {
10
+ flex: "none",
11
+ width: "0.375rem",
12
+ height: "0.375rem",
13
+ borderRadius: radius.full,
14
+ backgroundColor: color.accent,
15
+ animationName: { default: breathe, [REDUCED]: "none" },
16
+ animationDuration: "1.6s",
17
+ animationIterationCount: "infinite",
18
+ animationTimingFunction: "ease-in-out",
19
+ },
20
+ srOnly: {
21
+ position: "absolute",
22
+ width: 1,
23
+ height: 1,
24
+ margin: -1,
25
+ padding: 0,
26
+ overflow: "hidden",
27
+ clipPath: "inset(50%)",
28
+ whiteSpace: "nowrap",
29
+ },
30
+ });
31
+ export function LiveDot({ label, sx }) {
32
+ if (label == null)
33
+ return _jsx("span", { "aria-hidden": "true", ...stylex.props(styles.dot, sx) });
34
+ return (_jsxs("span", { role: "status", ...stylex.props(styles.wrap, sx), children: [_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.dot) }), _jsx("span", { ...stylex.props(styles.srOnly), children: label })] }));
35
+ }
@@ -1,5 +1,6 @@
1
1
  import type { CSSProperties } from "react";
2
2
  import { type ReactNode, type Ref } from "react";
3
+ import { type StyleArg } from "../../lib/styled.js";
3
4
  export declare function useMessageBody(): {
4
5
  current: HTMLElement | null;
5
6
  } | null;
@@ -21,8 +22,9 @@ export type AssistantMessageProps = {
21
22
  pendingLabel?: string;
22
23
  authorLabel?: string;
23
24
  children?: ReactNode;
25
+ sx?: StyleArg;
24
26
  };
25
- export declare function AssistantMessage({ streaming, pending, pendingLabel, authorLabel, children, }: AssistantMessageProps): import("react").JSX.Element;
27
+ export declare function AssistantMessage({ streaming, pending, pendingLabel, authorLabel, children, sx, }: AssistantMessageProps): import("react").JSX.Element;
26
28
  export type TextPartProps = {
27
29
  children: ReactNode;
28
30
  };
@@ -82,11 +82,11 @@ export function Thread({ children, ...rest }) {
82
82
  export function UserMessage({ children, authorLabel = "你說:" }) {
83
83
  return (_jsxs("div", { ...stylex.props(styles.turn, styles.userTurn), children: [_jsx("span", { ...stylex.props(styles.srOnly), children: authorLabel }), _jsx("div", { ...stylex.props(styles.bubble), children: children })] }));
84
84
  }
85
- export function AssistantMessage({ streaming = false, pending = false, pendingLabel = "回覆中", authorLabel = "助理說:", children, }) {
85
+ export function AssistantMessage({ streaming = false, pending = false, pendingLabel = "回覆中", authorLabel = "助理說:", children, sx, }) {
86
86
  const body = useRef(null);
87
87
  const parts = Children.toArray(children);
88
88
  const lastText = parts.reduce((found, part, index) => (isValidElement(part) && part.type === TextPart ? index : found), -1);
89
- return (_jsx(MessageBodyContext, { value: body, children: _jsxs("div", { ref: body, ...stylex.props(styles.turn, styles.assistant), children: [_jsx("span", { ...stylex.props(styles.srOnly), children: authorLabel }), pending ? (_jsxs("span", { role: "status", children: [_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.pending) }), _jsx("span", { ...stylex.props(styles.srOnly), children: pendingLabel })] })) : (parts.map((part, index) => (_jsx(StreamingContext, { value: streaming && index === lastText, children: part }, isValidElement(part) && part.key != null ? part.key : index))))] }) }));
89
+ return (_jsx(MessageBodyContext, { value: body, children: _jsxs("div", { ref: body, ...stylex.props(styles.turn, styles.assistant, sx), children: [_jsx("span", { ...stylex.props(styles.srOnly), children: authorLabel }), pending ? (_jsxs("span", { role: "status", children: [_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.pending) }), _jsx("span", { ...stylex.props(styles.srOnly), children: pendingLabel })] })) : (parts.map((part, index) => (_jsx(StreamingContext, { value: streaming && index === lastText, children: part }, isValidElement(part) && part.key != null ? part.key : index))))] }) }));
90
90
  }
91
91
  export function TextPart({ children }) {
92
92
  const streaming = useContext(StreamingContext);
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from "react";
2
+ import type { StyleArg } from "../../lib/styled.js";
3
+ export type SliderProps = {
4
+ value: number;
5
+ onChange: (value: number) => void;
6
+ /** @default 0 */
7
+ min?: number;
8
+ /** @default 1 */
9
+ max?: number;
10
+ /** @default 0.01 */
11
+ step?: number;
12
+ label?: ReactNode;
13
+ "aria-label"?: string;
14
+ /** 唸出來的值 —— 0.62 要唸成「多」不是「零點六二」。 */
15
+ valueText?: (value: number) => string;
16
+ disabled?: boolean;
17
+ sx?: StyleArg;
18
+ };
19
+ export declare function Slider({ value, onChange, min, max, step, label, valueText, disabled, sx, "aria-label": ariaLabel, }: SliderProps): import("react").JSX.Element;