@tangle-network/ui 11.9.0 → 11.10.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 (64) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +14 -0
  3. package/dist/auth.js +3 -2
  4. package/dist/chat.js +10 -9
  5. package/dist/{chunk-WUQDUBJG.js → chunk-3XXBZL53.js} +7 -1
  6. package/dist/{chunk-MKTSMWVD.js → chunk-42PAH5D2.js} +4 -1
  7. package/dist/{chunk-LDLOJKPC.js → chunk-55WHV7SS.js} +49 -30
  8. package/dist/{chunk-3TI5DEHV.js → chunk-BEBXZPHW.js} +4 -4
  9. package/dist/{chunk-7YOCS6IF.js → chunk-C5WF4WMV.js} +12 -6
  10. package/dist/{chunk-NWECTSOH.js → chunk-HMAPIH7L.js} +9 -3
  11. package/dist/{chunk-ZKCEAIIZ.js → chunk-K5XFRUS4.js} +9 -3
  12. package/dist/{chunk-NKDZ7GZE.js → chunk-KFQGHD73.js} +8 -2
  13. package/dist/{chunk-GYPQXTJU.js → chunk-M3QZK62R.js} +5 -2
  14. package/dist/{chunk-EOAX2ENE.js → chunk-OL5GYLHV.js} +1 -1
  15. package/dist/{chunk-XYAIX4SU.js → chunk-QGFYBNSO.js} +2 -2
  16. package/dist/{chunk-KNYSKNZX.js → chunk-R7B3UWPF.js} +2 -2
  17. package/dist/{chunk-Q56BYXQF.js → chunk-R7SUY4E3.js} +8 -4
  18. package/dist/chunk-WKWBTL2V.js +15 -0
  19. package/dist/{chunk-VPWY4I5O.js → chunk-X7CU776B.js} +1 -1
  20. package/dist/{chunk-TN4FUEWY.js → chunk-YU4E5J2N.js} +3 -3
  21. package/dist/{chunk-YJ6RE5WV.js → chunk-YZ6AK25N.js} +20 -9
  22. package/dist/document-editor-pane-QHDOBA43.js +13 -0
  23. package/dist/editor.js +5 -4
  24. package/dist/files.js +5 -4
  25. package/dist/hooks.js +7 -6
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +28 -16
  28. package/dist/markdown.js +3 -2
  29. package/dist/openui.js +6 -5
  30. package/dist/primitives.d.ts +2 -0
  31. package/dist/primitives.js +6 -5
  32. package/dist/run.js +10 -9
  33. package/dist/sdk-hooks.js +7 -6
  34. package/dist/tool-previews.js +3 -2
  35. package/dist/utils.d.ts +54 -1
  36. package/dist/utils.js +13 -1
  37. package/package.json +2 -2
  38. package/src/auth/auth.tsx +5 -1
  39. package/src/chat/agent-timeline.tsx +6 -1
  40. package/src/chat/chat-container.tsx +2 -1
  41. package/src/editor/document-editor-pane.test.tsx +3 -1
  42. package/src/editor/document-editor-pane.tsx +5 -1
  43. package/src/editor/markdown-document-editor.test.tsx +9 -4
  44. package/src/files/file-artifact-pane.tsx +3 -2
  45. package/src/files/file-preview.tsx +3 -1
  46. package/src/files/file-tabs.tsx +9 -2
  47. package/src/lib/focus.ts +63 -0
  48. package/src/markdown/code-block.tsx +5 -1
  49. package/src/primitives/badge.tsx +2 -1
  50. package/src/primitives/button.tsx +2 -1
  51. package/src/primitives/card.tsx +1 -2
  52. package/src/primitives/dialog.tsx +7 -1
  53. package/src/primitives/input.tsx +15 -14
  54. package/src/primitives/segmented-control.tsx +5 -3
  55. package/src/primitives/select.tsx +3 -1
  56. package/src/primitives/switch.tsx +3 -1
  57. package/src/primitives/tabs.tsx +6 -4
  58. package/src/primitives/terminal-display.tsx +6 -8
  59. package/src/primitives/toast.tsx +5 -1
  60. package/src/tool-previews/command-preview.tsx +5 -1
  61. package/src/tool-previews/web-search-preview.tsx +2 -1
  62. package/src/utils/index.ts +7 -0
  63. package/dist/document-editor-pane-EX25CEYW.js +0 -12
  64. /package/dist/{chunk-XGKULLYE.js → chunk-SNLR2AEA.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @tangle-network/ui
2
2
 
3
+ ## 11.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4649d22: Replace the hard 2px indigo focus ring with one soft focus treatment for every control.
8
+
9
+ brand adds `--focus-border`, `--focus-halo`, `--focus-border-danger`, `--focus-halo-danger` and `--border-strong`. Each derives from the ring, danger-ink, border and foreground tokens, so every theme, named theme and light island resolves its own values. The focus border clears 3:1 against the field in every theme, which `focus.test.ts` enforces.
10
+
11
+ brand's `globals.css` also gives any control that styles no focus of its own (a hand-rolled button, link, radio card or tab) the same keyboard focus line and halo in the base layer, in place of the browser's blue outline. Component utilities still override it.
12
+
13
+ ui exports `focusField`, `focusFieldWithin`, `focusFieldInvalid`, `focusRing` and `focusRingInset` from `utils`, and the primitives use them:
14
+
15
+ - `Input`, `Textarea`, `SelectTrigger` and `TerminalInput` rest on the neutral `border-border` hairline instead of the muted-text `border-input`, darken slightly on hover, and on focus shift the border to `--focus-border` with a 3px `--focus-halo` ring. The `default` and `sandbox` variants now render the same field.
16
+ - `Button`, `Tabs`, `Switch`, `Badge`, `SegmentedControl`, the dialog and toast close buttons, the auth menu trigger, and the chat and file controls show a 1px focus line with the same halo on `:focus-visible` only. A mouse click no longer draws a ring.
17
+ - Interactive `Card`, tool-preview and code-block hovers darken the border instead of tinting it indigo.
18
+
19
+ A consumer that removed the old ring with `focus-visible:ring-0` on a field should pass `focus:ring-0` instead, because the field ring now keys off `:focus`.
20
+
21
+ ### Patch Changes
22
+
23
+ - 53add99: `focusFieldWithin` lights the container only while a field inside it has focus: a text input, textarea, select or contenteditable editor. It used `:focus-within`, so clicking a composer's send button drew the field ring around the whole composer, and tabbing to that button showed two indicators. A focused button, checkbox or button-type input inside the container now shows only its own ring. Because the rule keys off `:has()`, a consumer's `focus-within:*` class does not override it; compose your own classes for a different container focus.
24
+
3
25
  ## 11.9.0
4
26
 
5
27
  ### Minor Changes
package/README.md CHANGED
@@ -69,6 +69,20 @@ One consequence worth knowing. This is a scan, not a manifest, so a consumer emi
69
69
 
70
70
  See [`@tangle-network/brand`'s README](../brand/README.md) for the token-level breakdown, the finer-grained imports, and the fonts, which brand deliberately does not bundle.
71
71
 
72
+ ### Focus treatment
73
+
74
+ Every control shares one focus treatment, exported from `utils` as class strings.
75
+ Compose them with `cn` so tailwind-merge resolves any override.
76
+
77
+ - `focusField` — a text field: neutral hairline, a slightly darker border on hover, and on `:focus` a `--focus-border` border with a 3px `--focus-halo` ring.
78
+ - `focusFieldWithin` — the same, for a container that wraps a borderless field, such as a composer around a textarea. It lights while a text input, textarea, native `<select>` or contenteditable editor inside it has focus, not while a button inside it does. It keys off `:has()`, so a `focus-within:*` class does not override it.
79
+ - `focusFieldInvalid` — the error state of a field. Compose it after `focusField`.
80
+ - `focusRing` — keyboard focus on a button, tab, switch or link: a 1px `--focus-border` outline with the halo beyond it, on `:focus-visible` only.
81
+ - `focusRingInset` — `focusRing` drawn inside the box, for a control in a scrolling strip.
82
+
83
+ The colors come from the `--focus-*` tokens in `@tangle-network/brand`, so they need brand 1.6 or later.
84
+ To remove the ring from a field nested in a focusable container, pass `focus:ring-0 border-0` and put `focusFieldWithin` on the container.
85
+
72
86
  ## Subpaths
73
87
 
74
88
  Sixteen named exports: `primitives`, `chat`, `run`, `openui`, `files`, `editor`, `markdown`, `auth`, `hooks`, `sdk-hooks`, `stores`, `types`, `utils`, `tool-previews`, `nav`, `redaction`.
package/dist/auth.js CHANGED
@@ -3,9 +3,10 @@ import {
3
3
  GitHubLoginButton,
4
4
  LoginLayout,
5
5
  UserMenu
6
- } from "./chunk-NKDZ7GZE.js";
6
+ } from "./chunk-KFQGHD73.js";
7
7
  import "./chunk-34A66VBG.js";
8
- import "./chunk-MKTSMWVD.js";
8
+ import "./chunk-42PAH5D2.js";
9
+ import "./chunk-WKWBTL2V.js";
9
10
  import "./chunk-RQHJBTEU.js";
10
11
  export {
11
12
  AuthHeader,
package/dist/chat.js CHANGED
@@ -5,20 +5,21 @@ import {
5
5
  MessageList,
6
6
  ThinkingIndicator,
7
7
  UserMessage
8
- } from "./chunk-7YOCS6IF.js";
8
+ } from "./chunk-C5WF4WMV.js";
9
9
  import "./chunk-565EATZZ.js";
10
- import "./chunk-TN4FUEWY.js";
11
- import "./chunk-KNYSKNZX.js";
10
+ import "./chunk-YU4E5J2N.js";
11
+ import "./chunk-R7B3UWPF.js";
12
12
  import "./chunk-ND3ZFXQK.js";
13
- import "./chunk-NWECTSOH.js";
13
+ import "./chunk-HMAPIH7L.js";
14
14
  import "./chunk-L57WRIND.js";
15
- import "./chunk-3TI5DEHV.js";
16
- import "./chunk-GYPQXTJU.js";
15
+ import "./chunk-BEBXZPHW.js";
16
+ import "./chunk-M3QZK62R.js";
17
17
  import "./chunk-4CLN43XT.js";
18
18
  import "./chunk-XU3HB5QL.js";
19
- import "./chunk-MKTSMWVD.js";
20
- import "./chunk-EOAX2ENE.js";
21
- import "./chunk-WUQDUBJG.js";
19
+ import "./chunk-42PAH5D2.js";
20
+ import "./chunk-OL5GYLHV.js";
21
+ import "./chunk-3XXBZL53.js";
22
+ import "./chunk-WKWBTL2V.js";
22
23
  import "./chunk-RQHJBTEU.js";
23
24
  export {
24
25
  AgentTimeline,
@@ -1,3 +1,6 @@
1
+ import {
2
+ focusRing
3
+ } from "./chunk-WKWBTL2V.js";
1
4
  import {
2
5
  cn
3
6
  } from "./chunk-RQHJBTEU.js";
@@ -154,7 +157,10 @@ var CopyButton = memo(({ text }) => {
154
157
  "button",
155
158
  {
156
159
  onClick: handleCopy,
157
- className: "flex items-center justify-center w-6 h-6 rounded-md bg-muted border border-border hover:border-primary/20 transition-colors",
160
+ className: cn(
161
+ "flex items-center justify-center w-6 h-6 rounded-md bg-muted border border-border hover:border-[var(--border-strong)] transition-colors",
162
+ focusRing
163
+ ),
158
164
  title: "Copy to clipboard",
159
165
  children: copied ? /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5 text-emerald-500" }) : /* @__PURE__ */ jsx(Copy, { className: "w-3.5 h-3.5 text-muted-foreground" })
160
166
  }
@@ -1,3 +1,6 @@
1
+ import {
2
+ focusRing
3
+ } from "./chunk-WKWBTL2V.js";
1
4
  import {
2
5
  cn
3
6
  } from "./chunk-RQHJBTEU.js";
@@ -8,7 +11,7 @@ import { cva } from "class-variance-authority";
8
11
  import * as React from "react";
9
12
  import { jsx, jsxs } from "react/jsx-runtime";
10
13
  var buttonVariants = cva(
11
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium text-sm transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
14
+ `inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium text-sm transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`,
12
15
  {
13
16
  variants: {
14
17
  variant: {
@@ -1,6 +1,13 @@
1
1
  import {
2
2
  Card
3
- } from "./chunk-GYPQXTJU.js";
3
+ } from "./chunk-M3QZK62R.js";
4
+ import {
5
+ focusField,
6
+ focusFieldInvalid,
7
+ focusFieldWithin,
8
+ focusRing,
9
+ focusRingInset
10
+ } from "./chunk-WKWBTL2V.js";
4
11
  import {
5
12
  cn
6
13
  } from "./chunk-RQHJBTEU.js";
@@ -53,10 +60,19 @@ var DialogContent = React.forwardRef(({ className, children, variant = "default"
53
60
  ...props,
54
61
  children: [
55
62
  children,
56
- /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute top-4 right-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [
57
- /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
58
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
59
- ] })
63
+ /* @__PURE__ */ jsxs(
64
+ DialogPrimitive.Close,
65
+ {
66
+ className: cn(
67
+ "absolute top-4 right-4 rounded-sm opacity-70 transition-[opacity,box-shadow] hover:opacity-100 focus-visible:opacity-100 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground",
68
+ focusRing
69
+ ),
70
+ children: [
71
+ /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
72
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
73
+ ]
74
+ }
75
+ )
60
76
  ]
61
77
  }
62
78
  )
@@ -118,13 +134,17 @@ import * as React2 from "react";
118
134
  import { cva } from "class-variance-authority";
119
135
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
120
136
  var inputVariants = cva(
121
- "flex w-full rounded-lg border bg-card px-4 py-2 text-sm transition-all duration-200 placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 file:border-0 file:bg-transparent file:font-medium file:text-sm",
137
+ cn(
138
+ "flex w-full rounded-lg border bg-card px-4 py-2 text-sm placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 file:border-0 file:bg-transparent file:font-medium file:text-sm",
139
+ focusField
140
+ ),
122
141
  {
123
142
  variants: {
143
+ // `default` and `sandbox` render the same field; both names stay valid.
124
144
  variant: {
125
- default: "border-input focus:ring-ring",
126
- sandbox: "border-border focus:border-[var(--border-accent-hover)] focus:ring-[var(--border-accent)]",
127
- error: "border-[var(--surface-danger-border)] focus:ring-[var(--surface-danger-border)]"
145
+ default: "",
146
+ sandbox: "",
147
+ error: focusFieldInvalid
128
148
  },
129
149
  size: {
130
150
  default: "h-11",
@@ -169,22 +189,18 @@ var Input = React2.forwardRef(
169
189
  );
170
190
  Input.displayName = "Input";
171
191
  var Textarea = React2.forwardRef(
172
- ({ className, variant = "default", label, error, hint, id, ...props }, ref) => {
192
+ ({ className, variant: _variant, label, error, hint, id, ...props }, ref) => {
173
193
  const textareaId = id ?? label?.toLowerCase().replace(/\s+/g, "-");
174
- const variants = {
175
- default: "border-input focus:ring-ring",
176
- sandbox: "border-border focus:border-[var(--border-accent-hover)] focus:ring-[var(--border-accent)]"
177
- };
178
194
  const textarea = /* @__PURE__ */ jsx2(
179
195
  "textarea",
180
196
  {
181
197
  id: textareaId,
182
198
  className: cn(
183
- "flex min-h-[120px] w-full resize-y rounded-lg border bg-card px-4 py-3 text-sm transition-all duration-200",
199
+ "flex min-h-[120px] w-full resize-y rounded-lg border bg-card px-4 py-3 text-sm",
184
200
  "placeholder:text-muted-foreground",
185
- "focus:outline-none focus:ring-2 focus:ring-offset-0",
186
201
  "disabled:cursor-not-allowed disabled:opacity-50",
187
- error ? "border-[var(--surface-danger-border)] focus:ring-[var(--surface-danger-border)]" : variants[variant],
202
+ focusField,
203
+ error && focusFieldInvalid,
188
204
  className
189
205
  ),
190
206
  ref,
@@ -222,7 +238,8 @@ var SelectTrigger = React3.forwardRef(({ className, children, ...props }, ref) =
222
238
  {
223
239
  ref,
224
240
  className: cn(
225
- "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-border bg-card px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
241
+ "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border bg-card px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
242
+ focusField,
226
243
  className
227
244
  ),
228
245
  ...props,
@@ -389,9 +406,10 @@ function SegmentedControl({
389
406
  if (!active) onValueChange(option.value);
390
407
  },
391
408
  className: cn(
392
- "relative inline-flex items-center gap-2 whitespace-nowrap text-sm transition-colors",
393
- variant === "row" && "rounded-md px-3 py-1.5",
394
- variant === "tabs" && "rounded-none border-b-2 -mb-px px-4 py-2",
409
+ "relative inline-flex items-center gap-2 whitespace-nowrap text-sm transition-[color,background-color,border-color,box-shadow]",
410
+ variant === "row" && ["rounded-md px-3 py-1.5", focusRing],
411
+ // The strip scrolls horizontally, which would clip an outer ring.
412
+ variant === "tabs" && ["rounded-none border-b-2 -mb-px px-4 py-2", focusRingInset],
395
413
  // Active styling is the ONLY styled state — unselected
396
414
  // segments stay transparent on purpose so they don't
397
415
  // compete visually with the selection.
@@ -467,7 +485,8 @@ var Switch = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
467
485
  SwitchPrimitives.Root,
468
486
  {
469
487
  className: cn(
470
- "peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
488
+ "peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-[background-color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
489
+ focusRing,
471
490
  className
472
491
  ),
473
492
  ...props,
@@ -597,7 +616,10 @@ function ToastComponent({
597
616
  {
598
617
  type: "button",
599
618
  onClick: () => onDismiss(id),
600
- className: "shrink-0 rounded-md p-1 opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2",
619
+ className: cn(
620
+ "shrink-0 rounded-md p-1 opacity-70 transition-[opacity,box-shadow] hover:opacity-100 focus-visible:opacity-100",
621
+ focusRing
622
+ ),
601
623
  "aria-label": "Dismiss notification",
602
624
  children: /* @__PURE__ */ jsx8(X2, { className: "h-4 w-4" })
603
625
  }
@@ -897,7 +919,8 @@ var TerminalCursor = React11.forwardRef(({ className, ...props }, ref) => /* @__
897
919
  ));
898
920
  TerminalCursor.displayName = "TerminalCursor";
899
921
  var TerminalInput = React11.forwardRef(
900
- ({ className, onSubmit, variant = "default", ...props }, ref) => {
922
+ // `variant` is taken out so it never reaches the DOM element.
923
+ ({ className, onSubmit, variant: _variant, ...props }, ref) => {
901
924
  const [value, setValue] = React11.useState("");
902
925
  const handleKeyDown = (e) => {
903
926
  if (e.key === "Enter" && value.trim() && onSubmit) {
@@ -905,16 +928,12 @@ var TerminalInput = React11.forwardRef(
905
928
  setValue("");
906
929
  }
907
930
  };
908
- const variants = {
909
- default: "border-border focus-within:border-border",
910
- sandbox: "border-border focus-within:border-[var(--border-accent-hover)]"
911
- };
912
931
  return /* @__PURE__ */ jsxs10(
913
932
  "div",
914
933
  {
915
934
  className: cn(
916
- "flex items-center rounded-lg border bg-background px-4 py-2.5 font-mono text-sm transition-colors",
917
- variants[variant],
935
+ "flex items-center rounded-lg border bg-background px-4 py-2.5 font-mono text-sm",
936
+ focusFieldWithin,
918
937
  className
919
938
  ),
920
939
  children: [
@@ -11,16 +11,16 @@ import {
11
11
  TableHead,
12
12
  TableHeader,
13
13
  TableRow
14
- } from "./chunk-GYPQXTJU.js";
14
+ } from "./chunk-M3QZK62R.js";
15
15
  import {
16
16
  Button
17
- } from "./chunk-MKTSMWVD.js";
17
+ } from "./chunk-42PAH5D2.js";
18
18
  import {
19
19
  Markdown
20
- } from "./chunk-EOAX2ENE.js";
20
+ } from "./chunk-OL5GYLHV.js";
21
21
  import {
22
22
  CodeBlock
23
- } from "./chunk-WUQDUBJG.js";
23
+ } from "./chunk-3XXBZL53.js";
24
24
  import {
25
25
  cn
26
26
  } from "./chunk-RQHJBTEU.js";
@@ -6,20 +6,23 @@ import {
6
6
  import {
7
7
  InlineThinkingItem,
8
8
  RunGroup
9
- } from "./chunk-TN4FUEWY.js";
9
+ } from "./chunk-YU4E5J2N.js";
10
10
  import {
11
11
  ToolCallGroup,
12
12
  ToolCallStep
13
- } from "./chunk-KNYSKNZX.js";
13
+ } from "./chunk-R7B3UWPF.js";
14
14
  import {
15
15
  getToolDisplayMetadata
16
16
  } from "./chunk-ND3ZFXQK.js";
17
17
  import {
18
18
  OpenUIArtifactRenderer
19
- } from "./chunk-3TI5DEHV.js";
19
+ } from "./chunk-BEBXZPHW.js";
20
20
  import {
21
21
  Markdown
22
- } from "./chunk-EOAX2ENE.js";
22
+ } from "./chunk-OL5GYLHV.js";
23
+ import {
24
+ focusRing
25
+ } from "./chunk-WKWBTL2V.js";
23
26
  import {
24
27
  cn
25
28
  } from "./chunk-RQHJBTEU.js";
@@ -241,7 +244,10 @@ function ArtifactCard({ item }) {
241
244
  item.onClick?.();
242
245
  }
243
246
  },
244
- className: "block w-full text-left transition-transform hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
247
+ className: cn(
248
+ "block w-full rounded-[var(--radius-lg)] text-left transition-transform hover:-translate-y-0.5",
249
+ focusRing
250
+ ),
245
251
  children: content
246
252
  }
247
253
  );
@@ -650,7 +656,7 @@ var ChatContainer = memo3(
650
656
  "flex items-center gap-1.5 px-3 py-1.5 rounded-full",
651
657
  "border border-border bg-card shadow-[0_6px_16px_rgba(15,23,42,0.08)]",
652
658
  "text-xs text-foreground transition-colors hover:bg-accent",
653
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60"
659
+ focusRing
654
660
  ),
655
661
  children: [
656
662
  /* @__PURE__ */ jsx5(ArrowDown, { className: "w-3 h-3" }),
@@ -4,7 +4,10 @@ import {
4
4
  import {
5
5
  CodeBlock,
6
6
  CopyButton
7
- } from "./chunk-WUQDUBJG.js";
7
+ } from "./chunk-3XXBZL53.js";
8
+ import {
9
+ focusRing
10
+ } from "./chunk-WKWBTL2V.js";
8
11
  import {
9
12
  cn
10
13
  } from "./chunk-RQHJBTEU.js";
@@ -121,7 +124,10 @@ var CommandPreview = memo(({ part }) => {
121
124
  "button",
122
125
  {
123
126
  onClick: () => setExpanded((value) => !value),
124
- className: "flex w-full items-center gap-2 rounded-[var(--radius-md)] border border-border bg-muted px-3 py-2 text-left transition-colors hover:border-[var(--border-accent-hover)] hover:bg-accent/45",
127
+ className: cn(
128
+ "flex w-full items-center gap-2 rounded-[var(--radius-md)] border border-border bg-muted px-3 py-2 text-left transition-colors hover:border-[var(--border-strong)] hover:bg-accent/45",
129
+ focusRing
130
+ ),
125
131
  children: [
126
132
  /* @__PURE__ */ jsx2("code", { className: "min-w-0 flex-1 truncate text-xs font-mono text-foreground", children: command }),
127
133
  lineCount > 0 ? /* @__PURE__ */ jsxs2("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
@@ -425,7 +431,7 @@ var WebSearchPreview = memo5(({ part }) => {
425
431
  href: result.url,
426
432
  target: "_blank",
427
433
  rel: "noreferrer",
428
- className: "block rounded-[var(--radius-md)] border border-border bg-muted/55 px-3 py-3 transition-colors hover:border-[var(--border-accent-hover)] hover:bg-accent/50",
434
+ className: `block rounded-[var(--radius-md)] border border-border bg-muted/55 px-3 py-3 transition-colors hover:border-[var(--border-strong)] hover:bg-accent/50 ${focusRing}`,
429
435
  children: [
430
436
  /* @__PURE__ */ jsx6("div", { className: "text-sm font-medium text-foreground", children: result.title }),
431
437
  /* @__PURE__ */ jsx6("div", { className: "mt-1 truncate text-xs text-primary", children: result.url }),
@@ -3,13 +3,16 @@ import {
3
3
  TabsContent,
4
4
  TabsList,
5
5
  TabsTrigger
6
- } from "./chunk-Q56BYXQF.js";
6
+ } from "./chunk-R7SUY4E3.js";
7
7
  import {
8
8
  ArtifactPane
9
9
  } from "./chunk-LTEKI7YJ.js";
10
10
  import {
11
11
  Markdown
12
- } from "./chunk-EOAX2ENE.js";
12
+ } from "./chunk-OL5GYLHV.js";
13
+ import {
14
+ focusRing
15
+ } from "./chunk-WKWBTL2V.js";
13
16
  import {
14
17
  cn
15
18
  } from "./chunk-RQHJBTEU.js";
@@ -1302,7 +1305,10 @@ function DocumentEditorPane({
1302
1305
  type: "button",
1303
1306
  onClick: () => void onSave(draft),
1304
1307
  disabled: saving || !isDirty,
1305
- className: "inline-flex items-center gap-2 rounded-[var(--radius-full)] border border-border bg-card px-3 py-1.5 text-xs font-semibold text-foreground transition-colors hover:border-primary/40 hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
1308
+ className: cn(
1309
+ "inline-flex items-center gap-2 rounded-[var(--radius-full)] border border-border bg-card px-3 py-1.5 text-xs font-semibold text-foreground transition-colors hover:border-[var(--border-strong)] hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
1310
+ focusRing
1311
+ ),
1306
1312
  children: [
1307
1313
  /* @__PURE__ */ jsx6(Save, { className: "h-3.5 w-3.5" }),
1308
1314
  saving ? "Saving..." : saveLabel
@@ -11,7 +11,10 @@ import {
11
11
  } from "./chunk-34A66VBG.js";
12
12
  import {
13
13
  Button
14
- } from "./chunk-MKTSMWVD.js";
14
+ } from "./chunk-42PAH5D2.js";
15
+ import {
16
+ focusRing
17
+ } from "./chunk-WKWBTL2V.js";
15
18
  import {
16
19
  cn
17
20
  } from "./chunk-RQHJBTEU.js";
@@ -82,7 +85,10 @@ function UserMenu({
82
85
  "button",
83
86
  {
84
87
  type: "button",
85
- className: "flex items-center gap-2 rounded-lg p-1.5 transition-colors hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring",
88
+ className: cn(
89
+ "flex items-center gap-2 rounded-lg p-1.5 transition-colors hover:bg-accent",
90
+ focusRing
91
+ ),
86
92
  children: [
87
93
  /* @__PURE__ */ jsxs(Avatar, { className: "h-8 w-8", children: [
88
94
  /* @__PURE__ */ jsx(AvatarImage, { src: void 0, alt: user.name ?? user.email }),
@@ -1,3 +1,6 @@
1
+ import {
2
+ focusRing
3
+ } from "./chunk-WKWBTL2V.js";
1
4
  import {
2
5
  cn
3
6
  } from "./chunk-RQHJBTEU.js";
@@ -20,7 +23,7 @@ var Card = React.forwardRef(({ className, variant = "default", hover = false, ..
20
23
  "rounded-[var(--radius-lg)] border text-card-foreground transition-[border-color,box-shadow]",
21
24
  "duration-[var(--transition-default)]",
22
25
  variants[variant],
23
- hover && "cursor-pointer hover:border-primary/30",
26
+ hover && "cursor-pointer hover:border-[var(--border-strong)]",
24
27
  className
25
28
  ),
26
29
  ...props
@@ -71,7 +74,7 @@ CardFooter.displayName = "CardFooter";
71
74
  import { cva } from "class-variance-authority";
72
75
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
73
76
  var badgeVariants = cva(
74
- "inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 font-medium text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
77
+ `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 font-medium text-xs transition-colors ${focusRing}`,
75
78
  {
76
79
  variants: {
77
80
  variant: {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  CodeBlock,
3
3
  CopyButton
4
- } from "./chunk-WUQDUBJG.js";
4
+ } from "./chunk-3XXBZL53.js";
5
5
  import {
6
6
  cn
7
7
  } from "./chunk-RQHJBTEU.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ToolCallGroup,
3
3
  ToolCallStep
4
- } from "./chunk-KNYSKNZX.js";
4
+ } from "./chunk-R7B3UWPF.js";
5
5
  import {
6
6
  Markdown
7
- } from "./chunk-EOAX2ENE.js";
7
+ } from "./chunk-OL5GYLHV.js";
8
8
  import {
9
9
  cn
10
10
  } from "./chunk-RQHJBTEU.js";
@@ -11,7 +11,7 @@ import {
11
11
  QuestionPreview,
12
12
  WebSearchPreview,
13
13
  WriteFilePreview
14
- } from "./chunk-NWECTSOH.js";
14
+ } from "./chunk-HMAPIH7L.js";
15
15
  import {
16
16
  TextShimmer
17
17
  } from "./chunk-L57WRIND.js";
@@ -20,7 +20,7 @@ import {
20
20
  } from "./chunk-4CLN43XT.js";
21
21
  import {
22
22
  CodeBlock
23
- } from "./chunk-WUQDUBJG.js";
23
+ } from "./chunk-3XXBZL53.js";
24
24
  import {
25
25
  cn
26
26
  } from "./chunk-RQHJBTEU.js";
@@ -1,3 +1,6 @@
1
+ import {
2
+ focusRing
3
+ } from "./chunk-WKWBTL2V.js";
1
4
  import {
2
5
  cn
3
6
  } from "./chunk-RQHJBTEU.js";
@@ -25,9 +28,9 @@ var TabsList = React.forwardRef(({ className, variant = "default", ...props }, r
25
28
  TabsList.displayName = TabsPrimitive.List.displayName;
26
29
  var TabsTrigger = React.forwardRef(({ className, variant = "default", ...props }, ref) => {
27
30
  const variants = {
28
- default: "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
29
- pills: "inline-flex items-center justify-center whitespace-nowrap rounded-full px-4 py-2 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=inactive]:text-muted-foreground data-[state=inactive]:hover:bg-muted",
30
- underline: "inline-flex items-center justify-center whitespace-nowrap pb-3 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:text-foreground data-[state=inactive]:text-muted-foreground"
31
+ default: `inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm`,
32
+ pills: `inline-flex items-center justify-center whitespace-nowrap rounded-full px-4 py-2 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=inactive]:text-muted-foreground data-[state=inactive]:hover:bg-muted`,
33
+ underline: `inline-flex items-center justify-center whitespace-nowrap rounded-t-md pb-3 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:text-foreground data-[state=inactive]:text-muted-foreground`
31
34
  };
32
35
  return /* @__PURE__ */ jsx(
33
36
  TabsPrimitive.Trigger,
@@ -44,7 +47,8 @@ var TabsContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE_
44
47
  {
45
48
  ref,
46
49
  className: cn(
47
- "mt-4 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
50
+ "mt-4",
51
+ focusRing,
48
52
  "data-[state=active]:fade-in-0 data-[state=active]:slide-in-from-bottom-2 data-[state=active]:animate-in",
49
53
  className
50
54
  ),
@@ -0,0 +1,15 @@
1
+ // src/lib/focus.ts
2
+ var focusField = "border-border transition-[border-color,box-shadow] duration-150 ease-out hover:border-[var(--border-strong)] focus:outline-hidden focus:border-[var(--focus-border)] focus:ring-3 focus:ring-[var(--focus-halo)]";
3
+ var focusFieldWithin = "border-border transition-[border-color,box-shadow] duration-150 ease-out hover:border-[var(--border-strong)] has-[:is(textarea,select,[contenteditable]:not([contenteditable=false]),input:not([type=button],[type=submit],[type=reset],[type=image],[type=checkbox],[type=radio],[type=range],[type=color],[type=file])):focus]:border-[var(--focus-border)] has-[:is(textarea,select,[contenteditable]:not([contenteditable=false]),input:not([type=button],[type=submit],[type=reset],[type=image],[type=checkbox],[type=radio],[type=range],[type=color],[type=file])):focus]:ring-3 has-[:is(textarea,select,[contenteditable]:not([contenteditable=false]),input:not([type=button],[type=submit],[type=reset],[type=image],[type=checkbox],[type=radio],[type=range],[type=color],[type=file])):focus]:ring-[var(--focus-halo)]";
4
+ var focusFieldInvalid = "border-[var(--surface-danger-border)] hover:border-[var(--surface-danger-border)] focus:border-[var(--focus-border-danger)] focus:ring-[var(--focus-halo-danger)]";
5
+ var focusRingBase = "focus-visible:outline-1 focus-visible:outline-[var(--focus-border)] focus-visible:ring-4 focus-visible:ring-[var(--focus-halo)]";
6
+ var focusRing = `${focusRingBase} focus-visible:outline-offset-0`;
7
+ var focusRingInset = `${focusRingBase} focus-visible:-outline-offset-1 focus-visible:ring-inset`;
8
+
9
+ export {
10
+ focusField,
11
+ focusFieldWithin,
12
+ focusFieldInvalid,
13
+ focusRing,
14
+ focusRingInset
15
+ };
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-OEX7NZE3.js";
12
12
  import {
13
13
  parseToolEvent
14
- } from "./chunk-XYAIX4SU.js";
14
+ } from "./chunk-QGFYBNSO.js";
15
15
 
16
16
  // src/hooks/use-dropdown-menu.ts
17
17
  import { useEffect, useRef, useState } from "react";
@@ -2,20 +2,20 @@ import {
2
2
  ExpandableContent,
3
3
  InlineToolItem,
4
4
  RunRowShell
5
- } from "./chunk-KNYSKNZX.js";
5
+ } from "./chunk-R7B3UWPF.js";
6
6
  import {
7
7
  TextShimmer
8
8
  } from "./chunk-L57WRIND.js";
9
9
  import {
10
10
  OpenUIArtifactRenderer
11
- } from "./chunk-3TI5DEHV.js";
11
+ } from "./chunk-BEBXZPHW.js";
12
12
  import {
13
13
  formatDuration,
14
14
  truncateText
15
15
  } from "./chunk-4CLN43XT.js";
16
16
  import {
17
17
  Markdown
18
- } from "./chunk-EOAX2ENE.js";
18
+ } from "./chunk-OL5GYLHV.js";
19
19
  import {
20
20
  cn
21
21
  } from "./chunk-RQHJBTEU.js";