@twentyfirstmedia/ui-core 0.5.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.
Files changed (163) hide show
  1. package/dist/components/admin.d.ts.map +1 -1
  2. package/dist/components/admin.js +31 -21
  3. package/dist/components/admin.js.map +1 -1
  4. package/dist/components/agents.d.ts.map +1 -1
  5. package/dist/components/agents.js +21 -18
  6. package/dist/components/agents.js.map +1 -1
  7. package/dist/components/app-chrome.d.ts.map +1 -1
  8. package/dist/components/app-chrome.js +5 -5
  9. package/dist/components/app-chrome.js.map +1 -1
  10. package/dist/components/approvals.d.ts.map +1 -1
  11. package/dist/components/approvals.js +2 -2
  12. package/dist/components/approvals.js.map +1 -1
  13. package/dist/components/building-blocks.d.ts.map +1 -1
  14. package/dist/components/building-blocks.js +13 -10
  15. package/dist/components/building-blocks.js.map +1 -1
  16. package/dist/components/charts.d.ts +2 -2
  17. package/dist/components/charts.d.ts.map +1 -1
  18. package/dist/components/charts.js +14 -11
  19. package/dist/components/charts.js.map +1 -1
  20. package/dist/components/chat.d.ts +2 -2
  21. package/dist/components/chat.d.ts.map +1 -1
  22. package/dist/components/chat.js +22 -18
  23. package/dist/components/chat.js.map +1 -1
  24. package/dist/components/commerce.d.ts.map +1 -1
  25. package/dist/components/commerce.js +20 -16
  26. package/dist/components/commerce.js.map +1 -1
  27. package/dist/components/data-display.d.ts +0 -5
  28. package/dist/components/data-display.d.ts.map +1 -1
  29. package/dist/components/data-display.js +41 -22
  30. package/dist/components/data-display.js.map +1 -1
  31. package/dist/components/data-table.d.ts +2 -2
  32. package/dist/components/data-table.d.ts.map +1 -1
  33. package/dist/components/data-table.js +3 -3
  34. package/dist/components/data-table.js.map +1 -1
  35. package/dist/components/developer.d.ts.map +1 -1
  36. package/dist/components/developer.js +7 -6
  37. package/dist/components/developer.js.map +1 -1
  38. package/dist/components/feedback.d.ts.map +1 -1
  39. package/dist/components/feedback.js +5 -4
  40. package/dist/components/feedback.js.map +1 -1
  41. package/dist/components/forms.d.ts.map +1 -1
  42. package/dist/components/forms.js +63 -21
  43. package/dist/components/forms.js.map +1 -1
  44. package/dist/components/foundation.d.ts.map +1 -1
  45. package/dist/components/foundation.js +13 -9
  46. package/dist/components/foundation.js.map +1 -1
  47. package/dist/components/household.d.ts +19 -0
  48. package/dist/components/household.d.ts.map +1 -0
  49. package/dist/components/household.js +137 -0
  50. package/dist/components/household.js.map +1 -0
  51. package/dist/components/index.d.ts +4 -1
  52. package/dist/components/index.d.ts.map +1 -1
  53. package/dist/components/index.js +4 -1
  54. package/dist/components/index.js.map +1 -1
  55. package/dist/components/inputs.d.ts.map +1 -1
  56. package/dist/components/inputs.js +33 -8
  57. package/dist/components/inputs.js.map +1 -1
  58. package/dist/components/life.d.ts +26 -0
  59. package/dist/components/life.d.ts.map +1 -0
  60. package/dist/components/life.js +186 -0
  61. package/dist/components/life.js.map +1 -0
  62. package/dist/components/mail.d.ts.map +1 -1
  63. package/dist/components/mail.js +5 -5
  64. package/dist/components/mail.js.map +1 -1
  65. package/dist/components/media.d.ts.map +1 -1
  66. package/dist/components/media.js +62 -11
  67. package/dist/components/media.js.map +1 -1
  68. package/dist/components/navigation.d.ts +3 -1
  69. package/dist/components/navigation.d.ts.map +1 -1
  70. package/dist/components/navigation.js +287 -20
  71. package/dist/components/navigation.js.map +1 -1
  72. package/dist/components/operations.d.ts.map +1 -1
  73. package/dist/components/operations.js +10 -10
  74. package/dist/components/operations.js.map +1 -1
  75. package/dist/components/overlays.d.ts.map +1 -1
  76. package/dist/components/overlays.js +2 -2
  77. package/dist/components/overlays.js.map +1 -1
  78. package/dist/components/previews.js +2 -2
  79. package/dist/components/previews.js.map +1 -1
  80. package/dist/components/primitives.js +2 -2
  81. package/dist/components/primitives.js.map +1 -1
  82. package/dist/components/qr-code.d.ts +8 -0
  83. package/dist/components/qr-code.d.ts.map +1 -0
  84. package/dist/components/qr-code.js +18 -0
  85. package/dist/components/qr-code.js.map +1 -0
  86. package/dist/components/real-estate.d.ts.map +1 -1
  87. package/dist/components/real-estate.js +5 -5
  88. package/dist/components/real-estate.js.map +1 -1
  89. package/dist/components/rich-text-editor.d.ts.map +1 -1
  90. package/dist/components/rich-text-editor.js +1 -1
  91. package/dist/components/rich-text-editor.js.map +1 -1
  92. package/dist/components/select-menu.js +1 -1
  93. package/dist/components/select-menu.js.map +1 -1
  94. package/dist/components/shell.d.ts +0 -4
  95. package/dist/components/shell.d.ts.map +1 -1
  96. package/dist/components/shell.js +100 -7
  97. package/dist/components/shell.js.map +1 -1
  98. package/dist/components/site.d.ts.map +1 -1
  99. package/dist/components/site.js +8 -6
  100. package/dist/components/site.js.map +1 -1
  101. package/dist/components/status.d.ts.map +1 -1
  102. package/dist/components/status.js +2 -2
  103. package/dist/components/status.js.map +1 -1
  104. package/dist/components/system.d.ts.map +1 -1
  105. package/dist/components/system.js +3 -2
  106. package/dist/components/system.js.map +1 -1
  107. package/dist/components/types.d.ts +589 -18
  108. package/dist/components/types.d.ts.map +1 -1
  109. package/dist/components/workspace.d.ts.map +1 -1
  110. package/dist/components/workspace.js +15 -12
  111. package/dist/components/workspace.js.map +1 -1
  112. package/dist/index.d.ts +1 -1
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +1 -1
  115. package/dist/index.js.map +1 -1
  116. package/dist/qr.d.ts +14 -0
  117. package/dist/qr.d.ts.map +1 -0
  118. package/dist/qr.js +298 -0
  119. package/dist/qr.js.map +1 -0
  120. package/dist/strings.d.ts +126 -4
  121. package/dist/strings.d.ts.map +1 -1
  122. package/dist/strings.js +88 -1
  123. package/dist/strings.js.map +1 -1
  124. package/dist/themes.css +15592 -10463
  125. package/package.json +2 -1
  126. package/src/components/admin.tsx +66 -23
  127. package/src/components/agents.tsx +22 -20
  128. package/src/components/app-chrome.tsx +9 -5
  129. package/src/components/approvals.tsx +3 -1
  130. package/src/components/building-blocks.tsx +24 -13
  131. package/src/components/charts.tsx +17 -12
  132. package/src/components/chat.tsx +54 -31
  133. package/src/components/commerce.tsx +58 -24
  134. package/src/components/data-display.tsx +106 -38
  135. package/src/components/data-table.tsx +11 -7
  136. package/src/components/developer.tsx +15 -6
  137. package/src/components/feedback.tsx +5 -3
  138. package/src/components/forms.tsx +101 -23
  139. package/src/components/foundation.tsx +9 -5
  140. package/src/components/household.tsx +489 -0
  141. package/src/components/index.ts +32 -1
  142. package/src/components/inputs.tsx +44 -7
  143. package/src/components/life.tsx +672 -0
  144. package/src/components/mail.tsx +7 -3
  145. package/src/components/media.tsx +88 -16
  146. package/src/components/navigation.tsx +651 -71
  147. package/src/components/operations.tsx +17 -8
  148. package/src/components/overlays.tsx +7 -2
  149. package/src/components/previews.tsx +2 -2
  150. package/src/components/primitives.tsx +2 -2
  151. package/src/components/qr-code.tsx +30 -0
  152. package/src/components/real-estate.tsx +7 -4
  153. package/src/components/rich-text-editor.tsx +7 -1
  154. package/src/components/select-menu.tsx +1 -1
  155. package/src/components/shell.tsx +237 -11
  156. package/src/components/site.tsx +18 -4
  157. package/src/components/status.tsx +2 -0
  158. package/src/components/system.tsx +3 -2
  159. package/src/components/types.ts +583 -18
  160. package/src/components/workspace.tsx +37 -15
  161. package/src/index.ts +2 -0
  162. package/src/qr.ts +300 -0
  163. package/src/strings.ts +297 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twentyfirstmedia/ui-core",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Components, hooks, icons and formatters shared by the Halo and Pro kits.",
5
5
  "license": "MIT",
6
6
  "author": "Twentyfirst Media (https://twentyfirst.media)",
@@ -63,6 +63,7 @@
63
63
  "@types/react": "19.3.0",
64
64
  "@types/react-dom": "19.3.0",
65
65
  "jsdom": "31.0.0",
66
+ "jsqr": "1.4.0",
66
67
  "react": "19.3.1",
67
68
  "react-dom": "19.3.1",
68
69
  "typescript": "6.0.3",
@@ -8,6 +8,7 @@ import { Icon } from "../Icon.js";
8
8
  import { toast } from "../toast-store.js";
9
9
  import { Portal } from "../portal.js";
10
10
  import { DatePicker, FormField, OtpInput, Segmented } from "./forms.js";
11
+ import { QrCode } from "./qr-code.js";
11
12
  import { Popover } from "./overlays.js";
12
13
  import { StatusIndicator, StatusShape } from "./status.js";
13
14
  import { Avatar, Button, Chip, IconButton, TintIcon } from "./foundation.js";
@@ -68,7 +69,7 @@ export function RangeSlider(p: RangeSliderProps) {
68
69
  v[1](n);
69
70
  }
70
71
  return (
71
- <FormField id={id} label={p.label} hint={p.hint}>
72
+ <FormField style={p.style} className={p.className} id={id} label={p.label} hint={p.hint}>
72
73
  <div className="tf-range">
73
74
  <div className="tf-range-track">
74
75
  <span
@@ -148,7 +149,7 @@ export function BarList(p: BarListProps) {
148
149
  max = Math.max.apply(null, items.map((i) => i.value).concat([1])),
149
150
  f = p.format || formatNumber;
150
151
  return (
151
- <div className="tf-barlist" role="list" aria-label={p.label}>
152
+ <div style={p.style} className={cx("tf-barlist", p.className)} role="list" aria-label={p.label}>
152
153
  {items.map((it, i) => (
153
154
  <div
154
155
  key={it.id || (typeof it.label === "string" ? it.label : i)}
@@ -202,6 +203,8 @@ export function StatusSelect(p: StatusSelectProps) {
202
203
  cur = opts.find((o) => o.value === v[0]) || opts[0] || {};
203
204
  return (
204
205
  <Popover
206
+ style={p.style}
207
+ className={p.className}
205
208
  width={p.width || 220}
206
209
  trigger={(s) => (
207
210
  <button
@@ -269,11 +272,14 @@ export function TilePicker(p: TilePickerProps) {
269
272
  }
270
273
  return (
271
274
  <div
272
- className="tf-tiles"
275
+ className={cx("tf-tiles", p.layout === "list" && "list", p.className)}
273
276
  role={multi ? "group" : "radiogroup"}
274
277
  aria-label={p.label}
275
278
  style={{
276
- gridTemplateColumns: "repeat(auto-fill, minmax(" + (p.min || 120) + "px, 1fr))",
279
+ ...(p.layout === "list"
280
+ ? null
281
+ : { gridTemplateColumns: "repeat(auto-fill, minmax(" + (p.min || 120) + "px, 1fr))" }),
282
+ ...p.style,
277
283
  }}
278
284
  >
279
285
  {(p.options || []).map((o) => (
@@ -336,7 +342,7 @@ export function LiveFeed(p: LiveFeedProps) {
336
342
  waiting = paused[0] ? Math.max(0, (p.items || []).length - heldItems.length) : 0;
337
343
  const types = p.types || [];
338
344
  return (
339
- <div className="tf-livefeed">
345
+ <div style={p.style} className={cx("tf-livefeed", p.className)}>
340
346
  <div className="tf-livefeed-head">
341
347
  <span className={cx("tf-live", paused[0] && "off")}>
342
348
  <span className="tf-live-dot" aria-hidden />
@@ -493,6 +499,7 @@ export function MapView(p: MapViewProps) {
493
499
  className={cx("tf-map", p.className)}
494
500
  style={{
495
501
  height: Hh,
502
+ ...p.style,
496
503
  }}
497
504
  role="region"
498
505
  aria-label={p.label || t.viewMap}
@@ -583,7 +590,7 @@ export function BalanceCard(p: BalanceCardProps) {
583
590
  low = p.low != null && p.balance <= p.low,
584
591
  st = p.status || (p.balance <= 0 ? "danger" : low ? "warn" : "ok");
585
592
  return (
586
- <div className={cx("tf-balance", "is-" + st)}>
593
+ <div style={p.style} className={cx("tf-balance", "is-" + st, p.className)}>
587
594
  <div className="tf-balance-head">
588
595
  {p.logo || <TintIcon icon={p.icon || "wallet"} tint={p.tint || "butter"} size={36} />}
589
596
  <div className="tf-option-text">
@@ -653,7 +660,7 @@ export function BalanceCard(p: BalanceCardProps) {
653
660
  export function ServiceMonitor(p: ServiceMonitorProps) {
654
661
  const { formatNumber } = useFormat();
655
662
  return (
656
- <div className="tf-svcmon" role="list" aria-label={p.label}>
663
+ <div style={p.style} className={cx("tf-svcmon", p.className)} role="list" aria-label={p.label}>
657
664
  {(p.items || []).map((s) => (
658
665
  <div key={s.id || s.name} role="listitem" className="tf-svcmon-row">
659
666
  <StatusShape status={s.status} />
@@ -690,7 +697,7 @@ export function RunHistory(p: RunHistoryProps) {
690
697
  const { formatNumber } = useFormat();
691
698
  const open = React.useState<string | number | null>(null);
692
699
  return (
693
- <div className="tf-runs" role="list" aria-label={p.label}>
700
+ <div style={p.style} className={cx("tf-runs", p.className)} role="list" aria-label={p.label}>
694
701
  {(p.items || []).map((r) => {
695
702
  const on = open[0] === r.id;
696
703
  return (
@@ -734,7 +741,7 @@ export function WebhookEndpoint(p: WebhookEndpointProps) {
734
741
  const { formatNumber } = useFormat();
735
742
  const open = React.useState(!!p.defaultOpen);
736
743
  return (
737
- <div className="tf-hook">
744
+ <div style={p.style} className={cx("tf-hook", p.className)}>
738
745
  <div className="tf-hook-head">
739
746
  <TintIcon icon="webhook" tint="lilac" size={36} />
740
747
  <div
@@ -812,7 +819,7 @@ export function WebhookEndpoint(p: WebhookEndpointProps) {
812
819
  export function BackupList(p: BackupListProps) {
813
820
  const t = useStrings(ADMIN_STRINGS);
814
821
  return (
815
- <div className="tf-backups" role="list" aria-label={p.label}>
822
+ <div style={p.style} className={cx("tf-backups", p.className)} role="list" aria-label={p.label}>
816
823
  {(p.items || []).map((b) => (
817
824
  <div key={b.id} role="listitem" className="tf-backups-row">
818
825
  <StatusShape status={b.status || "ok"} />
@@ -865,9 +872,10 @@ export function BlockList(p: BlockListProps) {
865
872
  );
866
873
  return (
867
874
  <div
868
- className="tf-col"
875
+ className={cx("tf-col", p.className)}
869
876
  style={{
870
877
  gap: 12,
878
+ ...p.style,
871
879
  }}
872
880
  >
873
881
  <div className="tf-filterbar">
@@ -942,7 +950,10 @@ export function ReviewCard(p: ReviewCardProps) {
942
950
  } as Record<string, [Status, string]>
943
951
  )[p.state || "pending"] as [Status, string];
944
952
  return (
945
- <article className={cx("tf-review", "is-" + (p.state || "pending"))}>
953
+ <article
954
+ style={p.style}
955
+ className={cx("tf-review", "is-" + (p.state || "pending"), p.className)}
956
+ >
946
957
  <div className="tf-review-head">
947
958
  <Avatar name={p.author} size={36} />
948
959
  <div
@@ -1064,7 +1075,7 @@ export function CouponCard(p: CouponCardProps) {
1064
1075
  const used = p.used || 0,
1065
1076
  st = p.status || (p.limit && used >= p.limit ? "idle" : "ok");
1066
1077
  return (
1067
- <div className={cx("tf-coupon", st !== "ok" && "off")}>
1078
+ <div style={p.style} className={cx("tf-coupon", st !== "ok" && "off", p.className)}>
1068
1079
  <div className="tf-coupon-val">
1069
1080
  <b>
1070
1081
  {p.kind === "fixed"
@@ -1155,7 +1166,14 @@ export function LocaleField(p: LocaleFieldProps) {
1155
1166
  }
1156
1167
  const done = locs.filter((l) => (v[0][l.code] || "").trim()).length;
1157
1168
  return (
1158
- <FormField id={id} label={p.label} hint={p.hint} error={p.error}>
1169
+ <FormField
1170
+ style={p.style}
1171
+ className={p.className}
1172
+ id={id}
1173
+ label={p.label}
1174
+ hint={p.hint}
1175
+ error={p.error}
1176
+ >
1159
1177
  <div className="tf-locfield">
1160
1178
  <div className="tf-locfield-tabs" role="tablist" aria-label={t.languages}>
1161
1179
  {locs.map((l) => {
@@ -1251,6 +1269,8 @@ export function LocaleSwitcher(p: LocaleSwitcherProps) {
1251
1269
  (p.languages || []).find((l) => l.code === lang[0]) || {};
1252
1270
  return (
1253
1271
  <Popover
1272
+ style={p.style}
1273
+ className={p.className}
1254
1274
  width={280}
1255
1275
  trigger={(s) => (
1256
1276
  <button
@@ -1345,18 +1365,22 @@ export function LocaleSwitcher(p: LocaleSwitcherProps) {
1345
1365
  }
1346
1366
 
1347
1367
  export function WorkspaceSwitcher(p: WorkspaceSwitcherProps) {
1368
+ const t = useStrings(ADMIN_STRINGS);
1348
1369
  const items = p.items || [],
1349
1370
  v = useControlled(p.value, items[0] && items[0].id, p.onChange),
1350
1371
  cur: Partial<WorkspaceSwitcherProps["items"][number]> = items.find((x) => x.id === v[0]) || {};
1351
1372
  return (
1352
1373
  <Popover
1374
+ style={p.style}
1375
+ className={p.className}
1353
1376
  width={p.width || 300}
1354
1377
  trigger={(s) => (
1355
1378
  <button
1356
1379
  type="button"
1357
- className={cx("tf-wssw", p.variant === "frame" && "on-frame")}
1380
+ className={cx("tf-wssw", p.variant === "frame" && "on-frame", p.compact && "compact")}
1358
1381
  aria-haspopup="listbox"
1359
1382
  aria-expanded={s.open}
1383
+ aria-label={p.compact ? `${t.switchWorkspace}: ${cur.name || ""}` : undefined}
1360
1384
  onClick={s.toggle}
1361
1385
  >
1362
1386
  {cur.logo || (
@@ -1364,11 +1388,15 @@ export function WorkspaceSwitcher(p: WorkspaceSwitcherProps) {
1364
1388
  {(cur.name || "?").charAt(0)}
1365
1389
  </span>
1366
1390
  )}
1367
- <span className="tf-option-text">
1368
- <b>{cur.name}</b>
1369
- {cur.sub && <small>{cur.sub}</small>}
1370
- </span>
1371
- <Icon name="chevronDown" size={14} />
1391
+ {!p.compact && (
1392
+ <>
1393
+ <span className="tf-option-text">
1394
+ <b>{cur.name}</b>
1395
+ {cur.sub && <small>{cur.sub}</small>}
1396
+ </span>
1397
+ <Icon name="chevronDown" size={14} />
1398
+ </>
1399
+ )}
1372
1400
  </button>
1373
1401
  )}
1374
1402
  >
@@ -1605,7 +1633,7 @@ export function TwoFactorSetup(p: TwoFactorSetupProps) {
1605
1633
  saved = React.useState(false);
1606
1634
  const steps = t.twoFactorSteps;
1607
1635
  return (
1608
- <div className="tf-2fa">
1636
+ <div style={p.style} className={cx("tf-2fa", p.className)}>
1609
1637
  <ol className="tf-2fa-steps">
1610
1638
  {steps.map((s, i) => (
1611
1639
  <li key={i} className={cx(i === step[0] && "on", i < step[0] && "done")}>
@@ -1617,7 +1645,12 @@ export function TwoFactorSetup(p: TwoFactorSetupProps) {
1617
1645
  {step[0] === 0 && (
1618
1646
  <div className="tf-2fa-scan">
1619
1647
  <div className="tf-2fa-qr" role="img" aria-label={t.qrCode}>
1620
- {p.qr || <Icon name="grid" size={48} />}
1648
+ {p.qr ||
1649
+ (p.qrValue ? (
1650
+ <QrCode value={p.qrValue} quiet={2} label={t.qrCode} />
1651
+ ) : (
1652
+ <Icon name="grid" size={48} />
1653
+ ))}
1621
1654
  </div>
1622
1655
  <div
1623
1656
  className="tf-col"
@@ -1679,6 +1712,8 @@ export function TwoFactorSetup(p: TwoFactorSetupProps) {
1679
1712
  <OtpInput
1680
1713
  error={!!err[0]}
1681
1714
  onComplete={(c) => {
1715
+ // a second wrong code turns the error on again, which empties the cells
1716
+ err[1](null);
1682
1717
  Promise.resolve(p.onVerify ? p.onVerify(c) : true).then((ok) => {
1683
1718
  if (ok) {
1684
1719
  err[1](null);
@@ -1688,6 +1723,14 @@ export function TwoFactorSetup(p: TwoFactorSetupProps) {
1688
1723
  }}
1689
1724
  />
1690
1725
  {err[0] && <span className="tf-field-error">{err[0]}</span>}
1726
+ {p.trustDevice && (
1727
+ <Checkbox
1728
+ defaultChecked={false}
1729
+ onChange={(e) => p.onTrustChange?.(e.currentTarget.checked)}
1730
+ >
1731
+ {typeof p.trustDevice === "string" ? p.trustDevice : t.trustDevice}
1732
+ </Checkbox>
1733
+ )}
1691
1734
  <div>
1692
1735
  <Button
1693
1736
  variant="ghost"
@@ -1781,7 +1824,7 @@ export function ColorField(p: ColorFieldProps) {
1781
1824
  b = lum(p.against || "#ffffff"),
1782
1825
  ratio = a != null && b != null ? (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05) : null;
1783
1826
  return (
1784
- <FormField id={id} label={p.label} hint={p.hint}>
1827
+ <FormField style={p.style} className={p.className} id={id} label={p.label} hint={p.hint}>
1785
1828
  <div className="tf-colorf">
1786
1829
  <label
1787
1830
  className="tf-colorf-sw"
@@ -42,7 +42,8 @@ export function AgentCard(p: AgentCardProps) {
42
42
  const t = useStrings(AGENTS_STRINGS);
43
43
  return (
44
44
  <div
45
- className={cx("tf-agentcard", p.onClick && "click")}
45
+ style={p.style}
46
+ className={cx("tf-agentcard", p.onClick && "click", p.className)}
46
47
  onClick={p.onClick}
47
48
  tabIndex={p.onClick ? 0 : undefined}
48
49
  >
@@ -80,7 +81,7 @@ export function AgentCard(p: AgentCardProps) {
80
81
  }
81
82
  export function AgentDetails(p: AgentDetailsProps) {
82
83
  return (
83
- <div className="tf-agentdetails">
84
+ <div style={p.style} className={cx("tf-agentdetails", p.className)}>
84
85
  <div className="tf-agentdetails-id">
85
86
  <AgentMark tint={p.tint} size={56} live={p.status === "running"} />
86
87
  <b>{p.name}</b>
@@ -119,7 +120,7 @@ export function ChatList(p: ChatListProps) {
119
120
  )
120
121
  .filter((g) => g.items.length);
121
122
  return (
122
- <div className="tf-chatlist">
123
+ <div style={p.style} className={cx("tf-chatlist", p.className)}>
123
124
  <SearchField
124
125
  placeholder={p.searchPlaceholder || t.searchChats}
125
126
  value={q[0]}
@@ -186,7 +187,7 @@ export function ChatList(p: ChatListProps) {
186
187
  }
187
188
  export function ChatWorkspace(p: ChatWorkspaceProps) {
188
189
  return (
189
- <div className={cx("tf-chatws", p.details && "with-details", p.className)}>
190
+ <div style={p.style} className={cx("tf-chatws", p.details && "with-details", p.className)}>
190
191
  <div className="tf-chatws-list">{p.list}</div>
191
192
  <div className="tf-chatws-thread">{p.thread}</div>
192
193
  {p.details && <div className="tf-chatws-details">{p.details}</div>}
@@ -196,7 +197,7 @@ export function ChatWorkspace(p: ChatWorkspaceProps) {
196
197
  export function SearchHits(p: SearchHitsProps) {
197
198
  const t = useStrings(AGENTS_STRINGS);
198
199
  return (
199
- <div className="tf-hits">
200
+ <div style={p.style} className={cx("tf-hits", p.className)}>
200
201
  {(p.items || []).length === 0 ? (
201
202
  <EmptyState icon="search" title={t.nothingFound} text={p.emptyText} />
202
203
  ) : (
@@ -231,7 +232,7 @@ export function SearchHits(p: SearchHitsProps) {
231
232
  export function ThinkingLine(p: ThinkingLineProps) {
232
233
  const t = useStrings(AGENTS_STRINGS);
233
234
  return (
234
- <div className="tf-thinking" role="status">
235
+ <div style={p.style} className={cx("tf-thinking", p.className)} role="status">
235
236
  <TypingDots />
236
237
  <span>{p.text || t.thinking}</span>
237
238
  </div>
@@ -252,7 +253,7 @@ export function FeedbackRating(p: FeedbackRatingProps) {
252
253
  </span>
253
254
  );
254
255
  return (
255
- <div className="tf-feedback">
256
+ <div style={p.style} className={cx("tf-feedback", p.className)}>
256
257
  <div
257
258
  className="tf-row"
258
259
  style={{
@@ -316,7 +317,7 @@ export function FeedbackRating(p: FeedbackRatingProps) {
316
317
  }
317
318
  export function ActivityFeed(p: ActivityFeedProps) {
318
319
  return (
319
- <div className="tf-activity">
320
+ <div style={p.style} className={cx("tf-activity", p.className)}>
320
321
  {(
321
322
  p.groups || [
322
323
  {
@@ -349,7 +350,7 @@ export function ActivityFeed(p: ActivityFeedProps) {
349
350
  export function DailyBriefing(p: DailyBriefingProps) {
350
351
  const t = useStrings(AGENTS_STRINGS);
351
352
  return (
352
- <div className="tf-briefing">
353
+ <div style={p.style} className={cx("tf-briefing", p.className)}>
353
354
  <div className="tf-briefing-head">
354
355
  <AgentMark tint={p.tint || "lilac"} size={40} />
355
356
  <div className="tf-option-text">
@@ -411,7 +412,7 @@ export function BudgetStop(p: BudgetStopProps) {
411
412
  const t = useStrings(AGENTS_STRINGS);
412
413
  const { formatMoney } = useFormat();
413
414
  return (
414
- <div className="tf-budgetstop">
415
+ <div style={p.style} className={cx("tf-budgetstop", p.className)}>
415
416
  <Alert
416
417
  tone="danger"
417
418
  title={p.title || t.budgetUsedUp}
@@ -458,7 +459,7 @@ export function MemoryViewer(p: MemoryViewerProps) {
458
459
  (!q[0] || m.text.toLowerCase().indexOf(q[0].toLowerCase()) > -1),
459
460
  );
460
461
  return (
461
- <div className="tf-memory">
462
+ <div style={p.style} className={cx("tf-memory", p.className)}>
462
463
  <div className="tf-filterbar">
463
464
  <SearchField
464
465
  placeholder={t.searchMemory}
@@ -557,7 +558,7 @@ export function OverrideDiff(p: OverrideDiffProps) {
557
558
  }
558
559
  }
559
560
  return (
560
- <div className="tf-diff">
561
+ <div style={p.style} className={cx("tf-diff", p.className)}>
561
562
  <div className="tf-diff-head">
562
563
  <div className="tf-option-text">
563
564
  <b>{p.title}</b>
@@ -602,7 +603,7 @@ const TRIGGER_ICON: Record<string, IconName> = {
602
603
  export function TriggerList(p: TriggerListProps) {
603
604
  const strings = useStrings(AGENTS_STRINGS);
604
605
  return (
605
- <div className="tf-triggers">
606
+ <div style={p.style} className={cx("tf-triggers", p.className)}>
606
607
  {(p.items || []).map((t) => (
607
608
  <div key={t.id} className={cx("tf-trigger", t.enabled === false && "off")}>
608
609
  <TintIcon
@@ -655,13 +656,14 @@ export function ScheduleCalendar(p: ScheduleCalendarProps) {
655
656
  rowH = 32;
656
657
  return (
657
658
  <div
658
- className="tf-sched"
659
- style={
660
- {
659
+ className={cx("tf-sched", p.className)}
660
+ style={{
661
+ ...({
661
662
  "--rows": hours.length,
662
663
  "--row-h": rowH + "px",
663
- } as React.CSSProperties
664
- }
664
+ } as React.CSSProperties),
665
+ ...p.style,
666
+ }}
665
667
  >
666
668
  <span />
667
669
  {days.map((d, i) => (
@@ -726,7 +728,7 @@ export function OrgChart(p: OrgChartProps) {
726
728
  );
727
729
  }
728
730
  return (
729
- <div className="tf-org">
731
+ <div style={p.style} className={cx("tf-org", p.className)}>
730
732
  <ul>
731
733
  {node(
732
734
  Object.assign(
@@ -746,7 +748,7 @@ export function VoiceMode(p: VoiceModeProps) {
746
748
  bars = [];
747
749
  for (let i = 0; i < 24; i++) bars.push(i);
748
750
  return (
749
- <div className={cx("tf-voice", "is-" + st)}>
751
+ <div style={p.style} className={cx("tf-voice", "is-" + st, p.className)}>
750
752
  <AgentMark tint={p.tint || "lilac"} size={72} live={st !== "idle"} />
751
753
  <b className="tf-voice-name">{p.name}</b>
752
754
  <span className="tf-voice-state">
@@ -19,7 +19,11 @@ import type {
19
19
  export function MobileTabBar(p: MobileTabBarProps) {
20
20
  const t = useStrings(APP_CHROME_STRINGS);
21
21
  return (
22
- <nav className="tf-tabbar" aria-label={p.label || t.mainNavigation}>
22
+ <nav
23
+ style={p.style}
24
+ className={cx("tf-tabbar", p.className)}
25
+ aria-label={p.label || t.mainNavigation}
26
+ >
23
27
  {(p.items || []).map((it) => {
24
28
  const on = it.id === p.active;
25
29
  return (
@@ -36,7 +40,7 @@ export function MobileTabBar(p: MobileTabBarProps) {
36
40
  <Icon name={it.icon} size={20} />
37
41
  {it.count ? <span className="tf-dotcount">{it.count}</span> : null}
38
42
  </span>
39
- <span>{it.label}</span>
43
+ <span className="tf-tabbar-label">{it.label}</span>
40
44
  </button>
41
45
  );
42
46
  })}
@@ -46,7 +50,7 @@ export function MobileTabBar(p: MobileTabBarProps) {
46
50
 
47
51
  export function PushNotification(p: PushNotificationProps) {
48
52
  return (
49
- <div className="tf-push">
53
+ <div style={p.style} className={cx("tf-push", p.className)}>
50
54
  <div className="tf-push-top">
51
55
  <span className="tf-push-app">{p.app}</span>
52
56
  <small>{p.time}</small>
@@ -76,7 +80,7 @@ export function ConfigPRSummary(p: ConfigPRSummaryProps) {
76
80
  file: "file",
77
81
  };
78
82
  return (
79
- <div className="tf-prsum">
83
+ <div style={p.style} className={cx("tf-prsum", p.className)}>
80
84
  <div className="tf-prsum-head">
81
85
  <TintIcon icon="branch" tint="lilac" />
82
86
  <span className="tf-option-text">
@@ -128,7 +132,7 @@ export function ConfigPRSummary(p: ConfigPRSummaryProps) {
128
132
  export function ModuleRegistry(p: ModuleRegistryProps) {
129
133
  const t = useStrings(APP_CHROME_STRINGS);
130
134
  return (
131
- <div className="tf-modules">
135
+ <div style={p.style} className={cx("tf-modules", p.className)}>
132
136
  {(p.items || []).map((m) => (
133
137
  <div key={m.id} className="tf-module">
134
138
  <TintIcon
@@ -109,6 +109,7 @@ export function ApprovalCard(p: ApprovalCardProps) {
109
109
  }
110
110
  return (
111
111
  <div
112
+ style={p.style}
112
113
  ref={ref}
113
114
  className={cx("tf-approval", p.compact && "compact", p.className)}
114
115
  tabIndex={0}
@@ -219,7 +220,8 @@ export function ApprovalInbox(p: ApprovalInboxProps) {
219
220
  const items = (p.items || []).slice().sort((a, b) => (b.ageMin || 0) - (a.ageMin || 0));
220
221
  return (
221
222
  <div
222
- className="tf-inbox"
223
+ style={p.style}
224
+ className={cx("tf-inbox", p.className)}
223
225
  role="listbox"
224
226
  aria-label={p.label || t.approvals}
225
227
  onKeyDown={(e) => {
@@ -24,7 +24,7 @@ import type {
24
24
  export function Kbd(p: KbdProps) {
25
25
  const keys = p.keys || [p.children];
26
26
  return (
27
- <span className="tf-kbds">
27
+ <span style={p.style} className={cx("tf-kbds", p.className)}>
28
28
  {keys.map((k, i) => (
29
29
  <kbd key={i} className="tf-kbd2">
30
30
  {k}
@@ -35,7 +35,7 @@ export function Kbd(p: KbdProps) {
35
35
  }
36
36
  export function KeyValue(p: KeyValueProps) {
37
37
  return (
38
- <dl className={cx("tf-kv", p.dense && "dense", p.className)}>
38
+ <dl style={p.style} className={cx("tf-kv", p.dense && "dense", p.className)}>
39
39
  {(p.items || []).map((it, i) => {
40
40
  const r = Array.isArray(it)
41
41
  ? {
@@ -60,7 +60,8 @@ export function Spinner(p: SpinnerProps) {
60
60
  const t = useStrings(BUILDING_BLOCKS_STRINGS);
61
61
  return (
62
62
  <span
63
- className={cx("tf-spinner", p.size === "lg" && "lg")}
63
+ style={p.style}
64
+ className={cx("tf-spinner", p.size === "lg" && "lg", p.className)}
64
65
  role="status"
65
66
  aria-label={p.label || t.loading}
66
67
  />
@@ -69,7 +70,7 @@ export function Spinner(p: SpinnerProps) {
69
70
  export function Collapsible(p: CollapsibleProps) {
70
71
  const o = useControlled(p.open, !!p.defaultOpen, p.onOpenChange);
71
72
  return (
72
- <div className={cx("tf-collapse", o[0] && "open", p.className)}>
73
+ <div style={p.style} className={cx("tf-collapse", o[0] && "open", p.className)}>
73
74
  <button
74
75
  type="button"
75
76
  className="tf-collapse-head"
@@ -106,8 +107,9 @@ export function ContextMenu(p: ContextMenuProps) {
106
107
  }
107
108
  return (
108
109
  <div
110
+ style={p.style}
109
111
  ref={ref}
110
- className="tf-ctx"
112
+ className={cx("tf-ctx", p.className)}
111
113
  onContextMenu={(e) => {
112
114
  e.preventDefault();
113
115
  openAt(e.clientX, e.clientY);
@@ -191,11 +193,12 @@ export function SplitPane(p: SplitPaneProps) {
191
193
  <div
192
194
  ref={wrap}
193
195
  className={cx("tf-split", p.className)}
194
- style={
195
- {
196
+ style={{
197
+ ...({
196
198
  "--split": size[0] + "px",
197
- } as React.CSSProperties
198
- }
199
+ } as React.CSSProperties),
200
+ ...p.style,
201
+ }}
199
202
  >
200
203
  <div className="tf-split-a">{p.left}</div>
201
204
  <div
@@ -264,7 +267,7 @@ export function TreeView(p: TreeViewProps) {
264
267
  );
265
268
  }
266
269
  return (
267
- <ul className="tf-tree" role="tree" aria-label={p.label}>
270
+ <ul style={p.style} className={cx("tf-tree", p.className)} role="tree" aria-label={p.label}>
268
271
  {(p.items || []).map((n) => node(n, 0))}
269
272
  </ul>
270
273
  );
@@ -279,7 +282,13 @@ export function InlineEdit(p: InlineEditProps) {
279
282
  if (!ed[0])
280
283
  return (
281
284
  <span
282
- className={cx("tf-inline", p.mono && "tf-mono", p.align === "right" && "right")}
285
+ className={cx(
286
+ "tf-inline",
287
+ p.mono && "tf-mono",
288
+ p.align === "right" && "right",
289
+ p.className,
290
+ )}
291
+ style={p.style}
283
292
  tabIndex={0}
284
293
  role="button"
285
294
  aria-label={p.label}
@@ -300,7 +309,8 @@ export function InlineEdit(p: InlineEditProps) {
300
309
  );
301
310
  return p.options ? (
302
311
  <select
303
- className="tf-input tf-inline-input"
312
+ className={cx("tf-input tf-inline-input", p.className)}
313
+ style={p.style}
304
314
  autoFocus
305
315
  value={v[0]}
306
316
  onChange={(e) => {
@@ -320,7 +330,8 @@ export function InlineEdit(p: InlineEditProps) {
320
330
  </select>
321
331
  ) : (
322
332
  <input
323
- className={cx("tf-input tf-inline-input", p.mono && "tf-mono")}
333
+ className={cx("tf-input tf-inline-input", p.mono && "tf-mono", p.className)}
334
+ style={p.style}
324
335
  autoFocus
325
336
  value={v[0]}
326
337
  type={p.type || "text"}