@anyknown/ui 0.6.1 → 0.8.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.
Files changed (70) hide show
  1. package/README.md +6 -7
  2. package/dist/brand.css +111 -124
  3. package/dist/components/badge/Badge.d.ts +11 -3
  4. package/dist/components/badge/Badge.js +21 -3
  5. package/dist/components/button/Button.d.ts +3 -2
  6. package/dist/components/button/Button.js +32 -48
  7. package/dist/components/checkbox/Checkbox.js +24 -48
  8. package/dist/components/data-table/DataTable.d.ts +7 -1
  9. package/dist/components/data-table/DataTable.js +34 -34
  10. package/dist/components/dialog/Dialog.d.ts +6 -1
  11. package/dist/components/dialog/Dialog.js +11 -3
  12. package/dist/components/empty-state/EmptyState.js +1 -1
  13. package/dist/components/ghost/Ghost.d.ts +16 -0
  14. package/dist/components/ghost/Ghost.js +50 -0
  15. package/dist/components/group/Group.d.ts +66 -0
  16. package/dist/components/group/Group.js +166 -0
  17. package/dist/components/handoff-receipt/HandoffReceipt.js +12 -42
  18. package/dist/components/icon/Chevron.d.ts +9 -0
  19. package/dist/components/icon/Chevron.js +6 -0
  20. package/dist/components/icon/icon.d.ts +35 -0
  21. package/dist/components/icon/icon.js +10 -0
  22. package/dist/components/icon-button/IconButton.d.ts +17 -0
  23. package/dist/components/icon-button/IconButton.js +66 -0
  24. package/dist/components/live-dot/LiveDot.d.ts +7 -0
  25. package/dist/components/live-dot/LiveDot.js +35 -0
  26. package/dist/components/message/Message.d.ts +3 -1
  27. package/dist/components/message/Message.js +2 -2
  28. package/dist/components/page/Page.d.ts +64 -0
  29. package/dist/components/page/Page.js +163 -0
  30. package/dist/components/popover/Popover.js +1 -1
  31. package/dist/components/progress/Progress.js +49 -104
  32. package/dist/components/radio/Radio.js +17 -28
  33. package/dist/components/segmented/Segmented.d.ts +12 -0
  34. package/dist/components/segmented/Segmented.js +43 -0
  35. package/dist/components/settings-rows/SettingsRows.d.ts +25 -0
  36. package/dist/components/settings-rows/SettingsRows.js +63 -0
  37. package/dist/components/slider/Slider.d.ts +19 -0
  38. package/dist/components/slider/Slider.js +103 -0
  39. package/dist/components/spin/Spin.d.ts +5 -0
  40. package/dist/components/spin/Spin.js +25 -0
  41. package/dist/components/status-chip/StatusChip.d.ts +18 -0
  42. package/dist/components/status-chip/StatusChip.js +68 -0
  43. package/dist/components/switch/Switch.js +20 -63
  44. package/dist/components/table/Table.d.ts +39 -0
  45. package/dist/components/table/Table.js +125 -0
  46. package/dist/components/table/TableCells.d.ts +52 -0
  47. package/dist/components/table/TableCells.js +103 -0
  48. package/dist/components/tabs/Tabs.js +16 -48
  49. package/dist/components/text/Text.js +2 -2
  50. package/dist/components/toast/Toast.js +7 -8
  51. package/dist/index.d.ts +14 -0
  52. package/dist/index.js +14 -0
  53. package/dist/lib/layers.d.ts +7 -0
  54. package/dist/lib/layers.js +12 -0
  55. package/dist/themes.stylex.d.ts +54 -206
  56. package/dist/themes.stylex.js +55 -157
  57. package/dist/tokens.css +63 -46
  58. package/dist/tokens.stylex.d.ts +41 -54
  59. package/dist/tokens.stylex.js +64 -85
  60. package/package.json +2 -2
  61. package/dist/lib/paths.d.ts +0 -3
  62. package/dist/lib/paths.js +0 -11
  63. package/dist/lib/progress.d.ts +0 -2
  64. package/dist/lib/progress.js +0 -28
  65. package/dist/lib/silk.d.ts +0 -66
  66. package/dist/lib/silk.js +0 -337
  67. package/dist/lib/svgId.d.ts +0 -1
  68. package/dist/lib/svgId.js +0 -4
  69. package/dist/lib/weave.d.ts +0 -28
  70. package/dist/lib/weave.js +0 -83
@@ -0,0 +1,166 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import * as stylex from "@stylexjs/stylex";
3
+ import { color, corner, font, motion, space, tone, type } from "../../tokens.stylex.js";
4
+ import { Chevron } from "../icon/Chevron.js";
5
+ import { icon } from "../icon/icon.js";
6
+ /**
7
+ * `.group` / `.item` / `.row`: a bordered list where each line is a thing (a server, a key, a
8
+ * rule) with its name, its state and its one action on the right. What a row opens unfolds under
9
+ * it in an `Expand`, never over the screen.
10
+ */
11
+ const REDUCED = "@media (prefers-reduced-motion: reduce)";
12
+ const styles = stylex.create({
13
+ group: {
14
+ backgroundColor: color.surface,
15
+ borderRadius: corner.card,
16
+ boxShadow: `inset 0 0 0 1px ${color.border}`,
17
+ color: color.text,
18
+ fontSize: type.t2,
19
+ lineHeight: type.body,
20
+ },
21
+ item: {
22
+ borderRadius: {
23
+ default: 0,
24
+ ":first-child": `${corner.card} ${corner.card} 0 0`,
25
+ ":last-child": `0 0 ${corner.card} ${corner.card}`,
26
+ },
27
+ boxShadow: { default: "none", ":not(:first-child)": `inset 0 1px 0 ${color.border}` },
28
+ overflow: "hidden",
29
+ },
30
+ go: { backgroundColor: { default: "transparent", ":hover": color.layer3 } },
31
+ row: {
32
+ alignItems: "center",
33
+ columnGap: space.sm,
34
+ display: "grid",
35
+ gridTemplateColumns: "minmax(0, 1fr) auto",
36
+ minHeight: 40,
37
+ paddingBlock: space.xs,
38
+ paddingInline: space.sm,
39
+ },
40
+ rowgo: {
41
+ alignItems: "center",
42
+ backgroundColor: "transparent",
43
+ borderRadius: corner.sm,
44
+ borderStyle: "none",
45
+ borderWidth: 0,
46
+ color: "inherit",
47
+ cursor: "pointer",
48
+ display: "flex",
49
+ flexWrap: "wrap",
50
+ fontFamily: "inherit",
51
+ fontSize: "inherit",
52
+ gap: `0 ${space.xs}`,
53
+ lineHeight: "inherit",
54
+ margin: 0,
55
+ minWidth: 0,
56
+ outline: { default: "none", ":focus-visible": `2px solid ${color.accent}` },
57
+ outlineOffset: 2,
58
+ padding: 0,
59
+ textAlign: "start",
60
+ },
61
+ still: { cursor: "default" },
62
+ mark: {
63
+ alignItems: "center",
64
+ borderRadius: corner.sm,
65
+ color: color.text,
66
+ display: "grid",
67
+ flex: "none",
68
+ fontSize: type.t1,
69
+ fontWeight: 600,
70
+ height: 22,
71
+ justifyItems: "center",
72
+ placeItems: "center",
73
+ width: 22,
74
+ },
75
+ markTint: (tint) => ({ backgroundColor: `color-mix(in srgb, ${tint}, ${color.layer3})` }),
76
+ name: {
77
+ flex: "none",
78
+ fontSize: type.t2,
79
+ fontWeight: 500,
80
+ overflow: "hidden",
81
+ textOverflow: "ellipsis",
82
+ whiteSpace: "nowrap",
83
+ },
84
+ sep: { color: tone.faint, flex: "none", fontSize: type.t2 },
85
+ status: {
86
+ alignItems: "center",
87
+ color: color.textMuted,
88
+ display: "inline-flex",
89
+ fontSize: type.t2,
90
+ gap: space.xs,
91
+ minWidth: 0,
92
+ },
93
+ statusText: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
94
+ warn: { color: color.warning },
95
+ dot: { backgroundColor: color.warning, borderRadius: "50%", flex: "none", height: 6, width: 6 },
96
+ tag: { color: color.textMuted, flex: "none", fontFamily: font.mono, fontSize: type.t2 },
97
+ act: { alignItems: "center", display: "flex", gap: space.xs },
98
+ chevron: { color: tone.faint },
99
+ expand: {
100
+ display: "grid",
101
+ gridTemplateRows: "0fr",
102
+ transitionDuration: { default: motion.normal, [REDUCED]: "0s" },
103
+ transitionProperty: "grid-template-rows",
104
+ transitionTimingFunction: motion.easeOut,
105
+ },
106
+ shown: { gridTemplateRows: "1fr" },
107
+ clip: { minHeight: 0, overflow: "hidden" },
108
+ in: {
109
+ alignItems: "flex-start",
110
+ display: "flex",
111
+ flexDirection: "column",
112
+ gap: space.xs,
113
+ paddingBlock: `0 ${space.sm}`,
114
+ paddingInline: `calc(${space.sm} + 22px + ${space.xs}) ${space.sm}`,
115
+ },
116
+ plain: { paddingBlock: `${space.xs} ${space.sm}`, paddingInline: space.sm },
117
+ text: { color: color.textMuted, fontSize: type.t2, lineHeight: type.body, margin: 0 },
118
+ faint: { color: tone.faint },
119
+ err: { color: color.warning },
120
+ acts: { alignItems: "center", display: "flex", gap: space.xs },
121
+ empty: {
122
+ color: color.textMuted,
123
+ fontSize: type.t2,
124
+ lineHeight: type.body,
125
+ margin: 0,
126
+ padding: space.sm,
127
+ },
128
+ });
129
+ export function Group({ children, sx }) {
130
+ return _jsx("div", { ...stylex.props(styles.group, sx), children: children });
131
+ }
132
+ export function Item({ children, go = false, sx }) {
133
+ return _jsx("div", { ...stylex.props(styles.item, go && styles.go, sx), children: children });
134
+ }
135
+ /** The 22px square with the first letter; `tint` is `<colour> <percent>%` for the mix. */
136
+ export function Mark({ letter, tint, sx }) {
137
+ return (_jsx("div", { "aria-hidden": "true", ...stylex.props(styles.mark, styles.markTint(tint), sx), children: letter }));
138
+ }
139
+ export function Status({ children, warn = false, sx }) {
140
+ return (_jsxs("span", { ...stylex.props(styles.status, warn && styles.warn, sx), children: [warn && _jsx("span", { ...stylex.props(styles.dot), "aria-hidden": "true" }), _jsx("span", { ...stylex.props(styles.statusText), children: children })] }));
141
+ }
142
+ export function Sep() {
143
+ return (_jsx("span", { ...stylex.props(styles.sep), "aria-hidden": "true", children: "\u00B7" }));
144
+ }
145
+ export function Row({ mark, name, children, actions, onPress, chevron = false, sx }) {
146
+ const inner = (_jsxs(_Fragment, { children: [mark, _jsx("span", { ...stylex.props(styles.name), children: name }), children !== undefined && _jsx(Sep, {}), children] }));
147
+ return (_jsxs("div", { ...stylex.props(styles.row, sx), children: [onPress ? (_jsx("button", { type: "button", onClick: onPress, ...stylex.props(styles.rowgo), children: inner })) : (_jsx("div", { ...stylex.props(styles.rowgo, styles.still), children: inner })), _jsxs("div", { ...stylex.props(styles.act), children: [actions, chevron && (_jsx(Chevron, { direction: "right", ...stylex.props(icon.sm, styles.chevron), "aria-hidden": "true" }))] })] }));
148
+ }
149
+ /** `.exp`: what a row opens, unfolding under it in 200ms and taking no room when shut. */
150
+ export function Expand({ open, plain = false, children, sx }) {
151
+ return (_jsx("div", { ...(open ? {} : { inert: true }), ...stylex.props(styles.expand, open && styles.shown, sx), children: _jsx("div", { ...stylex.props(styles.clip), children: _jsx("div", { ...stylex.props(styles.in, plain && styles.plain), children: open ? children : null }) }) }));
152
+ }
153
+ /** A sentence inside an `Expand`: what it means, or what went wrong. */
154
+ export function Note({ children, faint = false, err = false, sx }) {
155
+ return (_jsx("p", { role: err ? "alert" : undefined, ...stylex.props(styles.text, faint && styles.faint, err && styles.err, sx), children: children }));
156
+ }
157
+ export function Acts({ children, sx }) {
158
+ return _jsx("div", { ...stylex.props(styles.acts, sx), children: children });
159
+ }
160
+ /** `.empty`: nothing here yet, said inside the group in one muted line. */
161
+ export function Empty({ children, sx }) {
162
+ return _jsx("p", { ...stylex.props(styles.empty, sx), children: children });
163
+ }
164
+ export function Tag({ children, sx }) {
165
+ return _jsx("span", { ...stylex.props(styles.tag, sx), children: children });
166
+ }
@@ -1,13 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as stylex from "@stylexjs/stylex";
3
- import { useCallback, useId, useState } from "react";
4
- import { KNOT_LEAD, KNOT_LOOP } from "../../lib/paths.js";
5
- import { SilkBody } from "../../lib/silk.js";
6
- import { color, font, motion, radius, space, text, yarnSecondary } from "../../tokens.stylex.js";
3
+ import { useId, useState } from "react";
4
+ import { color, corner, font, motion, space, text } from "../../tokens.stylex.js";
7
5
  const REDUCED = "@media (prefers-reduced-motion: reduce)";
8
- // 卡面 = 淺色布(TEXTURE-GUIDE §3、handoff-receipt/NOTES.md):沒有 background,
9
- // 實心由紗織成;動態取「僅展示面」子集 — hover 帶動 + 光澤帶,無窩、無掃光。
10
- // 布是固定高度 480 的長布,展開只是露出同一塊布更多 → 高度變化零重織、織紋恆定。
11
6
  const styles = stylex.create({
12
7
  receipt: {
13
8
  position: "relative",
@@ -15,12 +10,12 @@ const styles = stylex.create({
15
10
  borderWidth: 1,
16
11
  borderStyle: "solid",
17
12
  borderColor: color.border,
18
- borderRadius: radius.md,
13
+ borderRadius: corner.md,
14
+ backgroundColor: color.surfaceRaised,
19
15
  },
20
- face: { position: "absolute", top: 0, left: 0, pointerEvents: "none" },
21
16
  row: {
22
17
  // 注意:StyleX 0.19 會靜默丟掉 `all: unset`(編不出任何規則),原生按鈕的
23
- // buttonface 底色與 outset 邊框會蓋住布 —— 要逐項重設
18
+ // buttonface 底色與 outset 邊框會留在畫面上 —— 要逐項重設
24
19
  appearance: "none",
25
20
  backgroundColor: "transparent",
26
21
  borderWidth: 0,
@@ -36,7 +31,7 @@ const styles = stylex.create({
36
31
  fontFamily: font.body,
37
32
  fontSize: text.xs,
38
33
  color: { default: color.textMuted, ":hover": color.text },
39
- borderRadius: radius.md,
34
+ borderRadius: corner.md,
40
35
  outline: { default: "none", ":focus-visible": `2px solid ${color.focusRing}` },
41
36
  outlineOffset: -2,
42
37
  },
@@ -52,23 +47,13 @@ const styles = stylex.create({
52
47
  ruleOpen: { borderTopStyle: "solid", borderTopColor: color.accent },
53
48
  label: { whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: space.xxs },
54
49
  mono: { fontFamily: font.mono, fontSize: "0.76rem", lineHeight: 1 },
55
- knot: { flex: "none", overflow: "visible" },
56
- lead: {
57
- stroke: color.textFaint,
58
- transitionProperty: "opacity",
50
+ link: {
51
+ flex: "none",
52
+ color: color.textFaint,
53
+ transitionProperty: "color",
59
54
  transitionDuration: { default: motion.normal, [REDUCED]: "0s" },
60
55
  },
61
- leadHidden: { opacity: 0 },
62
- loop: {
63
- stroke: color.accent,
64
- strokeDasharray: 100,
65
- strokeDashoffset: 100,
66
- transitionProperty: "stroke-dashoffset",
67
- transitionDuration: { default: "500ms", [REDUCED]: "0s" },
68
- transitionTimingFunction: "ease",
69
- transitionDelay: { default: "120ms", [REDUCED]: "0s" },
70
- },
71
- loopTied: { strokeDashoffset: 0 },
56
+ linkOpen: { color: color.accent },
72
57
  chevron: {
73
58
  flex: "none",
74
59
  color: color.textFaint,
@@ -128,20 +113,5 @@ function CheckIcon() {
128
113
  export function HandoffReceipt({ at, ctxPercent, reason = "soft-threshold", memory, ledgerCount, handoffSummary, defaultOpen = false, }) {
129
114
  const bodyId = useId();
130
115
  const [open, setOpen] = useState(defaultOpen);
131
- // ref callback + cleanup(React 19)管動態的生命週期,不用 effect;client-only
132
- const face = useCallback((node) => {
133
- if (!node)
134
- return;
135
- const silk = new SilkBody(node.parentElement, node, {
136
- palette: yarnSecondary,
137
- mode: "hover",
138
- bandMax: 0.5,
139
- pitch: 2,
140
- hiCount: 12,
141
- hiSpan: 0.95,
142
- fixedHeight: 480,
143
- });
144
- return () => silk.destroy();
145
- }, []);
146
- return (_jsxs("div", { ...stylex.props(styles.receipt), children: [_jsx("svg", { ref: face, "aria-hidden": "true", ...stylex.props(styles.face) }), _jsxs("button", { type: "button", "aria-expanded": open, "aria-controls": bodyId, onClick: () => setOpen((value) => !value), ...stylex.props(styles.row), children: [_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.rule, open && styles.ruleOpen) }), _jsxs("span", { ...stylex.props(styles.label), children: [_jsxs("svg", { width: "15", height: "13", viewBox: "0 0 15 13", fill: "none", strokeWidth: "1.7", strokeLinecap: "round", "aria-hidden": "true", ...stylex.props(styles.knot), children: [_jsx("path", { d: KNOT_LEAD, ...stylex.props(styles.lead, open && styles.leadHidden) }), _jsx("path", { d: KNOT_LOOP, pathLength: "100", ...stylex.props(styles.loop, open && styles.loopTied) })] }), _jsxs("span", { children: ["換班完成 · ", _jsx("span", { ...stylex.props(styles.mono), children: at }), " · ", _jsx("span", { ...stylex.props(styles.mono), children: `ctx ${ctxPercent}%` }), REASON_LABEL[reason], " → 新 session"] })] }), _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", ...stylex.props(styles.chevron, open && styles.chevronOpen), children: _jsx("path", { d: "m6 9 6 6 6-6" }) }), _jsx("span", { "aria-hidden": "true", ...stylex.props(styles.rule, open && styles.ruleOpen) })] }), _jsx("div", { ...stylex.props(styles.bodyWrap, open && styles.bodyWrapOpen), children: _jsx("div", { ...stylex.props(styles.bodyClip), children: _jsxs("div", { id: bodyId, inert: !open, ...stylex.props(styles.body, open && styles.bodyOpen), children: [_jsxs("p", { ...stylex.props(styles.check), children: [_jsx(CheckIcon, {}), _jsx("b", { ...stylex.props(styles.checkTitle), children: "\u8A18\u61B6" }), _jsxs("span", { ...stylex.props(styles.checkText), children: [`${memory.count} 筆耐久事實已落盤`, memory.items?.length ? `(${memory.items.join("、")})` : "", "\u3002"] })] }), _jsxs("p", { ...stylex.props(styles.check), children: [_jsx(CheckIcon, {}), _jsx("b", { ...stylex.props(styles.checkTitle), children: "\u6458\u8981" }), _jsx("span", { ...stylex.props(styles.checkText), children: "handoff \u5DF2\u4EA4\u7D66\u4E0B\u4E00\u8F2A,\u8B80\u5F8C\u5373\u92B7\u6BC0\u3002" })] }), _jsxs("p", { ...stylex.props(styles.check), children: [_jsx(CheckIcon, {}), _jsx("b", { ...stylex.props(styles.checkTitle), children: "Ledger" }), _jsx("span", { ...stylex.props(styles.checkText), children: `本輪 ${ledgerCount} 條收據可查,不進新 context。` })] }), handoffSummary != null && _jsx("p", { ...stylex.props(styles.summary), children: handoffSummary })] }) }) })] }));
116
+ return (_jsxs("div", { ...stylex.props(styles.receipt), children: [_jsxs("button", { type: "button", "aria-expanded": open, "aria-controls": bodyId, onClick: () => setOpen((value) => !value), ...stylex.props(styles.row), children: [_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.rule, open && styles.ruleOpen) }), _jsxs("span", { ...stylex.props(styles.label), children: [_jsx("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": "true", ...stylex.props(styles.link, open && styles.linkOpen), children: _jsx("path", { d: "M9 17H7A5 5 0 0 1 7 7h2M15 7h2a5 5 0 0 1 0 10h-2M8 12h8" }) }), _jsxs("span", { children: ["換班完成 · ", _jsx("span", { ...stylex.props(styles.mono), children: at }), " · ", _jsx("span", { ...stylex.props(styles.mono), children: `ctx ${ctxPercent}%` }), REASON_LABEL[reason], " → 新 session"] })] }), _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", ...stylex.props(styles.chevron, open && styles.chevronOpen), children: _jsx("path", { d: "m6 9 6 6 6-6" }) }), _jsx("span", { "aria-hidden": "true", ...stylex.props(styles.rule, open && styles.ruleOpen) })] }), _jsx("div", { ...stylex.props(styles.bodyWrap, open && styles.bodyWrapOpen), children: _jsx("div", { ...stylex.props(styles.bodyClip), children: _jsxs("div", { id: bodyId, inert: !open, ...stylex.props(styles.body, open && styles.bodyOpen), children: [_jsxs("p", { ...stylex.props(styles.check), children: [_jsx(CheckIcon, {}), _jsx("b", { ...stylex.props(styles.checkTitle), children: "\u8A18\u61B6" }), _jsxs("span", { ...stylex.props(styles.checkText), children: [`${memory.count} 筆耐久事實已落盤`, memory.items?.length ? `(${memory.items.join("、")})` : "", "\u3002"] })] }), _jsxs("p", { ...stylex.props(styles.check), children: [_jsx(CheckIcon, {}), _jsx("b", { ...stylex.props(styles.checkTitle), children: "\u6458\u8981" }), _jsx("span", { ...stylex.props(styles.checkText), children: "handoff \u5DF2\u4EA4\u7D66\u4E0B\u4E00\u8F2A,\u8B80\u5F8C\u5373\u92B7\u6BC0\u3002" })] }), _jsxs("p", { ...stylex.props(styles.check), children: [_jsx(CheckIcon, {}), _jsx("b", { ...stylex.props(styles.checkTitle), children: "Ledger" }), _jsx("span", { ...stylex.props(styles.checkText), children: `本輪 ${ledgerCount} 條收據可查,不進新 context。` })] }), handoffSummary != null && _jsx("p", { ...stylex.props(styles.summary), children: handoffSummary })] }) }) })] }));
147
117
  }
@@ -0,0 +1,9 @@
1
+ import type { SVGAttributes } from "react";
2
+ declare const PATH: {
3
+ readonly right: "m9 18 6-6-6-6";
4
+ readonly down: "m6 9 6 6 6-6";
5
+ };
6
+ export declare function Chevron({ direction, ...props }: SVGAttributes<SVGSVGElement> & {
7
+ direction: keyof typeof PATH;
8
+ }): import("react").JSX.Element;
9
+ export {};
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ICON_STROKE } from "./icon.js";
3
+ const PATH = { right: "m9 18 6-6-6-6", down: "m6 9 6 6 6-6" };
4
+ export function Chevron({ direction, ...props }) {
5
+ return (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: ICON_STROKE, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", ...props, children: _jsx("path", { d: PATH[direction] }) }));
6
+ }
@@ -0,0 +1,35 @@
1
+ import * as stylex from "@stylexjs/stylex";
2
+ /** An icon draws at the size it is given; these are the five in use. Stroke 2, currentColor. */
3
+ export declare const icon: Readonly<{
4
+ readonly xs: Readonly<{
5
+ readonly flexShrink: stylex.StyleXClassNameFor<"flexShrink", 0>;
6
+ readonly height: stylex.StyleXClassNameFor<"height", 12>;
7
+ readonly pointerEvents: stylex.StyleXClassNameFor<"pointerEvents", "none">;
8
+ readonly width: stylex.StyleXClassNameFor<"width", 12>;
9
+ }>;
10
+ readonly sm: Readonly<{
11
+ readonly flexShrink: stylex.StyleXClassNameFor<"flexShrink", 0>;
12
+ readonly height: stylex.StyleXClassNameFor<"height", 14>;
13
+ readonly pointerEvents: stylex.StyleXClassNameFor<"pointerEvents", "none">;
14
+ readonly width: stylex.StyleXClassNameFor<"width", 14>;
15
+ }>;
16
+ readonly md: Readonly<{
17
+ readonly flexShrink: stylex.StyleXClassNameFor<"flexShrink", 0>;
18
+ readonly height: stylex.StyleXClassNameFor<"height", 16>;
19
+ readonly pointerEvents: stylex.StyleXClassNameFor<"pointerEvents", "none">;
20
+ readonly width: stylex.StyleXClassNameFor<"width", 16>;
21
+ }>;
22
+ readonly base: Readonly<{
23
+ readonly flexShrink: stylex.StyleXClassNameFor<"flexShrink", 0>;
24
+ readonly height: stylex.StyleXClassNameFor<"height", 18>;
25
+ readonly pointerEvents: stylex.StyleXClassNameFor<"pointerEvents", "none">;
26
+ readonly width: stylex.StyleXClassNameFor<"width", 18>;
27
+ }>;
28
+ readonly lg: Readonly<{
29
+ readonly flexShrink: stylex.StyleXClassNameFor<"flexShrink", 0>;
30
+ readonly height: stylex.StyleXClassNameFor<"height", 20>;
31
+ readonly pointerEvents: stylex.StyleXClassNameFor<"pointerEvents", "none">;
32
+ readonly width: stylex.StyleXClassNameFor<"width", 20>;
33
+ }>;
34
+ }>;
35
+ export declare const ICON_STROKE = 2;
@@ -0,0 +1,10 @@
1
+ import * as stylex from "@stylexjs/stylex";
2
+ /** An icon draws at the size it is given; these are the five in use. Stroke 2, currentColor. */
3
+ export const icon = stylex.create({
4
+ xs: { flexShrink: 0, height: 12, pointerEvents: "none", width: 12 },
5
+ sm: { flexShrink: 0, height: 14, pointerEvents: "none", width: 14 },
6
+ md: { flexShrink: 0, height: 16, pointerEvents: "none", width: 16 },
7
+ base: { flexShrink: 0, height: 18, pointerEvents: "none", width: 18 },
8
+ lg: { flexShrink: 0, height: 20, pointerEvents: "none", width: 20 },
9
+ });
10
+ export const ICON_STROKE = 2;
@@ -0,0 +1,17 @@
1
+ import * as stylex from "@stylexjs/stylex";
2
+ import type { ButtonHTMLAttributes, ReactNode, Ref } from "react";
3
+ export type IconButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "type"> & {
4
+ /** The only place the name is written: the tooltip and the accessible name. */
5
+ label: string;
6
+ side?: "top" | "bottom" | "left" | "right";
7
+ /** `aria-current`: the page this button stands for is the one open. */
8
+ current?: boolean;
9
+ /** Pressed open: the popover it owns is showing. */
10
+ open?: boolean;
11
+ size?: "md" | "sm" | "xs";
12
+ badge?: number;
13
+ children: ReactNode;
14
+ ref?: Ref<HTMLButtonElement>;
15
+ sx?: stylex.StyleXStyles;
16
+ };
17
+ export declare function IconButton({ label, side, current, open, size, badge, children, sx, className, style, ...props }: IconButtonProps): import("react").JSX.Element;
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as stylex from "@stylexjs/stylex";
3
+ import { color, corner, font, ink, motion, type } from "../../tokens.stylex.js";
4
+ import { Tooltip } from "../tooltip/Tooltip.js";
5
+ /**
6
+ * `.ib`: a square that holds one glyph. Nothing at rest, an ink wash under the pointer, the
7
+ * name in a tooltip; a badge in the corner when it counts something.
8
+ */
9
+ const REDUCED = "@media (prefers-reduced-motion: reduce)";
10
+ const styles = stylex.create({
11
+ root: {
12
+ alignItems: "center",
13
+ backgroundColor: { default: "transparent", ":hover": ink.n8 },
14
+ borderRadius: corner.ib,
15
+ borderStyle: "none",
16
+ borderWidth: 0,
17
+ color: { default: color.textMuted, ":hover": color.text },
18
+ cursor: "pointer",
19
+ display: "inline-grid",
20
+ flex: "none",
21
+ fontFamily: "inherit",
22
+ fontSize: "inherit",
23
+ height: "2.25rem",
24
+ justifyItems: "center",
25
+ lineHeight: "inherit",
26
+ margin: 0,
27
+ outline: { default: "none", ":focus-visible": `2px solid ${color.accent}` },
28
+ outlineOffset: 2,
29
+ padding: 0,
30
+ placeItems: "center",
31
+ position: "relative",
32
+ scale: { default: "1", ":active": "0.96" },
33
+ transitionDuration: { default: motion.fast, [REDUCED]: "0s" },
34
+ transitionProperty: "background-color, color",
35
+ transitionTimingFunction: motion.easeOut,
36
+ width: "2.25rem",
37
+ },
38
+ current: { color: color.text },
39
+ open: { backgroundColor: ink.n8, color: color.text },
40
+ sm: { height: "2rem", width: "2rem" },
41
+ xs: { height: "1.75rem", width: "1.75rem" },
42
+ badge: {
43
+ backgroundColor: color.accent,
44
+ borderRadius: 8,
45
+ color: color.accentText,
46
+ fontFamily: font.mono,
47
+ fontSize: type.t1,
48
+ fontWeight: 500,
49
+ height: 16,
50
+ insetInlineEnd: 2,
51
+ lineHeight: "16px",
52
+ minWidth: 16,
53
+ paddingInline: 4,
54
+ position: "absolute",
55
+ textAlign: "center",
56
+ top: 2,
57
+ },
58
+ });
59
+ export function IconButton({ label, side = "right", current = false, open = false, size = "md", badge, children, sx, className, style, ...props }) {
60
+ // A tooltip renders its trigger through the child and hands it a class and a style; merged.
61
+ const own = stylex.props(styles.root, current && styles.current, open && styles.open, size === "sm" && styles.sm, size === "xs" && styles.xs, sx);
62
+ const button = (_jsxs("button", { type: "button", "aria-label": label, ...(current ? { "aria-current": "page" } : {}), ...props, className: [own.className, className].filter(Boolean).join(" "), style: { ...own.style, ...style }, children: [children, badge !== undefined && badge > 0 && _jsx("span", { ...stylex.props(styles.badge), children: badge })] }));
63
+ if (open)
64
+ return button;
65
+ return (_jsx(Tooltip, { content: label, side: side, children: button }));
66
+ }
@@ -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,64 @@
1
+ import * as stylex from "@stylexjs/stylex";
2
+ import type { ReactNode } from "react";
3
+ type Sx = {
4
+ sx?: stylex.StyleXStyles;
5
+ };
6
+ export type PageHeadProps = {
7
+ title: ReactNode;
8
+ /** The summary beside the title, before the spacer: `已連接 3 · 工具 49`. */
9
+ lead?: ReactNode;
10
+ /** The summary on the right, after the spacer: `已設定 3 / 8`, a stat line. */
11
+ summary?: ReactNode;
12
+ actions?: ReactNode;
13
+ } & Sx;
14
+ export declare function PageHead({ title, lead, summary, actions, sx }: PageHeadProps): import("react").JSX.Element;
15
+ /** A number inside a summary or a stat line: mono, in the text colour. */
16
+ export declare function B({ children }: {
17
+ children: ReactNode;
18
+ }): import("react").JSX.Element;
19
+ export type SectionLabelProps = {
20
+ children: ReactNode;
21
+ end?: ReactNode;
22
+ first?: boolean;
23
+ } & Sx;
24
+ export declare function SectionLabel({ children, end, first, sx }: SectionLabelProps): import("react").JSX.Element;
25
+ export declare function Sub({ children, sx }: {
26
+ children: ReactNode;
27
+ } & Sx): import("react").JSX.Element;
28
+ export declare function FootNote({ children, sx }: {
29
+ children: ReactNode;
30
+ } & Sx): import("react").JSX.Element;
31
+ export declare function Hint({ children, sx }: {
32
+ children: ReactNode;
33
+ } & Sx): import("react").JSX.Element;
34
+ export declare function Snippet({ children, sx }: {
35
+ children: ReactNode;
36
+ } & Sx): import("react").JSX.Element;
37
+ /** `.dpanel`: the bordered surface a section's rows sit on; `padded` when it holds prose. */
38
+ export declare function Panel({ children, padded, sx }: {
39
+ children: ReactNode;
40
+ padded?: boolean;
41
+ } & Sx): import("react").JSX.Element;
42
+ export type StatLineProps = {
43
+ children: ReactNode;
44
+ } & Sx;
45
+ export declare function StatLine({ children, sx }: StatLineProps): import("react").JSX.Element;
46
+ export declare function StatBar({ percent, label, text }: {
47
+ percent: number;
48
+ label?: string;
49
+ text?: string;
50
+ }): import("react").JSX.Element;
51
+ export type BarsProps = {
52
+ values: number[];
53
+ /** What a bar says when the pointer is on it. */
54
+ tip: (value: number, index: number) => string;
55
+ from: string;
56
+ to: string;
57
+ sx?: stylex.StyleXStyles;
58
+ };
59
+ /** `.bars` + `.bx`: one bar a day, the first and last day named under them. */
60
+ export declare function Bars({ values, tip, from, to, sx }: BarsProps): import("react").JSX.Element;
61
+ export declare function Faint({ children, sx }: {
62
+ children: ReactNode;
63
+ } & Sx): import("react").JSX.Element;
64
+ export {};