@meridianlabs/log-viewer 0.3.247 → 0.3.248

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 (49) hide show
  1. package/lib/index.js +1805 -891
  2. package/lib/index.js.map +1 -1
  3. package/lib/src/app/App.d.ts.map +1 -1
  4. package/lib/src/app/log-list/grid/LogListGrid.d.ts.map +1 -1
  5. package/lib/src/app/log-list/grid/columns/hooks.d.ts +3 -3
  6. package/lib/src/app/log-list/grid/columns/hooks.d.ts.map +1 -1
  7. package/lib/src/app/log-list/grid/useLogListData.d.ts.map +1 -1
  8. package/lib/src/app/log-view/LogViewLayout.d.ts.map +1 -1
  9. package/lib/src/app/log-view/tabs/SamplesTab.d.ts.map +1 -1
  10. package/lib/src/app/routing/url.d.ts +10 -1
  11. package/lib/src/app/routing/url.d.ts.map +1 -1
  12. package/lib/src/app/samples/SampleDetailComponent.d.ts.map +1 -1
  13. package/lib/src/app/samples/SampleDisplay.d.ts.map +1 -1
  14. package/lib/src/app/samples/sample-tools/filters.d.ts.map +1 -1
  15. package/lib/src/app/samples/transcript/TranscriptPanel.d.ts.map +1 -1
  16. package/lib/src/app/samples-panel/SamplesPanel.d.ts.map +1 -1
  17. package/lib/src/app_config/appConfig.d.ts.map +1 -1
  18. package/lib/src/client/api/client-api.d.ts.map +1 -1
  19. package/lib/src/client/api/static-http/api-static-http.d.ts.map +1 -1
  20. package/lib/src/client/api/types.d.ts +46 -2
  21. package/lib/src/client/api/types.d.ts.map +1 -1
  22. package/lib/src/client/database/index.d.ts +3 -3
  23. package/lib/src/client/database/index.d.ts.map +1 -1
  24. package/lib/src/client/database/manager.d.ts +4 -19
  25. package/lib/src/client/database/manager.d.ts.map +1 -1
  26. package/lib/src/client/database/schema.d.ts +26 -4
  27. package/lib/src/client/database/schema.d.ts.map +1 -1
  28. package/lib/src/client/database/service.d.ts +35 -19
  29. package/lib/src/client/database/service.d.ts.map +1 -1
  30. package/lib/src/client/utils/derive.d.ts +20 -0
  31. package/lib/src/client/utils/derive.d.ts.map +1 -0
  32. package/lib/src/client/utils/type-utils.d.ts +19 -2
  33. package/lib/src/client/utils/type-utils.d.ts.map +1 -1
  34. package/lib/src/log_data/fetchEngine.d.ts +3 -0
  35. package/lib/src/log_data/fetchEngine.d.ts.map +1 -1
  36. package/lib/src/log_data/logsContent.d.ts.map +1 -1
  37. package/lib/src/log_data/replicationControl.d.ts.map +1 -1
  38. package/lib/src/log_data/samplesListing.d.ts +5 -3
  39. package/lib/src/log_data/samplesListing.d.ts.map +1 -1
  40. package/lib/src/log_data/scoreSchema.d.ts +4 -4
  41. package/lib/src/log_data/scoreSchema.d.ts.map +1 -1
  42. package/lib/styles/index.css +125 -60
  43. package/package.json +3 -2
  44. package/lib/src/components/FindBand.d.ts +0 -8
  45. package/lib/src/components/FindBand.d.ts.map +0 -1
  46. package/lib/src/components/FindBandUI.d.ts +0 -18
  47. package/lib/src/components/FindBandUI.d.ts.map +0 -1
  48. package/lib/src/utils/dom.d.ts +0 -29
  49. package/lib/src/utils/dom.d.ts.map +0 -1
package/lib/index.js CHANGED
@@ -3,7 +3,6 @@ import React3, { Children, Component, Fragment, Suspense, createContext, createE
3
3
  import * as ReactDOM from "react-dom";
4
4
  import xo, { createPortal, flushSync } from "react-dom";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
- import markdownitMathjax3 from "markdown-it-mathjax3";
7
6
  //#region \0rolldown/runtime.js
8
7
  var __create = Object.create;
9
8
  var __defProp = Object.defineProperty;
@@ -23274,6 +23273,7 @@ var ExtendedFindContext = createContext(null);
23274
23273
  var ExtendedFindProvider = ({ children }) => {
23275
23274
  const virtualLists = useRef(/* @__PURE__ */ new Map());
23276
23275
  const matchCounters = useRef(/* @__PURE__ */ new Map());
23276
+ const matchCountersVersion = useRef(0);
23277
23277
  const contextValue = {
23278
23278
  extendedFindTerm: useCallback(async (term, direction) => {
23279
23279
  for (const [, searchFn] of virtualLists.current) if (await new Promise((resolve) => {
@@ -23311,10 +23311,13 @@ var ExtendedFindProvider = ({ children }) => {
23311
23311
  }, []),
23312
23312
  registerMatchCounter: useCallback((id, countFn) => {
23313
23313
  matchCounters.current.set(id, countFn);
23314
+ matchCountersVersion.current++;
23314
23315
  return () => {
23315
23316
  matchCounters.current.delete(id);
23317
+ matchCountersVersion.current++;
23316
23318
  };
23317
- }, [])
23319
+ }, []),
23320
+ getMatchCountersVersion: useCallback(() => matchCountersVersion.current, [])
23318
23321
  };
23319
23322
  return /* @__PURE__ */ jsx(ExtendedFindContext.Provider, {
23320
23323
  value: contextValue,
@@ -23465,7 +23468,7 @@ function isRegExp$1(value) {
23465
23468
  }
23466
23469
  //#endregion
23467
23470
  //#region ../../node_modules/.pnpm/arquero@8.0.3/node_modules/arquero/src/util/is-object.js
23468
- function isObject$1(value) {
23471
+ function isObject$2(value) {
23469
23472
  return value === Object(value);
23470
23473
  }
23471
23474
  //#endregion
@@ -23479,7 +23482,7 @@ function isObject$1(value) {
23479
23482
  * @return {boolean} True if equal, false if not.
23480
23483
  */
23481
23484
  function equal(a, b) {
23482
- return a == null || b == null || a !== a || b !== b ? false : a === b ? true : isDate(a) || isDate(b) ? +a === +b : isRegExp$1(a) && isRegExp$1(b) ? a + "" === b + "" : isObject$1(a) && isObject$1(b) ? deepEqual(a, b) : false;
23485
+ return a == null || b == null || a !== a || b !== b ? false : a === b ? true : isDate(a) || isDate(b) ? +a === +b : isRegExp$1(a) && isRegExp$1(b) ? a + "" === b + "" : isObject$2(a) && isObject$2(b) ? deepEqual(a, b) : false;
23483
23486
  }
23484
23487
  function deepEqual(a, b) {
23485
23488
  if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false;
@@ -23774,7 +23777,7 @@ var date_exports = /* @__PURE__ */ __exportAll({
23774
23777
  milliseconds: () => milliseconds,
23775
23778
  minutes: () => minutes,
23776
23779
  month: () => month,
23777
- now: () => now,
23780
+ now: () => now$1,
23778
23781
  quarter: () => quarter,
23779
23782
  seconds: () => seconds,
23780
23783
  timestamp: () => timestamp$2,
@@ -23833,7 +23836,7 @@ function format_utcdate(date, shorten) {
23833
23836
  * 1970 Universal Coordinated Time (UTC).
23834
23837
  * @return {number} The timestamp for now.
23835
23838
  */
23836
- function now() {
23839
+ function now$1() {
23837
23840
  return Date.now();
23838
23841
  }
23839
23842
  /**
@@ -24165,7 +24168,7 @@ var math_exports = /* @__PURE__ */ __exportAll({
24165
24168
  is_finite: () => is_finite,
24166
24169
  is_nan: () => is_nan,
24167
24170
  least: () => least,
24168
- log: () => log$7,
24171
+ log: () => log$8,
24169
24172
  log10: () => log10,
24170
24173
  log1p: () => log1p,
24171
24174
  log2: () => log2,
@@ -24305,7 +24308,7 @@ function least(...values) {
24305
24308
  * @param {number} value The input number value.
24306
24309
  * @return {number} The base-e log value.
24307
24310
  */
24308
- function log$7(value) {
24311
+ function log$8(value) {
24309
24312
  return Math.log(value);
24310
24313
  }
24311
24314
  /**
@@ -24944,16 +24947,16 @@ function bins(min, max, maxbins = 15, nice = true, minstep = 0, step) {
24944
24947
  }
24945
24948
  //#endregion
24946
24949
  //#region ../../node_modules/.pnpm/arquero@8.0.3/node_modules/arquero/src/util/key-function.js
24947
- function key$5(value) {
24950
+ function key$6(value) {
24948
24951
  const type = typeof value;
24949
- return type === "string" ? `"${value}"` : type !== "object" || !value ? value : isDate(value) ? +value : isArray$2(value) || isTypedArray(value) ? `[${value.map(key$5)}]` : isRegExp$1(value) ? value + "" : objectKey(value);
24952
+ return type === "string" ? `"${value}"` : type !== "object" || !value ? value : isDate(value) ? +value : isArray$2(value) || isTypedArray(value) ? `[${value.map(key$6)}]` : isRegExp$1(value) ? value + "" : objectKey(value);
24950
24953
  }
24951
24954
  function objectKey(value) {
24952
24955
  let s = "{";
24953
24956
  let i = -1;
24954
24957
  for (const k in value) {
24955
24958
  if (++i > 0) s += ",";
24956
- s += `"${k}":${key$5(value[k])}`;
24959
+ s += `"${k}":${key$6(value[k])}`;
24957
24960
  }
24958
24961
  s += "}";
24959
24962
  return s;
@@ -24970,7 +24973,7 @@ function distinctMap() {
24970
24973
  return Array.from(map.values(), (_) => _.v);
24971
24974
  },
24972
24975
  increment(v) {
24973
- const k = key$5(v);
24976
+ const k = key$6(v);
24974
24977
  const e = map.get(k);
24975
24978
  e ? ++e.n : map.set(k, {
24976
24979
  v,
@@ -24978,7 +24981,7 @@ function distinctMap() {
24978
24981
  });
24979
24982
  },
24980
24983
  decrement(v) {
24981
- const k = key$5(v);
24984
+ const k = key$6(v);
24982
24985
  const e = map.get(k);
24983
24986
  e.n === 1 ? map.delete(k) : --e.n;
24984
24987
  },
@@ -26937,10 +26940,10 @@ function nextLineBreak(code, from, end) {
26937
26940
  var nonASCIIwhitespace = /[\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
26938
26941
  var skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g;
26939
26942
  var ref = Object.prototype;
26940
- var hasOwnProperty$1 = ref.hasOwnProperty;
26943
+ var hasOwnProperty$2 = ref.hasOwnProperty;
26941
26944
  var toString$1 = ref.toString;
26942
26945
  var hasOwn = Object.hasOwn || (function(obj, propName) {
26943
- return hasOwnProperty$1.call(obj, propName);
26946
+ return hasOwnProperty$2.call(obj, propName);
26944
26947
  });
26945
26948
  var isArray$1 = Array.isArray || (function(obj) {
26946
26949
  return toString$1.call(obj) === "[object Array]";
@@ -31545,6 +31548,16 @@ var clearDocumentSelection = () => {
31545
31548
  }
31546
31549
  };
31547
31550
  /**
31551
+ * The focused element, pierced through shadow roots. document.activeElement
31552
+ * only returns the shadow host, so walk down to the real focused element
31553
+ * (e.g. the textarea inside a <vscode-textarea>).
31554
+ */
31555
+ function deepActiveElement() {
31556
+ let active = document.activeElement;
31557
+ while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
31558
+ return active;
31559
+ }
31560
+ /**
31548
31561
  * True if the element accepts text input (input/textarea/select/contentEditable).
31549
31562
  * Use to skip global keyboard handlers when focus is in a form field.
31550
31563
  */
@@ -33475,7 +33488,7 @@ function throttle(func, wait, options = {}) {
33475
33488
  * Creates a debounced version of a function that delays invoking the function
33476
33489
  * until after `wait` milliseconds have passed since the last time it was invoked.
33477
33490
  */
33478
- function debounce$2(func, wait, options = {}) {
33491
+ function debounce$3(func, wait, options = {}) {
33479
33492
  let timeout = null;
33480
33493
  let args;
33481
33494
  let result;
@@ -37824,7 +37837,7 @@ function _class(obj) {
37824
37837
  function isString(obj) {
37825
37838
  return _class(obj) === "[object String]";
37826
37839
  }
37827
- function isObject(obj) {
37840
+ function isObject$1(obj) {
37828
37841
  return _class(obj) === "[object Object]";
37829
37842
  }
37830
37843
  function isRegExp(obj) {
@@ -37916,7 +37929,7 @@ function compile(self) {
37916
37929
  link: null
37917
37930
  };
37918
37931
  self.__compiled__[name] = compiled;
37919
- if (isObject(val)) {
37932
+ if (isObject$1(val)) {
37920
37933
  if (isRegExp(val.validate)) compiled.validate = createValidator(val.validate);
37921
37934
  else if (isFunction$2(val.validate)) compiled.validate = val.validate;
37922
37935
  else schemaError(name, val);
@@ -39123,6 +39136,18 @@ MarkdownIt.prototype.renderInline = function(src, env) {
39123
39136
  * Pure functions for the markdown rendering pipeline.
39124
39137
  * Extracted for testability (no CSS/React imports).
39125
39138
  */
39139
+ var mathjaxPluginPromise = null;
39140
+ var getMathjaxPlugin = () => {
39141
+ if (!mathjaxPluginPromise) {
39142
+ const loading = import("markdown-it-mathjax3").then((m) => m.default);
39143
+ loading.catch(() => {
39144
+ if (mathjaxPluginPromise === loading) mathjaxPluginPromise = null;
39145
+ });
39146
+ mathjaxPluginPromise = loading;
39147
+ }
39148
+ return mathjaxPluginPromise;
39149
+ };
39150
+ var hasMathContent = (text) => text.includes("$") || text.includes("\\(") || text.includes("\\[");
39126
39151
  var mdInstanceCache = {};
39127
39152
  var defaultMarkdownRenderer = "full";
39128
39153
  /** Unescape HTML entities within math token content before MathJax processing.
@@ -39130,15 +39155,17 @@ var defaultMarkdownRenderer = "full";
39130
39155
  var unescapeHtmlForMath = (content) => {
39131
39156
  return content.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&apos;/g, "'").replace(/&quot;/g, "\"");
39132
39157
  };
39133
- var getMarkdownInstance = (renderer) => {
39134
- const cached = mdInstanceCache[renderer];
39158
+ var getMarkdownInstance = async (renderer, contentHasMath) => {
39159
+ const useMath = (renderer === "full" || renderer === "fragment") && !!contentHasMath;
39160
+ const cacheKey = `${renderer}:${useMath ? "1" : "0"}`;
39161
+ const cached = mdInstanceCache[cacheKey];
39135
39162
  if (cached) return cached;
39136
39163
  if (renderer === "textOnly") {
39137
39164
  const md = new MarkdownIt("zero", {
39138
39165
  breaks: true,
39139
39166
  html: false
39140
39167
  }).enable(["emphasis", "newline"]);
39141
- mdInstanceCache[renderer] = md;
39168
+ mdInstanceCache[cacheKey] = md;
39142
39169
  return md;
39143
39170
  }
39144
39171
  const md = new MarkdownIt({
@@ -39158,8 +39185,9 @@ var getMarkdownInstance = (renderer) => {
39158
39185
  if (!href) return escapedLabel;
39159
39186
  return `<a href="${md.utils.escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapedLabel}</a>`;
39160
39187
  };
39161
- if (renderer === "full" || renderer === "fragment") {
39162
- md.use(markdownitMathjax3);
39188
+ if (useMath) {
39189
+ const mathjaxPlugin = await getMathjaxPlugin();
39190
+ md.use(mathjaxPlugin);
39163
39191
  const origInline = md.renderer.rules.math_inline;
39164
39192
  const origBlock = md.renderer.rules.math_block;
39165
39193
  if (origInline) md.renderer.rules.math_inline = (tokens, idx, options, env, self) => {
@@ -39173,7 +39201,7 @@ var getMarkdownInstance = (renderer) => {
39173
39201
  return origBlock(tokens, idx, options, env, self);
39174
39202
  };
39175
39203
  }
39176
- mdInstanceCache[renderer] = md;
39204
+ mdInstanceCache[cacheKey] = md;
39177
39205
  return md;
39178
39206
  };
39179
39207
  var escapeHtmlCharacters$1 = (content) => {
@@ -39262,20 +39290,20 @@ function unescapeCodeHtmlEntities(str) {
39262
39290
  return starttag + content.replace(/&(?:amp|lt|gt|quot|#39|#x2F|#x5C|#96);/g, (entity) => htmlEntities[entity] || entity) + endtag;
39263
39291
  });
39264
39292
  }
39265
- var renderFullPipelineMarkdown = (markdown, renderer) => {
39293
+ var renderFullPipelineMarkdown = async (markdown, renderer) => {
39266
39294
  const preparedForMarkdown = restoreBackslashesForLatex(protectMarkdown(preRenderText(escapeHtmlCharacters$1(protectBackslashesInLatex(markdown)))));
39267
39295
  let html = preparedForMarkdown;
39268
39296
  try {
39269
- html = getMarkdownInstance(renderer).render(preparedForMarkdown);
39297
+ html = (await getMarkdownInstance(renderer, hasMathContent(markdown))).render(preparedForMarkdown);
39270
39298
  } catch (ex) {
39271
39299
  console.log("Unable to markdown render content");
39272
39300
  console.error(ex);
39273
39301
  }
39274
39302
  return unescapeSupHtmlEntities(unescapeCodeHtmlEntities(unprotectMarkdown(html)));
39275
39303
  };
39276
- var renderTextOnlyMarkdown = (markdown) => {
39304
+ var renderTextOnlyMarkdown = async (markdown) => {
39277
39305
  try {
39278
- return getMarkdownInstance("textOnly").render(markdown);
39306
+ return (await getMarkdownInstance("textOnly")).render(markdown);
39279
39307
  } catch (ex) {
39280
39308
  console.log("Unable to markdown render content");
39281
39309
  console.error(ex);
@@ -39365,7 +39393,7 @@ var booleanToString = unapply(Boolean.prototype.toString);
39365
39393
  var bigintToString = typeof BigInt === "undefined" ? null : unapply(BigInt.prototype.toString);
39366
39394
  var symbolToString = typeof Symbol === "undefined" ? null : unapply(Symbol.prototype.toString);
39367
39395
  var objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
39368
- var objectToString = unapply(Object.prototype.toString);
39396
+ var objectToString$1 = unapply(Object.prototype.toString);
39369
39397
  var regExpTest = unapply(RegExp.prototype.test);
39370
39398
  var typeErrorCreate = unconstruct(TypeError);
39371
39399
  /**
@@ -39460,19 +39488,19 @@ function stringifyValue(value) {
39460
39488
  case "boolean": return booleanToString(value);
39461
39489
  case "bigint": return bigintToString ? bigintToString(value) : "0";
39462
39490
  case "symbol": return symbolToString ? symbolToString(value) : "Symbol()";
39463
- case "undefined": return objectToString(value);
39491
+ case "undefined": return objectToString$1(value);
39464
39492
  case "function":
39465
39493
  case "object": {
39466
- if (value === null) return objectToString(value);
39494
+ if (value === null) return objectToString$1(value);
39467
39495
  const valueAsRecord = value;
39468
39496
  const valueToString = lookupGetter(valueAsRecord, "toString");
39469
39497
  if (typeof valueToString === "function") {
39470
39498
  const stringified = valueToString(valueAsRecord);
39471
- return typeof stringified === "string" ? stringified : objectToString(stringified);
39499
+ return typeof stringified === "string" ? stringified : objectToString$1(stringified);
39472
39500
  }
39473
- return objectToString(value);
39501
+ return objectToString$1(value);
39474
39502
  }
39475
- default: return objectToString(value);
39503
+ default: return objectToString$1(value);
39476
39504
  }
39477
39505
  }
39478
39506
  /**
@@ -41609,8 +41637,8 @@ var MarkdownDivComponent = forwardRef(({ markdown, renderer, style, className, p
41609
41637
  useEffect(() => {
41610
41638
  if (cachedHtml) {
41611
41639
  const finalHtml = applyPostProcess(cachedHtml);
41612
- if (renderedHtml !== finalHtml) startTransition(() => {
41613
- setRenderedHtml(finalHtml);
41640
+ startTransition(() => {
41641
+ setRenderedHtml((prev) => prev === finalHtml ? prev : finalHtml);
41614
41642
  });
41615
41643
  return;
41616
41644
  }
@@ -41623,9 +41651,8 @@ var MarkdownDivComponent = forwardRef(({ markdown, renderer, style, className, p
41623
41651
  }
41624
41652
  const sanitizedResult = sanitizeRenderedHtml(result);
41625
41653
  renderCache.set(cacheKey, sanitizedResult);
41626
- const finalHtml = applyPostProcess(sanitizedResult);
41627
41654
  startTransition(() => {
41628
- setRenderedHtml(finalHtml);
41655
+ setRenderedHtml(applyPostProcess(sanitizedResult));
41629
41656
  });
41630
41657
  }).catch((error) => {
41631
41658
  console.error("Markdown rendering error:", error);
@@ -41637,7 +41664,6 @@ var MarkdownDivComponent = forwardRef(({ markdown, renderer, style, className, p
41637
41664
  markdown,
41638
41665
  rendererName,
41639
41666
  cachedHtml,
41640
- renderedHtml,
41641
41667
  cacheKey,
41642
41668
  applyPostProcess
41643
41669
  ]);
@@ -41662,6 +41688,7 @@ var MarkdownRenderQueue = class {
41662
41688
  }
41663
41689
  enqueue(task) {
41664
41690
  let cancelled = false;
41691
+ let queueTask;
41665
41692
  const promise = new Promise((resolve, reject) => {
41666
41693
  const wrappedTask = async () => {
41667
41694
  if (cancelled) return;
@@ -41672,7 +41699,7 @@ var MarkdownRenderQueue = class {
41672
41699
  if (!cancelled) reject(error instanceof Error ? error : new Error(String(error)));
41673
41700
  }
41674
41701
  };
41675
- const queueTask = {
41702
+ queueTask = {
41676
41703
  task: wrappedTask,
41677
41704
  cancelled: false
41678
41705
  };
@@ -41681,8 +41708,7 @@ var MarkdownRenderQueue = class {
41681
41708
  });
41682
41709
  const cancel = () => {
41683
41710
  cancelled = true;
41684
- const index = this.queue.findIndex((t) => !t.cancelled);
41685
- if (index !== -1 && this.queue[index]) this.queue[index].cancelled = true;
41711
+ if (queueTask) queueTask.cancelled = true;
41686
41712
  };
41687
41713
  return {
41688
41714
  promise,
@@ -42891,7 +42917,7 @@ function orderModifiers(modifiers) {
42891
42917
  }
42892
42918
  //#endregion
42893
42919
  //#region ../../node_modules/.pnpm/@popperjs+core@2.11.8/node_modules/@popperjs/core/lib/utils/debounce.js
42894
- function debounce$1(fn) {
42920
+ function debounce$2(fn) {
42895
42921
  var pending;
42896
42922
  return function() {
42897
42923
  if (!pending) pending = new Promise(function(resolve) {
@@ -42995,7 +43021,7 @@ function popperGenerator(generatorOptions) {
42995
43021
  }) || state;
42996
43022
  }
42997
43023
  },
42998
- update: debounce$1(function() {
43024
+ update: debounce$2(function() {
42999
43025
  return new Promise(function(resolve) {
43000
43026
  instance.forceUpdate();
43001
43027
  resolve(state);
@@ -43961,6 +43987,433 @@ function useMapAsyncData(input, transform, errorTransform) {
43961
43987
  ]);
43962
43988
  }
43963
43989
  //#endregion
43990
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_freeGlobal.js
43991
+ /** Detect free variable `global` from Node.js. */
43992
+ var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
43993
+ //#endregion
43994
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_root.js
43995
+ /** Detect free variable `self`. */
43996
+ var freeSelf = typeof self == "object" && self && self.Object === Object && self;
43997
+ /** Used as a reference to the global object. */
43998
+ var root$5 = freeGlobal || freeSelf || Function("return this")();
43999
+ //#endregion
44000
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_Symbol.js
44001
+ /** Built-in value references. */
44002
+ var Symbol$1 = root$5.Symbol;
44003
+ //#endregion
44004
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_getRawTag.js
44005
+ /** Used for built-in method references. */
44006
+ var objectProto = Object.prototype;
44007
+ /** Used to check objects for own properties. */
44008
+ var hasOwnProperty$1 = objectProto.hasOwnProperty;
44009
+ /**
44010
+ * Used to resolve the
44011
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
44012
+ * of values.
44013
+ */
44014
+ var nativeObjectToString$1 = objectProto.toString;
44015
+ /** Built-in value references. */
44016
+ var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : void 0;
44017
+ /**
44018
+ * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
44019
+ *
44020
+ * @private
44021
+ * @param {*} value The value to query.
44022
+ * @returns {string} Returns the raw `toStringTag`.
44023
+ */
44024
+ function getRawTag(value) {
44025
+ var isOwn = hasOwnProperty$1.call(value, symToStringTag$1), tag = value[symToStringTag$1];
44026
+ try {
44027
+ value[symToStringTag$1] = void 0;
44028
+ var unmasked = true;
44029
+ } catch (e) {}
44030
+ var result = nativeObjectToString$1.call(value);
44031
+ if (unmasked) if (isOwn) value[symToStringTag$1] = tag;
44032
+ else delete value[symToStringTag$1];
44033
+ return result;
44034
+ }
44035
+ //#endregion
44036
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_objectToString.js
44037
+ /**
44038
+ * Used to resolve the
44039
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
44040
+ * of values.
44041
+ */
44042
+ var nativeObjectToString = Object.prototype.toString;
44043
+ /**
44044
+ * Converts `value` to a string using `Object.prototype.toString`.
44045
+ *
44046
+ * @private
44047
+ * @param {*} value The value to convert.
44048
+ * @returns {string} Returns the converted string.
44049
+ */
44050
+ function objectToString(value) {
44051
+ return nativeObjectToString.call(value);
44052
+ }
44053
+ //#endregion
44054
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_baseGetTag.js
44055
+ /** `Object#toString` result references. */
44056
+ var nullTag = "[object Null]", undefinedTag = "[object Undefined]";
44057
+ /** Built-in value references. */
44058
+ var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : void 0;
44059
+ /**
44060
+ * The base implementation of `getTag` without fallbacks for buggy environments.
44061
+ *
44062
+ * @private
44063
+ * @param {*} value The value to query.
44064
+ * @returns {string} Returns the `toStringTag`.
44065
+ */
44066
+ function baseGetTag(value) {
44067
+ if (value == null) return value === void 0 ? undefinedTag : nullTag;
44068
+ return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value);
44069
+ }
44070
+ //#endregion
44071
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/isObjectLike.js
44072
+ /**
44073
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
44074
+ * and has a `typeof` result of "object".
44075
+ *
44076
+ * @static
44077
+ * @memberOf _
44078
+ * @since 4.0.0
44079
+ * @category Lang
44080
+ * @param {*} value The value to check.
44081
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
44082
+ * @example
44083
+ *
44084
+ * _.isObjectLike({});
44085
+ * // => true
44086
+ *
44087
+ * _.isObjectLike([1, 2, 3]);
44088
+ * // => true
44089
+ *
44090
+ * _.isObjectLike(_.noop);
44091
+ * // => false
44092
+ *
44093
+ * _.isObjectLike(null);
44094
+ * // => false
44095
+ */
44096
+ function isObjectLike(value) {
44097
+ return value != null && typeof value == "object";
44098
+ }
44099
+ //#endregion
44100
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/isSymbol.js
44101
+ /** `Object#toString` result references. */
44102
+ var symbolTag = "[object Symbol]";
44103
+ /**
44104
+ * Checks if `value` is classified as a `Symbol` primitive or object.
44105
+ *
44106
+ * @static
44107
+ * @memberOf _
44108
+ * @since 4.0.0
44109
+ * @category Lang
44110
+ * @param {*} value The value to check.
44111
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
44112
+ * @example
44113
+ *
44114
+ * _.isSymbol(Symbol.iterator);
44115
+ * // => true
44116
+ *
44117
+ * _.isSymbol('abc');
44118
+ * // => false
44119
+ */
44120
+ function isSymbol(value) {
44121
+ return typeof value == "symbol" || isObjectLike(value) && baseGetTag(value) == symbolTag;
44122
+ }
44123
+ //#endregion
44124
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_trimmedEndIndex.js
44125
+ /** Used to match a single whitespace character. */
44126
+ var reWhitespace = /\s/;
44127
+ /**
44128
+ * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
44129
+ * character of `string`.
44130
+ *
44131
+ * @private
44132
+ * @param {string} string The string to inspect.
44133
+ * @returns {number} Returns the index of the last non-whitespace character.
44134
+ */
44135
+ function trimmedEndIndex(string) {
44136
+ var index = string.length;
44137
+ while (index-- && reWhitespace.test(string.charAt(index)));
44138
+ return index;
44139
+ }
44140
+ //#endregion
44141
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_baseTrim.js
44142
+ /** Used to match leading whitespace. */
44143
+ var reTrimStart = /^\s+/;
44144
+ /**
44145
+ * The base implementation of `_.trim`.
44146
+ *
44147
+ * @private
44148
+ * @param {string} string The string to trim.
44149
+ * @returns {string} Returns the trimmed string.
44150
+ */
44151
+ function baseTrim(string) {
44152
+ return string ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, "") : string;
44153
+ }
44154
+ //#endregion
44155
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/isObject.js
44156
+ /**
44157
+ * Checks if `value` is the
44158
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
44159
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
44160
+ *
44161
+ * @static
44162
+ * @memberOf _
44163
+ * @since 0.1.0
44164
+ * @category Lang
44165
+ * @param {*} value The value to check.
44166
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
44167
+ * @example
44168
+ *
44169
+ * _.isObject({});
44170
+ * // => true
44171
+ *
44172
+ * _.isObject([1, 2, 3]);
44173
+ * // => true
44174
+ *
44175
+ * _.isObject(_.noop);
44176
+ * // => true
44177
+ *
44178
+ * _.isObject(null);
44179
+ * // => false
44180
+ */
44181
+ function isObject(value) {
44182
+ var type = typeof value;
44183
+ return value != null && (type == "object" || type == "function");
44184
+ }
44185
+ //#endregion
44186
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/toNumber.js
44187
+ /** Used as references for various `Number` constants. */
44188
+ var NAN = NaN;
44189
+ /** Used to detect bad signed hexadecimal string values. */
44190
+ var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
44191
+ /** Used to detect binary string values. */
44192
+ var reIsBinary = /^0b[01]+$/i;
44193
+ /** Used to detect octal string values. */
44194
+ var reIsOctal = /^0o[0-7]+$/i;
44195
+ /** Built-in method references without a dependency on `root`. */
44196
+ var freeParseInt = parseInt;
44197
+ /**
44198
+ * Converts `value` to a number.
44199
+ *
44200
+ * @static
44201
+ * @memberOf _
44202
+ * @since 4.0.0
44203
+ * @category Lang
44204
+ * @param {*} value The value to process.
44205
+ * @returns {number} Returns the number.
44206
+ * @example
44207
+ *
44208
+ * _.toNumber(3.2);
44209
+ * // => 3.2
44210
+ *
44211
+ * _.toNumber(Number.MIN_VALUE);
44212
+ * // => 5e-324
44213
+ *
44214
+ * _.toNumber(Infinity);
44215
+ * // => Infinity
44216
+ *
44217
+ * _.toNumber('3.2');
44218
+ * // => 3.2
44219
+ */
44220
+ function toNumber(value) {
44221
+ if (typeof value == "number") return value;
44222
+ if (isSymbol(value)) return NAN;
44223
+ if (isObject(value)) {
44224
+ var other = typeof value.valueOf == "function" ? value.valueOf() : value;
44225
+ value = isObject(other) ? other + "" : other;
44226
+ }
44227
+ if (typeof value != "string") return value === 0 ? value : +value;
44228
+ value = baseTrim(value);
44229
+ var isBinary = reIsBinary.test(value);
44230
+ return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
44231
+ }
44232
+ //#endregion
44233
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/now.js
44234
+ /**
44235
+ * Gets the timestamp of the number of milliseconds that have elapsed since
44236
+ * the Unix epoch (1 January 1970 00:00:00 UTC).
44237
+ *
44238
+ * @static
44239
+ * @memberOf _
44240
+ * @since 2.4.0
44241
+ * @category Date
44242
+ * @returns {number} Returns the timestamp.
44243
+ * @example
44244
+ *
44245
+ * _.defer(function(stamp) {
44246
+ * console.log(_.now() - stamp);
44247
+ * }, _.now());
44248
+ * // => Logs the number of milliseconds it took for the deferred invocation.
44249
+ */
44250
+ var now = function() {
44251
+ return root$5.Date.now();
44252
+ };
44253
+ //#endregion
44254
+ //#region ../../node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/debounce.js
44255
+ /** Error message constants. */
44256
+ var FUNC_ERROR_TEXT = "Expected a function";
44257
+ var nativeMax = Math.max, nativeMin = Math.min;
44258
+ /**
44259
+ * Creates a debounced function that delays invoking `func` until after `wait`
44260
+ * milliseconds have elapsed since the last time the debounced function was
44261
+ * invoked. The debounced function comes with a `cancel` method to cancel
44262
+ * delayed `func` invocations and a `flush` method to immediately invoke them.
44263
+ * Provide `options` to indicate whether `func` should be invoked on the
44264
+ * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
44265
+ * with the last arguments provided to the debounced function. Subsequent
44266
+ * calls to the debounced function return the result of the last `func`
44267
+ * invocation.
44268
+ *
44269
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
44270
+ * invoked on the trailing edge of the timeout only if the debounced function
44271
+ * is invoked more than once during the `wait` timeout.
44272
+ *
44273
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
44274
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
44275
+ *
44276
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
44277
+ * for details over the differences between `_.debounce` and `_.throttle`.
44278
+ *
44279
+ * @static
44280
+ * @memberOf _
44281
+ * @since 0.1.0
44282
+ * @category Function
44283
+ * @param {Function} func The function to debounce.
44284
+ * @param {number} [wait=0] The number of milliseconds to delay.
44285
+ * @param {Object} [options={}] The options object.
44286
+ * @param {boolean} [options.leading=false]
44287
+ * Specify invoking on the leading edge of the timeout.
44288
+ * @param {number} [options.maxWait]
44289
+ * The maximum time `func` is allowed to be delayed before it's invoked.
44290
+ * @param {boolean} [options.trailing=true]
44291
+ * Specify invoking on the trailing edge of the timeout.
44292
+ * @returns {Function} Returns the new debounced function.
44293
+ * @example
44294
+ *
44295
+ * // Avoid costly calculations while the window size is in flux.
44296
+ * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
44297
+ *
44298
+ * // Invoke `sendMail` when clicked, debouncing subsequent calls.
44299
+ * jQuery(element).on('click', _.debounce(sendMail, 300, {
44300
+ * 'leading': true,
44301
+ * 'trailing': false
44302
+ * }));
44303
+ *
44304
+ * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
44305
+ * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
44306
+ * var source = new EventSource('/stream');
44307
+ * jQuery(source).on('message', debounced);
44308
+ *
44309
+ * // Cancel the trailing debounced invocation.
44310
+ * jQuery(window).on('popstate', debounced.cancel);
44311
+ */
44312
+ function debounce$1(func, wait, options) {
44313
+ var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true;
44314
+ if (typeof func != "function") throw new TypeError(FUNC_ERROR_TEXT);
44315
+ wait = toNumber(wait) || 0;
44316
+ if (isObject(options)) {
44317
+ leading = !!options.leading;
44318
+ maxing = "maxWait" in options;
44319
+ maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
44320
+ trailing = "trailing" in options ? !!options.trailing : trailing;
44321
+ }
44322
+ function invokeFunc(time) {
44323
+ var args = lastArgs, thisArg = lastThis;
44324
+ lastArgs = lastThis = void 0;
44325
+ lastInvokeTime = time;
44326
+ result = func.apply(thisArg, args);
44327
+ return result;
44328
+ }
44329
+ function leadingEdge(time) {
44330
+ lastInvokeTime = time;
44331
+ timerId = setTimeout(timerExpired, wait);
44332
+ return leading ? invokeFunc(time) : result;
44333
+ }
44334
+ function remainingWait(time) {
44335
+ var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, timeWaiting = wait - timeSinceLastCall;
44336
+ return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
44337
+ }
44338
+ function shouldInvoke(time) {
44339
+ var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime;
44340
+ return lastCallTime === void 0 || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
44341
+ }
44342
+ function timerExpired() {
44343
+ var time = now();
44344
+ if (shouldInvoke(time)) return trailingEdge(time);
44345
+ timerId = setTimeout(timerExpired, remainingWait(time));
44346
+ }
44347
+ function trailingEdge(time) {
44348
+ timerId = void 0;
44349
+ if (trailing && lastArgs) return invokeFunc(time);
44350
+ lastArgs = lastThis = void 0;
44351
+ return result;
44352
+ }
44353
+ function cancel() {
44354
+ if (timerId !== void 0) clearTimeout(timerId);
44355
+ lastInvokeTime = 0;
44356
+ lastArgs = lastCallTime = lastThis = timerId = void 0;
44357
+ }
44358
+ function flush() {
44359
+ return timerId === void 0 ? result : trailingEdge(now());
44360
+ }
44361
+ function debounced() {
44362
+ var time = now(), isInvoking = shouldInvoke(time);
44363
+ lastArgs = arguments;
44364
+ lastThis = this;
44365
+ lastCallTime = time;
44366
+ if (isInvoking) {
44367
+ if (timerId === void 0) return leadingEdge(lastCallTime);
44368
+ if (maxing) {
44369
+ clearTimeout(timerId);
44370
+ timerId = setTimeout(timerExpired, wait);
44371
+ return invokeFunc(lastCallTime);
44372
+ }
44373
+ }
44374
+ if (timerId === void 0) timerId = setTimeout(timerExpired, wait);
44375
+ return result;
44376
+ }
44377
+ debounced.cancel = cancel;
44378
+ debounced.flush = flush;
44379
+ return debounced;
44380
+ }
44381
+ //#endregion
44382
+ //#region ../../packages/react/src/hooks/useDebouncedCallback.ts
44383
+ /**
44384
+ * Creates a debounced version of a callback that always calls the latest version.
44385
+ *
44386
+ * Unlike `useMemo(() => debounce(fn), [])`, this hook ensures the debounced function
44387
+ * always invokes the most recent callback, eliminating the need for refs to access
44388
+ * current state values.
44389
+ *
44390
+ * @param callback - The function to debounce. Can close over current state/props.
44391
+ * @param delay - Debounce delay in milliseconds
44392
+ * @returns A stable debounced function that calls the latest callback
44393
+ *
44394
+ * @example
44395
+ * const debouncedSave = useDebouncedCallback(() => {
44396
+ * // Access current state directly - no refs needed
44397
+ * if (!workingCase) return;
44398
+ * mutation.mutate({ data: workingCase });
44399
+ * }, 600);
44400
+ */
44401
+ function useDebouncedCallback(callback, delay) {
44402
+ const callbackRef = useRef(callback);
44403
+ useLayoutEffect(() => {
44404
+ callbackRef.current = callback;
44405
+ });
44406
+ const debouncedFn = useMemo(() => debounce$1((...args) => {
44407
+ callbackRef.current(...args);
44408
+ }, delay), [delay]);
44409
+ useEffect(() => {
44410
+ return () => {
44411
+ debouncedFn.cancel();
44412
+ };
44413
+ }, [debouncedFn]);
44414
+ return debouncedFn;
44415
+ }
44416
+ //#endregion
43964
44417
  //#region ../../packages/react/src/hooks/useAsyncDataFromQuery.ts
43965
44418
  /**
43966
44419
  * Custom hook that retrieves data using `useQuery` from the `@tanstack/react-query`
@@ -44288,7 +44741,7 @@ var useCollapsibleIds = (key) => {
44288
44741
  };
44289
44742
  //#endregion
44290
44743
  //#region ../../packages/react/src/hooks/useStatefulScrollPosition.ts
44291
- var log$6 = createLogger("scrolling");
44744
+ var log$7 = createLogger("scrolling");
44292
44745
  function useStatefulScrollPosition(elementRef, elementKey, delay = 1e3, scrollable = true) {
44293
44746
  const [scrollPosition, setScrollPosition] = useProperty("scrollPosition", elementKey);
44294
44747
  const scrollPositionRef = useRef(scrollPosition);
@@ -44297,10 +44750,10 @@ function useStatefulScrollPosition(elementRef, elementKey, delay = 1e3, scrollab
44297
44750
  }, [scrollPosition]);
44298
44751
  const handleScrollInner = useCallback((e) => {
44299
44752
  const position = e.target.scrollTop;
44300
- log$6.debug(`Storing scroll position`, elementKey, position);
44753
+ log$7.debug(`Storing scroll position`, elementKey, position);
44301
44754
  setScrollPosition(position);
44302
44755
  }, [elementKey, setScrollPosition]);
44303
- const handleScroll = useMemo(() => debounce$2(handleScrollInner, delay), [handleScrollInner, delay]);
44756
+ const handleScroll = useMemo(() => debounce$3(handleScrollInner, delay), [handleScrollInner, delay]);
44304
44757
  const restoreScrollPosition = useCallback(() => {
44305
44758
  const element = elementRef.current;
44306
44759
  const savedPosition = scrollPositionRef.current;
@@ -44314,15 +44767,15 @@ function useStatefulScrollPosition(elementRef, elementKey, delay = 1e3, scrollab
44314
44767
  useEffect(() => {
44315
44768
  const element = elementRef.current;
44316
44769
  if (!element || !scrollable) return;
44317
- log$6.debug(`Restore Scroll Hook`, elementKey);
44770
+ log$7.debug(`Restore Scroll Hook`, elementKey);
44318
44771
  const savedPosition = scrollPositionRef.current;
44319
44772
  if (savedPosition !== void 0) {
44320
- log$6.debug(`Restoring scroll position`, savedPosition);
44773
+ log$7.debug(`Restoring scroll position`, savedPosition);
44321
44774
  const tryRestoreScroll = () => {
44322
44775
  if (element.scrollHeight > element.clientHeight) {
44323
44776
  if (element.scrollTop !== savedPosition) {
44324
44777
  element.scrollTop = savedPosition;
44325
- log$6.debug(`Scroll position restored to ${savedPosition}`);
44778
+ log$7.debug(`Scroll position restored to ${savedPosition}`);
44326
44779
  }
44327
44780
  return true;
44328
44781
  }
@@ -44333,7 +44786,7 @@ function useStatefulScrollPosition(elementRef, elementKey, delay = 1e3, scrollab
44333
44786
  const maxAttempts = 20;
44334
44787
  const pollForRender = () => {
44335
44788
  if (tryRestoreScroll() || attempts >= maxAttempts) {
44336
- if (attempts >= maxAttempts) log$6.debug(`Failed to restore scroll after ${maxAttempts} attempts`);
44789
+ if (attempts >= maxAttempts) log$7.debug(`Failed to restore scroll after ${maxAttempts} attempts`);
44337
44790
  return;
44338
44791
  }
44339
44792
  attempts++;
@@ -44343,10 +44796,10 @@ function useStatefulScrollPosition(elementRef, elementKey, delay = 1e3, scrollab
44343
44796
  }
44344
44797
  }
44345
44798
  if (element.addEventListener) element.addEventListener("scroll", handleScroll);
44346
- else log$6.warn("Element has no way to add event listener", element);
44799
+ else log$7.warn("Element has no way to add event listener", element);
44347
44800
  return () => {
44348
44801
  if (element.removeEventListener) element.removeEventListener("scroll", handleScroll);
44349
- else log$6.warn("Element has no way to remove event listener", element);
44802
+ else log$7.warn("Element has no way to remove event listener", element);
44350
44803
  };
44351
44804
  }, [
44352
44805
  elementKey,
@@ -44543,7 +44996,7 @@ function useListKeyboardNavigation({ listHandle, scrollRef, itemCount }) {
44543
44996
  const isUp = event.key === "ArrowUp" && hasModifier || event.key === "Home" && hasModifier;
44544
44997
  const isDown = event.key === "ArrowDown" && hasModifier || event.key === "End" && hasModifier;
44545
44998
  if (!isUp && !isDown) return;
44546
- if (isEditableTarget(document.activeElement)) return;
44999
+ if (isEditableTarget(deepActiveElement())) return;
44547
45000
  event.preventDefault();
44548
45001
  event.stopImmediatePropagation();
44549
45002
  const handle = listHandle.current;
@@ -44878,6 +45331,56 @@ var ToolDropdownButton = forwardRef(({ label, icon, className, items, dropdownAl
44878
45331
  })] }), document.body)] });
44879
45332
  });
44880
45333
  ToolDropdownButton.displayName = "ToolDropdownButton";
45334
+ var NavPills_module_default = {
45335
+ visible: "_visible_tm52u_1",
45336
+ hidden: "_hidden_tm52u_5",
45337
+ pills: "_pills_tm52u_9",
45338
+ pill: "_pill_tm52u_9"
45339
+ };
45340
+ //#endregion
45341
+ //#region ../../packages/react/src/components/NavPills.tsx
45342
+ var NavPills = ({ id, children }) => {
45343
+ const [activeItem, setActiveItem] = useProperty(id, "active", { defaultValue: children ? children?.[0]?.props["title"] : "" });
45344
+ if (!activeItem || !children) return;
45345
+ const navPills = children.map((nav, idx) => {
45346
+ return /* @__PURE__ */ jsx(NavPill, {
45347
+ title: typeof nav === "object" ? nav["props"]?.title || `Tab ${idx}` : `Tab ${idx}`,
45348
+ activeItem,
45349
+ setActiveItem
45350
+ }, `nav-pill-contents-${idx}`);
45351
+ });
45352
+ const navBodies = children.map((child, idx) => {
45353
+ return /* @__PURE__ */ jsx("div", {
45354
+ className: child["props"]?.title === activeItem ? NavPills_module_default.visible : NavPills_module_default.hidden,
45355
+ children: child
45356
+ }, `nav-pill-container-${idx}`);
45357
+ });
45358
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("ul", {
45359
+ className: clsx("nav", "nav-pills", NavPills_module_default.pills),
45360
+ role: "tablist",
45361
+ "aria-orientation": "horizontal",
45362
+ children: navPills
45363
+ }), navBodies] });
45364
+ };
45365
+ var NavPill = ({ title, activeItem, setActiveItem, children }) => {
45366
+ const active = activeItem === title;
45367
+ const handleClick = useCallback((e) => {
45368
+ const target = e.currentTarget.dataset.target;
45369
+ if (target) setActiveItem(target);
45370
+ }, [setActiveItem]);
45371
+ return /* @__PURE__ */ jsxs("li", {
45372
+ className: "nav-item",
45373
+ children: [/* @__PURE__ */ jsx("button", {
45374
+ type: "button",
45375
+ role: "tab",
45376
+ "aria-selected": active,
45377
+ className: clsx("nav-link", "text-style-label", active ? "active " : "", NavPills_module_default.pill),
45378
+ "data-target": title,
45379
+ onClick: handleClick,
45380
+ children: title
45381
+ }), children]
45382
+ });
45383
+ };
44881
45384
  var TabSet_module_default = {
44882
45385
  tabs: "_tabs_1792l_1",
44883
45386
  tabContents: "_tabContents_1792l_5",
@@ -45324,6 +45827,503 @@ var useComponentNavigation = () => {
45324
45827
  if (!navigation) throw new Error("useComponentNavigation must be used within a ComponentNavigationProvider");
45325
45828
  return navigation;
45326
45829
  };
45830
+ //#endregion
45831
+ //#region ../../packages/react/src/components/findBandDom.ts
45832
+ /**
45833
+ * Finds the nearest scrollable ancestor with enough overflow to benefit from
45834
+ * programmatic scrolling.
45835
+ */
45836
+ function findScrollableParent(element, options) {
45837
+ const minBuffer = options?.minScrollBuffer ?? 100;
45838
+ let current = element instanceof HTMLElement ? element : element?.parentElement;
45839
+ while (current && current !== document.body) {
45840
+ const style = getComputedStyle(current);
45841
+ if ((style.overflowY === "auto" || style.overflowY === "scroll") && current.scrollHeight > current.clientHeight + minBuffer) return current;
45842
+ current = current.parentElement;
45843
+ }
45844
+ return null;
45845
+ }
45846
+ /**
45847
+ * Centers the selected text range rather than its containing element, which
45848
+ * keeps matches in large elements such as code blocks correctly positioned.
45849
+ */
45850
+ function scrollRangeToCenter(range, options) {
45851
+ const { behavior = "auto", fallbackToScrollIntoView = true } = options ?? {};
45852
+ const rects = range.getClientRects();
45853
+ if (rects.length === 0) return;
45854
+ const selectionRect = rects[0];
45855
+ if (selectionRect === void 0) return;
45856
+ const scrollableParent = findScrollableParent(range.startContainer.parentElement);
45857
+ if (scrollableParent) {
45858
+ const parentRect = scrollableParent.getBoundingClientRect();
45859
+ const targetScrollTop = selectionRect.top - parentRect.top + scrollableParent.scrollTop - scrollableParent.clientHeight / 2;
45860
+ scrollableParent.scrollTo({
45861
+ top: Math.max(0, targetScrollTop),
45862
+ behavior
45863
+ });
45864
+ } else if (fallbackToScrollIntoView) range.startContainer.parentElement?.scrollIntoView({
45865
+ behavior,
45866
+ block: "center"
45867
+ });
45868
+ }
45869
+ //#endregion
45870
+ //#region ../../packages/react/src/components/FindBandUI.tsx
45871
+ var FindBandUI = ({ onClose, onNext, onPrevious, onKeyDown, onChange, onBeforeInput, value, matchCount, matchIndex, noResults = false, disableNav, inputRef: externalRef }) => {
45872
+ const icons = useComponentIcons();
45873
+ const internalRef = useRef(null);
45874
+ const inputRef = externalRef ?? internalRef;
45875
+ const inputProps = {
45876
+ type: "text",
45877
+ placeholder: "Find",
45878
+ onKeyDown,
45879
+ onBeforeInput,
45880
+ onChange
45881
+ };
45882
+ if (value !== void 0) inputProps.value = value;
45883
+ const hasCount = matchCount !== void 0 && matchIndex !== void 0;
45884
+ const showStatus = noResults || hasCount && matchCount > 0;
45885
+ const statusText = !noResults && hasCount && matchCount > 0 ? `${matchIndex + 1} of ${matchCount}` : "No results";
45886
+ return /* @__PURE__ */ jsxs("div", {
45887
+ "data-unsearchable": "true",
45888
+ className: clsx("findBand"),
45889
+ children: [
45890
+ /* @__PURE__ */ jsx("input", {
45891
+ ref: inputRef,
45892
+ ...inputProps
45893
+ }),
45894
+ /* @__PURE__ */ jsx("span", {
45895
+ className: clsx("findBand-match-count", noResults && "findBand-no-results"),
45896
+ style: { visibility: showStatus ? "visible" : "hidden" },
45897
+ children: statusText
45898
+ }),
45899
+ /* @__PURE__ */ jsx("button", {
45900
+ type: "button",
45901
+ title: "Previous match",
45902
+ className: "btn prev",
45903
+ onClick: onPrevious,
45904
+ disabled: disableNav,
45905
+ children: /* @__PURE__ */ jsx("i", { className: icons.arrowUp })
45906
+ }),
45907
+ /* @__PURE__ */ jsx("button", {
45908
+ type: "button",
45909
+ title: "Next match",
45910
+ className: "btn next",
45911
+ onClick: onNext,
45912
+ disabled: disableNav,
45913
+ children: /* @__PURE__ */ jsx("i", { className: icons.arrowDown })
45914
+ }),
45915
+ /* @__PURE__ */ jsx("button", {
45916
+ type: "button",
45917
+ title: "Close",
45918
+ className: "btn close",
45919
+ onClick: onClose,
45920
+ children: /* @__PURE__ */ jsx("i", { className: icons.close })
45921
+ })
45922
+ ]
45923
+ });
45924
+ };
45925
+ //#endregion
45926
+ //#region ../../packages/react/src/components/findShortcuts.ts
45927
+ function isFindShortcut(e) {
45928
+ return (e.ctrlKey || e.metaKey) && !e.shiftKey && e.key.toLowerCase() === "f";
45929
+ }
45930
+ function isFindNextShortcut(e) {
45931
+ return (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "g";
45932
+ }
45933
+ //#endregion
45934
+ //#region ../../packages/react/src/components/FindBand.tsx
45935
+ var findConfig = {
45936
+ caseSensitive: false,
45937
+ wrapAround: false,
45938
+ wholeWord: false,
45939
+ searchInFrames: false,
45940
+ showDialog: false
45941
+ };
45942
+ var FindBand = ({ onClose, debounceMs = 100 }) => {
45943
+ const searchBoxRef = useRef(null);
45944
+ const { extendedFindTerm, countAllMatches, getMatchCountersVersion } = useExtendedFind();
45945
+ const setFindTarget = useFindTargetSetter();
45946
+ const lastFoundItem = useRef(null);
45947
+ const currentSearchTerm = useRef("");
45948
+ const needsCursorRestoreRef = useRef(false);
45949
+ const scrollTimeoutRef = useRef(null);
45950
+ const focusTimeoutRef = useRef(null);
45951
+ const searchIdRef = useRef(0);
45952
+ const cachedCount = useRef({
45953
+ term: "",
45954
+ version: -1,
45955
+ count: 0
45956
+ });
45957
+ const lastNoResult = useRef(null);
45958
+ const [matchCount, setMatchCount] = useState(null);
45959
+ const [currentMatchIndex, setCurrentMatchIndex] = useState(0);
45960
+ const [noResults, setNoResults] = useState(false);
45961
+ const handleSearch = useCallback(async (back = false, skipKnownMiss = false) => {
45962
+ const thisSearchId = ++searchIdRef.current;
45963
+ const searchTerm = searchBoxRef.current?.value ?? "";
45964
+ if (!searchTerm) {
45965
+ setMatchCount(null);
45966
+ setCurrentMatchIndex(0);
45967
+ setNoResults(false);
45968
+ setFindTarget(null);
45969
+ return;
45970
+ }
45971
+ const countersVersion = getMatchCountersVersion();
45972
+ if (skipKnownMiss && lastNoResult.current && lastNoResult.current.version === countersVersion && searchTerm.startsWith(lastNoResult.current.term)) {
45973
+ setMatchCount(null);
45974
+ setNoResults(true);
45975
+ return;
45976
+ }
45977
+ const termChanged = currentSearchTerm.current !== searchTerm;
45978
+ if (termChanged) {
45979
+ lastFoundItem.current = null;
45980
+ currentSearchTerm.current = searchTerm;
45981
+ setCurrentMatchIndex(0);
45982
+ }
45983
+ let total;
45984
+ if (cachedCount.current.term === searchTerm && cachedCount.current.version === countersVersion) total = cachedCount.current.count;
45985
+ else {
45986
+ total = countAllMatches(searchTerm);
45987
+ cachedCount.current = {
45988
+ term: searchTerm,
45989
+ version: countersVersion,
45990
+ count: total
45991
+ };
45992
+ }
45993
+ setMatchCount(total > 0 ? total : null);
45994
+ const focusedElement = document.activeElement;
45995
+ const selection = window.getSelection();
45996
+ let savedRange = null;
45997
+ if (selection && selection.rangeCount > 0) savedRange = selection.getRangeAt(0).cloneRange();
45998
+ const savedScrollParent = savedRange ? findScrollableParent(savedRange.startContainer.parentElement) : null;
45999
+ const savedScrollTop = savedScrollParent?.scrollTop ?? 0;
46000
+ const result = await findExtendedInDOM(searchTerm, back, lastFoundItem.current, extendedFindTerm);
46001
+ if (searchIdRef.current !== thisSearchId) return;
46002
+ setNoResults(!result);
46003
+ lastNoResult.current = result ? null : {
46004
+ term: searchTerm,
46005
+ version: countersVersion
46006
+ };
46007
+ if (!result && savedRange) {
46008
+ const sel = window.getSelection();
46009
+ if (sel) {
46010
+ sel.removeAllRanges();
46011
+ sel.addRange(savedRange);
46012
+ }
46013
+ if (savedScrollParent) savedScrollParent.scrollTop = savedScrollTop;
46014
+ }
46015
+ if (result) {
46016
+ const selection = window.getSelection();
46017
+ if (selection && selection.rangeCount > 0) {
46018
+ const range = selection.getRangeAt(0);
46019
+ const parentElement = range.startContainer.parentElement || range.commonAncestorContainer;
46020
+ const isNewMatch = !isLastFoundItem(range, lastFoundItem.current);
46021
+ lastFoundItem.current = {
46022
+ text: range.toString(),
46023
+ offset: range.startOffset,
46024
+ parentElement
46025
+ };
46026
+ if (termChanged) setFindTarget({
46027
+ term: searchTerm,
46028
+ eventId: ""
46029
+ });
46030
+ if (isNewMatch) setCurrentMatchIndex((prev) => {
46031
+ if (back) return prev <= 1 ? total : prev - 1;
46032
+ else return prev >= total ? 1 : prev + 1;
46033
+ });
46034
+ if (scrollTimeoutRef.current !== null) window.clearTimeout(scrollTimeoutRef.current);
46035
+ scrollTimeoutRef.current = window.setTimeout(() => {
46036
+ scrollRangeToCenter(range);
46037
+ }, 100);
46038
+ }
46039
+ }
46040
+ focusedElement?.focus();
46041
+ }, [
46042
+ setFindTarget,
46043
+ extendedFindTerm,
46044
+ countAllMatches,
46045
+ getMatchCountersVersion
46046
+ ]);
46047
+ useEffect(() => {
46048
+ focusTimeoutRef.current = window.setTimeout(() => {
46049
+ searchBoxRef.current?.focus();
46050
+ searchBoxRef.current?.select();
46051
+ }, 10);
46052
+ const focusTimeout = focusTimeoutRef.current;
46053
+ return () => {
46054
+ if (scrollTimeoutRef.current !== null) window.clearTimeout(scrollTimeoutRef.current);
46055
+ if (focusTimeout !== null) window.clearTimeout(focusTimeout);
46056
+ setFindTarget(null);
46057
+ };
46058
+ }, [setFindTarget]);
46059
+ const handleKeyDown = useCallback((e) => {
46060
+ if (e.key === "Escape") onClose();
46061
+ else if (e.key === "Enter") handleSearch(e.shiftKey);
46062
+ else if (isFindNextShortcut(e)) {
46063
+ e.preventDefault();
46064
+ handleSearch(e.shiftKey);
46065
+ } else if (isFindShortcut(e)) {
46066
+ searchBoxRef.current?.focus();
46067
+ searchBoxRef.current?.select();
46068
+ }
46069
+ }, [onClose, handleSearch]);
46070
+ const findPrevious = useCallback(() => {
46071
+ handleSearch(true);
46072
+ }, [handleSearch]);
46073
+ const findNext = useCallback(() => {
46074
+ handleSearch(false);
46075
+ }, [handleSearch]);
46076
+ const restoreCursor = useCallback(() => {
46077
+ if (!needsCursorRestoreRef.current) return;
46078
+ needsCursorRestoreRef.current = false;
46079
+ const input = searchBoxRef.current;
46080
+ if (input) {
46081
+ const len = input.value.length;
46082
+ input.setSelectionRange(len, len);
46083
+ }
46084
+ }, []);
46085
+ const handleInputChange = useDebouncedCallback(useCallback(async () => {
46086
+ if (!searchBoxRef.current) return;
46087
+ await handleSearch(false, true);
46088
+ needsCursorRestoreRef.current = true;
46089
+ }, [handleSearch]), debounceMs);
46090
+ const restoreCursorIfNeeded = useCallback(() => {
46091
+ const input = searchBoxRef.current;
46092
+ if (!input) return;
46093
+ if (input.selectionStart === 0 && input.selectionEnd === 0 && input.value.length > 0) restoreCursor();
46094
+ else needsCursorRestoreRef.current = false;
46095
+ }, [restoreCursor]);
46096
+ const handleBeforeInput = useCallback(() => {
46097
+ restoreCursorIfNeeded();
46098
+ }, [restoreCursorIfNeeded]);
46099
+ useEffect(() => {
46100
+ const handleGlobalKeyDown = (e) => {
46101
+ if (e.key === "F3") {
46102
+ e.preventDefault();
46103
+ handleSearch(e.shiftKey);
46104
+ return;
46105
+ }
46106
+ if (isFindShortcut(e)) {
46107
+ e.preventDefault();
46108
+ e.stopPropagation();
46109
+ searchBoxRef.current?.focus();
46110
+ searchBoxRef.current?.select();
46111
+ return;
46112
+ }
46113
+ if (isFindNextShortcut(e)) {
46114
+ e.preventDefault();
46115
+ e.stopPropagation();
46116
+ handleSearch(e.shiftKey);
46117
+ return;
46118
+ }
46119
+ if (e.ctrlKey || e.metaKey || e.altKey) return;
46120
+ if (e.key.length !== 1 && e.key !== "Backspace" && e.key !== "Delete") return;
46121
+ const input = searchBoxRef.current;
46122
+ if (!input) return;
46123
+ if (document.activeElement !== input) {
46124
+ if (isEditableTarget(deepActiveElement())) return;
46125
+ restoreCursor();
46126
+ input.focus();
46127
+ } else restoreCursorIfNeeded();
46128
+ };
46129
+ document.addEventListener("keydown", handleGlobalKeyDown, true);
46130
+ return () => {
46131
+ document.removeEventListener("keydown", handleGlobalKeyDown, true);
46132
+ };
46133
+ }, [
46134
+ handleSearch,
46135
+ restoreCursor,
46136
+ restoreCursorIfNeeded
46137
+ ]);
46138
+ return /* @__PURE__ */ jsx(FindBandUI, {
46139
+ inputRef: searchBoxRef,
46140
+ onClose,
46141
+ onNext: findNext,
46142
+ onPrevious: findPrevious,
46143
+ onKeyDown: handleKeyDown,
46144
+ onBeforeInput: handleBeforeInput,
46145
+ onChange: handleInputChange,
46146
+ noResults,
46147
+ matchCount: matchCount ?? void 0,
46148
+ matchIndex: matchCount !== null && matchCount > 0 ? currentMatchIndex - 1 : void 0
46149
+ });
46150
+ };
46151
+ function windowFind(searchTerm, back) {
46152
+ return window.find?.(searchTerm, findConfig.caseSensitive, back, findConfig.wrapAround, findConfig.wholeWord, findConfig.searchInFrames, findConfig.showDialog) ?? false;
46153
+ }
46154
+ function positionSelectionForWrap(back) {
46155
+ if (!back) return;
46156
+ const sel = window.getSelection();
46157
+ if (sel) {
46158
+ const range = document.createRange();
46159
+ range.selectNodeContents(document.body);
46160
+ range.collapse(false);
46161
+ sel.removeAllRanges();
46162
+ sel.addRange(range);
46163
+ }
46164
+ }
46165
+ async function findExtendedInDOM(searchTerm, back, lastFoundItem, extendedFindTerm) {
46166
+ let result = false;
46167
+ let hasTriedExtendedSearch = false;
46168
+ let extendedSearchSucceeded = false;
46169
+ const maxAttempts = 25;
46170
+ for (let attempts = 0; attempts < maxAttempts; attempts++) {
46171
+ result = windowFind(searchTerm, back);
46172
+ if (result) {
46173
+ const selection = window.getSelection();
46174
+ if (selection && selection.rangeCount > 0) {
46175
+ const range = selection.getRangeAt(0);
46176
+ const isUnsearchable = inUnsearchableElement(range);
46177
+ const isSameAsLast = isLastFoundItem(range, lastFoundItem);
46178
+ if (!isUnsearchable && !isSameAsLast) break;
46179
+ if (isSameAsLast) {
46180
+ if (!hasTriedExtendedSearch) {
46181
+ hasTriedExtendedSearch = true;
46182
+ window.getSelection()?.removeAllRanges();
46183
+ if (await extendedFindTerm(searchTerm, back ? "backward" : "forward")) {
46184
+ extendedSearchSucceeded = true;
46185
+ await waitForTextInDOM(searchTerm);
46186
+ continue;
46187
+ }
46188
+ }
46189
+ if (extendedSearchSucceeded) {
46190
+ const sel = window.getSelection();
46191
+ if (sel?.rangeCount) sel.getRangeAt(0).collapse(!back);
46192
+ } else {
46193
+ window.getSelection()?.removeAllRanges();
46194
+ positionSelectionForWrap(back);
46195
+ }
46196
+ result = windowFind(searchTerm, back);
46197
+ if (result) {
46198
+ const sel = window.getSelection();
46199
+ if (sel && sel.rangeCount > 0) {
46200
+ if (inUnsearchableElement(sel.getRangeAt(0))) continue;
46201
+ }
46202
+ }
46203
+ break;
46204
+ }
46205
+ }
46206
+ } else if (!hasTriedExtendedSearch) {
46207
+ hasTriedExtendedSearch = true;
46208
+ window.getSelection()?.removeAllRanges();
46209
+ if (await extendedFindTerm(searchTerm, back ? "backward" : "forward")) {
46210
+ extendedSearchSucceeded = true;
46211
+ await waitForTextInDOM(searchTerm);
46212
+ continue;
46213
+ }
46214
+ positionSelectionForWrap(back);
46215
+ result = windowFind(searchTerm, back);
46216
+ if (result) {
46217
+ const sel = window.getSelection();
46218
+ if (sel && sel.rangeCount > 0) {
46219
+ if (inUnsearchableElement(sel.getRangeAt(0))) continue;
46220
+ }
46221
+ }
46222
+ break;
46223
+ } else break;
46224
+ }
46225
+ if (result) {
46226
+ const sel = window.getSelection();
46227
+ if (sel?.rangeCount && inUnsearchableElement(sel.getRangeAt(0))) {
46228
+ sel.removeAllRanges();
46229
+ result = false;
46230
+ }
46231
+ }
46232
+ return result;
46233
+ }
46234
+ function isLastFoundItem(range, lastFoundItem) {
46235
+ if (!lastFoundItem) return false;
46236
+ const currentText = range.toString();
46237
+ const currentOffset = range.startOffset;
46238
+ const currentParentElement = range.startContainer.parentElement || range.commonAncestorContainer;
46239
+ return currentText === lastFoundItem.text && currentOffset === lastFoundItem.offset && currentParentElement === lastFoundItem.parentElement;
46240
+ }
46241
+ function inUnsearchableElement(range) {
46242
+ let element = selectionParentElement(range);
46243
+ let isUnsearchable = false;
46244
+ while (element) {
46245
+ if (element.hasAttribute("data-unsearchable") || getComputedStyle(element).userSelect === "none") {
46246
+ isUnsearchable = true;
46247
+ break;
46248
+ }
46249
+ element = element.parentElement;
46250
+ }
46251
+ return isUnsearchable;
46252
+ }
46253
+ function selectionParentElement(range) {
46254
+ let element = null;
46255
+ if (range.startContainer.nodeType === Node.ELEMENT_NODE) element = range.startContainer;
46256
+ else element = range.startContainer.parentElement;
46257
+ if (!element && range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE) element = range.commonAncestorContainer;
46258
+ else if (!element && range.commonAncestorContainer.parentElement) element = range.commonAncestorContainer.parentElement;
46259
+ return element;
46260
+ }
46261
+ /**
46262
+ * Polls until the search term appears in a searchable (non-unsearchable) DOM
46263
+ * text node. After Virtuoso scrolls a virtual list item into view, the
46264
+ * onContentReady callback may fire before the content is actually rendered,
46265
+ * especially for large scroll distances. This ensures we wait for the text
46266
+ * to be present before calling window.find().
46267
+ */
46268
+ function waitForTextInDOM(searchTerm, timeoutMs = 2e3) {
46269
+ const lowerTerm = searchTerm.toLowerCase();
46270
+ const isTextInSearchableDOM = () => {
46271
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, { acceptNode: (node) => {
46272
+ let el = node.parentElement;
46273
+ while (el) {
46274
+ if (el.hasAttribute("data-unsearchable")) return NodeFilter.FILTER_REJECT;
46275
+ el = el.parentElement;
46276
+ }
46277
+ return NodeFilter.FILTER_ACCEPT;
46278
+ } });
46279
+ while (walker.nextNode()) if (walker.currentNode.textContent?.toLowerCase().includes(lowerTerm)) return true;
46280
+ return false;
46281
+ };
46282
+ return new Promise((resolve) => {
46283
+ const interval = 50;
46284
+ let elapsed = 0;
46285
+ const check = () => {
46286
+ if (isTextInSearchableDOM()) {
46287
+ resolve(true);
46288
+ return;
46289
+ }
46290
+ elapsed += interval;
46291
+ if (elapsed >= timeoutMs) {
46292
+ resolve(false);
46293
+ return;
46294
+ }
46295
+ setTimeout(check, interval);
46296
+ };
46297
+ check();
46298
+ });
46299
+ }
46300
+ //#endregion
46301
+ //#region ../../packages/react/src/components/useFindBandShortcut.ts
46302
+ /**
46303
+ * Global Ctrl/Cmd+F shortcut companion to `FindBand`: opens the band
46304
+ * (blocking the browser's own find dialog) and optionally closes it on
46305
+ * Escape. Pass stable callbacks — they are effect dependencies.
46306
+ */
46307
+ function useFindBandShortcut(onOpen, options) {
46308
+ const { onClose, isOpen = false, enabled = true } = options ?? {};
46309
+ useEffect(() => {
46310
+ if (!enabled) return;
46311
+ const handleKeyDown = (e) => {
46312
+ if (isFindShortcut(e)) {
46313
+ e.preventDefault();
46314
+ e.stopPropagation();
46315
+ onOpen();
46316
+ } else if (e.key === "Escape" && isOpen && onClose) onClose();
46317
+ };
46318
+ document.addEventListener("keydown", handleKeyDown, true);
46319
+ return () => document.removeEventListener("keydown", handleKeyDown, true);
46320
+ }, [
46321
+ enabled,
46322
+ onOpen,
46323
+ onClose,
46324
+ isOpen
46325
+ ]);
46326
+ }
45327
46327
  var MarkdownDivWithReferences_module_default = { cite: "_cite_1t1bm_1" };
45328
46328
  //#endregion
45329
46329
  //#region ../../packages/react/src/components/MarkdownDivWithReferences.tsx
@@ -45645,6 +46645,165 @@ var TreeNode$2 = memo(({ name, value, defaultExpanded = false }) => {
45645
46645
  });
45646
46646
  TreeNode$2.displayName = "TreeNode";
45647
46647
  //#endregion
46648
+ //#region ../../packages/inspect-common/src/utils/expandEvents.ts
46649
+ /**
46650
+ * Expand range-encoded refs against a pool.
46651
+ * Each ref is [start, endExclusive] → pool.slice(start, endExclusive).
46652
+ */
46653
+ function expandRefs$1(refs, pool) {
46654
+ return refs.flatMap(([start, end]) => pool.slice(start, end));
46655
+ }
46656
+ function isModelEvent(event) {
46657
+ return event.event === "model";
46658
+ }
46659
+ /**
46660
+ * Expand condensed ModelEvent input/call refs back to inline data.
46661
+ *
46662
+ * Pure function — returns a new array; input is not mutated.
46663
+ * Non-ModelEvents and ModelEvents without refs pass through unchanged.
46664
+ */
46665
+ function expandEvents(events, eventsData) {
46666
+ if (!eventsData) return events;
46667
+ const { messages, calls } = eventsData;
46668
+ const hasMessages = messages.length > 0;
46669
+ const hasCalls = calls.length > 0;
46670
+ if (!hasMessages && !hasCalls) return events;
46671
+ return events.map((event) => {
46672
+ if (!isModelEvent(event)) return event;
46673
+ let changed = false;
46674
+ let input = event.input;
46675
+ let call = event.call;
46676
+ if (event.input_refs != null && hasMessages) {
46677
+ input = expandRefs$1(event.input_refs, messages);
46678
+ changed = true;
46679
+ }
46680
+ if (call?.call_refs != null && hasCalls) {
46681
+ const key = call.call_key ?? "messages";
46682
+ const expandedMsgs = expandRefs$1(call.call_refs, calls);
46683
+ call = {
46684
+ ...call,
46685
+ request: {
46686
+ ...call.request,
46687
+ [key]: expandedMsgs
46688
+ },
46689
+ call_refs: null,
46690
+ call_key: null
46691
+ };
46692
+ changed = true;
46693
+ }
46694
+ return changed ? {
46695
+ ...event,
46696
+ input,
46697
+ input_refs: null,
46698
+ call
46699
+ } : event;
46700
+ });
46701
+ }
46702
+ //#endregion
46703
+ //#region ../../packages/inspect-common/src/utils/inputString.ts
46704
+ /**
46705
+ * Gets a string for a sample input.
46706
+ */
46707
+ var inputString = (input) => {
46708
+ if (typeof input === "string") return [input];
46709
+ else return input.map((inp) => {
46710
+ if (typeof inp === "string") return inp;
46711
+ else {
46712
+ const content = inp.content;
46713
+ if (typeof content === "string") return content;
46714
+ else return content.map((con) => {
46715
+ if (con.type === "text") return con.text;
46716
+ else return "";
46717
+ }).join("\n");
46718
+ }
46719
+ });
46720
+ };
46721
+ //#endregion
46722
+ //#region ../../packages/inspect-common/src/utils/modelFallbacks.ts
46723
+ /**
46724
+ * Total generate calls served via fallback across a sample's rollup.
46725
+ */
46726
+ var totalModelFallbacks = (fallbacks) => (fallbacks ?? []).reduce((sum, f) => sum + (f.count ?? 1), 0);
46727
+ /**
46728
+ * One "requested → served (×N)" line per fallback rollup entry.
46729
+ */
46730
+ var modelFallbackLines = (fallbacks) => (fallbacks ?? []).map((f) => `${f.model} → ${f.fallback_model}${(f.count ?? 1) > 1 ? ` (×${f.count})` : ""}`);
46731
+ //#endregion
46732
+ //#region src/client/utils/derive.ts
46733
+ var deriveLogFields = (header) => {
46734
+ let total_tokens;
46735
+ if (header.stats?.model_usage) {
46736
+ total_tokens = 0;
46737
+ for (const usage of Object.values(header.stats.model_usage)) total_tokens += usage.total_tokens;
46738
+ }
46739
+ let duration;
46740
+ if (header.stats?.started_at && header.stats?.completed_at) {
46741
+ const start = new Date(header.stats.started_at).getTime();
46742
+ const end = new Date(header.stats.completed_at).getTime();
46743
+ if (start && end && end > start) duration = (end - start) / 1e3;
46744
+ }
46745
+ const taskArgsSource = header.eval.task_args_passed ?? header.eval.task_args;
46746
+ let task_args;
46747
+ if (taskArgsSource) {
46748
+ const entries = Object.entries(taskArgsSource);
46749
+ if (entries.length > 0) task_args = entries.map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
46750
+ }
46751
+ let percent_completed;
46752
+ const total = header.results?.total_samples;
46753
+ const completed = header.results?.completed_samples;
46754
+ if (total && total > 0 && completed !== void 0) percent_completed = completed / total * 100;
46755
+ const sample_limits = header.sampleLimits.length > 0 ? header.sampleLimits.join(", ") : void 0;
46756
+ let scores;
46757
+ if (header.results?.scores) {
46758
+ for (const evalScore of header.results.scores) if (evalScore.metrics) for (const [metricName, metric] of Object.entries(evalScore.metrics)) {
46759
+ scores ??= {};
46760
+ (scores[evalScore.name] ??= {})[metricName] = metric.value;
46761
+ }
46762
+ }
46763
+ return {
46764
+ total_tokens,
46765
+ duration,
46766
+ task_args,
46767
+ percent_completed,
46768
+ sample_limits,
46769
+ scores
46770
+ };
46771
+ };
46772
+ /** The sample facts baked onto the stored `LogHeader` by `toLogHeader` —
46773
+ * persisted derivation like the fields above, so it lives under
46774
+ * `DERIVE_VERSION` with them. */
46775
+ var deriveSampleFacts = (summaries) => {
46776
+ const limits = /* @__PURE__ */ new Set();
46777
+ let errorCount = 0;
46778
+ for (const sample of summaries) {
46779
+ if (sample.error) errorCount += 1;
46780
+ if (sample.limit) limits.add(sample.limit);
46781
+ }
46782
+ return {
46783
+ sampleCount: summaries.length,
46784
+ sampleErrorCount: errorCount,
46785
+ sampleLimits: [...limits].sort()
46786
+ };
46787
+ };
46788
+ /** A sample's total token spend across models (undefined before any usage).
46789
+ * The single home for this sum — the stored `derived.tokens` column and
46790
+ * read-time consumers (filter variables, sample rows) must agree. */
46791
+ var totalSampleTokens = (modelUsage) => modelUsage ? Object.values(modelUsage).reduce((sum, u) => sum + (u.total_tokens ?? 0), 0) : void 0;
46792
+ var deriveSampleFields = (summary) => {
46793
+ let scores;
46794
+ if (summary.scores) {
46795
+ scores = {};
46796
+ for (const [scoreName, score] of Object.entries(summary.scores)) scores[scoreName] = score.value;
46797
+ }
46798
+ return {
46799
+ tokens: totalSampleTokens(summary.model_usage),
46800
+ input: summary.input !== void 0 ? inputString(summary.input).join("\n") : "",
46801
+ target: arrayToString(summary.target ?? ""),
46802
+ fallbacks: totalModelFallbacks(summary.model_fallbacks) || void 0,
46803
+ scores
46804
+ };
46805
+ };
46806
+ //#endregion
45648
46807
  //#region src/client/utils/type-utils.ts
45649
46808
  var kDepthOrder = {
45650
46809
  listed: 0,
@@ -45671,27 +46830,31 @@ var previewTier = (preview) => ({
45671
46830
  primary_metric: preview.primary_metric
45672
46831
  });
45673
46832
  /** The detailed-tier attributes: the flat columns re-derived from the header
45674
- * plus the header itself. */
46833
+ * plus the header itself and the derived listing columns. */
45675
46834
  var detailTier = (header) => ({
45676
46835
  ...previewTier(toLogPreview(header)),
45677
46836
  depth: "detailed",
45678
- header
46837
+ header,
46838
+ derived: deriveLogFields(header)
45679
46839
  });
45680
46840
  /** Split a details payload into its stored header form: everything but the
45681
46841
  * sample summaries, plus the sample facts derived from them. */
45682
46842
  var toLogHeader = (details) => {
45683
46843
  const { sampleSummaries, ...header } = details;
45684
- const limits = /* @__PURE__ */ new Set();
45685
- let errorCount = 0;
45686
- for (const sample of sampleSummaries) {
45687
- if (sample.error) errorCount += 1;
45688
- if (sample.limit) limits.add(sample.limit);
45689
- }
45690
46844
  return {
45691
46845
  ...header,
45692
- sampleCount: sampleSummaries.length,
45693
- sampleErrorCount: errorCount,
45694
- sampleLimits: [...limits].sort()
46846
+ ...deriveSampleFacts(sampleSummaries)
46847
+ };
46848
+ };
46849
+ var prepareLogDetails = (details) => {
46850
+ const header = toLogHeader(details);
46851
+ return {
46852
+ header,
46853
+ patch: detailTier(header),
46854
+ summaries: details.sampleSummaries.map((summary) => ({
46855
+ summary,
46856
+ derived: deriveSampleFields(summary)
46857
+ }))
45695
46858
  };
45696
46859
  };
45697
46860
  var toLogPreview = (header) => {
@@ -46153,7 +47316,7 @@ var FetchEngine = class {
46153
47316
  this.stop();
46154
47317
  this._deps = deps;
46155
47318
  if (!deps.database) return;
46156
- const rows = await deps.database.readLogs();
47319
+ const rows = await deps.database.readLogs({ prefix: deps.logDir });
46157
47320
  if (!rows) return;
46158
47321
  deps.sink.seedRows(rows);
46159
47322
  this._handles = rows;
@@ -46456,10 +47619,11 @@ var FetchEngine = class {
46456
47619
  }
46457
47620
  }
46458
47621
  async updateDbStats() {
46459
- const database = this._deps?.database;
46460
- if (!database?.opened()) return;
47622
+ const deps = this._deps;
47623
+ const database = deps?.database;
47624
+ if (deps === void 0 || !database?.opened()) return;
46461
47625
  try {
46462
- const stats = await database.getCacheStats();
47626
+ const stats = await database.getCacheStats({ prefix: deps.logDir });
46463
47627
  this.setStatus({ dbStats: {
46464
47628
  logCount: stats.logFiles,
46465
47629
  previewCount: stats.logSummaries,
@@ -50296,26 +51460,29 @@ var fromLogRecord = (record) => {
50296
51460
  name: file_path
50297
51461
  };
50298
51462
  };
50299
- function resolveDBName(databaseHandle) {
50300
- return `InspectAI_${databaseHandle.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
50301
- }
51463
+ var DB_VERSION = 1301;
51464
+ var DB_NAME = "InspectAI";
51465
+ /** The boundary-safe prefix for scoping `file_path` queries to a dir:
51466
+ * `/logs/important` must not match `/logs/important-2`. */
51467
+ var scopePrefix = (dir) => dir.endsWith("/") ? dir : `${dir}/`;
50302
51468
  var AppDatabase = class extends Dexie {
50303
51469
  logs;
50304
51470
  sample_summaries;
51471
+ sync_scopes;
50305
51472
  /**
50306
51473
  * Check if an existing database needs to be recreated due to version mismatch.
50307
51474
  * Returns true if the database should be deleted and recreated.
50308
51475
  */
50309
- static async checkVersionMismatch(databaseHandle) {
50310
- const dbName = resolveDBName(databaseHandle);
51476
+ static async checkVersionMismatch() {
51477
+ const dbName = DB_NAME;
50311
51478
  try {
50312
51479
  if (!await Dexie.exists(dbName)) return false;
50313
51480
  const tempDb = new Dexie(dbName);
50314
51481
  await tempDb.open();
50315
51482
  const currentVersion = tempDb.verno;
50316
51483
  tempDb.close();
50317
- if (currentVersion !== 12) {
50318
- console.log(`Database version mismatch (found v${currentVersion}, expected v12)`);
51484
+ if (currentVersion !== DB_VERSION) {
51485
+ console.log(`Database version mismatch (found v${currentVersion}, expected v${DB_VERSION})`);
50319
51486
  return true;
50320
51487
  }
50321
51488
  return false;
@@ -50323,52 +51490,61 @@ var AppDatabase = class extends Dexie {
50323
51490
  return false;
50324
51491
  }
50325
51492
  }
50326
- constructor(databaseHandle) {
50327
- super(resolveDBName(databaseHandle));
50328
- this.version(12).stores({
50329
- logs: "++id, &file_path, mtime, task, task_id, depth, cached_at",
51493
+ constructor() {
51494
+ super(DB_NAME);
51495
+ this.version(DB_VERSION).stores({
51496
+ logs: "++id, &file_path, mtime, task, task_id, depth, cached_at, [depth+file_path]",
50330
51497
  sample_summaries: "[file_path+id+epoch], file_path, summary.completed_at",
51498
+ sync_scopes: "prefix",
50331
51499
  log_previews: null,
50332
51500
  log_details: null,
50333
51501
  log_fetch_state: null
50334
- }).upgrade((tx) => Promise.all(["logs", "sample_summaries"].map((table) => tx.table(table).clear())));
51502
+ }).upgrade((tx) => Promise.all([
51503
+ "logs",
51504
+ "sample_summaries",
51505
+ "sync_scopes"
51506
+ ].map((table) => tx.table(table).clear())));
50335
51507
  }
50336
51508
  };
51509
+ /**
51510
+ * Delete pre-unification per-dir databases (`InspectAI_<sanitized dir>`).
51511
+ * Best-effort: `indexedDB.databases()` is not universally available and a
51512
+ * database held open by another (older) tab may survive until it closes.
51513
+ */
51514
+ var deleteLegacyDatabases = async () => {
51515
+ try {
51516
+ const databases = await indexedDB.databases();
51517
+ await Promise.all(databases.map((info) => info.name).filter((name) => !!name?.startsWith(`${DB_NAME}_`)).map((name) => Dexie.delete(name).catch(() => {})));
51518
+ } catch {}
51519
+ };
50337
51520
  //#endregion
50338
51521
  //#region src/client/database/manager.ts
50339
- var log$5 = createLogger("DatabaseManager");
51522
+ var log$6 = createLogger("DatabaseManager");
50340
51523
  /**
50341
- * Manages database instances for different log directories.
50342
- * Each instance of this class manages a single database connection.
51524
+ * Manages the (single, per-origin) database connection. Log dirs are query
51525
+ * scopes over the unified database, not separate databases.
50343
51526
  */
50344
51527
  var DatabaseManager = class {
50345
51528
  database = null;
50346
- databaseHandle = null;
50347
- /**
50348
- * Opens a database for the specified log directory.
50349
- * If already connected to this directory, returns the existing connection.
50350
- * If connected to a different directory, closes the current connection first.
50351
- */
50352
- async openDatabase(databaseHandle) {
50353
- if (this.databaseHandle === databaseHandle && this.database) return this.database;
50354
- log$5.debug(`Opening database for log directory: ${databaseHandle}`);
50355
- if (this.database && this.databaseHandle !== databaseHandle) await this.close();
50356
- if (await AppDatabase.checkVersionMismatch(databaseHandle)) {
50357
- log$5.info(`Recreating database due to version mismatch for: ${databaseHandle}`);
50358
- const dbName = `InspectAI_${databaseHandle.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
50359
- await Dexie.delete(dbName);
50360
- log$5.debug(`Deleted old database: ${dbName}`);
50361
- }
50362
- this.database = new AppDatabase(databaseHandle);
50363
- this.databaseHandle = databaseHandle;
51529
+ /**
51530
+ * Open the database, returning the existing connection when already open.
51531
+ */
51532
+ async openDatabase() {
51533
+ if (this.database) return this.database;
51534
+ if (await AppDatabase.checkVersionMismatch()) {
51535
+ log$6.info("Recreating database due to version mismatch");
51536
+ await Dexie.delete(DB_NAME);
51537
+ log$6.debug(`Deleted old database: ${DB_NAME}`);
51538
+ }
51539
+ this.database = new AppDatabase();
50364
51540
  try {
50365
51541
  await this.database.open();
50366
- log$5.debug(`Successfully opened database for: ${databaseHandle}`);
51542
+ log$6.debug("Successfully opened database");
51543
+ deleteLegacyDatabases();
50367
51544
  return this.database;
50368
51545
  } catch (error) {
50369
- log$5.error(`Failed to open database for ${databaseHandle}:`, error);
51546
+ log$6.error("Failed to open database:", error);
50370
51547
  this.database = null;
50371
- this.databaseHandle = null;
50372
51548
  throw error;
50373
51549
  }
50374
51550
  }
@@ -50380,21 +51556,13 @@ var DatabaseManager = class {
50380
51556
  return this.database;
50381
51557
  }
50382
51558
  /**
50383
- * Get the current log directory.
50384
- * Returns null if no database is open.
50385
- */
50386
- getDatabaseHandle() {
50387
- return this.databaseHandle;
50388
- }
50389
- /**
50390
51559
  * Close the current database connection.
50391
51560
  */
50392
51561
  close() {
50393
51562
  if (this.database) {
50394
- log$5.debug(`Closing database for: ${this.databaseHandle}`);
51563
+ log$6.debug("Closing database");
50395
51564
  this.database.close();
50396
51565
  this.database = null;
50397
- this.databaseHandle = null;
50398
51566
  }
50399
51567
  return Promise.resolve();
50400
51568
  }
@@ -50402,21 +51570,12 @@ var DatabaseManager = class {
50402
51570
  * Check if a database is currently open.
50403
51571
  */
50404
51572
  isOpen() {
50405
- return this.database !== null && this.databaseHandle !== null;
50406
- }
50407
- /**
50408
- * Get database info for debugging.
50409
- */
50410
- getInfo() {
50411
- return {
50412
- databaseHandle: this.databaseHandle,
50413
- isOpen: this.isOpen()
50414
- };
51573
+ return this.database !== null;
50415
51574
  }
50416
51575
  };
50417
51576
  //#endregion
50418
51577
  //#region src/client/database/service.ts
50419
- var log$4 = createLogger("DatabaseService");
51578
+ var log$5 = createLogger("DatabaseService");
50420
51579
  var newRow$1 = (handle) => ({
50421
51580
  ...handle,
50422
51581
  depth: "listed",
@@ -50446,10 +51605,10 @@ var DatabaseService = class {
50446
51605
  return this.manager.getDatabase() !== null;
50447
51606
  }
50448
51607
  /**
50449
- * Open a database for the specified log directory.
51608
+ * Open the (unified) database.
50450
51609
  */
50451
- async openDatabase(databaseHandle) {
50452
- await this.manager.openDatabase(databaseHandle);
51610
+ async openDatabase() {
51611
+ await this.manager.openDatabase();
50453
51612
  }
50454
51613
  /**
50455
51614
  * Close the current database connection.
@@ -50458,12 +51617,6 @@ var DatabaseService = class {
50458
51617
  await this.manager.close();
50459
51618
  }
50460
51619
  /**
50461
- * Get the current log directory.
50462
- */
50463
- getDatabaseHandle() {
50464
- return this.manager.getDatabaseHandle();
50465
- }
50466
- /**
50467
51620
  * Upsert the listing identity tier: new files get fresh listed-depth rows;
50468
51621
  * known files update identity fields only (depth, content, and retrieval
50469
51622
  * facts are preserved).
@@ -50471,7 +51624,7 @@ var DatabaseService = class {
50471
51624
  async writeLogs(handles) {
50472
51625
  const db = this.getDb();
50473
51626
  const now = (/* @__PURE__ */ new Date()).toISOString();
50474
- const existingRecords = await db.logs.toArray();
51627
+ const existingRecords = await db.logs.where("file_path").anyOf(handles.map((handle) => handle.name)).toArray();
50475
51628
  const existingByPath = new Map(existingRecords.map((record) => [record.file_path, record]));
50476
51629
  const records = handles.map((handle) => {
50477
51630
  const existing = existingByPath.get(handle.name);
@@ -50483,25 +51636,25 @@ var DatabaseService = class {
50483
51636
  cached_at: now
50484
51637
  } : toLogRecord(newRow$1(handle), void 0, now);
50485
51638
  });
50486
- log$4.debug(`Upserting ${records.length} log rows (identity tier)`);
51639
+ log$5.debug(`Upserting ${records.length} log rows (identity tier)`);
50487
51640
  await db.logs.bulkPut(records);
50488
51641
  }
50489
- async readLogs() {
51642
+ async readLogs(scope) {
50490
51643
  try {
50491
51644
  if (!this.opened()) {
50492
- log$4.debug("Database not open");
51645
+ log$5.debug("Database not open");
50493
51646
  return null;
50494
51647
  }
50495
- const records = await this.getDb().logs.toArray();
51648
+ const records = await this.getDb().logs.where("file_path").startsWith(scopePrefix(scope.prefix)).toArray();
50496
51649
  records.sort((a, b) => {
50497
51650
  if (a.mtime != null && b.mtime != null) return b.mtime - a.mtime;
50498
51651
  if (a.id != null && b.id != null) return a.id - b.id;
50499
51652
  return 0;
50500
51653
  });
50501
- log$4.debug(`Retrieved ${records.length} log rows`);
51654
+ log$5.debug(`Retrieved ${records.length} log rows`);
50502
51655
  return records.map(fromLogRecord);
50503
51656
  } catch (error) {
50504
- log$4.error("Error retrieving log rows:", error);
51657
+ log$5.error("Error retrieving log rows:", error);
50505
51658
  return null;
50506
51659
  }
50507
51660
  }
@@ -50510,7 +51663,7 @@ var DatabaseService = class {
50510
51663
  const record = await this.getDb().logs.where("file_path").equals(filePath).first();
50511
51664
  return record ? fromLogRecord(record) : null;
50512
51665
  } catch (error) {
50513
- log$4.error(`Error retrieving log row for ${filePath}:`, error);
51666
+ log$5.error(`Error retrieving log row for ${filePath}:`, error);
50514
51667
  return null;
50515
51668
  }
50516
51669
  }
@@ -50521,7 +51674,7 @@ var DatabaseService = class {
50521
51674
  for (const record of records) result[record.file_path] = fromLogRecord(record);
50522
51675
  return result;
50523
51676
  } catch (error) {
50524
- log$4.error("Error retrieving log rows:", error);
51677
+ log$5.error("Error retrieving log rows:", error);
50525
51678
  return {};
50526
51679
  }
50527
51680
  }
@@ -50549,39 +51702,41 @@ var DatabaseService = class {
50549
51702
  await db.logs.bulkPut(records);
50550
51703
  }
50551
51704
  async writeLogPreviews(previews) {
50552
- log$4.debug(`Upserting ${Object.keys(previews).length} log rows (previewed tier)`);
51705
+ log$5.debug(`Upserting ${Object.keys(previews).length} log rows (previewed tier)`);
50553
51706
  await this.mergeRows(Object.fromEntries(Object.entries(previews).map(([file, preview]) => [file, previewTier(preview)])));
50554
51707
  }
50555
51708
  /**
50556
- * Ingest details payloads: merge the detailed tier into each log row and
50557
- * replace the file's sample summary rows, in one transaction per call so a
50558
- * reader never sees a header whose summary rows are from an older
50559
- * ingestion.
51709
+ * Ingest prepared details payloads (`prepareLogDetails` the seam
51710
+ * normalizes once for both stores): merge the detailed tier into each log
51711
+ * row and replace the file's sample summary rows, in one transaction per
51712
+ * call so a reader never sees a header whose summary rows are from an
51713
+ * older ingestion.
50560
51714
  */
50561
51715
  async writeLogDetails(details) {
50562
51716
  const db = this.getDb();
50563
51717
  const now = (/* @__PURE__ */ new Date()).toISOString();
50564
51718
  const entries = Object.entries(details);
50565
- log$4.debug(`Ingesting ${entries.length} log details (split)`);
51719
+ log$5.debug(`Ingesting ${entries.length} log details (split)`);
50566
51720
  await db.transaction("rw", db.logs, db.sample_summaries, async () => {
50567
- await this.mergeRows(Object.fromEntries(entries.map(([file, payload]) => [file, detailTier(toLogHeader(payload))])));
51721
+ await this.mergeRows(Object.fromEntries(entries.map(([file, { patch }]) => [file, patch])));
50568
51722
  const files = entries.map(([filePath]) => filePath);
50569
51723
  await db.sample_summaries.where("file_path").anyOf(files).delete();
50570
- await db.sample_summaries.bulkPut(entries.flatMap(([filePath, payload]) => payload.sampleSummaries.map((summary) => ({
51724
+ await db.sample_summaries.bulkPut(entries.flatMap(([filePath, { summaries }]) => summaries.map(({ summary, derived }) => ({
50571
51725
  file_path: filePath,
50572
51726
  id: summary.id,
50573
51727
  epoch: summary.epoch,
50574
51728
  summary,
51729
+ derived,
50575
51730
  cached_at: now
50576
51731
  }))));
50577
51732
  });
50578
51733
  }
50579
51734
  async readSampleSummaries(scope) {
50580
51735
  const db = this.getDb();
50581
- return ("file" in scope ? db.sample_summaries.where("file_path").equals(scope.file) : db.sample_summaries.where("file_path").startsWith(scope.prefix)).toArray();
51736
+ return ("file" in scope ? db.sample_summaries.where("file_path").equals(scope.file) : db.sample_summaries.where("file_path").startsWith(scopePrefix(scope.prefix))).toArray();
50582
51737
  }
50583
51738
  async writeFetchStates(states) {
50584
- log$4.debug(`Merging retrieval facts into ${Object.keys(states).length} log rows`);
51739
+ log$5.debug(`Merging retrieval facts into ${Object.keys(states).length} log rows`);
50585
51740
  await this.mergeRows(states);
50586
51741
  }
50587
51742
  /**
@@ -50606,34 +51761,95 @@ var DatabaseService = class {
50606
51761
  /** Remove a deleted file's row and its sample summaries. */
50607
51762
  async clearCacheForFile(filePath) {
50608
51763
  const db = this.getDb();
50609
- log$4.debug(`Clearing cache for file: ${filePath}`);
51764
+ log$5.debug(`Clearing cache for file: ${filePath}`);
50610
51765
  await Promise.all([db.logs.where("file_path").equals(filePath).delete(), db.sample_summaries.where("file_path").equals(filePath).delete()]);
50611
51766
  }
50612
51767
  /**
50613
- * Clear all cached data from all tables.
51768
+ * Wipe every table the "Clear Local Database" escape hatch. Unlike
51769
+ * `clearScope`, this reaches rows persisted under names outside any synced
51770
+ * scope's namespace (see `namesInScope` in logsContent).
51771
+ */
51772
+ async clearAllData() {
51773
+ const db = this.getDb();
51774
+ log$5.debug("Clearing all cached data");
51775
+ await db.transaction("rw", [
51776
+ db.logs,
51777
+ db.sample_summaries,
51778
+ db.sync_scopes
51779
+ ], () => Promise.all([
51780
+ db.logs.clear(),
51781
+ db.sample_summaries.clear(),
51782
+ db.sync_scopes.clear()
51783
+ ]));
51784
+ }
51785
+ /**
51786
+ * Clear all cached data under a scope: its log rows, their sample
51787
+ * summaries, and the scope's sync record. Other scopes' rows are untouched.
50614
51788
  */
50615
- async clearAllCaches() {
51789
+ async clearScope(scope) {
50616
51790
  const db = this.getDb();
50617
- log$4.debug("Clearing all caches");
50618
- await Promise.all([db.logs.clear(), db.sample_summaries.clear()]);
51791
+ const prefix = scopePrefix(scope.prefix);
51792
+ log$5.debug(`Clearing caches under: ${prefix}`);
51793
+ await db.transaction("rw", [
51794
+ db.logs,
51795
+ db.sample_summaries,
51796
+ db.sync_scopes
51797
+ ], () => Promise.all([
51798
+ db.logs.where("file_path").startsWith(prefix).delete(),
51799
+ db.sample_summaries.where("file_path").startsWith(prefix).delete(),
51800
+ db.sync_scopes.where("prefix").startsWith(prefix).delete()
51801
+ ]));
51802
+ }
51803
+ /** Record that a scope is active (creating its row on first contact). */
51804
+ async touchSyncScope(prefix) {
51805
+ const db = this.getDb();
51806
+ const key = scopePrefix(prefix);
51807
+ const now = (/* @__PURE__ */ new Date()).toISOString();
51808
+ await db.transaction("rw", db.sync_scopes, async () => {
51809
+ const existing = await db.sync_scopes.get(key);
51810
+ await db.sync_scopes.put({
51811
+ ...existing,
51812
+ prefix: key,
51813
+ last_accessed: now
51814
+ });
51815
+ });
51816
+ }
51817
+ /** Read a scope's sync record (undefined when never activated). */
51818
+ async getSyncScope(prefix) {
51819
+ return this.getDb().sync_scopes.get(scopePrefix(prefix));
51820
+ }
51821
+ /** Record that a listing sync persisted under a scope. */
51822
+ async markScopeSynced(prefix) {
51823
+ const db = this.getDb();
51824
+ const key = scopePrefix(prefix);
51825
+ const now = (/* @__PURE__ */ new Date()).toISOString();
51826
+ await db.transaction("rw", db.sync_scopes, async () => {
51827
+ const existing = await db.sync_scopes.get(key);
51828
+ await db.sync_scopes.put({
51829
+ prefix: key,
51830
+ last_accessed: existing?.last_accessed ?? now,
51831
+ last_synced: now
51832
+ });
51833
+ });
50619
51834
  }
50620
51835
  /**
50621
- * Get cache statistics.
51836
+ * Get cache statistics for a scope.
50622
51837
  */
50623
- async getCacheStats() {
51838
+ async getCacheStats(scope) {
50624
51839
  const db = this.getDb();
50625
- const [logFiles, logSummaries, logHeaders, sampleSummaries] = await Promise.all([
50626
- db.logs.count(),
50627
- db.logs.where("depth").anyOf(["previewed", "detailed"]).count(),
50628
- db.logs.where("depth").equals("detailed").count(),
50629
- db.sample_summaries.count()
51840
+ const prefix = scopePrefix(scope.prefix);
51841
+ const depthCount = (depth) => db.logs.where("[depth+file_path]").between([depth, prefix], [depth, prefix + "￿"]).count();
51842
+ const [logFiles, previewed, detailed, sampleSummaries] = await Promise.all([
51843
+ db.logs.where("file_path").startsWith(prefix).count(),
51844
+ depthCount("previewed"),
51845
+ depthCount("detailed"),
51846
+ db.sample_summaries.where("file_path").startsWith(prefix).count()
50630
51847
  ]);
50631
51848
  return {
50632
51849
  logFiles,
50633
- logSummaries,
50634
- logHeaders,
50635
- sampleSummaries,
50636
- logHandle: this.manager.getDatabaseHandle()
51850
+ logSummaries: previewed + detailed,
51851
+ logHeaders: detailed,
51852
+ sampleSummaries
50637
51853
  };
50638
51854
  }
50639
51855
  };
@@ -50744,11 +51960,12 @@ var rowContext = (row) => row === void 0 ? {} : {
50744
51960
  model: row.model,
50745
51961
  status: row.status
50746
51962
  };
50747
- /** Assemble listing rows from an ingested payload's parts (the sink's push
51963
+ /** Assemble listing rows from a prepared payload's parts (the sink's push
50748
51964
  * path — the db read below produces the same shape). */
50749
- var toSamplesListingRows = (logFile, header, summaries) => summaries.map((summary) => ({
51965
+ var toSamplesListingRows = (logFile, header, summaries) => summaries.map(({ summary, derived }) => ({
50750
51966
  logFile,
50751
51967
  summary,
51968
+ derived,
50752
51969
  log: logContext(header)
50753
51970
  }));
50754
51971
  var pageOf = (rows, params) => {
@@ -50765,6 +51982,7 @@ var readSamplesListing = async (params) => {
50765
51982
  return pageOf(records.map((record) => ({
50766
51983
  logFile: record.file_path,
50767
51984
  summary: record.summary,
51985
+ derived: record.derived,
50768
51986
  log: contexts.get(record.file_path) ?? {}
50769
51987
  })), params);
50770
51988
  };
@@ -50840,6 +52058,7 @@ var removeSamplesListings = (logDir) => {
50840
52058
  * `set*`/`merge*`/`seed*` primitives) are allowed; the invariant is
50841
52059
  * one-directional (db ⟹ cache).
50842
52060
  */
52061
+ var log$4 = createLogger("logsContent");
50843
52062
  var EMPTY_LOGS = [];
50844
52063
  var logsKey = (logDir) => [
50845
52064
  "log_data",
@@ -50949,15 +52168,44 @@ var clearCache = (logDir) => {
50949
52168
  removeSamplesListings(logDir);
50950
52169
  };
50951
52170
  /**
52171
+ * The store scopes every read by `file_path.startsWith(scopePrefix(logDir))`,
52172
+ * so persistence requires the listing's names to live in `logDir`'s
52173
+ * namespace. A server can violate that (e.g. an older view server hands out
52174
+ * an aliased local path as log_dir while names are `file://` URIs) —
52175
+ * persisting under such a scope would strand the rows where no scoped read,
52176
+ * clear, or seed can reach them, and the empty read-back would blank the
52177
+ * just-synced listing. Degrade to cache-only instead.
52178
+ *
52179
+ * Only listing persistence is gated: the keyed writes below (previews,
52180
+ * details, fetch states) still persist out-of-namespace rows, which serve
52181
+ * exact-path reads but which no listing sync governs and no scoped clear can
52182
+ * remove. "Clear Local Database" (`clearAll`) wipes them along with
52183
+ * everything else.
52184
+ */
52185
+ var warnedScopes = /* @__PURE__ */ new Set();
52186
+ var namesInScope = (logDir, handles) => {
52187
+ const prefix = scopePrefix(logDir);
52188
+ const misnamed = handles.find((handle) => !handle.name.startsWith(prefix));
52189
+ if (misnamed !== void 0) {
52190
+ if (!warnedScopes.has(prefix)) {
52191
+ warnedScopes.add(prefix);
52192
+ log$4.warn(`Listing names (e.g. ${misnamed.name}) are outside the log dir's namespace (${prefix}); skipping persistence for this scope.`);
52193
+ }
52194
+ return false;
52195
+ }
52196
+ return true;
52197
+ };
52198
+ /**
50952
52199
  * Persist the listing identity tier and cache the resulting full row list.
50953
52200
  * The db write is a merge-upsert (rows keep depth/content) and the cache
50954
52201
  * holds the full re-read, so the full list is read back and returned for the
50955
52202
  * caller's continued sync logic.
50956
52203
  */
50957
52204
  var writeListing = async (db, logDir, handles) => {
50958
- if (db?.opened()) {
52205
+ if (db?.opened() && namesInScope(logDir, handles)) {
50959
52206
  await db.writeLogs(handles);
50960
- const all = await db.readLogs();
52207
+ await db.markScopeSynced(logDir);
52208
+ const all = await db.readLogs({ prefix: logDir });
50961
52209
  if (all) {
50962
52210
  setRows(logDir, all);
50963
52211
  return all;
@@ -50982,14 +52230,11 @@ var writePreviews = async (db, logDir, previews) => {
50982
52230
  * clobber them with an empty read — so the sweep is persistence-gated.
50983
52231
  */
50984
52232
  var writeDetails = async (db, logDir, details) => {
50985
- const headers = Object.fromEntries(Object.entries(details).map(([name, payload]) => [name, toLogHeader(payload)]));
50986
- await Promise.all(Object.entries(details).map(([name, payload]) => {
50987
- const header = headers[name];
50988
- return header === void 0 ? Promise.resolve() : pushFileSamples(logDir, name, toSamplesListingRows(name, header, payload.sampleSummaries));
50989
- }));
50990
- mergePatches(logDir, Object.fromEntries(Object.entries(headers).map(([name, header]) => [name, detailTier(header)])));
52233
+ const prepared = Object.entries(details).map(([name, payload]) => [name, prepareLogDetails(payload)]);
52234
+ await Promise.all(prepared.map(([name, file]) => pushFileSamples(logDir, name, toSamplesListingRows(name, file.header, file.summaries))));
52235
+ mergePatches(logDir, Object.fromEntries(prepared.map(([name, file]) => [name, file.patch])));
50991
52236
  if (db?.opened()) {
50992
- await db.writeLogDetails(details);
52237
+ await db.writeLogDetails(Object.fromEntries(prepared));
50993
52238
  invalidateSamplesListings(logDir);
50994
52239
  }
50995
52240
  };
@@ -51023,7 +52268,7 @@ var clearFile = async (db, logDir, name) => {
51023
52268
  };
51024
52269
  var clearAll = async (db, logDir) => {
51025
52270
  clearCache(logDir);
51026
- if (db?.opened()) await db.clearAllCaches();
52271
+ if (db?.opened()) await db.clearAllData();
51027
52272
  };
51028
52273
  /**
51029
52274
  * The seam as a fetch-engine sink: the write surface bound to a directory and
@@ -51073,7 +52318,8 @@ var resolveLogKey = (logDir, logFile) => {
51073
52318
  var openLogDirDatabase = async (logDir) => {
51074
52319
  const databaseService = getDatabaseService();
51075
52320
  try {
51076
- await databaseService.openDatabase(getApi().get_log_dir_handle(logDir));
52321
+ await databaseService.openDatabase();
52322
+ await databaseService.touchSyncScope(logDir);
51077
52323
  return databaseService;
51078
52324
  } catch (e) {
51079
52325
  console.log(e);
@@ -51081,15 +52327,15 @@ var openLogDirDatabase = async (logDir) => {
51081
52327
  }
51082
52328
  };
51083
52329
  var ensureActive = async (logDir) => {
51084
- const databaseHandle = getApi().get_log_dir_handle(logDir);
51085
- if (!fetchEngine.isStarted() || engineDir !== logDir || getDatabaseService().getDatabaseHandle() !== databaseHandle) {
52330
+ if (!fetchEngine.isStarted() || engineDir !== logDir || !getDatabaseService().opened()) {
51086
52331
  fetchEngine.stop();
51087
52332
  const opened = await openLogDirDatabase(logDir);
51088
52333
  if (!opened) throw new Error("Database service not available");
51089
52334
  await fetchEngine.start({
51090
52335
  api: getApi(),
51091
52336
  database: opened,
51092
- sink: createLogsContentSink(opened, logDir)
52337
+ sink: createLogsContentSink(opened, logDir),
52338
+ logDir
51093
52339
  });
51094
52340
  engineDir = logDir;
51095
52341
  }
@@ -51114,7 +52360,8 @@ var ensureFetchEngine = (logDir) => {
51114
52360
  await fetchEngine.start({
51115
52361
  api: getApi(),
51116
52362
  database,
51117
- sink: createLogsContentSink(database, logDir)
52363
+ sink: createLogsContentSink(database, logDir),
52364
+ logDir
51118
52365
  });
51119
52366
  engineDir = logDir;
51120
52367
  }
@@ -51537,90 +52784,6 @@ var sampleHandlesEqual = (sample1, sample2) => {
51537
52784
  return sampleIdsEqual(sample1.id, sample2.id) && sample1.epoch === sample2.epoch && sample1.logFile === sample2.logFile;
51538
52785
  };
51539
52786
  //#endregion
51540
- //#region ../../packages/inspect-common/src/utils/expandEvents.ts
51541
- /**
51542
- * Expand range-encoded refs against a pool.
51543
- * Each ref is [start, endExclusive] → pool.slice(start, endExclusive).
51544
- */
51545
- function expandRefs$1(refs, pool) {
51546
- return refs.flatMap(([start, end]) => pool.slice(start, end));
51547
- }
51548
- function isModelEvent(event) {
51549
- return event.event === "model";
51550
- }
51551
- /**
51552
- * Expand condensed ModelEvent input/call refs back to inline data.
51553
- *
51554
- * Pure function — returns a new array; input is not mutated.
51555
- * Non-ModelEvents and ModelEvents without refs pass through unchanged.
51556
- */
51557
- function expandEvents(events, eventsData) {
51558
- if (!eventsData) return events;
51559
- const { messages, calls } = eventsData;
51560
- const hasMessages = messages.length > 0;
51561
- const hasCalls = calls.length > 0;
51562
- if (!hasMessages && !hasCalls) return events;
51563
- return events.map((event) => {
51564
- if (!isModelEvent(event)) return event;
51565
- let changed = false;
51566
- let input = event.input;
51567
- let call = event.call;
51568
- if (event.input_refs != null && hasMessages) {
51569
- input = expandRefs$1(event.input_refs, messages);
51570
- changed = true;
51571
- }
51572
- if (call?.call_refs != null && hasCalls) {
51573
- const key = call.call_key ?? "messages";
51574
- const expandedMsgs = expandRefs$1(call.call_refs, calls);
51575
- call = {
51576
- ...call,
51577
- request: {
51578
- ...call.request,
51579
- [key]: expandedMsgs
51580
- },
51581
- call_refs: null,
51582
- call_key: null
51583
- };
51584
- changed = true;
51585
- }
51586
- return changed ? {
51587
- ...event,
51588
- input,
51589
- input_refs: null,
51590
- call
51591
- } : event;
51592
- });
51593
- }
51594
- //#endregion
51595
- //#region ../../packages/inspect-common/src/utils/inputString.ts
51596
- /**
51597
- * Gets a string for a sample input.
51598
- */
51599
- var inputString = (input) => {
51600
- if (typeof input === "string") return [input];
51601
- else return input.map((inp) => {
51602
- if (typeof inp === "string") return inp;
51603
- else {
51604
- const content = inp.content;
51605
- if (typeof content === "string") return content;
51606
- else return content.map((con) => {
51607
- if (con.type === "text") return con.text;
51608
- else return "";
51609
- }).join("\n");
51610
- }
51611
- });
51612
- };
51613
- //#endregion
51614
- //#region ../../packages/inspect-common/src/utils/modelFallbacks.ts
51615
- /**
51616
- * Total generate calls served via fallback across a sample's rollup.
51617
- */
51618
- var totalModelFallbacks = (fallbacks) => (fallbacks ?? []).reduce((sum, f) => sum + (f.count ?? 1), 0);
51619
- /**
51620
- * One "requested → served (×N)" line per fallback rollup entry.
51621
- */
51622
- var modelFallbackLines = (fallbacks) => (fallbacks ?? []).map((f) => `${f.model} → ${f.fallback_model}${(f.count ?? 1) > 1 ? ` (×${f.count})` : ""}`);
51623
- //#endregion
51624
52787
  //#region src/utils/attachments.ts
51625
52788
  var resolveAttachments = (value, attachments, onFailedResolve) => {
51626
52789
  const CONTENT_PROTOCOL = "tc://";
@@ -52269,13 +53432,14 @@ var scorerMetricKey = (scorerName, metricName) => `${scorerName}/${metricName}`;
52269
53432
  * scorers emitting the same metric (e.g. two "accuracy"s) into one column.
52270
53433
  *
52271
53434
  * @param logs - The directory's Log rows
52272
- * @param scopePrefix - When set, only logs whose name starts with this
52273
- * prefix contribute (folder view scoping)
53435
+ * @param scopeDir - When set, only logs under this directory contribute
53436
+ * (folder view scoping)
52274
53437
  */
52275
- function computeScorerMap(logs, scopePrefix) {
53438
+ function computeScorerMap(logs, scopeDir) {
52276
53439
  const info = {};
53440
+ const prefix = scopeDir ? scopePrefix(scopeDir) : void 0;
52277
53441
  for (const log of logs) {
52278
- if (scopePrefix && !log.name.startsWith(scopePrefix)) continue;
53442
+ if (prefix && !log.name.startsWith(prefix)) continue;
52279
53443
  if (log.header?.results?.scores) {
52280
53444
  for (const evalScore of log.header.results.scores) if (evalScore.metrics) for (const [metricName, metric] of Object.entries(evalScore.metrics)) {
52281
53445
  const key = scorerMetricKey(evalScore.name, metricName);
@@ -52309,8 +53473,8 @@ function scorerMapsEqual(a, b) {
52309
53473
  * across flushes.
52310
53474
  */
52311
53475
  var asyncScorerMapsEqual = (a, b) => a.loading === b.loading && a.error === b.error && (a.data !== void 0 && b.data !== void 0 ? scorerMapsEqual(a.data, b.data) : a.data === b.data);
52312
- var useScoreSchema = (logDir, scopePrefix) => {
52313
- return useStableValue(useMapAsyncData(useLogs(logDir), useCallback((rows) => computeScorerMap(rows, scopePrefix), [scopePrefix])), asyncScorerMapsEqual);
53476
+ var useScoreSchema = (logDir, scopeDir) => {
53477
+ return useStableValue(useMapAsyncData(useLogs(logDir), useCallback((rows) => computeScorerMap(rows, scopeDir), [scopeDir])), asyncScorerMapsEqual);
52314
53478
  };
52315
53479
  //#endregion
52316
53480
  //#region src/utils/uri.ts
@@ -55227,7 +56391,7 @@ var initializeStore = (capabilities, storage) => {
55227
56391
  getItem: (name) => {
55228
56392
  return storage ? storage.getItem(name) : null;
55229
56393
  },
55230
- setItem: debounce$2((name, value) => {
56394
+ setItem: debounce$3((name, value) => {
55231
56395
  if (storage) storage.setItem(name, value);
55232
56396
  }, 1e3),
55233
56397
  removeItem: (name) => {
@@ -57832,9 +58996,6 @@ var clientApi = (api, log_file, debug = false) => {
57832
58996
  return api.client_events();
57833
58997
  }),
57834
58998
  get_log_dir: middleware("get_log_dir", get_log_dir),
57835
- get_log_dir_handle: middleware("get_log_dir_handle", (log_dir) => {
57836
- return api.get_log_dir_handle ? api.get_log_dir_handle(log_dir) : log_dir || "default_log_dir";
57837
- }),
57838
58999
  get_logs: middleware("get_log_files", get_logs),
57839
59000
  get_log_root: middleware("get_log_root", get_log_root),
57840
59001
  get_eval_set: middleware("get_eval_set", (dir) => {
@@ -58025,6 +59186,15 @@ function joinURI(...segments) {
58025
59186
  }
58026
59187
  //#endregion
58027
59188
  //#region src/client/api/static-http/api-static-http.ts
59189
+ /** The canonical, origin-unique URL of a deployment's log dir. A relative
59190
+ * configured log_dir is page-relative for fetching, but page-relative
59191
+ * strings are not identities: two bundles at different paths on one origin
59192
+ * would collide in the shared per-origin IndexedDB. So everything the app
59193
+ * sees (log_dir, file names) is absolute. */
59194
+ var canonicalDirUrl = (log_dir) => {
59195
+ if (isUri(log_dir)) return log_dir;
59196
+ return joinURI(`${window.location.origin}${window.location.pathname.substring(0, window.location.pathname.lastIndexOf("/"))}`, log_dir);
59197
+ };
58028
59198
  var kFallbackAppConfig = {
58029
59199
  inspect_version: "unknown",
58030
59200
  scout_version: null
@@ -58049,6 +59219,7 @@ function staticHttpApi(log_dir, log_file, abs_log_dir, app_config) {
58049
59219
  */
58050
59220
  function staticHttpApiForLog(logInfo) {
58051
59221
  const log_dir = logInfo.log_dir;
59222
+ const canonical_log_dir = log_dir === void 0 ? void 0 : canonicalDirUrl(log_dir);
58052
59223
  const abs_log_dir = logInfo.abs_log_dir;
58053
59224
  const app_config = logInfo.app_config ?? kFallbackAppConfig;
58054
59225
  let manifest = void 0;
@@ -58069,27 +59240,24 @@ function staticHttpApiForLog(logInfo) {
58069
59240
  return Promise.resolve([]);
58070
59241
  },
58071
59242
  get_log_root: async () => {
58072
- if (log_dir) {
59243
+ if (log_dir && canonical_log_dir) {
58073
59244
  const manifest = await getManifest();
58074
59245
  if (manifest) {
58075
59246
  const logs = Object.entries(manifest).map(([key, preview]) => {
58076
59247
  return {
58077
- name: joinURI(log_dir, key),
59248
+ name: joinURI(canonical_log_dir, key),
58078
59249
  task: preview.task,
58079
59250
  task_id: preview.task_id
58080
59251
  };
58081
59252
  });
58082
59253
  return Promise.resolve({
58083
59254
  logs,
58084
- log_dir,
59255
+ log_dir: canonical_log_dir,
58085
59256
  abs_log_dir
58086
59257
  });
58087
59258
  }
58088
59259
  }
58089
59260
  },
58090
- get_log_dir_handle: (log_dir) => {
58091
- return joinURI(`${window.location.origin}${window.location.pathname.substring(0, window.location.pathname.lastIndexOf("/"))}`, log_dir || "default_log_dir");
58092
- },
58093
59261
  get_eval_set: async (dir) => {
58094
59262
  const dirSegments = [];
58095
59263
  if (log_dir) dirSegments.push(log_dir);
@@ -58128,7 +59296,7 @@ function staticHttpApiForLog(logInfo) {
58128
59296
  if (manifest) {
58129
59297
  const manifestAbs = {};
58130
59298
  Object.entries(manifest).forEach(([key, preview]) => {
58131
- manifestAbs[joinURI(log_dir || "", key)] = preview;
59299
+ manifestAbs[joinURI(canonical_log_dir || "", key)] = preview;
58132
59300
  });
58133
59301
  const header = manifestAbs[log_file];
58134
59302
  if (header) return header;
@@ -58928,12 +60096,13 @@ var resolveLogRoot = async (bs) => {
58928
60096
  */
58929
60097
  var loadResolvedAppConfig = async (bs) => {
58930
60098
  const [versions, logRoot] = await Promise.all([bs.api.get_app_config(), resolveLogRoot(bs)]);
58931
- if (!logRoot.log_dir) throw new Error("Log dir not resolved");
60099
+ const logDir = logRoot.log_dir_uri ?? logRoot.log_dir;
60100
+ if (!logDir) throw new Error("Log dir not resolved");
58932
60101
  return {
58933
60102
  ...bs,
58934
60103
  inspect_version: versions.inspect_version,
58935
60104
  scout_version: versions.scout_version ?? null,
58936
- logDir: logRoot.log_dir,
60105
+ logDir,
58937
60106
  absLogDir: logRoot.abs_log_dir
58938
60107
  };
58939
60108
  };
@@ -62419,7 +63588,7 @@ var useVirtuosoState = (virtuosoRef, elementKey, delay = 1e3) => {
62419
63588
  setListPosition(state);
62420
63589
  }, [elementKey, setListPosition]);
62421
63590
  useEffect(() => {
62422
- debouncedFnRef.current = debounce$2((isScrolling) => {
63591
+ debouncedFnRef.current = debounce$3((isScrolling) => {
62423
63592
  log$1.debug("List scroll", isScrolling);
62424
63593
  const element = virtuosoRef.current;
62425
63594
  if (!element) return;
@@ -65632,11 +66801,6 @@ var getNestedPropertyValue = (obj, path) => {
65632
66801
  else return;
65633
66802
  return current;
65634
66803
  };
65635
- var totalTokens = (sample) => {
65636
- if (!sample.model_usage) return null;
65637
- return Object.values(sample.model_usage).reduce((sum, u) => sum + (u.total_tokens ?? 0), 0);
65638
- };
65639
- var targetString = (target) => Array.isArray(target) ? target.join(", ") : target ?? "";
65640
66804
  var sampleVariables = (sample, samplesDescriptor) => {
65641
66805
  return {
65642
66806
  epoch: sample.epoch,
@@ -65648,13 +66812,13 @@ var sampleVariables = (sample, samplesDescriptor) => {
65648
66812
  id: sample.id,
65649
66813
  uuid: sample.uuid ?? null,
65650
66814
  input: inputString(sample.input).join(" "),
65651
- target: targetString(sample.target),
66815
+ target: arrayToString(sample.target ?? ""),
65652
66816
  answer: samplesDescriptor?.selectedScorerDescriptor(sample)?.answer() ?? null,
65653
66817
  error: sample.error ?? null,
65654
66818
  limit: sample.limit ?? null,
65655
66819
  retries: sample.retries ?? 0,
65656
66820
  fallbacks: totalModelFallbacks(sample.model_fallbacks),
65657
- tokens: totalTokens(sample),
66821
+ tokens: totalSampleTokens(sample.model_usage) ?? null,
65658
66822
  duration: sample.total_time ?? null,
65659
66823
  metadata: sample.metadata
65660
66824
  };
@@ -66332,11 +67496,11 @@ var baseUrl = (logPath, sampleId, sampleEpoch, prefix = "/logs") => {
66332
67496
  };
66333
67497
  var useSampleUrlBuilder = () => {
66334
67498
  const location = useLocation();
66335
- const prefix = location.pathname.startsWith("/tasks") ? "/tasks" : "/logs";
66336
- return (logPath, sampleId, sampleEpoch, sampleTabId) => {
67499
+ return useCallback((logPath, sampleId, sampleEpoch, sampleTabId) => {
67500
+ const prefix = location.pathname.startsWith("/tasks") ? "/tasks" : "/logs";
66337
67501
  if (sampleId && sampleEpoch && location.pathname.startsWith("/samples/")) return samplesSampleUrl(logPath, sampleId, sampleEpoch, sampleTabId);
66338
67502
  else return logSamplesUrl(logPath, sampleId, sampleEpoch, sampleTabId, prefix);
66339
- };
67503
+ }, [location.pathname]);
66340
67504
  };
66341
67505
  var logSamplesUrl = (logPath, sampleId, sampleEpoch, sampleTabId, prefix = "/logs") => {
66342
67506
  const decodedLogPath = decodeUrlParam(logPath) || logPath;
@@ -66361,6 +67525,27 @@ var sampleEventUrl = (builder, eventId, logPath, sampleId, sampleEpoch) => {
66361
67525
  var sampleMessageUrl = (builder, messageId, logPath, sampleId, sampleEpoch, tab = kSampleMessagesTabId) => {
66362
67526
  return `${builder(logPath, sampleId, sampleEpoch, tab)}?message=${messageId}`;
66363
67527
  };
67528
+ /**
67529
+ * Returns a builder for *shareable* message links: the relative hash route
67530
+ * from `sampleMessageUrl` wrapped with the host page's origin/path via
67531
+ * `toFullUrl`. Copy-to-clipboard consumers (ChatMessage's copy button) must
67532
+ * use this rather than the bare route, which only works for in-app router
67533
+ * navigation.
67534
+ */
67535
+ var useFullSampleMessageUrlBuilder = () => {
67536
+ const builder = useSampleUrlBuilder();
67537
+ const { logPath: urlLogPath, id: urlSampleId, epoch: urlEpoch } = useLogOrSampleRouteParams();
67538
+ const log_file = useStore((state) => state.logs.selectedLogFile);
67539
+ const log_dir = useLogDir();
67540
+ let targetLogPath = urlLogPath;
67541
+ if (!targetLogPath && log_file) targetLogPath = makeLogsPath(log_file, log_dir);
67542
+ return useCallback((messageId) => toFullUrlMaybe(targetLogPath ? sampleMessageUrl(builder, messageId, targetLogPath, urlSampleId, urlEpoch) : void 0), [
67543
+ builder,
67544
+ targetLogPath,
67545
+ urlSampleId,
67546
+ urlEpoch
67547
+ ]);
67548
+ };
66364
67549
  var tasksUrl = (log_file, log_dir) => {
66365
67550
  const path = makeLogsPath(log_file, log_dir);
66366
67551
  return encodePathParts(`/tasks/${decodeUrlParam(path) || path}`);
@@ -66406,6 +67591,14 @@ var logsUrlRaw = (log_segment, tabId, prefix = "/logs") => {
66406
67591
  if (tabId) return encodePathParts(`${prefix}/${decodedLogSegment}/${tabId}`);
66407
67592
  else return encodePathParts(`${prefix}/${decodedLogSegment}`);
66408
67593
  };
67594
+ var toFullUrl = (path) => {
67595
+ return `${window.location.origin}${window.location.pathname}${window.location.search}#${path}`;
67596
+ };
67597
+ var toFullUrlMaybe = (route) => route ? toFullUrl(route) : void 0;
67598
+ var routeFromFullUrl = (url) => {
67599
+ const hashIndex = url.indexOf("#");
67600
+ return hashIndex >= 0 ? url.slice(hashIndex + 1) : url;
67601
+ };
66409
67602
  var FlowButton_module_default = {
66410
67603
  button: "_button_1bbut_1",
66411
67604
  viewerOptions: "_viewerOptions_1bbut_7"
@@ -66746,7 +67939,7 @@ var ViewerOptionsPopover = ({ showing, positionEl, setShowing }) => {
66746
67939
  }),
66747
67940
  /* @__PURE__ */ jsx("div", {
66748
67941
  className: clsx(),
66749
- children: 12
67942
+ children: DB_VERSION
66750
67943
  }),
66751
67944
  /* @__PURE__ */ jsx("div", { className: clsx(ViewerOptionsPopover_module_default.spacer) }),
66752
67945
  /* @__PURE__ */ jsx("div", {
@@ -67310,11 +68503,11 @@ var scorerMetricHeader = (scorerName, metricName) => `${scorerName} / ${metricNa
67310
68503
  * the per-scorer `score_<scorer>/<metric>` field keys.
67311
68504
  */
67312
68505
  var byMetricField = (metricName) => `metric_${metricName}`;
67313
- var useLogListColumns = (mode = "logs", scopePrefix, viewMode = "by-metric") => {
68506
+ var useLogListColumns = (mode = "logs", scopeDir, viewMode = "by-metric") => {
67314
68507
  const columnVisibility = useStore((state) => state.logs.listing.columnVisibility);
67315
68508
  const setColumnVisibility = useStore((state) => state.logsActions.setLogsColumnVisibility);
67316
68509
  const logDir = useLogDir();
67317
- const scorerMap = useScoreSchema(logDir, scopePrefix).data ?? kNoScorerMap;
68510
+ const scorerMap = useScoreSchema(logDir, scopeDir).data ?? kNoScorerMap;
67318
68511
  const allColumns = useMemo(() => {
67319
68512
  const baseColumns = [
67320
68513
  {
@@ -67829,7 +69022,10 @@ var useLogListColumns = (mode = "logs", scopePrefix, viewMode = "by-metric") =>
67829
69022
  return allCols;
67830
69023
  }, [scorerMap, mode]);
67831
69024
  const listingRows = useLogListing(logDir).data ?? kNoListingRows$1;
67832
- const hasSampleLimits = useMemo(() => listingRows.some((row) => (!scopePrefix || row.name.startsWith(scopePrefix)) && (row.header?.sampleLimits.length ?? 0) > 0), [listingRows, scopePrefix]);
69025
+ const hasSampleLimits = useMemo(() => {
69026
+ const prefix = scopeDir ? scopePrefix(scopeDir) : void 0;
69027
+ return listingRows.some((row) => (!prefix || row.name.startsWith(prefix)) && (row.header?.sampleLimits.length ?? 0) > 0);
69028
+ }, [listingRows, scopeDir]);
67833
69029
  const defaultHiddenFields = useMemo(() => {
67834
69030
  const hidden = /* @__PURE__ */ new Set();
67835
69031
  if (mode === "tasks") {
@@ -67894,61 +69090,6 @@ var useLogListColumns = (mode = "logs", scopePrefix, viewMode = "by-metric") =>
67894
69090
  };
67895
69091
  };
67896
69092
  //#endregion
67897
- //#region src/components/FindBandUI.tsx
67898
- var FindBandUI = ({ onClose, onNext, onPrevious, onKeyDown, onChange, onBeforeInput, value, matchCount, matchIndex, noResults = false, disableNav, inputRef: externalRef }) => {
67899
- const internalRef = useRef(null);
67900
- const inputRef = externalRef ?? internalRef;
67901
- const inputProps = {
67902
- type: "text",
67903
- placeholder: "Find",
67904
- onKeyDown,
67905
- onBeforeInput,
67906
- onChange
67907
- };
67908
- if (value !== void 0) inputProps.value = value;
67909
- const hasCount = matchCount !== void 0 && matchIndex !== void 0;
67910
- const showStatus = noResults || hasCount && matchCount > 0;
67911
- const statusText = hasCount && matchCount > 0 ? `${matchIndex + 1} of ${matchCount}` : "No results";
67912
- return /* @__PURE__ */ jsxs("div", {
67913
- "data-unsearchable": "true",
67914
- className: clsx("findBand"),
67915
- children: [
67916
- /* @__PURE__ */ jsx("input", {
67917
- ref: inputRef,
67918
- ...inputProps
67919
- }),
67920
- /* @__PURE__ */ jsx("span", {
67921
- className: clsx("findBand-match-count", noResults && "findBand-no-results"),
67922
- style: { visibility: showStatus ? "visible" : "hidden" },
67923
- children: statusText
67924
- }),
67925
- /* @__PURE__ */ jsx("button", {
67926
- type: "button",
67927
- title: "Previous match",
67928
- className: "btn prev",
67929
- onClick: onPrevious,
67930
- disabled: disableNav,
67931
- children: /* @__PURE__ */ jsx("i", { className: ApplicationIcons.arrows.up })
67932
- }),
67933
- /* @__PURE__ */ jsx("button", {
67934
- type: "button",
67935
- title: "Next match",
67936
- className: "btn next",
67937
- onClick: onNext,
67938
- disabled: disableNav,
67939
- children: /* @__PURE__ */ jsx("i", { className: ApplicationIcons.arrows.down })
67940
- }),
67941
- /* @__PURE__ */ jsx("button", {
67942
- type: "button",
67943
- title: "Close",
67944
- className: "btn close",
67945
- onClick: onClose,
67946
- children: /* @__PURE__ */ jsx("i", { className: ApplicationIcons.close })
67947
- })
67948
- ]
67949
- });
67950
- };
67951
- //#endregion
67952
69093
  //#region ../../node_modules/.pnpm/@tanstack+table-core@8.21.3/node_modules/@tanstack/table-core/build/lib/index.mjs
67953
69094
  /**
67954
69095
  * table-core
@@ -73194,9 +74335,9 @@ function findMatches(index, term) {
73194
74335
  var LogListGrid = ({ rows, totalRowCount, sorting, columnFilters, currentPath, scopeKey, mode = "logs", busy }) => {
73195
74336
  const { gridStateByScope, patchGridState } = useLogsListing();
73196
74337
  const navigate = useNavigate();
73197
- const scopePrefix = mode === "logs" ? currentPath : void 0;
74338
+ const scopeDir = mode === "logs" ? currentPath : void 0;
73198
74339
  const [scoresViewMode] = useProperty("log-list-scores-view", "mode", { defaultValue: "by-metric" });
73199
- const { columns, visibility } = useLogListColumns(mode, scopePrefix, scoresViewMode);
74340
+ const { columns, visibility } = useLogListColumns(mode, scopeDir, scoresViewMode);
73200
74341
  const handleRowActivate = useCallback((row) => {
73201
74342
  if (row.url) navigate(row.url);
73202
74343
  }, [navigate]);
@@ -73282,19 +74423,13 @@ var LogListGrid = ({ rows, totalRowCount, sorting, columnFilters, currentPath, s
73282
74423
  activeMatchIndex,
73283
74424
  closeFind
73284
74425
  ]);
73285
- useEffect(() => {
73286
- const handleFindKeyDown = (e) => {
73287
- if ((e.metaKey || e.ctrlKey) && e.key === "f") {
73288
- e.preventDefault();
73289
- e.stopPropagation();
73290
- setShowFind(true);
73291
- setTimeout(() => findInputRef.current?.focus(), 100);
73292
- }
73293
- if (e.key === "Escape" && showFind) closeFind();
73294
- };
73295
- document.addEventListener("keydown", handleFindKeyDown, true);
73296
- return () => document.removeEventListener("keydown", handleFindKeyDown, true);
73297
- }, [closeFind, showFind]);
74426
+ useFindBandShortcut(useCallback(() => {
74427
+ setShowFind(true);
74428
+ setTimeout(() => findInputRef.current?.focus(), 100);
74429
+ }, []), {
74430
+ onClose: closeFind,
74431
+ isOpen: showFind
74432
+ });
73298
74433
  return /* @__PURE__ */ jsxs("div", {
73299
74434
  className: clsx(gridCells_module_default.gridWrapper),
73300
74435
  children: [showFind && /* @__PURE__ */ jsx(FindBandUI, {
@@ -73587,29 +74722,8 @@ var rowForItem = (item) => item.type === "file" ? item.log : void 0;
73587
74722
  var buildLogListRow = (item) => {
73588
74723
  const log = rowForItem(item);
73589
74724
  const details = log?.header;
73590
- let totalTokens;
73591
- if (details?.stats?.model_usage) {
73592
- totalTokens = 0;
73593
- for (const usage of Object.values(details.stats.model_usage)) totalTokens += usage.total_tokens;
73594
- }
73595
- let duration;
73596
- if (details?.stats?.started_at && details?.stats?.completed_at) {
73597
- const start = new Date(details.stats.started_at).getTime();
73598
- const end = new Date(details.stats.completed_at).getTime();
73599
- if (start && end && end > start) duration = (end - start) / 1e3;
73600
- }
74725
+ const derived = log?.derived;
73601
74726
  const taskArgsSource = details?.eval?.task_args_passed ?? details?.eval?.task_args;
73602
- let taskArgs;
73603
- if (taskArgsSource) {
73604
- const entries = Object.entries(taskArgsSource);
73605
- if (entries.length > 0) taskArgs = entries.map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
73606
- }
73607
- let percentCompleted;
73608
- const total = details?.results?.total_samples;
73609
- const completed = details?.results?.completed_samples;
73610
- if (total && total > 0 && completed !== void 0) percentCompleted = completed / total * 100;
73611
- const sampleErrors = details?.sampleErrorCount;
73612
- const sampleLimits = details !== void 0 && details.sampleLimits.length > 0 ? details.sampleLimits.join(", ") : void 0;
73613
74727
  const row = {
73614
74728
  id: item.id,
73615
74729
  name: item.name,
@@ -73628,20 +74742,18 @@ var buildLogListRow = (item) => {
73628
74742
  totalSamples: details?.results?.total_samples,
73629
74743
  completedSamples: details?.results?.completed_samples,
73630
74744
  sandbox: details?.eval?.sandbox?.type,
73631
- totalTokens,
73632
- duration,
74745
+ totalTokens: derived?.total_tokens,
74746
+ duration: derived?.duration,
73633
74747
  taskFile: details?.eval?.task_file ?? void 0,
73634
- taskArgs,
74748
+ taskArgs: derived?.task_args,
73635
74749
  taskArgsRaw: taskArgsSource ?? void 0,
73636
74750
  tags: details?.tags,
73637
- percentCompleted,
73638
- sampleErrors,
73639
- sampleLimits,
74751
+ percentCompleted: derived?.percent_completed,
74752
+ sampleErrors: details?.sampleErrorCount,
74753
+ sampleLimits: derived?.sample_limits,
73640
74754
  errorMessage: details?.error?.message
73641
74755
  };
73642
- if (details?.results?.scores) {
73643
- for (const evalScore of details.results.scores) if (evalScore.metrics) for (const [metricName, metric] of Object.entries(evalScore.metrics)) row[`score_${evalScore.name}/${metricName}`] = metric.value;
73644
- }
74756
+ if (derived?.scores) for (const [scorerName, metrics] of Object.entries(derived.scores)) for (const [metricName, value] of Object.entries(metrics)) row[`score_${scorerName}/${metricName}`] = value;
73645
74757
  return row;
73646
74758
  };
73647
74759
  /**
@@ -73850,9 +74962,9 @@ var LogsPanel = ({ maybeShowSingleLog, mode = "logs" }) => {
73850
74962
  logDir,
73851
74963
  showRetriedLogs
73852
74964
  ]);
73853
- const scopePrefix = mode === "logs" ? currentDir : void 0;
74965
+ const scopeDir = mode === "logs" ? currentDir : void 0;
73854
74966
  const [scoresViewMode, setScoresViewMode] = useProperty("log-list-scores-view", "mode", { defaultValue: "by-metric" });
73855
- const { pickerColumns, visibility, setColumnVisibility, getValue, getComparator, getFilterType } = useLogListColumns(mode, scopePrefix, scoresViewMode);
74967
+ const { pickerColumns, visibility, setColumnVisibility, getValue, getComparator, getFilterType } = useLogListColumns(mode, scopeDir, scoresViewMode);
73856
74968
  const listData = useLogListData({
73857
74969
  items: logItems,
73858
74970
  scopeKey,
@@ -74212,418 +75324,6 @@ var useLogSampleNavigationActions = () => {
74212
75324
  hasNext
74213
75325
  };
74214
75326
  };
74215
- //#endregion
74216
- //#region src/utils/dom.ts
74217
- /**
74218
- * Finds the nearest scrollable ancestor of an element.
74219
- * Useful for programmatic scrolling when you need to find the scroll container.
74220
- *
74221
- * @param element - The element to start searching from
74222
- * @param options.minScrollBuffer - Minimum difference between scrollHeight and clientHeight
74223
- * to consider an element scrollable (default: 100)
74224
- * @returns The scrollable parent element, or null if none found
74225
- */
74226
- function findScrollableParent(element, options) {
74227
- const minBuffer = options?.minScrollBuffer ?? 100;
74228
- let current = element instanceof HTMLElement ? element : element?.parentElement;
74229
- while (current && current !== document.body) {
74230
- const style = getComputedStyle(current);
74231
- if ((style.overflowY === "auto" || style.overflowY === "scroll") && current.scrollHeight > current.clientHeight + minBuffer) return current;
74232
- current = current.parentElement;
74233
- }
74234
- return null;
74235
- }
74236
- /**
74237
- * Scrolls a Range (text selection) to the center of its scrollable container.
74238
- * Unlike element.scrollIntoView({ block: "center" }), this works correctly
74239
- * for selections within large elements (e.g., code blocks) by scrolling to
74240
- * the actual selection position rather than the element's top.
74241
- *
74242
- * @param range - The Range to scroll into view
74243
- * @param options.behavior - Scroll behavior ('auto' or 'smooth'), default: 'auto'
74244
- * @param options.fallbackToScrollIntoView - Whether to fall back to scrollIntoView
74245
- * if no scrollable parent is found (default: true)
74246
- */
74247
- function scrollRangeToCenter(range, options) {
74248
- const { behavior = "auto", fallbackToScrollIntoView = true } = options ?? {};
74249
- const rects = range.getClientRects();
74250
- if (rects.length === 0) return;
74251
- const selectionRect = rects[0];
74252
- if (selectionRect === void 0) return;
74253
- const scrollableParent = findScrollableParent(range.startContainer.parentElement);
74254
- if (scrollableParent) {
74255
- const parentRect = scrollableParent.getBoundingClientRect();
74256
- const targetScrollTop = selectionRect.top - parentRect.top + scrollableParent.scrollTop - scrollableParent.clientHeight / 2;
74257
- scrollableParent.scrollTo({
74258
- top: Math.max(0, targetScrollTop),
74259
- behavior
74260
- });
74261
- } else if (fallbackToScrollIntoView) range.startContainer.parentElement?.scrollIntoView({
74262
- behavior,
74263
- block: "center"
74264
- });
74265
- }
74266
- //#endregion
74267
- //#region src/components/FindBand.tsx
74268
- var findConfig = {
74269
- caseSensitive: false,
74270
- wrapAround: false,
74271
- wholeWord: false,
74272
- searchInFrames: false,
74273
- showDialog: false
74274
- };
74275
- var FindBand = () => {
74276
- const searchBoxRef = useRef(null);
74277
- const storeHideFind = useStore((state) => state.appActions.hideFind);
74278
- const { extendedFindTerm, countAllMatches } = useExtendedFind();
74279
- const setFindTarget = useFindTargetSetter();
74280
- const lastFoundItem = useRef(null);
74281
- const currentSearchTerm = useRef("");
74282
- const needsCursorRestoreRef = useRef(false);
74283
- const scrollTimeoutRef = useRef(null);
74284
- const focusTimeoutRef = useRef(null);
74285
- const searchIdRef = useRef(0);
74286
- const cachedCount = useRef({
74287
- term: "",
74288
- count: 0
74289
- });
74290
- const [matchCount, setMatchCount] = useState(null);
74291
- const [currentMatchIndex, setCurrentMatchIndex] = useState(0);
74292
- const [noResults, setNoResults] = useState(false);
74293
- const handleSearch = useCallback(async (back = false) => {
74294
- const thisSearchId = ++searchIdRef.current;
74295
- const searchTerm = searchBoxRef.current?.value ?? "";
74296
- if (!searchTerm) {
74297
- setMatchCount(null);
74298
- setCurrentMatchIndex(0);
74299
- setNoResults(false);
74300
- setFindTarget(null);
74301
- return;
74302
- }
74303
- const termChanged = currentSearchTerm.current !== searchTerm;
74304
- if (termChanged) {
74305
- lastFoundItem.current = null;
74306
- currentSearchTerm.current = searchTerm;
74307
- setCurrentMatchIndex(0);
74308
- }
74309
- let total;
74310
- if (cachedCount.current.term === searchTerm) total = cachedCount.current.count;
74311
- else {
74312
- total = countAllMatches(searchTerm);
74313
- cachedCount.current = {
74314
- term: searchTerm,
74315
- count: total
74316
- };
74317
- }
74318
- setMatchCount(total > 0 ? total : null);
74319
- const focusedElement = document.activeElement;
74320
- const selection = window.getSelection();
74321
- let savedRange = null;
74322
- if (selection && selection.rangeCount > 0) savedRange = selection.getRangeAt(0).cloneRange();
74323
- const savedScrollParent = savedRange ? findScrollableParent(savedRange.startContainer.parentElement) : null;
74324
- const savedScrollTop = savedScrollParent?.scrollTop ?? 0;
74325
- const result = await findExtendedInDOM(searchTerm, back, lastFoundItem.current, extendedFindTerm);
74326
- if (searchIdRef.current !== thisSearchId) return;
74327
- setNoResults(!result);
74328
- if (!result && savedRange) {
74329
- const sel = window.getSelection();
74330
- if (sel) {
74331
- sel.removeAllRanges();
74332
- sel.addRange(savedRange);
74333
- }
74334
- if (savedScrollParent) savedScrollParent.scrollTop = savedScrollTop;
74335
- }
74336
- if (result) {
74337
- const selection = window.getSelection();
74338
- if (selection && selection.rangeCount > 0) {
74339
- const range = selection.getRangeAt(0);
74340
- const parentElement = range.startContainer.parentElement || range.commonAncestorContainer;
74341
- const isNewMatch = !isLastFoundItem(range, lastFoundItem.current);
74342
- lastFoundItem.current = {
74343
- text: range.toString(),
74344
- offset: range.startOffset,
74345
- parentElement
74346
- };
74347
- if (termChanged) setFindTarget({
74348
- term: searchTerm,
74349
- eventId: ""
74350
- });
74351
- if (isNewMatch) setCurrentMatchIndex((prev) => {
74352
- if (back) return prev <= 1 ? total : prev - 1;
74353
- else return prev >= total ? 1 : prev + 1;
74354
- });
74355
- if (scrollTimeoutRef.current !== null) window.clearTimeout(scrollTimeoutRef.current);
74356
- scrollTimeoutRef.current = window.setTimeout(() => {
74357
- scrollRangeToCenter(range);
74358
- }, 100);
74359
- }
74360
- }
74361
- focusedElement?.focus();
74362
- }, [
74363
- setFindTarget,
74364
- extendedFindTerm,
74365
- countAllMatches
74366
- ]);
74367
- useEffect(() => {
74368
- focusTimeoutRef.current = window.setTimeout(() => {
74369
- searchBoxRef.current?.focus();
74370
- searchBoxRef.current?.select();
74371
- }, 10);
74372
- const focusTimeout = focusTimeoutRef.current;
74373
- return () => {
74374
- if (scrollTimeoutRef.current !== null) window.clearTimeout(scrollTimeoutRef.current);
74375
- if (focusTimeout !== null) window.clearTimeout(focusTimeout);
74376
- setFindTarget(null);
74377
- };
74378
- }, [setFindTarget]);
74379
- const handleKeyDown = useCallback((e) => {
74380
- if (e.key === "Escape") storeHideFind();
74381
- else if (e.key === "Enter") handleSearch(e.shiftKey);
74382
- else if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "g") {
74383
- e.preventDefault();
74384
- handleSearch(e.shiftKey);
74385
- } else if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "f") {
74386
- searchBoxRef.current?.focus();
74387
- searchBoxRef.current?.select();
74388
- }
74389
- }, [storeHideFind, handleSearch]);
74390
- const findPrevious = useCallback(() => {
74391
- handleSearch(true);
74392
- }, [handleSearch]);
74393
- const findNext = useCallback(() => {
74394
- handleSearch(false);
74395
- }, [handleSearch]);
74396
- const restoreCursor = useCallback(() => {
74397
- if (!needsCursorRestoreRef.current) return;
74398
- needsCursorRestoreRef.current = false;
74399
- const input = searchBoxRef.current;
74400
- if (input) {
74401
- const len = input.value.length;
74402
- input.setSelectionRange(len, len);
74403
- }
74404
- }, []);
74405
- const runDebouncedSearch = useCallback(async () => {
74406
- if (!searchBoxRef.current) return;
74407
- await handleSearch(false);
74408
- needsCursorRestoreRef.current = true;
74409
- }, [handleSearch]);
74410
- const latestRunSearchRef = useRef(runDebouncedSearch);
74411
- useEffect(() => {
74412
- latestRunSearchRef.current = runDebouncedSearch;
74413
- return () => {
74414
- latestRunSearchRef.current = null;
74415
- };
74416
- }, [runDebouncedSearch]);
74417
- const debouncedSearchRef = useRef(null);
74418
- const handleInputChange = useCallback(() => {
74419
- debouncedSearchRef.current ??= debounce$2(() => void latestRunSearchRef.current?.(), 100);
74420
- debouncedSearchRef.current();
74421
- }, []);
74422
- const handleBeforeInput = useCallback(() => {
74423
- const input = searchBoxRef.current;
74424
- if (input) {
74425
- if (!(input.selectionStart !== input.selectionEnd)) restoreCursor();
74426
- }
74427
- }, [restoreCursor]);
74428
- useEffect(() => {
74429
- const handleGlobalKeyDown = (e) => {
74430
- if (e.key === "F3") {
74431
- e.preventDefault();
74432
- handleSearch(e.shiftKey);
74433
- return;
74434
- }
74435
- if ((e.ctrlKey || e.metaKey) && e.key === "f") {
74436
- e.preventDefault();
74437
- e.stopPropagation();
74438
- searchBoxRef.current?.focus();
74439
- searchBoxRef.current?.select();
74440
- return;
74441
- }
74442
- if ((e.ctrlKey || e.metaKey) && e.key === "g") {
74443
- e.preventDefault();
74444
- e.stopPropagation();
74445
- handleSearch(e.shiftKey);
74446
- return;
74447
- }
74448
- if (e.ctrlKey || e.metaKey || e.altKey) return;
74449
- if (e.key.length !== 1 && e.key !== "Backspace" && e.key !== "Delete") return;
74450
- const input = searchBoxRef.current;
74451
- if (!input) return;
74452
- if (document.activeElement !== input) {
74453
- let active = document.activeElement;
74454
- while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
74455
- if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active instanceof HTMLElement && active.isContentEditable) return;
74456
- }
74457
- if (!(input.selectionStart !== input.selectionEnd)) restoreCursor();
74458
- if (document.activeElement !== input) input.focus();
74459
- };
74460
- document.addEventListener("keydown", handleGlobalKeyDown, true);
74461
- return () => {
74462
- document.removeEventListener("keydown", handleGlobalKeyDown, true);
74463
- };
74464
- }, [handleSearch, restoreCursor]);
74465
- return /* @__PURE__ */ jsx(FindBandUI, {
74466
- inputRef: searchBoxRef,
74467
- onClose: storeHideFind,
74468
- onNext: findNext,
74469
- onPrevious: findPrevious,
74470
- onKeyDown: handleKeyDown,
74471
- onBeforeInput: handleBeforeInput,
74472
- onChange: handleInputChange,
74473
- noResults,
74474
- matchCount: matchCount ?? void 0,
74475
- matchIndex: matchCount !== null && matchCount > 0 ? currentMatchIndex - 1 : void 0
74476
- });
74477
- };
74478
- function windowFind(searchTerm, back) {
74479
- return window.find(searchTerm, findConfig.caseSensitive, back, findConfig.wrapAround, findConfig.wholeWord, findConfig.searchInFrames, findConfig.showDialog);
74480
- }
74481
- function positionSelectionForWrap(back) {
74482
- if (!back) return;
74483
- const sel = window.getSelection();
74484
- if (sel) {
74485
- const range = document.createRange();
74486
- range.selectNodeContents(document.body);
74487
- range.collapse(false);
74488
- sel.removeAllRanges();
74489
- sel.addRange(range);
74490
- }
74491
- }
74492
- async function findExtendedInDOM(searchTerm, back, lastFoundItem, extendedFindTerm) {
74493
- let result = false;
74494
- let hasTriedExtendedSearch = false;
74495
- let extendedSearchSucceeded = false;
74496
- const maxAttempts = 25;
74497
- for (let attempts = 0; attempts < maxAttempts; attempts++) {
74498
- result = windowFind(searchTerm, back);
74499
- if (result) {
74500
- const selection = window.getSelection();
74501
- if (selection && selection.rangeCount > 0) {
74502
- const range = selection.getRangeAt(0);
74503
- const isUnsearchable = inUnsearchableElement(range);
74504
- const isSameAsLast = isLastFoundItem(range, lastFoundItem);
74505
- if (!isUnsearchable && !isSameAsLast) break;
74506
- if (isSameAsLast) {
74507
- if (!hasTriedExtendedSearch) {
74508
- hasTriedExtendedSearch = true;
74509
- window.getSelection()?.removeAllRanges();
74510
- if (await extendedFindTerm(searchTerm, back ? "backward" : "forward")) {
74511
- extendedSearchSucceeded = true;
74512
- await waitForTextInDOM(searchTerm);
74513
- continue;
74514
- }
74515
- }
74516
- if (extendedSearchSucceeded) {
74517
- const sel = window.getSelection();
74518
- if (sel?.rangeCount) sel.getRangeAt(0).collapse(!back);
74519
- } else {
74520
- window.getSelection()?.removeAllRanges();
74521
- positionSelectionForWrap(back);
74522
- }
74523
- result = windowFind(searchTerm, back);
74524
- if (result) {
74525
- const sel = window.getSelection();
74526
- if (sel && sel.rangeCount > 0) {
74527
- if (inUnsearchableElement(sel.getRangeAt(0))) continue;
74528
- }
74529
- }
74530
- break;
74531
- }
74532
- }
74533
- } else if (!hasTriedExtendedSearch) {
74534
- hasTriedExtendedSearch = true;
74535
- window.getSelection()?.removeAllRanges();
74536
- if (await extendedFindTerm(searchTerm, back ? "backward" : "forward")) {
74537
- extendedSearchSucceeded = true;
74538
- await waitForTextInDOM(searchTerm);
74539
- continue;
74540
- }
74541
- positionSelectionForWrap(back);
74542
- result = windowFind(searchTerm, back);
74543
- if (result) {
74544
- const sel = window.getSelection();
74545
- if (sel && sel.rangeCount > 0) {
74546
- if (inUnsearchableElement(sel.getRangeAt(0))) continue;
74547
- }
74548
- }
74549
- break;
74550
- } else break;
74551
- }
74552
- if (result) {
74553
- const sel = window.getSelection();
74554
- if (sel?.rangeCount && inUnsearchableElement(sel.getRangeAt(0))) {
74555
- sel.removeAllRanges();
74556
- result = false;
74557
- }
74558
- }
74559
- return result;
74560
- }
74561
- function isLastFoundItem(range, lastFoundItem) {
74562
- if (!lastFoundItem) return false;
74563
- const currentText = range.toString();
74564
- const currentOffset = range.startOffset;
74565
- const currentParentElement = range.startContainer.parentElement || range.commonAncestorContainer;
74566
- return currentText === lastFoundItem.text && currentOffset === lastFoundItem.offset && currentParentElement === lastFoundItem.parentElement;
74567
- }
74568
- function inUnsearchableElement(range) {
74569
- let element = selectionParentElement(range);
74570
- let isUnsearchable = false;
74571
- while (element) {
74572
- if (element.hasAttribute("data-unsearchable") || getComputedStyle(element).userSelect === "none") {
74573
- isUnsearchable = true;
74574
- break;
74575
- }
74576
- element = element.parentElement;
74577
- }
74578
- return isUnsearchable;
74579
- }
74580
- function selectionParentElement(range) {
74581
- let element = null;
74582
- if (range.startContainer.nodeType === Node.ELEMENT_NODE) element = range.startContainer;
74583
- else element = range.startContainer.parentElement;
74584
- if (!element && range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE) element = range.commonAncestorContainer;
74585
- else if (!element && range.commonAncestorContainer.parentElement) element = range.commonAncestorContainer.parentElement;
74586
- return element;
74587
- }
74588
- /**
74589
- * Polls until the search term appears in a searchable (non-unsearchable) DOM
74590
- * text node. After Virtuoso scrolls a virtual list item into view, the
74591
- * onContentReady callback may fire before the content is actually rendered,
74592
- * especially for large scroll distances. This ensures we wait for the text
74593
- * to be present before calling window.find().
74594
- */
74595
- function waitForTextInDOM(searchTerm, timeoutMs = 2e3) {
74596
- const lowerTerm = searchTerm.toLowerCase();
74597
- const isTextInSearchableDOM = () => {
74598
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, { acceptNode: (node) => {
74599
- let el = node.parentElement;
74600
- while (el) {
74601
- if (el.hasAttribute("data-unsearchable")) return NodeFilter.FILTER_REJECT;
74602
- el = el.parentElement;
74603
- }
74604
- return NodeFilter.FILTER_ACCEPT;
74605
- } });
74606
- while (walker.nextNode()) if (walker.currentNode.textContent?.toLowerCase().includes(lowerTerm)) return true;
74607
- return false;
74608
- };
74609
- return new Promise((resolve) => {
74610
- const interval = 50;
74611
- let elapsed = 0;
74612
- const check = () => {
74613
- if (isTextInSearchableDOM()) {
74614
- resolve(true);
74615
- return;
74616
- }
74617
- elapsed += interval;
74618
- if (elapsed >= timeoutMs) {
74619
- resolve(false);
74620
- return;
74621
- }
74622
- setTimeout(check, interval);
74623
- };
74624
- check();
74625
- });
74626
- }
74627
75327
  var InlineSampleDisplay_module_default = {
74628
75328
  container: "_container_ly812_1",
74629
75329
  scroller: "_scroller_ly812_7"
@@ -76134,6 +76834,184 @@ var MessageContents = ({ message, references }) => {
76134
76834
  references
76135
76835
  }) });
76136
76836
  };
76837
+ var AnnotatedScreenshot_module_default = {
76838
+ container: "_container_nfqzf_1",
76839
+ screenshot: "_screenshot_nfqzf_10",
76840
+ overlay: "_overlay_nfqzf_16",
76841
+ badge: "_badge_nfqzf_24",
76842
+ type: "_type_nfqzf_39",
76843
+ key: "_key_nfqzf_45"
76844
+ };
76845
+ //#endregion
76846
+ //#region ../../packages/inspect-components/src/chat/tools/browserActionUtils.ts
76847
+ /**
76848
+ * Tools whose actions can be annotated onto a screenshot. Membership requires
76849
+ * the tool's coordinate arguments to be in screenshot-pixel space and its
76850
+ * results to carry post-action screenshots (element-based browser tools like
76851
+ * web_browser_* don't qualify). The set plays two roles: it gates which calls
76852
+ * get a selfAnnotation, and the before-state scan in computeVisualActionContext
76853
+ * stops at the first tool call outside it — so adding a tool here also changes
76854
+ * which screenshots earlier actions may pair with.
76855
+ */
76856
+ var BROWSER_TOOL_FUNCTIONS = /* @__PURE__ */ new Set(["browser", "computer"]);
76857
+ var CLICK_ACTIONS = /* @__PURE__ */ new Set([
76858
+ "left_click",
76859
+ "right_click",
76860
+ "middle_click",
76861
+ "double_click",
76862
+ "triple_click"
76863
+ ]);
76864
+ var VISUAL_BROWSER_ACTIONS = /* @__PURE__ */ new Set([
76865
+ ...CLICK_ACTIONS,
76866
+ "scroll",
76867
+ "type",
76868
+ "key"
76869
+ ]);
76870
+ function asString(value) {
76871
+ return typeof value === "string" ? value : void 0;
76872
+ }
76873
+ function asCoordinate(value) {
76874
+ if (Array.isArray(value) && value.length === 2 && typeof value[0] === "number" && Number.isFinite(value[0]) && typeof value[1] === "number" && Number.isFinite(value[1])) return [value[0], value[1]];
76875
+ }
76876
+ /**
76877
+ * Narrows a tool call's arguments into a renderable annotation, or undefined
76878
+ * when the call is not a visual browser action (or its args are malformed).
76879
+ */
76880
+ function buildSelfAnnotation(functionName, args) {
76881
+ if (!BROWSER_TOOL_FUNCTIONS.has(functionName)) return void 0;
76882
+ const action = asString(args.action);
76883
+ if (action === void 0 || !VISUAL_BROWSER_ACTIONS.has(action)) return void 0;
76884
+ return {
76885
+ action,
76886
+ coordinate: asCoordinate(args.coordinate),
76887
+ text: asString(args.text),
76888
+ scrollDirection: asString(args.scroll_direction)
76889
+ };
76890
+ }
76891
+ //#endregion
76892
+ //#region ../../packages/inspect-components/src/chat/tools/AnnotatedScreenshot.tsx
76893
+ /**
76894
+ * Renders a tool result's content with the action annotation drawn over its
76895
+ * screenshot. Only the last renderable image is annotated: results may carry
76896
+ * text alongside the screenshot, and the last image reflects the page state
76897
+ * the action targeted.
76898
+ */
76899
+ var AnnotatedScreenshotOutput = ({ contents, annotation, context }) => {
76900
+ const annotatedIndex = contents.findLastIndex((c) => c.type === "image" && isRenderableImageSource(c.image));
76901
+ return /* @__PURE__ */ jsx(Fragment$1, { children: contents.map((c, i) => i === annotatedIndex && c.type === "image" ? /* @__PURE__ */ jsx(AnnotatedScreenshot, {
76902
+ src: c.image,
76903
+ annotation
76904
+ }, i) : /* @__PURE__ */ jsx(MessageContent, {
76905
+ contents: [c],
76906
+ context
76907
+ }, i)) });
76908
+ };
76909
+ /**
76910
+ * A screenshot with the action annotation drawn on top. The SVG overlay's
76911
+ * viewBox is the image's natural size, so annotation coordinates (which are
76912
+ * in screenshot pixels) map to display position without any JS measurement,
76913
+ * and the overlay rescales with the image automatically.
76914
+ */
76915
+ var AnnotatedScreenshot = ({ src, annotation }) => {
76916
+ const [naturalSize, setNaturalSize] = useState();
76917
+ return /* @__PURE__ */ jsxs("div", {
76918
+ className: AnnotatedScreenshot_module_default.container,
76919
+ children: [
76920
+ /* @__PURE__ */ jsx("img", {
76921
+ src,
76922
+ className: AnnotatedScreenshot_module_default.screenshot,
76923
+ onLoad: (e) => setNaturalSize({
76924
+ width: e.currentTarget.naturalWidth,
76925
+ height: e.currentTarget.naturalHeight
76926
+ })
76927
+ }),
76928
+ naturalSize && naturalSize.width > 0 && naturalSize.height > 0 && /* @__PURE__ */ jsx("svg", {
76929
+ viewBox: `0 0 ${naturalSize.width} ${naturalSize.height}`,
76930
+ preserveAspectRatio: "none",
76931
+ className: AnnotatedScreenshot_module_default.overlay,
76932
+ children: renderSvgAnnotation(annotation)
76933
+ }),
76934
+ renderHtmlAnnotation(annotation)
76935
+ ]
76936
+ });
76937
+ };
76938
+ function renderSvgAnnotation(annotation) {
76939
+ const { action, coordinate } = annotation;
76940
+ if (CLICK_ACTIONS.has(action) && coordinate) {
76941
+ const [x, y] = coordinate;
76942
+ return /* @__PURE__ */ jsxs("g", {
76943
+ transform: `translate(${x}, ${y})`,
76944
+ children: [/* @__PURE__ */ jsx("circle", {
76945
+ cx: "0",
76946
+ cy: "0",
76947
+ r: "16",
76948
+ stroke: "rgba(239,68,68,0.8)",
76949
+ strokeWidth: "3",
76950
+ fill: "none",
76951
+ filter: "drop-shadow(0 0 6px rgba(239,68,68,0.4))"
76952
+ }), /* @__PURE__ */ jsx("svg", {
76953
+ viewBox: "0 0 32 32",
76954
+ width: "30",
76955
+ height: "30",
76956
+ x: "-10",
76957
+ y: "-7",
76958
+ children: /* @__PURE__ */ jsxs("g", {
76959
+ fill: "none",
76960
+ fillRule: "evenodd",
76961
+ transform: "translate(10 7)",
76962
+ children: [/* @__PURE__ */ jsx("path", {
76963
+ d: "m6.148 18.473 1.863-1.003 1.615-.839-2.568-4.816h4.332l-11.379-11.408v16.015l3.316-3.221z",
76964
+ fill: "#fff"
76965
+ }), /* @__PURE__ */ jsx("path", {
76966
+ d: "m6.431 17 1.765-.941-2.775-5.202h3.604l-8.025-8.043v11.188l2.53-2.442z",
76967
+ fill: "#000"
76968
+ })]
76969
+ })
76970
+ })]
76971
+ });
76972
+ }
76973
+ if (action === "scroll" && coordinate) {
76974
+ const [x, y] = coordinate;
76975
+ let arrow = "↕";
76976
+ if (annotation.scrollDirection) {
76977
+ const dir = annotation.scrollDirection.toLowerCase();
76978
+ if (dir.includes("up")) arrow = "↑";
76979
+ else if (dir.includes("down")) arrow = "↓";
76980
+ else if (dir.includes("left")) arrow = "←";
76981
+ else if (dir.includes("right")) arrow = "→";
76982
+ }
76983
+ return /* @__PURE__ */ jsxs("g", {
76984
+ transform: `translate(${x}, ${y})`,
76985
+ children: [/* @__PURE__ */ jsx("circle", {
76986
+ cx: "0",
76987
+ cy: "0",
76988
+ r: "18",
76989
+ fill: "rgba(59,130,246,0.8)"
76990
+ }), /* @__PURE__ */ jsx("text", {
76991
+ x: "0",
76992
+ y: "0",
76993
+ fill: "white",
76994
+ fontSize: "20",
76995
+ textAnchor: "middle",
76996
+ dominantBaseline: "central",
76997
+ fontWeight: "bold",
76998
+ children: arrow
76999
+ })]
77000
+ });
77001
+ }
77002
+ return null;
77003
+ }
77004
+ function renderHtmlAnnotation(annotation) {
77005
+ const { action, text } = annotation;
77006
+ if (action === "type" || action === "key") {
77007
+ const isKey = action === "key";
77008
+ return /* @__PURE__ */ jsxs("div", {
77009
+ className: clsx(AnnotatedScreenshot_module_default.badge, isKey ? AnnotatedScreenshot_module_default.key : AnnotatedScreenshot_module_default.type),
77010
+ children: ["⌨ ", text || ""]
77011
+ });
77012
+ }
77013
+ return null;
77014
+ }
76137
77015
  var customToolRendering_module_default = { submitView: "_submitView_1ru17_1" };
76138
77016
  var ToolSearchView_module_default = {
76139
77017
  catalog: "_catalog_gx8eg_1",
@@ -76246,7 +77124,7 @@ var ToolCallView_module_default = { toolCallView: "_toolCallView_x6cus_1" };
76246
77124
  /**
76247
77125
  * Renders the ToolCallView component.
76248
77126
  */
76249
- var ToolCallView = ({ id, tool, functionCall, input, description, contentType, view, output, mode, collapsible = true, section = "all", getCustomToolView }) => {
77127
+ var ToolCallView = ({ id, tool, functionCall, input, selfAnnotation, inputScreenshot, description, contentType, view, output, mode, collapsible = true, section = "all", getCustomToolView }) => {
76250
77128
  const displayMode = useDisplayMode();
76251
77129
  function isContentImage(value) {
76252
77130
  if (value && typeof value === "object") {
@@ -76282,6 +77160,8 @@ var ToolCallView = ({ id, tool, functionCall, input, description, contentType, v
76282
77160
  description,
76283
77161
  contentType,
76284
77162
  output,
77163
+ selfAnnotation,
77164
+ inputScreenshot,
76285
77165
  mode
76286
77166
  };
76287
77167
  const customView = displayMode === "rendered" ? getCustomToolView?.(props) ?? getDefaultCustomToolView(props) : void 0;
@@ -76327,9 +77207,25 @@ var ToolCallView = ({ id, tool, functionCall, input, description, contentType, v
76327
77207
  contents: normalizedContent,
76328
77208
  context
76329
77209
  }) : null;
77210
+ const actionElement = selfAnnotation && inputScreenshot ? /* @__PURE__ */ jsx(AnnotatedScreenshotOutput, {
77211
+ contents: inputScreenshot,
77212
+ annotation: selfAnnotation,
77213
+ context
77214
+ }) : null;
77215
+ let outputContent = outputSection;
77216
+ if (actionElement) outputContent = hasContent ? /* @__PURE__ */ jsxs(NavPills, {
77217
+ id: `${id}-browser-action`,
77218
+ children: [/* @__PURE__ */ jsx("div", {
77219
+ title: "Result",
77220
+ children: outputSection
77221
+ }), /* @__PURE__ */ jsx("div", {
77222
+ title: "Action",
77223
+ children: actionElement
77224
+ })]
77225
+ }) : actionElement;
76330
77226
  return /* @__PURE__ */ jsxs("div", {
76331
77227
  className: clsx(ToolCallView_module_default.toolCallView),
76332
- children: [section !== "output" ? callSection : null, section !== "call" ? outputSection : null]
77228
+ children: [section !== "output" ? callSection : null, section !== "call" ? outputContent : null]
76333
77229
  });
76334
77230
  };
76335
77231
  /**
@@ -76366,7 +77262,7 @@ var ClientToolCall_module_default = { custom: "_custom_v2cay_4" };
76366
77262
  * header (terminal icon · mono tool name · args summary), the input zone
76367
77263
  * (e.g. code) and the output well stacked beneath.
76368
77264
  */
76369
- var ClientToolCall = ({ id, tool, title, functionCall, input, description, contentType, view, output, error, className, getCustomToolView }) => {
77265
+ var ClientToolCall = ({ id, tool, title, functionCall, input, description, contentType, view, output, selfAnnotation, inputScreenshot, error, className, getCustomToolView }) => {
76370
77266
  const displayMode = useDisplayMode();
76371
77267
  const viewProps = {
76372
77268
  id,
@@ -76376,7 +77272,9 @@ var ClientToolCall = ({ id, tool, title, functionCall, input, description, conte
76376
77272
  description,
76377
77273
  contentType,
76378
77274
  view,
76379
- output
77275
+ output,
77276
+ selfAnnotation,
77277
+ inputScreenshot
76380
77278
  };
76381
77279
  const customView = displayMode === "rendered" ? getCustomToolView?.(viewProps) ?? getDefaultCustomToolView(viewProps) : void 0;
76382
77280
  if (customView) return /* @__PURE__ */ jsx("div", {
@@ -76385,7 +77283,8 @@ var ClientToolCall = ({ id, tool, title, functionCall, input, description, conte
76385
77283
  });
76386
77284
  const hasInput = input !== void 0 && input !== null && input !== "" || !!view?.content;
76387
77285
  const showError = !!error;
76388
- const showOutput = !showError && hasOutputContent(output);
77286
+ const showAnnotation = !!selfAnnotation && !!inputScreenshot;
77287
+ const showOutput = !showError && (hasOutputContent(output) || showAnnotation);
76389
77288
  return /* @__PURE__ */ jsxs(ToolBlock, {
76390
77289
  id,
76391
77290
  icon: iconForTool(tool),
@@ -76403,7 +77302,11 @@ var ClientToolCall = ({ id, tool, title, functionCall, input, description, conte
76403
77302
  contents: input,
76404
77303
  toolCallView: view
76405
77304
  })
76406
- }) }) : null, showError ? /* @__PURE__ */ jsx(ToolBlockOutput, { children: /* @__PURE__ */ jsx(ToolCallErrorView, { error }) }) : showOutput ? /* @__PURE__ */ jsx(ToolBlockOutput, { children: /* @__PURE__ */ jsx(ToolCallView, {
77305
+ }) }) : null, showError ? /* @__PURE__ */ jsxs(ToolBlockOutput, { children: [/* @__PURE__ */ jsx(ToolCallErrorView, { error }), selfAnnotation && inputScreenshot ? /* @__PURE__ */ jsx(AnnotatedScreenshotOutput, {
77306
+ contents: inputScreenshot,
77307
+ annotation: selfAnnotation,
77308
+ context: defaultContext()
77309
+ }) : null] }) : showOutput ? /* @__PURE__ */ jsx(ToolBlockOutput, { children: /* @__PURE__ */ jsx(ToolCallView, {
76407
77310
  ...viewProps,
76408
77311
  section: "output"
76409
77312
  }) }) : null]
@@ -88412,7 +89315,9 @@ var ToolEventView = ({ eventNode, childNodes, className, context, eventCallbacks
88412
89315
  description,
88413
89316
  contentType,
88414
89317
  output: event.result ?? "",
88415
- error: showError ? event.error : void 0,
89318
+ selfAnnotation: context?.selfAnnotation,
89319
+ inputScreenshot: context?.inputScreenshot,
89320
+ error: showError && event.error ? event.error : void 0,
88416
89321
  view: resolvedView
88417
89322
  });
88418
89323
  const toolLabel = toolLabels.messageLabels?.[event.id];
@@ -88517,6 +89422,40 @@ var TranscriptVirtualListComponent_module_default = {
88517
89422
  runningTool: "_runningTool_t3xlr_31"
88518
89423
  };
88519
89424
  //#endregion
89425
+ //#region ../../packages/inspect-components/src/transcript/transcriptVisualActions.ts
89426
+ function computeVisualActionContext(eventNodes, index) {
89427
+ const node = eventNodes[index];
89428
+ if (!node || node.event.event !== "tool") return {};
89429
+ const toolEvent = node.event;
89430
+ const selfAnnotation = buildSelfAnnotation(toolEvent.function, toolEvent.arguments);
89431
+ if (!selfAnnotation) return {};
89432
+ for (let i = index - 1; i >= 0; i--) {
89433
+ const candidate = eventNodes[i];
89434
+ if (!candidate) continue;
89435
+ if (candidate.depth > node.depth) continue;
89436
+ if (candidate.depth < node.depth) break;
89437
+ if (candidate.event.event !== "tool") continue;
89438
+ const candEvent = candidate.event;
89439
+ if (!BROWSER_TOOL_FUNCTIONS.has(candEvent.function)) break;
89440
+ const inputScreenshot = normalizeScreenshotResult(candEvent.result);
89441
+ if (inputScreenshot && inputScreenshot.some((c) => c.type === "image")) return {
89442
+ inputScreenshot,
89443
+ selfAnnotation
89444
+ };
89445
+ }
89446
+ return {};
89447
+ }
89448
+ function normalizeScreenshotResult(result) {
89449
+ if (Array.isArray(result)) {
89450
+ const filtered = result.filter((item) => item.type !== "document");
89451
+ return filtered.length > 0 ? filtered : void 0;
89452
+ }
89453
+ if (result && typeof result === "object" && "type" in result) {
89454
+ if (result.type === "document") return void 0;
89455
+ return [result];
89456
+ }
89457
+ }
89458
+ //#endregion
88520
89459
  //#region ../../packages/inspect-components/src/transcript/TranscriptVirtualListComponent.tsx
88521
89460
  /**
88522
89461
  * Renders the Transcript component.
@@ -88541,10 +89480,13 @@ var TranscriptVirtualListComponent = ({ id, listHandle, eventNodes, scrollRef, r
88541
89480
  for (const [i, node] of eventNodes.entries()) {
88542
89481
  const hasToolEvents = hasToolEventsLookup[i] ?? false;
88543
89482
  const turnInfo = turnMap?.get(node.id);
89483
+ const { inputScreenshot, selfAnnotation } = computeVisualActionContext(eventNodes, i);
88544
89484
  map.set(node.id, {
88545
89485
  hasToolEvents,
88546
89486
  turnInfo,
88547
- ...eventNodeContext
89487
+ ...eventNodeContext,
89488
+ inputScreenshot,
89489
+ selfAnnotation
88548
89490
  });
88549
89491
  }
88550
89492
  return map;
@@ -94712,8 +95654,9 @@ var TranscriptPanel = memo((props) => {
94712
95654
  logFile,
94713
95655
  logDir
94714
95656
  ]);
95657
+ const getFullEventUrl = useCallback((eventId) => toFullUrlMaybe(getEventUrl(eventId)), [getEventUrl]);
94715
95658
  const renderLink = useCallback((url, children) => /* @__PURE__ */ jsx(Link, {
94716
- to: url,
95659
+ to: routeFromFullUrl(url),
94717
95660
  replace: true,
94718
95661
  children
94719
95662
  }), []);
@@ -94751,8 +95694,8 @@ var TranscriptPanel = memo((props) => {
94751
95694
  listId: id,
94752
95695
  initialEventId,
94753
95696
  initialMessageId,
94754
- getEventUrl,
94755
- linkingEnabled: true,
95697
+ getEventUrl: getFullEventUrl,
95698
+ linkingEnabled: isHostedEnvironment(),
94756
95699
  bulkCollapse,
94757
95700
  collapseState,
94758
95701
  eventsListRef,
@@ -94857,14 +95800,7 @@ var SampleDisplay = ({ id, scrollRef, showActivity, focusOnLoad }) => {
94857
95800
  navigate
94858
95801
  ]);
94859
95802
  const setNativeFind = useStore((state) => state.appActions.setNativeFind);
94860
- const getMessageUrl = useCallback((messageId) => {
94861
- return urlLogPath ? sampleMessageUrl(sampleUrlBuilder, messageId, urlLogPath, urlSampleId, urlEpoch) : void 0;
94862
- }, [
94863
- sampleUrlBuilder,
94864
- urlLogPath,
94865
- urlSampleId,
94866
- urlEpoch
94867
- ]);
95803
+ const getMessageUrl = useFullSampleMessageUrlBuilder();
94868
95804
  const chatDisplay = useMemo(() => ({
94869
95805
  indented: true,
94870
95806
  formatDateTime
@@ -95528,37 +96464,27 @@ var SampleDetailComponent = ({ sampleId, epoch, tabId, navigation, navbarConfig
95528
96464
  useEffect(() => {
95529
96465
  if (tabId) setSampleTab(tabId);
95530
96466
  }, [tabId, setSampleTab]);
96467
+ useFindBandShortcut(useCallback(() => setShowFind(true), [setShowFind]), {
96468
+ onClose: hideFind,
96469
+ isOpen: showFind,
96470
+ enabled: !nativeFind
96471
+ });
95531
96472
  const handleKeyDown = useCallback((e) => {
95532
- let activeElement = document.activeElement;
95533
- while (activeElement?.shadowRoot?.activeElement) activeElement = activeElement.shadowRoot.activeElement;
95534
- const isInputFocused = activeElement && (activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA" || activeElement.tagName === "SELECT" || activeElement instanceof HTMLElement && activeElement.isContentEditable);
95535
- if ((e.ctrlKey || e.metaKey) && e.key === "f") {
95536
- if (!nativeFind) {
96473
+ if (isEditableTarget(deepActiveElement())) return;
96474
+ if (e.key === "ArrowLeft") {
96475
+ if (hasPrevious) {
95537
96476
  e.preventDefault();
95538
- e.stopPropagation();
95539
- setShowFind(true);
96477
+ onPrevious();
95540
96478
  }
95541
- } else if (e.key === "Escape") {
95542
- if (!nativeFind) hideFind();
95543
- } else if (!isInputFocused) {
95544
- if (e.key === "ArrowLeft") {
95545
- if (hasPrevious) {
95546
- e.preventDefault();
95547
- onPrevious();
95548
- }
95549
- } else if (e.key === "ArrowRight") {
95550
- if (hasNext) {
95551
- e.preventDefault();
95552
- onNext();
95553
- }
96479
+ } else if (e.key === "ArrowRight") {
96480
+ if (hasNext) {
96481
+ e.preventDefault();
96482
+ onNext();
95554
96483
  }
95555
96484
  }
95556
96485
  }, [
95557
- setShowFind,
95558
- hideFind,
95559
96486
  hasPrevious,
95560
96487
  hasNext,
95561
- nativeFind,
95562
96488
  onPrevious,
95563
96489
  onNext
95564
96490
  ]);
@@ -95574,7 +96500,7 @@ var SampleDetailComponent = ({ sampleId, epoch, tabId, navigation, navbarConfig
95574
96500
  action();
95575
96501
  }
95576
96502
  }, []);
95577
- return /* @__PURE__ */ jsx(ExtendedFindProvider, { children: /* @__PURE__ */ jsxs(FindTargetProvider, { children: [showFind ? /* @__PURE__ */ jsx(FindBand, {}) : "", /* @__PURE__ */ jsxs("div", {
96503
+ return /* @__PURE__ */ jsx(ExtendedFindProvider, { children: /* @__PURE__ */ jsxs(FindTargetProvider, { children: [showFind ? /* @__PURE__ */ jsx(FindBand, { onClose: hideFind }) : "", /* @__PURE__ */ jsxs("div", {
95578
96504
  className: SampleDetailComponent_module_default.detail,
95579
96505
  children: [/* @__PURE__ */ jsx(ApplicationNavbar, {
95580
96506
  currentPath,
@@ -120440,7 +121366,7 @@ var SampleFilter = () => {
120440
121366
  activateOnCompletion: (c) => c.label.endsWith(" ")
120441
121367
  }), [filterItems, samples]);
120442
121368
  const makeLinter = useCallback(() => linter((view) => getLints(view, filterError)), [filterError]);
120443
- const debounceSetFilter = useMemo(() => debounce$2((value) => {
121369
+ const debounceSetFilter = useMemo(() => debounce$3((value) => {
120444
121370
  setFilter(value);
120445
121371
  }, 200), [setFilter]);
120446
121372
  const makeUpdateListener = useCallback(() => EditorView.updateListener.of((update) => {
@@ -120885,7 +121811,7 @@ var SamplesTab = ({ running, scrollRef, showColumnSelector, setShowColumnSelecto
120885
121811
  const items = useMemo(() => {
120886
121812
  if (!samplesDescriptor || !selectedLogFile) return [];
120887
121813
  return sampleSummaries.map((sample) => {
120888
- const tokens = sample.model_usage ? Object.values(sample.model_usage).reduce((sum, u) => sum + (u.total_tokens ?? 0), 0) : void 0;
121814
+ const tokens = totalSampleTokens(sample.model_usage);
120889
121815
  return {
120890
121816
  logFile: selectedLogFile,
120891
121817
  sampleId: sample.id,
@@ -122592,30 +123518,17 @@ var LogViewLayout = () => {
122592
123518
  const prefix = useRoutePrefix();
122593
123519
  const navigationUrl = (file, log_dir) => logsUrl(file, log_dir, void 0, prefix);
122594
123520
  const mainAppRef = useRef(null);
122595
- useEffect(() => {
122596
- if (nativeFind) return;
122597
- const handleGlobalKeyDown = (e) => {
122598
- if ((e.ctrlKey || e.metaKey) && e.key === "f") {
122599
- e.preventDefault();
122600
- e.stopPropagation();
122601
- if (setShowFind) setShowFind(true);
122602
- } else if (e.key === "Escape") hideFind();
122603
- };
122604
- document.addEventListener("keydown", handleGlobalKeyDown, true);
122605
- return () => {
122606
- document.removeEventListener("keydown", handleGlobalKeyDown, true);
122607
- };
122608
- }, [
122609
- setShowFind,
122610
- hideFind,
122611
- nativeFind
122612
- ]);
123521
+ useFindBandShortcut(useCallback(() => setShowFind(true), [setShowFind]), {
123522
+ onClose: hideFind,
123523
+ isOpen: showFind,
123524
+ enabled: !nativeFind
123525
+ });
122613
123526
  return /* @__PURE__ */ jsx(ExtendedFindProvider, { children: /* @__PURE__ */ jsx(FindTargetProvider, { children: /* @__PURE__ */ jsxs("div", {
122614
123527
  ref: mainAppRef,
122615
123528
  className: clsx("app-main-grid", singleFileMode ? "single-file-mode" : void 0, "log-view"),
122616
123529
  tabIndex: 0,
122617
123530
  children: [
122618
- showFind ? /* @__PURE__ */ jsx(FindBand, {}) : "",
123531
+ showFind ? /* @__PURE__ */ jsx(FindBand, { onClose: hideFind }) : "",
122619
123532
  !singleFileMode ? /* @__PURE__ */ jsx(ApplicationNavbar, {
122620
123533
  fnNavigationUrl: navigationUrl,
122621
123534
  currentPath: logPath
@@ -123213,7 +124126,7 @@ var SamplesPanel = () => {
123213
124126
  const currentDirLogFiles = useMemo(() => {
123214
124127
  const files = [];
123215
124128
  for (const logFile of logFiles) {
123216
- const inCurrentDir = logFile.name.startsWith(currentDir);
124129
+ const inCurrentDir = logFile.name.startsWith(scopePrefix(currentDir));
123217
124130
  const skipped = !showRetriedLogs && logFile.retried;
123218
124131
  if (inCurrentDir && !skipped) files.push(logFile);
123219
124132
  }
@@ -123324,8 +124237,7 @@ var SamplesPanel = () => {
123324
124237
  acc[log.name] = log;
123325
124238
  return acc;
123326
124239
  }, {});
123327
- const allRows = scopedSamples.map(({ logFile, summary: sample, log }) => {
123328
- const tokens = sample.model_usage ? Object.values(sample.model_usage).reduce((sum, u) => sum + (u.total_tokens ?? 0), 0) : void 0;
124240
+ const allRows = scopedSamples.map(({ logFile, summary: sample, derived, log }) => {
123329
124241
  const row = {
123330
124242
  logFile,
123331
124243
  sampleId: sample.id,
@@ -123335,17 +124247,17 @@ var SamplesPanel = () => {
123335
124247
  task: log.task || "",
123336
124248
  model: log.model || "",
123337
124249
  status: log.status,
123338
- input: inputString(sample.input).join("\n"),
123339
- target: Array.isArray(sample.target) ? sample.target.join(", ") : sample.target,
124250
+ input: derived.input,
124251
+ target: derived.target,
123340
124252
  error: sample.error,
123341
124253
  limit: sample.limit,
123342
124254
  retries: sample.retries,
123343
- fallbacks: totalModelFallbacks(sample.model_fallbacks) || void 0,
124255
+ fallbacks: derived.fallbacks,
123344
124256
  completed: sample.completed,
123345
- tokens,
124257
+ tokens: derived.tokens,
123346
124258
  duration: sample.total_time ?? void 0
123347
124259
  };
123348
- if (sample.scores) for (const [scoreName, score] of Object.entries(sample.scores)) row[`${SCORE_FIELD_RAW_PREFIX}${scoreName}`] = score.value;
124260
+ if (derived.scores) for (const [scoreName, value] of Object.entries(derived.scores)) row[`${SCORE_FIELD_RAW_PREFIX}${scoreName}`] = value;
123349
124261
  return row;
123350
124262
  });
123351
124263
  const _sampleRows = allRows.filter((row) => row.logFile in logInCurrentDirByName);
@@ -123570,6 +124482,8 @@ var AppRouter = createHashRouter([{
123570
124482
  //#endregion
123571
124483
  //#region src/app/App.tsx
123572
124484
  var componentIcons = {
124485
+ arrowDown: ApplicationIcons.arrows.down,
124486
+ arrowUp: ApplicationIcons.arrows.up,
123573
124487
  chevronDown: ApplicationIcons.chevron.down,
123574
124488
  chevronUp: ApplicationIcons.collapse.up,
123575
124489
  clearText: ApplicationIcons["clear-text"],