@reopt-ai/opt-ui 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -66,7 +66,7 @@ import {
66
66
  formatOptTime,
67
67
  getShaderFragment,
68
68
  getShaderPreset
69
- } from "./chunk-ELWICXYY.js";
69
+ } from "./chunk-VBGPEW45.js";
70
70
  import {
71
71
  AppMenubar,
72
72
  BranchSelect,
@@ -101,7 +101,7 @@ import {
101
101
  WorkflowCanvas,
102
102
  toast,
103
103
  useToast
104
- } from "./chunk-QWBHD54V.js";
104
+ } from "./chunk-3XD4HIFL.js";
105
105
  import {
106
106
  Alert,
107
107
  AlertDialogPanel,
@@ -3138,10 +3138,18 @@ function SqlEditor({
3138
3138
  const viewRef = React6.useRef(
3139
3139
  null
3140
3140
  );
3141
+ const latestValueRef = React6.useRef(value);
3142
+ const onChangeRef = React6.useRef(onChange);
3143
+ const onRunRef = React6.useRef(onRun);
3144
+ latestValueRef.current = value;
3145
+ onChangeRef.current = onChange;
3146
+ onRunRef.current = onRun;
3141
3147
  React6.useEffect(() => {
3142
3148
  let view = null;
3149
+ let cancelled = false;
3143
3150
  async function init() {
3144
- if (!editorRef.current) return;
3151
+ const parent = editorRef.current;
3152
+ if (!parent) return;
3145
3153
  const [
3146
3154
  { EditorView, keymap, placeholder: cmPlaceholder },
3147
3155
  { EditorState },
@@ -3153,6 +3161,7 @@ function SqlEditor({
3153
3161
  import("@codemirror/lang-sql"),
3154
3162
  import("@codemirror/theme-one-dark")
3155
3163
  ]);
3164
+ if (cancelled || !parent.isConnected) return;
3156
3165
  const extensions = [
3157
3166
  sql(),
3158
3167
  oneDark,
@@ -3169,18 +3178,19 @@ function SqlEditor({
3169
3178
  EditorView.lineWrapping,
3170
3179
  EditorView.updateListener.of((update) => {
3171
3180
  if (update.docChanged) {
3172
- onChange(update.state.doc.toString());
3181
+ onChangeRef.current(update.state.doc.toString());
3173
3182
  }
3174
3183
  }),
3175
3184
  EditorView.editable.of(!readOnly)
3176
3185
  ];
3177
- if (onRun) {
3186
+ if (onRunRef.current) {
3178
3187
  extensions.push(
3179
3188
  keymap.of([
3180
3189
  {
3181
3190
  key: "Mod-Enter",
3182
3191
  run: (v) => {
3183
- onRun(v.state.doc.toString());
3192
+ var _a;
3193
+ (_a = onRunRef.current) == null ? void 0 : _a.call(onRunRef, v.state.doc.toString());
3184
3194
  return true;
3185
3195
  }
3186
3196
  }
@@ -3189,17 +3199,20 @@ function SqlEditor({
3189
3199
  }
3190
3200
  view = new EditorView({
3191
3201
  state: EditorState.create({
3192
- doc: value,
3202
+ doc: latestValueRef.current,
3193
3203
  extensions
3194
3204
  }),
3195
- parent: editorRef.current
3205
+ parent
3196
3206
  });
3197
3207
  viewRef.current = view;
3198
3208
  }
3199
3209
  init();
3200
3210
  return () => {
3211
+ cancelled = true;
3201
3212
  view == null ? void 0 : view.destroy();
3202
- viewRef.current = null;
3213
+ if (viewRef.current === view) {
3214
+ viewRef.current = null;
3215
+ }
3203
3216
  };
3204
3217
  }, []);
3205
3218
  React6.useEffect(() => {
@@ -6389,8 +6402,39 @@ function SidebarHeader({
6389
6402
  SidebarHeader.displayName = "Sidebar.Header";
6390
6403
  function SidebarContent({
6391
6404
  children,
6392
- className
6405
+ className,
6406
+ onKeyDown
6393
6407
  }) {
6408
+ const handleKeyDown = (event) => {
6409
+ var _a;
6410
+ onKeyDown == null ? void 0 : onKeyDown(event);
6411
+ if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
6412
+ return;
6413
+ }
6414
+ const items = Array.from(
6415
+ event.currentTarget.querySelectorAll(
6416
+ "[data-sidebar-item]:not([disabled]):not([aria-disabled='true'])"
6417
+ )
6418
+ );
6419
+ const target = event.target instanceof HTMLElement ? event.target.closest("[data-sidebar-item]") : null;
6420
+ const currentIndex = target ? items.indexOf(target) : -1;
6421
+ if (currentIndex < 0 || items.length === 0) return;
6422
+ let nextIndex = null;
6423
+ if (event.key === "ArrowDown") {
6424
+ nextIndex = (currentIndex + 1) % items.length;
6425
+ } else if (event.key === "ArrowUp") {
6426
+ nextIndex = (currentIndex - 1 + items.length) % items.length;
6427
+ } else if (event.key === "Home") {
6428
+ nextIndex = 0;
6429
+ } else if (event.key === "End") {
6430
+ nextIndex = items.length - 1;
6431
+ }
6432
+ if (nextIndex === null) return;
6433
+ event.preventDefault();
6434
+ const nextItem = items[nextIndex];
6435
+ nextItem == null ? void 0 : nextItem.focus({ preventScroll: true });
6436
+ (_a = nextItem == null ? void 0 : nextItem.scrollIntoView) == null ? void 0 : _a.call(nextItem, { block: "nearest" });
6437
+ };
6394
6438
  return /* @__PURE__ */ jsx34(
6395
6439
  "nav",
6396
6440
  {
@@ -6398,6 +6442,7 @@ function SidebarContent({
6398
6442
  "flex-1 overflow-x-hidden overflow-y-auto px-2 py-1",
6399
6443
  className
6400
6444
  ),
6445
+ onKeyDown: handleKeyDown,
6401
6446
  children
6402
6447
  }
6403
6448
  );
@@ -6528,6 +6573,7 @@ function SidebarItem({
6528
6573
  {
6529
6574
  href,
6530
6575
  onClick,
6576
+ "data-sidebar-item": "",
6531
6577
  "aria-current": active ? "page" : void 0,
6532
6578
  "aria-label": ariaLabel,
6533
6579
  className: sharedClassName,
@@ -6538,6 +6584,7 @@ function SidebarItem({
6538
6584
  {
6539
6585
  type: "button",
6540
6586
  onClick,
6587
+ "data-sidebar-item": "",
6541
6588
  "aria-current": active ? "page" : void 0,
6542
6589
  "aria-label": ariaLabel,
6543
6590
  className: sharedClassName,
@@ -6545,9 +6592,9 @@ function SidebarItem({
6545
6592
  }
6546
6593
  );
6547
6594
  if (isCollapsed && tooltipText) {
6548
- return /* @__PURE__ */ jsxs29(TooltipProvider, { placement: "right", children: [
6595
+ return /* @__PURE__ */ jsxs29(TooltipProvider, { placement: "right", portal: true, children: [
6549
6596
  /* @__PURE__ */ jsx34(TooltipAnchor, { render: element }),
6550
- /* @__PURE__ */ jsx34(Tooltip, { className: "bg-foreground text-background z-50 rounded-md px-2 py-1 text-xs shadow-lg", children: tooltipText })
6597
+ /* @__PURE__ */ jsx34(Tooltip, { className: "bg-foreground text-background z-[120] rounded-md px-2 py-1 text-xs shadow-lg", children: tooltipText })
6551
6598
  ] });
6552
6599
  }
6553
6600
  return element;
@@ -6612,7 +6659,7 @@ function SidebarRoot({
6612
6659
  "data-collapsed": collapsed || void 0,
6613
6660
  "data-side": side,
6614
6661
  className: cn(
6615
- "bg-surface sticky top-0 flex h-screen flex-col transition-[width] duration-200",
6662
+ "bg-surface sticky top-0 flex h-screen shrink-0 flex-col transition-[width] duration-200",
6616
6663
  className
6617
6664
  ),
6618
6665
  style: { width: currentWidth },
@@ -6659,7 +6706,7 @@ function AppShellSidebar({
6659
6706
  children,
6660
6707
  className
6661
6708
  }) {
6662
- return /* @__PURE__ */ jsx35("div", { className: cn("hidden xl:block", className), children });
6709
+ return /* @__PURE__ */ jsx35("div", { className: cn("shrink-0", className), children });
6663
6710
  }
6664
6711
  AppShellSidebar.displayName = "AppShell.Sidebar";
6665
6712
  function AppShellContent({
@@ -7043,13 +7090,16 @@ function BulkActions({
7043
7090
 
7044
7091
  // src/shells/saved-filters.tsx
7045
7092
  import * as React20 from "react";
7093
+ import { Trash2 } from "lucide-react";
7046
7094
  import { jsx as jsx41, jsxs as jsxs36 } from "react/jsx-runtime";
7047
7095
  var defaultLabels24 = {
7048
7096
  triggerLabel: (count) => `Saved Filters (${count})`,
7049
7097
  emptyMessage: "No saved filters",
7050
7098
  deleteLabel: (name) => `Delete ${name}`,
7051
7099
  namePlaceholder: "Filter name\u2026",
7052
- saveLabel: "Save"
7100
+ saveLabel: "Save",
7101
+ confirmDeleteLabel: "Confirm",
7102
+ cancelDeleteLabel: "Cancel"
7053
7103
  };
7054
7104
  function SavedFilters({
7055
7105
  currentFilters,
@@ -7062,10 +7112,41 @@ function SavedFilters({
7062
7112
  }) {
7063
7113
  const labels = __spreadValues(__spreadValues({}, defaultLabels24), customLabels);
7064
7114
  const [newName, setNewName] = React20.useState("");
7065
- return /* @__PURE__ */ jsxs36(PopoverRoot, { "data-opt-id": "DAB08", "data-opt-slug": "saved-filters.SavedFilters", children: [
7115
+ const [pendingDeleteId, setPendingDeleteId] = React20.useState(
7116
+ null
7117
+ );
7118
+ const triggerId = React20.useId();
7119
+ const restoreDeleteTrigger = React20.useCallback((presetId) => {
7120
+ requestAnimationFrame(() => {
7121
+ var _a;
7122
+ (_a = Array.from(
7123
+ document.querySelectorAll("[data-saved-filter-delete]")
7124
+ ).find((element) => element.dataset.savedFilterDelete === presetId)) == null ? void 0 : _a.focus();
7125
+ });
7126
+ }, []);
7127
+ const cancelPendingDelete = React20.useCallback(
7128
+ (presetId) => {
7129
+ setPendingDeleteId(null);
7130
+ restoreDeleteTrigger(presetId);
7131
+ },
7132
+ [restoreDeleteTrigger]
7133
+ );
7134
+ React20.useEffect(() => {
7135
+ if (!pendingDeleteId) return;
7136
+ const cancelDelete = (event) => {
7137
+ if (event.key === "Escape") {
7138
+ event.preventDefault();
7139
+ cancelPendingDelete(pendingDeleteId);
7140
+ }
7141
+ };
7142
+ window.addEventListener("keydown", cancelDelete);
7143
+ return () => window.removeEventListener("keydown", cancelDelete);
7144
+ }, [cancelPendingDelete, pendingDeleteId]);
7145
+ return /* @__PURE__ */ jsxs36(PopoverRoot, { "data-opt-id": "SPRCB", "data-opt-slug": "saved-filters.SavedFilters", children: [
7066
7146
  /* @__PURE__ */ jsx41(
7067
7147
  PopoverTrigger,
7068
7148
  {
7149
+ id: triggerId,
7069
7150
  className: cn(
7070
7151
  "border-border bg-surface text-text-primary hover:bg-bg-subtle inline-flex h-9 items-center gap-1.5 rounded-lg border px-3 text-sm font-medium",
7071
7152
  className
@@ -7073,63 +7154,159 @@ function SavedFilters({
7073
7154
  children: labels.triggerLabel(presets.length)
7074
7155
  }
7075
7156
  ),
7076
- /* @__PURE__ */ jsx41(PopoverContent, { className: "w-64", children: /* @__PURE__ */ jsxs36("div", { className: "gap-group flex flex-col p-2", children: [
7077
- presets.length > 0 && /* @__PURE__ */ jsx41("div", { className: "gap-element flex flex-col", children: presets.map((preset) => /* @__PURE__ */ jsxs36(
7078
- "div",
7157
+ /* @__PURE__ */ jsx41(PopoverContent, { className: "w-72", children: /* @__PURE__ */ jsxs36("section", { className: "gap-group flex flex-col p-2", children: [
7158
+ presets.length > 0 && /* @__PURE__ */ jsx41(
7159
+ "ul",
7160
+ {
7161
+ className: "gap-element m-0 flex list-none flex-col p-0",
7162
+ onKeyDown: (event) => {
7163
+ var _a;
7164
+ if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || !(event.target instanceof HTMLElement)) {
7165
+ return;
7166
+ }
7167
+ const items = Array.from(
7168
+ event.currentTarget.querySelectorAll(
7169
+ "[data-saved-filter-item]"
7170
+ )
7171
+ );
7172
+ const current = event.target.closest(
7173
+ "[data-saved-filter-item]"
7174
+ );
7175
+ const index = current ? items.indexOf(current) : -1;
7176
+ if (index < 0 || items.length === 0) return;
7177
+ let next = null;
7178
+ if (event.key === "ArrowDown") {
7179
+ next = (index + 1) % items.length;
7180
+ } else if (event.key === "ArrowUp") {
7181
+ next = (index - 1 + items.length) % items.length;
7182
+ } else if (event.key === "Home") {
7183
+ next = 0;
7184
+ } else if (event.key === "End") {
7185
+ next = items.length - 1;
7186
+ }
7187
+ if (next === null) return;
7188
+ event.preventDefault();
7189
+ (_a = items[next]) == null ? void 0 : _a.focus({ preventScroll: true });
7190
+ },
7191
+ children: presets.map((preset) => /* @__PURE__ */ jsxs36(
7192
+ "li",
7193
+ {
7194
+ className: "gap-element hover:bg-bg-subtle flex min-h-9 items-center justify-between rounded-md px-2 py-1.5",
7195
+ children: [
7196
+ /* @__PURE__ */ jsx41(
7197
+ "button",
7198
+ {
7199
+ type: "button",
7200
+ "data-saved-filter-item": "",
7201
+ className: "text-text-primary min-w-0 flex-1 truncate text-left text-sm",
7202
+ onClick: () => {
7203
+ setPendingDeleteId(null);
7204
+ onLoadPreset(preset);
7205
+ },
7206
+ children: preset.name
7207
+ }
7208
+ ),
7209
+ /* @__PURE__ */ jsx41(
7210
+ Button,
7211
+ {
7212
+ variant: "ghost",
7213
+ size: "sm",
7214
+ className: "text-text-tertiary hover:text-danger size-7 p-0",
7215
+ "data-saved-filter-delete": preset.id,
7216
+ "aria-expanded": pendingDeleteId === preset.id,
7217
+ "aria-controls": pendingDeleteId === preset.id ? `${triggerId}-${preset.id}` : void 0,
7218
+ onClick: () => setPendingDeleteId(preset.id),
7219
+ "aria-label": labels.deleteLabel(preset.name),
7220
+ children: /* @__PURE__ */ jsx41(Trash2, { size: 14, "aria-hidden": "true" })
7221
+ }
7222
+ ),
7223
+ pendingDeleteId === preset.id ? /* @__PURE__ */ jsxs36(
7224
+ "span",
7225
+ {
7226
+ className: "flex flex-none items-center gap-1",
7227
+ onKeyDownCapture: (event) => {
7228
+ if (event.key === "Escape") {
7229
+ event.preventDefault();
7230
+ event.stopPropagation();
7231
+ cancelPendingDelete(preset.id);
7232
+ }
7233
+ },
7234
+ children: [
7235
+ /* @__PURE__ */ jsx41(
7236
+ Button,
7237
+ {
7238
+ id: `${triggerId}-${preset.id}`,
7239
+ variant: "danger",
7240
+ size: "sm",
7241
+ className: "h-7 px-2",
7242
+ autoFocus: true,
7243
+ onClick: () => {
7244
+ onDeletePreset(preset.id);
7245
+ setPendingDeleteId(null);
7246
+ requestAnimationFrame(() => {
7247
+ var _a;
7248
+ (_a = document.getElementById(triggerId)) == null ? void 0 : _a.focus();
7249
+ });
7250
+ },
7251
+ children: labels.confirmDeleteLabel
7252
+ }
7253
+ ),
7254
+ /* @__PURE__ */ jsx41(
7255
+ Button,
7256
+ {
7257
+ variant: "ghost",
7258
+ size: "sm",
7259
+ className: "h-7 px-2",
7260
+ onClick: () => cancelPendingDelete(preset.id),
7261
+ children: labels.cancelDeleteLabel
7262
+ }
7263
+ )
7264
+ ]
7265
+ }
7266
+ ) : null
7267
+ ]
7268
+ },
7269
+ preset.id
7270
+ ))
7271
+ }
7272
+ ),
7273
+ presets.length === 0 && /* @__PURE__ */ jsx41("p", { className: "text-text-tertiary px-2 py-1 text-sm", children: labels.emptyMessage }),
7274
+ /* @__PURE__ */ jsxs36(
7275
+ "form",
7079
7276
  {
7080
- className: "hover:bg-bg-subtle flex items-center justify-between rounded-md px-2 py-1.5",
7277
+ className: "gap-element border-border flex border-t pt-2",
7278
+ onSubmit: (event) => {
7279
+ event.preventDefault();
7280
+ if (newName.trim()) {
7281
+ onSavePreset(newName.trim(), currentFilters);
7282
+ setNewName("");
7283
+ setPendingDeleteId(null);
7284
+ }
7285
+ },
7081
7286
  children: [
7082
7287
  /* @__PURE__ */ jsx41(
7083
- "button",
7288
+ Input,
7084
7289
  {
7085
- type: "button",
7086
- className: "text-text-primary flex-1 text-left text-sm",
7087
- onClick: () => onLoadPreset(preset),
7088
- children: preset.name
7290
+ value: newName,
7291
+ onChange: (e) => setNewName(e.target.value),
7292
+ placeholder: labels.namePlaceholder,
7293
+ "aria-label": labels.namePlaceholder,
7294
+ className: "h-8 text-sm"
7089
7295
  }
7090
7296
  ),
7091
7297
  /* @__PURE__ */ jsx41(
7092
- "button",
7298
+ Button,
7093
7299
  {
7094
- type: "button",
7095
- className: "text-text-tertiary hover:text-danger text-xs",
7096
- onClick: () => onDeletePreset(preset.id),
7097
- "aria-label": labels.deleteLabel(preset.name),
7098
- children: "\xD7"
7300
+ type: "submit",
7301
+ size: "sm",
7302
+ disabled: !newName.trim(),
7303
+ className: "h-8",
7304
+ children: labels.saveLabel
7099
7305
  }
7100
7306
  )
7101
7307
  ]
7102
- },
7103
- preset.id
7104
- )) }),
7105
- presets.length === 0 && /* @__PURE__ */ jsx41("p", { className: "text-text-tertiary px-2 py-1 text-sm", children: labels.emptyMessage }),
7106
- /* @__PURE__ */ jsxs36("div", { className: "gap-element border-border flex border-t pt-2", children: [
7107
- /* @__PURE__ */ jsx41(
7108
- Input,
7109
- {
7110
- value: newName,
7111
- onChange: (e) => setNewName(e.target.value),
7112
- placeholder: labels.namePlaceholder,
7113
- "aria-label": labels.namePlaceholder,
7114
- className: "h-8 text-sm"
7115
- }
7116
- ),
7117
- /* @__PURE__ */ jsx41(
7118
- "button",
7119
- {
7120
- type: "button",
7121
- disabled: !newName.trim(),
7122
- className: "bg-accent text-accent-fg hover:bg-accent-hover inline-flex h-8 items-center rounded-md px-3 text-sm font-medium disabled:opacity-50",
7123
- onClick: () => {
7124
- if (newName.trim()) {
7125
- onSavePreset(newName.trim(), currentFilters);
7126
- setNewName("");
7127
- }
7128
- },
7129
- children: labels.saveLabel
7130
- }
7131
- )
7132
- ] })
7308
+ }
7309
+ )
7133
7310
  ] }) })
7134
7311
  ] });
7135
7312
  }
@@ -8324,7 +8501,7 @@ function MegaMenu({
8324
8501
  MegaMenu.displayName = "MegaMenu";
8325
8502
 
8326
8503
  // src/shells/chat-sidebar.tsx
8327
- import { useState as useState24, useEffect as useEffect7, useCallback as useCallback12 } from "react";
8504
+ import { useState as useState24, useEffect as useEffect8, useCallback as useCallback13 } from "react";
8328
8505
  import { jsx as jsx53, jsxs as jsxs48 } from "react/jsx-runtime";
8329
8506
  var defaultLabels30 = {
8330
8507
  newChat: "\uC0C8 \uB300\uD654",
@@ -8344,10 +8521,10 @@ function ChatSidebar({
8344
8521
  const labels = __spreadValues(__spreadValues({}, defaultLabels30), customLabels);
8345
8522
  const [searchQuery, setSearchQuery] = useState24("");
8346
8523
  const [isHydrated, setIsHydrated] = useState24(false);
8347
- useEffect7(() => {
8524
+ useEffect8(() => {
8348
8525
  setIsHydrated(true);
8349
8526
  }, []);
8350
- const handleSearch = useCallback12(
8527
+ const handleSearch = useCallback13(
8351
8528
  (value) => {
8352
8529
  setSearchQuery(value);
8353
8530
  onSearch == null ? void 0 : onSearch(value);
@@ -8495,7 +8672,7 @@ ChatSidebar.displayName = "ChatSidebar";
8495
8672
  // src/shells/chat-input.tsx
8496
8673
  import {
8497
8674
  useRef as useRef8,
8498
- useCallback as useCallback13,
8675
+ useCallback as useCallback14,
8499
8676
  useLayoutEffect
8500
8677
  } from "react";
8501
8678
  import { jsx as jsx54, jsxs as jsxs49 } from "react/jsx-runtime";
@@ -8544,13 +8721,13 @@ function ChatInput({
8544
8721
  const maxHeight = lineHeight * maxRows;
8545
8722
  textarea.style.height = `${Math.min(textarea.scrollHeight, maxHeight)}px`;
8546
8723
  }, [value, maxRows]);
8547
- const handleSend = useCallback13(() => {
8724
+ const handleSend = useCallback14(() => {
8548
8725
  const trimmed = value.trim();
8549
8726
  if (!trimmed || disabled) return;
8550
8727
  onSend == null ? void 0 : onSend(trimmed);
8551
8728
  setValue("");
8552
8729
  }, [value, disabled, onSend, setValue]);
8553
- const handleKeyDown = useCallback13(
8730
+ const handleKeyDown = useCallback14(
8554
8731
  (e) => {
8555
8732
  if (e.key === "Enter" && !e.shiftKey) {
8556
8733
  e.preventDefault();
@@ -8715,8 +8892,8 @@ ChatInput.displayName = "ChatInput";
8715
8892
  // src/shells/chat-message-list.tsx
8716
8893
  import {
8717
8894
  useRef as useRef9,
8718
- useEffect as useEffect8,
8719
- useCallback as useCallback14,
8895
+ useEffect as useEffect9,
8896
+ useCallback as useCallback15,
8720
8897
  useState as useState25
8721
8898
  } from "react";
8722
8899
  import { jsx as jsx55, jsxs as jsxs50 } from "react/jsx-runtime";
@@ -8741,19 +8918,19 @@ function ChatMessageList({
8741
8918
  const labels = __spreadValues(__spreadValues(__spreadValues({}, defaultLabels32), customLabels), userName ? { userLabel: userName } : {});
8742
8919
  const scrollRef = useRef9(null);
8743
8920
  const [isUserScrolledUp, setIsUserScrolledUp] = useState25(false);
8744
- const scrollToBottom = useCallback14(() => {
8921
+ const scrollToBottom = useCallback15(() => {
8745
8922
  const el = scrollRef.current;
8746
8923
  if (!el) return;
8747
8924
  el.scrollTop = el.scrollHeight;
8748
8925
  }, []);
8749
- const handleScroll = useCallback14(() => {
8926
+ const handleScroll = useCallback15(() => {
8750
8927
  const el = scrollRef.current;
8751
8928
  if (!el) return;
8752
8929
  const threshold = 50;
8753
8930
  const isAtBottom = el.scrollHeight - el.scrollTop - el.clientHeight < threshold;
8754
8931
  setIsUserScrolledUp(!isAtBottom);
8755
8932
  }, []);
8756
- useEffect8(() => {
8933
+ useEffect9(() => {
8757
8934
  if (autoScroll && !isUserScrolledUp) {
8758
8935
  scrollToBottom();
8759
8936
  }
@@ -8875,11 +9052,11 @@ ChatMessageList.displayName = "ChatMessageList";
8875
9052
 
8876
9053
  // src/utils/route-focus-manager.tsx
8877
9054
  import { usePathname } from "next/navigation";
8878
- import { useEffect as useEffect9, useRef as useRef10 } from "react";
9055
+ import { useEffect as useEffect10, useRef as useRef10 } from "react";
8879
9056
  function RouteFocusManager() {
8880
9057
  const pathname = usePathname();
8881
9058
  const prevPathname = useRef10(pathname);
8882
- useEffect9(() => {
9059
+ useEffect10(() => {
8883
9060
  if (prevPathname.current === pathname) return;
8884
9061
  prevPathname.current = pathname;
8885
9062
  const timer = setTimeout(() => {
@@ -9380,7 +9557,7 @@ function DesignGuidePanel({ className }) {
9380
9557
  }
9381
9558
 
9382
9559
  // src/lib/spatial-nav.ts
9383
- import { useEffect as useEffect10 } from "react";
9560
+ import { useEffect as useEffect11 } from "react";
9384
9561
  var TABBABLE_SELECTOR = [
9385
9562
  "a[href]:not([tabindex='-1'])",
9386
9563
  "button:not([disabled]):not([tabindex='-1'])",
@@ -9563,7 +9740,7 @@ function emitDecision(detail) {
9563
9740
  }
9564
9741
  }
9565
9742
  function useSpatialNav() {
9566
- useEffect10(() => {
9743
+ useEffect11(() => {
9567
9744
  function handleKeyDown(e) {
9568
9745
  var _a;
9569
9746
  if (!ARROW_KEYS.has(e.key)) return;