@sentry/junior-dashboard 0.60.0 → 0.60.1

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.
@@ -10,6 +10,8 @@ export declare function formatTime(value: string | undefined): string;
10
10
  export declare function formatRelativeTime(value: string | undefined): string;
11
11
  /** Format millisecond durations for compact transcript metadata. */
12
12
  export declare function formatMs(value: number | undefined): string;
13
+ /** Format aggregate runtime across turn summaries when duration data exists. */
14
+ export declare function formatDurationTotal(durations: Array<number | undefined>): string;
13
15
  /** Format transcript event timestamps independently from turn start offsets. */
14
16
  export declare function formatMessageTimestamp(value: number | undefined): string;
15
17
  /** Format a transcript event as an offset from the current turn start. */
@@ -22,6 +24,8 @@ export declare function turnMessageCount(turn: ConversationTurn): number;
22
24
  export declare function turnToolCallCount(turn: ConversationTurn): number;
23
25
  /** Format known token counters without estimating per-message usage. */
24
26
  export declare function formatTokenTotal(usage: TurnUsage | undefined): string;
27
+ /** Format the aggregate token count across conversation turns. */
28
+ export declare function formatUsageTotal(usages: Array<TurnUsage | undefined>): string;
25
29
  /** Format known token counters with available input/output detail. */
26
30
  export declare function formatUsage(usage: TurnUsage | undefined): string;
27
31
  /** Format a conversation span from first turn start to latest activity. */
package/dist/client.js CHANGED
@@ -15891,12 +15891,12 @@ var require_use_sync_external_store_shim_production = __commonJS({
15891
15891
  return x2 === y2 && (0 !== x2 || 1 / x2 === 1 / y2) || x2 !== x2 && y2 !== y2;
15892
15892
  }
15893
15893
  var objectIs = "function" === typeof Object.is ? Object.is : is4;
15894
- var useState21 = React53.useState;
15894
+ var useState23 = React53.useState;
15895
15895
  var useEffect27 = React53.useEffect;
15896
15896
  var useLayoutEffect12 = React53.useLayoutEffect;
15897
15897
  var useDebugValue2 = React53.useDebugValue;
15898
15898
  function useSyncExternalStore$2(subscribe, getSnapshot) {
15899
- var value = getSnapshot(), _useState = useState21({ inst: { value, getSnapshot } }), inst = _useState[0].inst, forceUpdate = _useState[1];
15899
+ var value = getSnapshot(), _useState = useState23({ inst: { value, getSnapshot } }), inst = _useState[0].inst, forceUpdate = _useState[1];
15900
15900
  useLayoutEffect12(
15901
15901
  function() {
15902
15902
  inst.value = value;
@@ -19462,7 +19462,7 @@ function useQuery(options, queryClient) {
19462
19462
  }
19463
19463
 
19464
19464
  // src/client.tsx
19465
- var import_react51 = __toESM(require_react(), 1);
19465
+ var import_react53 = __toESM(require_react(), 1);
19466
19466
  var import_client = __toESM(require_client(), 1);
19467
19467
 
19468
19468
  // ../../node_modules/.pnpm/react-router@7.16.0_react-dom@19.2.6_react@19.2.6__react@19.2.6/node_modules/react-router/dist/development/chunk-QUQL4437.mjs
@@ -30771,6 +30771,13 @@ function formatMs(value) {
30771
30771
  const remainingSeconds = Math.round(seconds2 % 60);
30772
30772
  return `${minutes}m ${remainingSeconds}s`;
30773
30773
  }
30774
+ function formatDurationTotal(durations) {
30775
+ const total = durations.reduce((sum, value) => {
30776
+ if (typeof value !== "number" || !Number.isFinite(value)) return sum;
30777
+ return (sum ?? 0) + Math.max(0, Math.floor(value));
30778
+ }, void 0);
30779
+ return total === void 0 ? "" : formatMs(total);
30780
+ }
30774
30781
  function formatMessageTimestamp(value) {
30775
30782
  if (typeof value !== "number" || !Number.isFinite(value))
30776
30783
  return "no timestamp";
@@ -30798,6 +30805,21 @@ function formatNumber(value) {
30798
30805
  const formatted = scaled >= 100 || Number.isInteger(scaled) ? Math.round(scaled).toString() : scaled >= 10 ? Math.round(scaled).toString() : (Math.floor(scaled * 10) / 10).toFixed(1).replace(/\.0$/, "");
30799
30806
  return `${formatted}${suffix2}`;
30800
30807
  }
30808
+ function getFiniteTokenCount(value) {
30809
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : void 0;
30810
+ }
30811
+ function getUsageComponentTotal(usage) {
30812
+ return [
30813
+ usage.inputTokens,
30814
+ usage.outputTokens,
30815
+ usage.cachedInputTokens,
30816
+ usage.cacheCreationTokens
30817
+ ].reduce((sum, value) => {
30818
+ const count = getFiniteTokenCount(value);
30819
+ if (count === void 0) return sum;
30820
+ return (sum ?? 0) + count;
30821
+ }, void 0);
30822
+ }
30801
30823
  function formatBytes(value) {
30802
30824
  if (typeof value !== "number" || !Number.isFinite(value)) return "0b";
30803
30825
  const bytes = Math.max(0, Math.floor(value));
@@ -30814,8 +30836,28 @@ function formatBytes(value) {
30814
30836
  function transcriptSource(turn) {
30815
30837
  return turn.transcriptAvailable ? turn.transcript : turn.transcriptMetadata ?? [];
30816
30838
  }
30839
+ function isConversationMessageRole(role) {
30840
+ const normalized = role.toLowerCase();
30841
+ return normalized === "user" || normalized === "assistant";
30842
+ }
30843
+ function hasTextPart(message) {
30844
+ return message.parts.some((part) => {
30845
+ if (part.type !== "text") return false;
30846
+ if (part.redacted) return true;
30847
+ return typeof part.text === "string" && part.text.trim().length > 0;
30848
+ });
30849
+ }
30850
+ function isConversationMessage(message) {
30851
+ if (!isConversationMessageRole(message.role)) return false;
30852
+ if (message.role.toLowerCase() === "assistant") return hasTextPart(message);
30853
+ return message.parts.length > 0;
30854
+ }
30817
30855
  function turnMessageCount(turn) {
30818
- return turn.transcriptMessageCount ?? transcriptSource(turn).length;
30856
+ const source = transcriptSource(turn);
30857
+ if (source.length > 0) {
30858
+ return source.filter(isConversationMessage).length;
30859
+ }
30860
+ return turn.transcriptMessageCount ?? 0;
30819
30861
  }
30820
30862
  function turnToolCallCount(turn) {
30821
30863
  return transcriptSource(turn).reduce((count, message) => {
@@ -30824,23 +30866,20 @@ function turnToolCallCount(turn) {
30824
30866
  }
30825
30867
  function totalUsageTokens(usage) {
30826
30868
  if (!usage) return void 0;
30827
- if (typeof usage.totalTokens === "number" && Number.isFinite(usage.totalTokens)) {
30828
- return usage.totalTokens;
30829
- }
30830
- return [
30831
- usage.inputTokens,
30832
- usage.outputTokens,
30833
- usage.cachedInputTokens,
30834
- usage.cacheCreationTokens
30835
- ].reduce((sum, value) => {
30836
- if (typeof value !== "number" || !Number.isFinite(value)) return sum;
30837
- return (sum ?? 0) + Math.max(0, Math.floor(value));
30838
- }, void 0);
30869
+ return getUsageComponentTotal(usage) ?? getFiniteTokenCount(usage.totalTokens);
30839
30870
  }
30840
30871
  function formatTokenTotal(usage) {
30841
30872
  const total = totalUsageTokens(usage);
30842
30873
  return total === void 0 ? "" : `${formatNumber(total)} tokens`;
30843
30874
  }
30875
+ function formatUsageTotal(usages) {
30876
+ const total = usages.reduce((sum, usage) => {
30877
+ const tokens = totalUsageTokens(usage);
30878
+ if (tokens === void 0) return sum;
30879
+ return (sum ?? 0) + tokens;
30880
+ }, void 0);
30881
+ return total === void 0 ? "" : `${formatNumber(total)} tokens`;
30882
+ }
30844
30883
  function formatUsage(usage) {
30845
30884
  const total = totalUsageTokens(usage);
30846
30885
  if (total === void 0) return "";
@@ -31250,13 +31289,24 @@ function Stat(props) {
31250
31289
  // src/client/components/ConversationRowStats.tsx
31251
31290
  var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
31252
31291
  function ConversationRowStats(props) {
31292
+ const tokens = formatUsageTotal(
31293
+ props.conversation.turns.map((turn) => turn.cumulativeUsage)
31294
+ );
31295
+ const runtime = formatDurationTotal(
31296
+ props.conversation.turns.map((turn) => turn.cumulativeDurationMs)
31297
+ );
31298
+ const primaryStats = [
31299
+ `${props.conversation.turns.length} turns`,
31300
+ tokens,
31301
+ runtime ? `${runtime} runtime` : void 0
31302
+ ].filter(Boolean);
31303
+ const secondaryStats = [
31304
+ props.timeLabel,
31305
+ slackLocationLabel(props.conversation, { includeId: false })
31306
+ ].filter(Boolean);
31253
31307
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "grid min-w-0 justify-items-end gap-1 text-right max-md:justify-items-start max-md:text-left", children: [
31254
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "text-[0.84rem] leading-relaxed text-[#b8b8b8]", children: [
31255
- props.conversation.turns.length,
31256
- " turns \xB7 ",
31257
- props.timeLabel
31258
- ] }),
31259
- props.conversation.channel ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "max-w-full break-words text-[0.84rem] leading-relaxed text-[#888] md:truncate", children: slackLocationLabel(props.conversation, { includeId: false }) }) : null
31308
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-[0.84rem] leading-relaxed text-[#b8b8b8]", children: primaryStats.join(" \xB7 ") }),
31309
+ secondaryStats.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "max-w-full break-words text-[0.84rem] leading-relaxed text-[#888] md:truncate", children: secondaryStats.join(" \xB7 ") }) : null
31260
31310
  ] });
31261
31311
  }
31262
31312
 
@@ -54640,7 +54690,7 @@ function CommandCenter(props) {
54640
54690
  }
54641
54691
 
54642
54692
  // src/client/components/Transcript.tsx
54643
- var import_react50 = __toESM(require_react(), 1);
54693
+ var import_react52 = __toESM(require_react(), 1);
54644
54694
 
54645
54695
  // src/client/components/TranscriptHeader.tsx
54646
54696
  var import_jsx_runtime16 = __toESM(require_jsx_runtime(), 1);
@@ -54674,7 +54724,11 @@ function TranscriptViewToggle(props) {
54674
54724
  );
54675
54725
  }
54676
54726
 
54727
+ // src/client/components/TranscriptTurn.tsx
54728
+ var import_react51 = __toESM(require_react(), 1);
54729
+
54677
54730
  // src/client/code.tsx
54731
+ var import_react50 = __toESM(require_react(), 1);
54678
54732
  var import_jsx_runtime17 = __toESM(require_jsx_runtime(), 1);
54679
54733
  function countStructuredBlockChildren(block) {
54680
54734
  if (!canRenderStructuredMarkup(block.language)) return 1;
@@ -54711,8 +54765,12 @@ function MarkupNodeView(props) {
54711
54765
  if (props.node.type === "text") {
54712
54766
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "min-w-0 whitespace-pre-wrap break-words text-white", children: props.node.text.trim() });
54713
54767
  }
54768
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(MarkupElementView, { defaultOpen: props.defaultOpen, node: props.node });
54769
+ }
54770
+ function MarkupElementView(props) {
54714
54771
  const children = props.node.children;
54715
54772
  const hasChildren = children.length > 0;
54773
+ const [open, setOpen] = (0, import_react50.useState)(props.defaultOpen ?? true);
54716
54774
  const attributes = props.node.attributes.map(([name, value]) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "ml-1.5 text-[#b8b8b8]", children: [
54717
54775
  name,
54718
54776
  "=",
@@ -54733,12 +54791,15 @@ function MarkupNodeView(props) {
54733
54791
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
54734
54792
  "details",
54735
54793
  {
54736
- className: "group min-w-0 break-words",
54737
- open: props.defaultOpen ?? true,
54794
+ className: "min-w-0 break-words",
54795
+ onToggle: (event) => {
54796
+ if (event.currentTarget !== event.target) return;
54797
+ setOpen(event.currentTarget.open);
54798
+ },
54799
+ open,
54738
54800
  children: [
54739
54801
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("summary", { className: "-ml-1 flex w-full max-w-full cursor-pointer list-none flex-wrap items-baseline px-1 py-0.5 transition-colors hover:bg-white/[0.05] hover:text-white [&::-webkit-details-marker]:hidden", children: [
54740
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "mr-1 w-2 text-white group-open:hidden", children: "+" }),
54741
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "mr-1 hidden w-2 text-white group-open:inline", children: "-" }),
54802
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "mr-1 w-2 text-white", children: open ? "-" : "+" }),
54742
54803
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-[#888]", children: "<" }),
54743
54804
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "font-bold text-white", children: props.node.tagName }),
54744
54805
  attributes,
@@ -55437,20 +55498,7 @@ function TranscriptPartView(props) {
55437
55498
  }
55438
55499
  const value = part.output;
55439
55500
  if (part.type === "thinking") {
55440
- const rendered2 = stringifyPartValue(value);
55441
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("details", { className: "border border-[#beaaff]/20 bg-white/[0.03] transition-colors hover:border-[#beaaff]/45 hover:bg-[rgba(190,170,255,0.06)]", children: [
55442
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("summary", { className: "grid cursor-pointer grid-cols-[auto_minmax(0,1fr)] items-center gap-3 px-3 py-2 font-mono text-[0.8rem] leading-tight text-[#888] hover:bg-[rgba(190,170,255,0.07)] max-md:grid-cols-1 max-md:gap-1", children: [
55443
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "uppercase text-[#b8b8b8]", children: "thinking" }),
55444
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-0 truncate", children: previewToolValue(value) })
55445
- ] }),
55446
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
55447
- HighlightedCode,
55448
- {
55449
- code: rendered2 || "{}",
55450
- language: detectLanguage(rendered2)
55451
- }
55452
- )
55453
- ] });
55501
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ThinkingPartView, { value });
55454
55502
  }
55455
55503
  const rendered = stringifyPartValue(value);
55456
55504
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("details", { className: toolFrameClass(), children: [
@@ -55462,6 +55510,34 @@ function TranscriptPartView(props) {
55462
55510
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(HighlightedCode, { code: rendered || "{}", language: "json" })
55463
55511
  ] });
55464
55512
  }
55513
+ function ThinkingPartView(props) {
55514
+ const [open, setOpen] = (0, import_react51.useState)(false);
55515
+ const rendered = stringifyPartValue(props.value);
55516
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
55517
+ "details",
55518
+ {
55519
+ className: "border border-[#beaaff]/20 bg-white/[0.03] transition-colors hover:border-[#beaaff]/45 hover:bg-[rgba(190,170,255,0.06)]",
55520
+ onToggle: (event) => {
55521
+ if (event.currentTarget !== event.target) return;
55522
+ setOpen(event.currentTarget.open);
55523
+ },
55524
+ open,
55525
+ children: [
55526
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("summary", { className: "grid cursor-pointer grid-cols-[auto_minmax(0,1fr)] items-center gap-3 px-3 py-2 font-mono text-[0.8rem] leading-tight text-[#888] hover:bg-[rgba(190,170,255,0.07)] max-md:grid-cols-1 max-md:gap-1", children: [
55527
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "uppercase text-[#b8b8b8]", children: "thinking" }),
55528
+ open ? null : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "min-w-0 truncate", children: previewToolValue(props.value) })
55529
+ ] }),
55530
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "border-t border-[#beaaff]/15 px-3 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
55531
+ HighlightedCode,
55532
+ {
55533
+ code: rendered || "{}",
55534
+ language: detectLanguage(rendered)
55535
+ }
55536
+ ) })
55537
+ ]
55538
+ }
55539
+ );
55540
+ }
55465
55541
  function isString3(value) {
55466
55542
  return typeof value === "string" && value.length > 0;
55467
55543
  }
@@ -55469,7 +55545,7 @@ function isString3(value) {
55469
55545
  // src/client/components/Transcript.tsx
55470
55546
  var import_jsx_runtime22 = __toESM(require_jsx_runtime(), 1);
55471
55547
  function Transcript(props) {
55472
- const [view, setView] = (0, import_react50.useState)("rich");
55548
+ const [view, setView] = (0, import_react52.useState)("rich");
55473
55549
  const hasRedactedTurns = props.turns.some((turn) => turn.transcriptRedacted);
55474
55550
  if (props.turns.length === 0) {
55475
55551
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: transcriptEmptyClass(), children: "No transcript is available for this conversation." });
@@ -55573,11 +55649,17 @@ function ConversationStats(props) {
55573
55649
  (count, turn) => count + turnToolCallCount(turn),
55574
55650
  0
55575
55651
  ) : void 0;
55652
+ const tokens = formatUsageTotal(
55653
+ (props.detail?.turns ?? props.conversation.turns).map(
55654
+ (turn) => turn.cumulativeUsage
55655
+ )
55656
+ );
55576
55657
  const stats = [
55577
55658
  slackLocationLabel(props.conversation, { includeId: false }),
55578
55659
  `${props.conversation.turns.length} turns`,
55579
55660
  messages === void 0 ? "messages loading" : `${messages} messages`,
55580
55661
  toolCalls === void 0 ? "tool calls loading" : `${toolCalls} tool calls`,
55662
+ tokens,
55581
55663
  formatConversationDuration(props.conversation),
55582
55664
  `started ${formatTime(props.conversation.startedAt)}`
55583
55665
  ].filter(Boolean);
@@ -55865,7 +55947,7 @@ function LegacyConversationRedirect() {
55865
55947
 
55866
55948
  // src/client.tsx
55867
55949
  var import_jsx_runtime29 = __toESM(require_jsx_runtime(), 1);
55868
- var DashboardErrorBoundary = class extends import_react51.Component {
55950
+ var DashboardErrorBoundary = class extends import_react53.Component {
55869
55951
  state = { error: null };
55870
55952
  static getDerivedStateFromError(error) {
55871
55953
  return { error };
package/dist/tailwind.css CHANGED
@@ -1,2 +1,2 @@
1
1
  /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, "system-ui", "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif;--font-mono:"Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-400:oklch(76.5% .177 163.223);--color-violet-200:oklch(89.4% .057 293.283);--color-violet-300:oklch(81.1% .111 293.571);--color-rose-300:oklch(81% .117 11.638);--color-rose-400:oklch(71.2% .194 13.428);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-lg:32rem;--container-3xl:48rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-5xl:3rem;--text-5xl--line-height:1;--text-6xl:3.75rem;--text-6xl--line-height:1;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-normal:0em;--leading-tight:1.25;--leading-snug:1.375;--leading-relaxed:1.625;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-0{top:calc(var(--spacing) * 0)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.z-10{z-index:10}.z-\[1\]{z-index:1}.col-span-full{grid-column:1/-1}.m-0{margin:calc(var(--spacing) * 0)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mr-1{margin-right:calc(var(--spacing) * 1)}.mr-3{margin-right:calc(var(--spacing) * 3)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.size-1\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.max-h-\[60vh\]{max-height:60vh}.min-h-16{min-height:calc(var(--spacing) * 16)}.min-h-28{min-height:calc(var(--spacing) * 28)}.min-h-48{min-height:calc(var(--spacing) * 48)}.min-h-\[4\.5rem\]{min-height:4.5rem}.min-h-\[calc\(100vh-5rem\)\]{min-height:calc(100vh - 5rem)}.min-h-screen{min-height:100vh}.w-1{width:calc(var(--spacing) * 1)}.w-2{width:calc(var(--spacing) * 2)}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.max-w-3xl{max-width:var(--container-3xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-64{min-width:calc(var(--spacing) * 64)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.basis-1\/2{flex-basis:50%}.animate-pulse{animation:var(--animate-pulse)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.grid-cols-\[0\.875rem_minmax\(0\,1fr\)\]{grid-template-columns:.875rem minmax(0,1fr)}.grid-cols-\[auto_minmax\(0\,1fr\)\]{grid-template-columns:auto minmax(0,1fr)}.grid-cols-\[auto_minmax\(0\,1fr\)_auto\]{grid-template-columns:auto minmax(0,1fr) auto}.grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.grid-cols-\[minmax\(0\,1fr\)_minmax\(12rem\,max-content\)\]{grid-template-columns:minmax(0,1fr) minmax(12rem,max-content)}.grid-cols-\[minmax\(13rem\,1\.7fr\)_minmax\(13rem\,1fr\)\]{grid-template-columns:minmax(13rem,1.7fr) minmax(13rem,1fr)}.grid-rows-\[auto_1fr\]{grid-template-rows:auto 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-end{justify-content:flex-end}.justify-items-end{justify-items:end}.gap-0{gap:calc(var(--spacing) * 0)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-x-1{column-gap:calc(var(--spacing) * 1)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-0\.5{row-gap:calc(var(--spacing) * .5)}.gap-y-1{row-gap:calc(var(--spacing) * 1)}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.justify-self-start{justify-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-4{border-bottom-style:var(--tw-border-style);border-bottom-width:4px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-\[\#beaaff\]\/20{border-color:oklab(78.6663% .0494663 -.109708/.2)}.border-\[\#beaaff\]\/70{border-color:oklab(78.6663% .0494663 -.109708/.7)}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-400\/25{border-color:#fcbb0040}@supports (color:color-mix(in lab, red, red)){.border-amber-400\/25{border-color:color-mix(in oklab, var(--color-amber-400) 25%, transparent)}}.border-emerald-300{border-color:var(--color-emerald-300)}.border-emerald-400\/25{border-color:#00d29440}@supports (color:color-mix(in lab, red, red)){.border-emerald-400\/25{border-color:color-mix(in oklab, var(--color-emerald-400) 25%, transparent)}}.border-rose-300{border-color:var(--color-rose-300)}.border-rose-400{border-color:var(--color-rose-400)}.border-rose-400\/25{border-color:#ff667f40}@supports (color:color-mix(in lab, red, red)){.border-rose-400\/25{border-color:color-mix(in oklab, var(--color-rose-400) 25%, transparent)}}.border-rose-400\/50{border-color:#ff667f80}@supports (color:color-mix(in lab, red, red)){.border-rose-400\/50{border-color:color-mix(in oklab, var(--color-rose-400) 50%, transparent)}}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab, red, red)){.border-white\/10{border-color:color-mix(in oklab, var(--color-white) 10%, transparent)}}.border-white\/15{border-color:#ffffff26}@supports (color:color-mix(in lab, red, red)){.border-white\/15{border-color:color-mix(in oklab, var(--color-white) 15%, transparent)}}.border-white\/30{border-color:#ffffff4d}@supports (color:color-mix(in lab, red, red)){.border-white\/30{border-color:color-mix(in oklab, var(--color-white) 30%, transparent)}}.border-b-\[\#beaaff\]{border-bottom-color:#beaaff}.border-b-transparent{border-bottom-color:#0000}.border-b-white\/10{border-bottom-color:#ffffff1a}@supports (color:color-mix(in lab, red, red)){.border-b-white\/10{border-bottom-color:color-mix(in oklab, var(--color-white) 10%, transparent)}}.border-l-\[\#888\]{border-left-color:#888}.border-l-\[\#beaaff\]\/60{border-left-color:oklab(78.6663% .0494663 -.109708/.6)}.border-l-amber-300{border-left-color:var(--color-amber-300)}.border-l-amber-400{border-left-color:var(--color-amber-400)}.border-l-emerald-400{border-left-color:var(--color-emerald-400)}.border-l-rose-400{border-left-color:var(--color-rose-400)}.border-l-violet-300{border-left-color:var(--color-violet-300)}.border-l-white{border-left-color:var(--color-white)}.border-l-white\/25{border-left-color:#ffffff40}@supports (color:color-mix(in lab, red, red)){.border-l-white\/25{border-left-color:color-mix(in oklab, var(--color-white) 25%, transparent)}}.border-l-white\/35{border-left-color:#ffffff59}@supports (color:color-mix(in lab, red, red)){.border-l-white\/35{border-left-color:color-mix(in oklab, var(--color-white) 35%, transparent)}}.border-l-white\/70{border-left-color:#ffffffb3}@supports (color:color-mix(in lab, red, red)){.border-l-white\/70{border-left-color:color-mix(in oklab, var(--color-white) 70%, transparent)}}.bg-\[\#0b0b0b\]{background-color:#0b0b0b}.bg-\[\#111\]{background-color:#111}.bg-\[\#050505\]{background-color:#050505}.bg-\[\#050505\]\/95{background-color:oklab(11.4918% 7.45058e-9 7.45058e-9/.95)}.bg-\[\#b8b8b8\]{background-color:#b8b8b8}.bg-\[\#beaaff\]\/20{background-color:oklab(78.6663% .0494663 -.109708/.2)}.bg-\[\#beaaff\]\/50{background-color:oklab(78.6663% .0494663 -.109708/.5)}.bg-\[rgba\(190\,170\,255\,0\.08\)\]{background-color:#beaaff14}.bg-amber-300{background-color:var(--color-amber-300)}.bg-amber-300\/\[0\.06\]{background-color:#ffd2360f}@supports (color:color-mix(in lab, red, red)){.bg-amber-300\/\[0\.06\]{background-color:color-mix(in oklab, var(--color-amber-300) 6%, transparent)}}.bg-amber-400{background-color:var(--color-amber-400)}.bg-amber-400\/10{background-color:#fcbb001a}@supports (color:color-mix(in lab, red, red)){.bg-amber-400\/10{background-color:color-mix(in oklab, var(--color-amber-400) 10%, transparent)}}.bg-black{background-color:var(--color-black)}.bg-emerald-300{background-color:var(--color-emerald-300)}.bg-emerald-400\/10{background-color:#00d2941a}@supports (color:color-mix(in lab, red, red)){.bg-emerald-400\/10{background-color:color-mix(in oklab, var(--color-emerald-400) 10%, transparent)}}.bg-rose-300{background-color:var(--color-rose-300)}.bg-rose-400{background-color:var(--color-rose-400)}.bg-rose-400\/10{background-color:#ff667f1a}@supports (color:color-mix(in lab, red, red)){.bg-rose-400\/10{background-color:color-mix(in oklab, var(--color-rose-400) 10%, transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/20{background-color:#fff3}@supports (color:color-mix(in lab, red, red)){.bg-white\/20{background-color:color-mix(in oklab, var(--color-white) 20%, transparent)}}.bg-white\/35{background-color:#ffffff59}@supports (color:color-mix(in lab, red, red)){.bg-white\/35{background-color:color-mix(in oklab, var(--color-white) 35%, transparent)}}.bg-white\/\[0\.03\]{background-color:#ffffff08}@supports (color:color-mix(in lab, red, red)){.bg-white\/\[0\.03\]{background-color:color-mix(in oklab, var(--color-white) 3%, transparent)}}.bg-white\/\[0\.04\]{background-color:#ffffff0a}@supports (color:color-mix(in lab, red, red)){.bg-white\/\[0\.04\]{background-color:color-mix(in oklab, var(--color-white) 4%, transparent)}}.p-0{padding:calc(var(--spacing) * 0)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-8{padding:calc(var(--spacing) * 8)}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pb-1\.5{padding-bottom:calc(var(--spacing) * 1.5)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-left{text-align:left}.text-right{text-align:right}.font-\[inherit\]{font-family:inherit}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.8rem\]{font-size:.8rem}.text-\[0\.9rem\]{font-size:.9rem}.text-\[0\.68rem\]{font-size:.68rem}.text-\[0\.72rem\]{font-size:.72rem}.text-\[0\.76rem\]{font-size:.76rem}.text-\[0\.78rem\]{font-size:.78rem}.text-\[0\.82rem\]{font-size:.82rem}.text-\[0\.84rem\]{font-size:.84rem}.text-\[0\.86rem\]{font-size:.86rem}.text-\[0\.88rem\]{font-size:.88rem}.text-\[0\.92rem\]{font-size:.92rem}.text-\[0\.98rem\]{font-size:.98rem}.text-\[1\.04rem\]{font-size:1.04rem}.text-\[1\.05rem\]{font-size:1.05rem}.text-\[1\.75rem\]{font-size:1.75rem}.leading-\[1\.45\]{--tw-leading:1.45;line-height:1.45}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-normal{--tw-tracking:var(--tracking-normal);letter-spacing:var(--tracking-normal)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#666\]{color:#666}.text-\[\#777\]{color:#777}.text-\[\#888\]{color:#888}.text-\[\#b8b8b8\]{color:#b8b8b8}.text-\[\#d6d6d6\]{color:#d6d6d6}.text-\[\#d8ccff\]{color:#d8ccff}.text-\[\#f4f4f4\]{color:#f4f4f4}.text-amber-200{color:var(--color-amber-200)}.text-amber-300{color:var(--color-amber-300)}.text-black{color:var(--color-black)}.text-emerald-300{color:var(--color-emerald-300)}.text-inherit{color:inherit}.text-rose-300{color:var(--color-rose-300)}.text-violet-200{color:var(--color-violet-200)}.text-white{color:var(--color-white)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.decoration-white{-webkit-text-decoration-color:var(--color-white);-webkit-text-decoration-color:var(--color-white);text-decoration-color:var(--color-white)}.underline-offset-4{text-underline-offset:4px}.\[color-scheme\:dark\]{color-scheme:dark}.saturate-50{--tw-saturate:saturate(50%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-\[filter\,stroke\,stroke-width\]{transition-property:filter,stroke,stroke-width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.group-open\:hidden:is(:where(.group):is([open],:popover-open,:open) *){display:none}.group-open\:inline:is(:where(.group):is([open],:popover-open,:open) *){display:inline}.first\:border-t-0:first-child{border-top-style:var(--tw-border-style);border-top-width:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:border-\[\#beaaff\]\/45:hover{border-color:oklab(78.6663% .0494663 -.109708/.45)}.hover\:border-white:hover{border-color:var(--color-white)}.hover\:border-white\/25:hover{border-color:#ffffff40}@supports (color:color-mix(in lab, red, red)){.hover\:border-white\/25:hover{border-color:color-mix(in oklab, var(--color-white) 25%, transparent)}}.hover\:border-white\/30:hover{border-color:#ffffff4d}@supports (color:color-mix(in lab, red, red)){.hover\:border-white\/30:hover{border-color:color-mix(in oklab, var(--color-white) 30%, transparent)}}.hover\:border-b-white\/45:hover{border-bottom-color:#ffffff73}@supports (color:color-mix(in lab, red, red)){.hover\:border-b-white\/45:hover{border-bottom-color:color-mix(in oklab, var(--color-white) 45%, transparent)}}.hover\:bg-\[\#151515\]:hover{background-color:#151515}.hover\:bg-\[rgba\(190\,170\,255\,0\.06\)\]:hover{background-color:#beaaff0f}.hover\:bg-\[rgba\(190\,170\,255\,0\.07\)\]:hover{background-color:#beaaff12}.hover\:bg-white\/\[0\.04\]:hover{background-color:#ffffff0a}@supports (color:color-mix(in lab, red, red)){.hover\:bg-white\/\[0\.04\]:hover{background-color:color-mix(in oklab, var(--color-white) 4%, transparent)}}.hover\:bg-white\/\[0\.05\]:hover{background-color:#ffffff0d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-white\/\[0\.05\]:hover{background-color:color-mix(in oklab, var(--color-white) 5%, transparent)}}.hover\:text-white:hover{color:var(--color-white)}.hover\:underline:hover{text-decoration-line:underline}.hover\:brightness-125:hover{--tw-brightness:brightness(125%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}}.focus-visible\:stroke-emerald-400:focus-visible{stroke:var(--color-emerald-400)}.focus-visible\:stroke-2:focus-visible{stroke-width:2px}@media not all and (min-width:48rem){.max-md\:hidden{display:none}.max-md\:w-full{width:100%}.max-md\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.max-md\:flex-col{flex-direction:column}.max-md\:flex-wrap{flex-wrap:wrap}.max-md\:items-stretch{align-items:stretch}.max-md\:justify-between{justify-content:space-between}.max-md\:justify-items-start{justify-items:start}.max-md\:gap-1{gap:calc(var(--spacing) * 1)}.max-md\:px-4{padding-inline:calc(var(--spacing) * 4)}.max-md\:py-4{padding-block:calc(var(--spacing) * 4)}.max-md\:text-left{text-align:left}}@media (min-width:40rem){.sm\:basis-1\/3{flex-basis:33.3333%}}@media (min-width:48rem){.md\:grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.md\:truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.md\:px-8{padding-inline:calc(var(--spacing) * 8)}.md\:text-right{text-align:right}.md\:text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}}@media (min-width:64rem){.lg\:grid-cols-\[minmax\(21rem\,0\.32fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(21rem,.32fr) minmax(0,1fr)}}.\[\&_code\]\:break-words code{overflow-wrap:break-word}.\[\&_code\]\:whitespace-pre-wrap code{white-space:pre-wrap}.\[\&_pre\]\:\!m-0 pre{margin:calc(var(--spacing) * 0)!important}.\[\&_pre\]\:\!overflow-visible pre{overflow:visible!important}.\[\&_pre\]\:\!bg-transparent pre{background-color:#0000!important}.\[\&_pre\]\:\!p-0 pre{padding:calc(var(--spacing) * 0)!important}.\[\&_pre\]\:font-mono pre{font-family:var(--font-mono)}.\[\&_pre\]\:text-\[0\.86rem\] pre{font-size:.86rem}.\[\&_pre\]\:leading-relaxed pre{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_pre\]\:break-words pre{overflow-wrap:break-word}.\[\&_pre\]\:whitespace-pre-wrap pre{white-space:pre-wrap}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}}
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, "system-ui", "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif;--font-mono:"Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-400:oklch(76.5% .177 163.223);--color-violet-200:oklch(89.4% .057 293.283);--color-violet-300:oklch(81.1% .111 293.571);--color-rose-300:oklch(81% .117 11.638);--color-rose-400:oklch(71.2% .194 13.428);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-lg:32rem;--container-3xl:48rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-5xl:3rem;--text-5xl--line-height:1;--text-6xl:3.75rem;--text-6xl--line-height:1;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-normal:0em;--leading-tight:1.25;--leading-snug:1.375;--leading-relaxed:1.625;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-0{top:calc(var(--spacing) * 0)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.z-10{z-index:10}.z-\[1\]{z-index:1}.col-span-full{grid-column:1/-1}.m-0{margin:calc(var(--spacing) * 0)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mr-1{margin-right:calc(var(--spacing) * 1)}.mr-3{margin-right:calc(var(--spacing) * 3)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.size-1\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.max-h-\[60vh\]{max-height:60vh}.min-h-16{min-height:calc(var(--spacing) * 16)}.min-h-28{min-height:calc(var(--spacing) * 28)}.min-h-48{min-height:calc(var(--spacing) * 48)}.min-h-\[4\.5rem\]{min-height:4.5rem}.min-h-\[calc\(100vh-5rem\)\]{min-height:calc(100vh - 5rem)}.min-h-screen{min-height:100vh}.w-1{width:calc(var(--spacing) * 1)}.w-2{width:calc(var(--spacing) * 2)}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.max-w-3xl{max-width:var(--container-3xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-64{min-width:calc(var(--spacing) * 64)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.basis-1\/2{flex-basis:50%}.animate-pulse{animation:var(--animate-pulse)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.grid-cols-\[0\.875rem_minmax\(0\,1fr\)\]{grid-template-columns:.875rem minmax(0,1fr)}.grid-cols-\[auto_minmax\(0\,1fr\)\]{grid-template-columns:auto minmax(0,1fr)}.grid-cols-\[auto_minmax\(0\,1fr\)_auto\]{grid-template-columns:auto minmax(0,1fr) auto}.grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.grid-cols-\[minmax\(0\,1fr\)_minmax\(12rem\,max-content\)\]{grid-template-columns:minmax(0,1fr) minmax(12rem,max-content)}.grid-cols-\[minmax\(13rem\,1\.7fr\)_minmax\(13rem\,1fr\)\]{grid-template-columns:minmax(13rem,1.7fr) minmax(13rem,1fr)}.grid-rows-\[auto_1fr\]{grid-template-rows:auto 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-end{justify-content:flex-end}.justify-items-end{justify-items:end}.gap-0{gap:calc(var(--spacing) * 0)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-x-1{column-gap:calc(var(--spacing) * 1)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-0\.5{row-gap:calc(var(--spacing) * .5)}.gap-y-1{row-gap:calc(var(--spacing) * 1)}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.justify-self-start{justify-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-4{border-bottom-style:var(--tw-border-style);border-bottom-width:4px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-\[\#beaaff\]\/15{border-color:oklab(78.6663% .0494663 -.109708/.15)}.border-\[\#beaaff\]\/20{border-color:oklab(78.6663% .0494663 -.109708/.2)}.border-\[\#beaaff\]\/70{border-color:oklab(78.6663% .0494663 -.109708/.7)}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-400\/25{border-color:#fcbb0040}@supports (color:color-mix(in lab, red, red)){.border-amber-400\/25{border-color:color-mix(in oklab, var(--color-amber-400) 25%, transparent)}}.border-emerald-300{border-color:var(--color-emerald-300)}.border-emerald-400\/25{border-color:#00d29440}@supports (color:color-mix(in lab, red, red)){.border-emerald-400\/25{border-color:color-mix(in oklab, var(--color-emerald-400) 25%, transparent)}}.border-rose-300{border-color:var(--color-rose-300)}.border-rose-400{border-color:var(--color-rose-400)}.border-rose-400\/25{border-color:#ff667f40}@supports (color:color-mix(in lab, red, red)){.border-rose-400\/25{border-color:color-mix(in oklab, var(--color-rose-400) 25%, transparent)}}.border-rose-400\/50{border-color:#ff667f80}@supports (color:color-mix(in lab, red, red)){.border-rose-400\/50{border-color:color-mix(in oklab, var(--color-rose-400) 50%, transparent)}}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab, red, red)){.border-white\/10{border-color:color-mix(in oklab, var(--color-white) 10%, transparent)}}.border-white\/15{border-color:#ffffff26}@supports (color:color-mix(in lab, red, red)){.border-white\/15{border-color:color-mix(in oklab, var(--color-white) 15%, transparent)}}.border-white\/30{border-color:#ffffff4d}@supports (color:color-mix(in lab, red, red)){.border-white\/30{border-color:color-mix(in oklab, var(--color-white) 30%, transparent)}}.border-b-\[\#beaaff\]{border-bottom-color:#beaaff}.border-b-transparent{border-bottom-color:#0000}.border-b-white\/10{border-bottom-color:#ffffff1a}@supports (color:color-mix(in lab, red, red)){.border-b-white\/10{border-bottom-color:color-mix(in oklab, var(--color-white) 10%, transparent)}}.border-l-\[\#888\]{border-left-color:#888}.border-l-\[\#beaaff\]\/60{border-left-color:oklab(78.6663% .0494663 -.109708/.6)}.border-l-amber-300{border-left-color:var(--color-amber-300)}.border-l-amber-400{border-left-color:var(--color-amber-400)}.border-l-emerald-400{border-left-color:var(--color-emerald-400)}.border-l-rose-400{border-left-color:var(--color-rose-400)}.border-l-violet-300{border-left-color:var(--color-violet-300)}.border-l-white{border-left-color:var(--color-white)}.border-l-white\/25{border-left-color:#ffffff40}@supports (color:color-mix(in lab, red, red)){.border-l-white\/25{border-left-color:color-mix(in oklab, var(--color-white) 25%, transparent)}}.border-l-white\/35{border-left-color:#ffffff59}@supports (color:color-mix(in lab, red, red)){.border-l-white\/35{border-left-color:color-mix(in oklab, var(--color-white) 35%, transparent)}}.border-l-white\/70{border-left-color:#ffffffb3}@supports (color:color-mix(in lab, red, red)){.border-l-white\/70{border-left-color:color-mix(in oklab, var(--color-white) 70%, transparent)}}.bg-\[\#0b0b0b\]{background-color:#0b0b0b}.bg-\[\#111\]{background-color:#111}.bg-\[\#050505\]{background-color:#050505}.bg-\[\#050505\]\/95{background-color:oklab(11.4918% 7.45058e-9 7.45058e-9/.95)}.bg-\[\#b8b8b8\]{background-color:#b8b8b8}.bg-\[\#beaaff\]\/20{background-color:oklab(78.6663% .0494663 -.109708/.2)}.bg-\[\#beaaff\]\/50{background-color:oklab(78.6663% .0494663 -.109708/.5)}.bg-\[rgba\(190\,170\,255\,0\.08\)\]{background-color:#beaaff14}.bg-amber-300{background-color:var(--color-amber-300)}.bg-amber-300\/\[0\.06\]{background-color:#ffd2360f}@supports (color:color-mix(in lab, red, red)){.bg-amber-300\/\[0\.06\]{background-color:color-mix(in oklab, var(--color-amber-300) 6%, transparent)}}.bg-amber-400{background-color:var(--color-amber-400)}.bg-amber-400\/10{background-color:#fcbb001a}@supports (color:color-mix(in lab, red, red)){.bg-amber-400\/10{background-color:color-mix(in oklab, var(--color-amber-400) 10%, transparent)}}.bg-black{background-color:var(--color-black)}.bg-emerald-300{background-color:var(--color-emerald-300)}.bg-emerald-400\/10{background-color:#00d2941a}@supports (color:color-mix(in lab, red, red)){.bg-emerald-400\/10{background-color:color-mix(in oklab, var(--color-emerald-400) 10%, transparent)}}.bg-rose-300{background-color:var(--color-rose-300)}.bg-rose-400{background-color:var(--color-rose-400)}.bg-rose-400\/10{background-color:#ff667f1a}@supports (color:color-mix(in lab, red, red)){.bg-rose-400\/10{background-color:color-mix(in oklab, var(--color-rose-400) 10%, transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/20{background-color:#fff3}@supports (color:color-mix(in lab, red, red)){.bg-white\/20{background-color:color-mix(in oklab, var(--color-white) 20%, transparent)}}.bg-white\/35{background-color:#ffffff59}@supports (color:color-mix(in lab, red, red)){.bg-white\/35{background-color:color-mix(in oklab, var(--color-white) 35%, transparent)}}.bg-white\/\[0\.03\]{background-color:#ffffff08}@supports (color:color-mix(in lab, red, red)){.bg-white\/\[0\.03\]{background-color:color-mix(in oklab, var(--color-white) 3%, transparent)}}.bg-white\/\[0\.04\]{background-color:#ffffff0a}@supports (color:color-mix(in lab, red, red)){.bg-white\/\[0\.04\]{background-color:color-mix(in oklab, var(--color-white) 4%, transparent)}}.p-0{padding:calc(var(--spacing) * 0)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-8{padding:calc(var(--spacing) * 8)}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pb-1\.5{padding-bottom:calc(var(--spacing) * 1.5)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-left{text-align:left}.text-right{text-align:right}.font-\[inherit\]{font-family:inherit}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.8rem\]{font-size:.8rem}.text-\[0\.9rem\]{font-size:.9rem}.text-\[0\.68rem\]{font-size:.68rem}.text-\[0\.72rem\]{font-size:.72rem}.text-\[0\.76rem\]{font-size:.76rem}.text-\[0\.78rem\]{font-size:.78rem}.text-\[0\.82rem\]{font-size:.82rem}.text-\[0\.84rem\]{font-size:.84rem}.text-\[0\.86rem\]{font-size:.86rem}.text-\[0\.88rem\]{font-size:.88rem}.text-\[0\.92rem\]{font-size:.92rem}.text-\[0\.98rem\]{font-size:.98rem}.text-\[1\.04rem\]{font-size:1.04rem}.text-\[1\.05rem\]{font-size:1.05rem}.text-\[1\.75rem\]{font-size:1.75rem}.leading-\[1\.45\]{--tw-leading:1.45;line-height:1.45}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-normal{--tw-tracking:var(--tracking-normal);letter-spacing:var(--tracking-normal)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#666\]{color:#666}.text-\[\#777\]{color:#777}.text-\[\#888\]{color:#888}.text-\[\#b8b8b8\]{color:#b8b8b8}.text-\[\#d6d6d6\]{color:#d6d6d6}.text-\[\#d8ccff\]{color:#d8ccff}.text-\[\#f4f4f4\]{color:#f4f4f4}.text-amber-200{color:var(--color-amber-200)}.text-amber-300{color:var(--color-amber-300)}.text-black{color:var(--color-black)}.text-emerald-300{color:var(--color-emerald-300)}.text-inherit{color:inherit}.text-rose-300{color:var(--color-rose-300)}.text-violet-200{color:var(--color-violet-200)}.text-white{color:var(--color-white)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.decoration-white{-webkit-text-decoration-color:var(--color-white);-webkit-text-decoration-color:var(--color-white);text-decoration-color:var(--color-white)}.underline-offset-4{text-underline-offset:4px}.\[color-scheme\:dark\]{color-scheme:dark}.saturate-50{--tw-saturate:saturate(50%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-\[filter\,stroke\,stroke-width\]{transition-property:filter,stroke,stroke-width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.first\:border-t-0:first-child{border-top-style:var(--tw-border-style);border-top-width:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:border-\[\#beaaff\]\/45:hover{border-color:oklab(78.6663% .0494663 -.109708/.45)}.hover\:border-white:hover{border-color:var(--color-white)}.hover\:border-white\/25:hover{border-color:#ffffff40}@supports (color:color-mix(in lab, red, red)){.hover\:border-white\/25:hover{border-color:color-mix(in oklab, var(--color-white) 25%, transparent)}}.hover\:border-white\/30:hover{border-color:#ffffff4d}@supports (color:color-mix(in lab, red, red)){.hover\:border-white\/30:hover{border-color:color-mix(in oklab, var(--color-white) 30%, transparent)}}.hover\:border-b-white\/45:hover{border-bottom-color:#ffffff73}@supports (color:color-mix(in lab, red, red)){.hover\:border-b-white\/45:hover{border-bottom-color:color-mix(in oklab, var(--color-white) 45%, transparent)}}.hover\:bg-\[\#151515\]:hover{background-color:#151515}.hover\:bg-\[rgba\(190\,170\,255\,0\.06\)\]:hover{background-color:#beaaff0f}.hover\:bg-\[rgba\(190\,170\,255\,0\.07\)\]:hover{background-color:#beaaff12}.hover\:bg-white\/\[0\.04\]:hover{background-color:#ffffff0a}@supports (color:color-mix(in lab, red, red)){.hover\:bg-white\/\[0\.04\]:hover{background-color:color-mix(in oklab, var(--color-white) 4%, transparent)}}.hover\:bg-white\/\[0\.05\]:hover{background-color:#ffffff0d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-white\/\[0\.05\]:hover{background-color:color-mix(in oklab, var(--color-white) 5%, transparent)}}.hover\:text-white:hover{color:var(--color-white)}.hover\:underline:hover{text-decoration-line:underline}.hover\:brightness-125:hover{--tw-brightness:brightness(125%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}}.focus-visible\:stroke-emerald-400:focus-visible{stroke:var(--color-emerald-400)}.focus-visible\:stroke-2:focus-visible{stroke-width:2px}@media not all and (min-width:48rem){.max-md\:hidden{display:none}.max-md\:w-full{width:100%}.max-md\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.max-md\:flex-col{flex-direction:column}.max-md\:flex-wrap{flex-wrap:wrap}.max-md\:items-stretch{align-items:stretch}.max-md\:justify-between{justify-content:space-between}.max-md\:justify-items-start{justify-items:start}.max-md\:gap-1{gap:calc(var(--spacing) * 1)}.max-md\:px-4{padding-inline:calc(var(--spacing) * 4)}.max-md\:py-4{padding-block:calc(var(--spacing) * 4)}.max-md\:text-left{text-align:left}}@media (min-width:40rem){.sm\:basis-1\/3{flex-basis:33.3333%}}@media (min-width:48rem){.md\:grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.md\:truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.md\:px-8{padding-inline:calc(var(--spacing) * 8)}.md\:text-right{text-align:right}.md\:text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}}@media (min-width:64rem){.lg\:grid-cols-\[minmax\(21rem\,0\.32fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(21rem,.32fr) minmax(0,1fr)}}.\[\&_code\]\:break-words code{overflow-wrap:break-word}.\[\&_code\]\:whitespace-pre-wrap code{white-space:pre-wrap}.\[\&_pre\]\:\!m-0 pre{margin:calc(var(--spacing) * 0)!important}.\[\&_pre\]\:\!overflow-visible pre{overflow:visible!important}.\[\&_pre\]\:\!bg-transparent pre{background-color:#0000!important}.\[\&_pre\]\:\!p-0 pre{padding:calc(var(--spacing) * 0)!important}.\[\&_pre\]\:font-mono pre{font-family:var(--font-mono)}.\[\&_pre\]\:text-\[0\.86rem\] pre{font-size:.86rem}.\[\&_pre\]\:leading-relaxed pre{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_pre\]\:break-words pre{overflow-wrap:break-word}.\[\&_pre\]\:whitespace-pre-wrap pre{white-space:pre-wrap}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-dashboard",
3
- "version": "0.60.0",
3
+ "version": "0.60.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -39,7 +39,7 @@
39
39
  "react-router": "^7.16.0",
40
40
  "recharts": "^3.8.1",
41
41
  "shiki": "4.1.0",
42
- "@sentry/junior": "0.60.0"
42
+ "@sentry/junior": "0.60.1"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@tailwindcss/cli": "^4.3.0",
@@ -1,3 +1,4 @@
1
+ import { useState } from "react";
1
2
  import { useQuery } from "@tanstack/react-query";
2
3
  import { codeToHtml, type BundledLanguage } from "shiki/bundle/web";
3
4
 
@@ -53,8 +54,18 @@ function MarkupNodeView(props: { defaultOpen?: boolean; node: MarkupNode }) {
53
54
  );
54
55
  }
55
56
 
57
+ return (
58
+ <MarkupElementView defaultOpen={props.defaultOpen} node={props.node} />
59
+ );
60
+ }
61
+
62
+ function MarkupElementView(props: {
63
+ defaultOpen?: boolean;
64
+ node: Extract<MarkupNode, { type: "element" }>;
65
+ }) {
56
66
  const children = props.node.children;
57
67
  const hasChildren = children.length > 0;
68
+ const [open, setOpen] = useState(props.defaultOpen ?? true);
58
69
  const attributes = props.node.attributes.map(([name, value]) => (
59
70
  <span className="ml-1.5 text-[#b8b8b8]" key={name}>
60
71
  {name}=<span className="text-white">"{value}"</span>
@@ -74,12 +85,15 @@ function MarkupNodeView(props: { defaultOpen?: boolean; node: MarkupNode }) {
74
85
 
75
86
  return (
76
87
  <details
77
- className="group min-w-0 break-words"
78
- open={props.defaultOpen ?? true}
88
+ className="min-w-0 break-words"
89
+ onToggle={(event) => {
90
+ if (event.currentTarget !== event.target) return;
91
+ setOpen(event.currentTarget.open);
92
+ }}
93
+ open={open}
79
94
  >
80
95
  <summary className="-ml-1 flex w-full max-w-full cursor-pointer list-none flex-wrap items-baseline px-1 py-0.5 transition-colors hover:bg-white/[0.05] hover:text-white [&::-webkit-details-marker]:hidden">
81
- <span className="mr-1 w-2 text-white group-open:hidden">+</span>
82
- <span className="mr-1 hidden w-2 text-white group-open:inline">-</span>
96
+ <span className="mr-1 w-2 text-white">{open ? "-" : "+"}</span>
83
97
  <span className="text-[#888]">&lt;</span>
84
98
  <span className="font-bold text-white">{props.node.tagName}</span>
85
99
  {attributes}
@@ -1,4 +1,8 @@
1
- import { slackLocationLabel } from "../format";
1
+ import {
2
+ formatDurationTotal,
3
+ formatUsageTotal,
4
+ slackLocationLabel,
5
+ } from "../format";
2
6
  import type { Conversation } from "../types";
3
7
 
4
8
  /** Render compact conversation metadata aligned to row context. */
@@ -6,14 +10,30 @@ export function ConversationRowStats(props: {
6
10
  conversation: Conversation;
7
11
  timeLabel: string;
8
12
  }) {
13
+ const tokens = formatUsageTotal(
14
+ props.conversation.turns.map((turn) => turn.cumulativeUsage),
15
+ );
16
+ const runtime = formatDurationTotal(
17
+ props.conversation.turns.map((turn) => turn.cumulativeDurationMs),
18
+ );
19
+ const primaryStats = [
20
+ `${props.conversation.turns.length} turns`,
21
+ tokens,
22
+ runtime ? `${runtime} runtime` : undefined,
23
+ ].filter(Boolean);
24
+ const secondaryStats = [
25
+ props.timeLabel,
26
+ slackLocationLabel(props.conversation, { includeId: false }),
27
+ ].filter(Boolean);
28
+
9
29
  return (
10
30
  <div className="grid min-w-0 justify-items-end gap-1 text-right max-md:justify-items-start max-md:text-left">
11
31
  <div className="text-[0.84rem] leading-relaxed text-[#b8b8b8]">
12
- {props.conversation.turns.length} turns · {props.timeLabel}
32
+ {primaryStats.join(" · ")}
13
33
  </div>
14
- {props.conversation.channel ? (
34
+ {secondaryStats.length > 0 ? (
15
35
  <div className="max-w-full break-words text-[0.84rem] leading-relaxed text-[#888] md:truncate">
16
- {slackLocationLabel(props.conversation, { includeId: false })}
36
+ {secondaryStats.join(" · ")}
17
37
  </div>
18
38
  ) : null}
19
39
  </div>
@@ -1,4 +1,4 @@
1
- import type { ClipboardEventHandler, ReactNode } from "react";
1
+ import { useState, type ClipboardEventHandler, type ReactNode } from "react";
2
2
 
3
3
  import { HighlightedCode } from "../code";
4
4
  import {
@@ -450,19 +450,7 @@ function TranscriptPartView(props: {
450
450
 
451
451
  const value = part.output;
452
452
  if (part.type === "thinking") {
453
- const rendered = stringifyPartValue(value);
454
- return (
455
- <details className="border border-[#beaaff]/20 bg-white/[0.03] transition-colors hover:border-[#beaaff]/45 hover:bg-[rgba(190,170,255,0.06)]">
456
- <summary className="grid cursor-pointer grid-cols-[auto_minmax(0,1fr)] items-center gap-3 px-3 py-2 font-mono text-[0.8rem] leading-tight text-[#888] hover:bg-[rgba(190,170,255,0.07)] max-md:grid-cols-1 max-md:gap-1">
457
- <span className="uppercase text-[#b8b8b8]">thinking</span>
458
- <span className="min-w-0 truncate">{previewToolValue(value)}</span>
459
- </summary>
460
- <HighlightedCode
461
- code={rendered || "{}"}
462
- language={detectLanguage(rendered)}
463
- />
464
- </details>
465
- );
453
+ return <ThinkingPartView value={value} />;
466
454
  }
467
455
 
468
456
  const rendered = stringifyPartValue(value);
@@ -482,6 +470,37 @@ function TranscriptPartView(props: {
482
470
  );
483
471
  }
484
472
 
473
+ function ThinkingPartView(props: { value: unknown }) {
474
+ const [open, setOpen] = useState(false);
475
+ const rendered = stringifyPartValue(props.value);
476
+
477
+ return (
478
+ <details
479
+ className="border border-[#beaaff]/20 bg-white/[0.03] transition-colors hover:border-[#beaaff]/45 hover:bg-[rgba(190,170,255,0.06)]"
480
+ onToggle={(event) => {
481
+ if (event.currentTarget !== event.target) return;
482
+ setOpen(event.currentTarget.open);
483
+ }}
484
+ open={open}
485
+ >
486
+ <summary className="grid cursor-pointer grid-cols-[auto_minmax(0,1fr)] items-center gap-3 px-3 py-2 font-mono text-[0.8rem] leading-tight text-[#888] hover:bg-[rgba(190,170,255,0.07)] max-md:grid-cols-1 max-md:gap-1">
487
+ <span className="uppercase text-[#b8b8b8]">thinking</span>
488
+ {open ? null : (
489
+ <span className="min-w-0 truncate">
490
+ {previewToolValue(props.value)}
491
+ </span>
492
+ )}
493
+ </summary>
494
+ <div className="border-t border-[#beaaff]/15 px-3 py-3">
495
+ <HighlightedCode
496
+ code={rendered || "{}"}
497
+ language={detectLanguage(rendered)}
498
+ />
499
+ </div>
500
+ </details>
501
+ );
502
+ }
503
+
485
504
  function isString(value: string | undefined): value is string {
486
505
  return typeof value === "string" && value.length > 0;
487
506
  }
@@ -104,6 +104,17 @@ export function formatMs(value: number | undefined): string {
104
104
  return `${minutes}m ${remainingSeconds}s`;
105
105
  }
106
106
 
107
+ /** Format aggregate runtime across turn summaries when duration data exists. */
108
+ export function formatDurationTotal(
109
+ durations: Array<number | undefined>,
110
+ ): string {
111
+ const total = durations.reduce<number | undefined>((sum, value) => {
112
+ if (typeof value !== "number" || !Number.isFinite(value)) return sum;
113
+ return (sum ?? 0) + Math.max(0, Math.floor(value));
114
+ }, undefined);
115
+ return total === undefined ? "" : formatMs(total);
116
+ }
117
+
107
118
  /** Format transcript event timestamps independently from turn start offsets. */
108
119
  export function formatMessageTimestamp(value: number | undefined): string {
109
120
  if (typeof value !== "number" || !Number.isFinite(value))
@@ -151,6 +162,25 @@ function formatNumber(value: number | undefined): string {
151
162
  return `${formatted}${suffix}`;
152
163
  }
153
164
 
165
+ function getFiniteTokenCount(value: unknown): number | undefined {
166
+ return typeof value === "number" && Number.isFinite(value)
167
+ ? Math.max(0, Math.floor(value))
168
+ : undefined;
169
+ }
170
+
171
+ function getUsageComponentTotal(usage: TurnUsage): number | undefined {
172
+ return [
173
+ usage.inputTokens,
174
+ usage.outputTokens,
175
+ usage.cachedInputTokens,
176
+ usage.cacheCreationTokens,
177
+ ].reduce<number | undefined>((sum, value) => {
178
+ const count = getFiniteTokenCount(value);
179
+ if (count === undefined) return sum;
180
+ return (sum ?? 0) + count;
181
+ }, undefined);
182
+ }
183
+
154
184
  /** Format byte counts in lowercase compact units for transcript metadata. */
155
185
  export function formatBytes(value: number | undefined): string {
156
186
  if (typeof value !== "number" || !Number.isFinite(value)) return "0b";
@@ -174,9 +204,36 @@ function transcriptSource(turn: ConversationTurn) {
174
204
  : (turn.transcriptMetadata ?? []);
175
205
  }
176
206
 
207
+ function isConversationMessageRole(role: string): boolean {
208
+ const normalized = role.toLowerCase();
209
+ return normalized === "user" || normalized === "assistant";
210
+ }
211
+
212
+ function hasTextPart(
213
+ message: Pick<ConversationTurn["transcript"][number], "parts" | "role">,
214
+ ): boolean {
215
+ return message.parts.some((part) => {
216
+ if (part.type !== "text") return false;
217
+ if (part.redacted) return true;
218
+ return typeof part.text === "string" && part.text.trim().length > 0;
219
+ });
220
+ }
221
+
222
+ function isConversationMessage(
223
+ message: Pick<ConversationTurn["transcript"][number], "parts" | "role">,
224
+ ): boolean {
225
+ if (!isConversationMessageRole(message.role)) return false;
226
+ if (message.role.toLowerCase() === "assistant") return hasTextPart(message);
227
+ return message.parts.length > 0;
228
+ }
229
+
177
230
  /** Count visible or redacted message records for a turn. */
178
231
  export function turnMessageCount(turn: ConversationTurn): number {
179
- return turn.transcriptMessageCount ?? transcriptSource(turn).length;
232
+ const source = transcriptSource(turn);
233
+ if (source.length > 0) {
234
+ return source.filter(isConversationMessage).length;
235
+ }
236
+ return turn.transcriptMessageCount ?? 0;
180
237
  }
181
238
 
182
239
  /** Count tool calls from visible transcripts or safe redacted metadata. */
@@ -190,21 +247,9 @@ export function turnToolCallCount(turn: ConversationTurn): number {
190
247
 
191
248
  function totalUsageTokens(usage: TurnUsage | undefined): number | undefined {
192
249
  if (!usage) return undefined;
193
- if (
194
- typeof usage.totalTokens === "number" &&
195
- Number.isFinite(usage.totalTokens)
196
- ) {
197
- return usage.totalTokens;
198
- }
199
- return [
200
- usage.inputTokens,
201
- usage.outputTokens,
202
- usage.cachedInputTokens,
203
- usage.cacheCreationTokens,
204
- ].reduce<number | undefined>((sum, value) => {
205
- if (typeof value !== "number" || !Number.isFinite(value)) return sum;
206
- return (sum ?? 0) + Math.max(0, Math.floor(value));
207
- }, undefined);
250
+ return (
251
+ getUsageComponentTotal(usage) ?? getFiniteTokenCount(usage.totalTokens)
252
+ );
208
253
  }
209
254
 
210
255
  /** Format known token counters without estimating per-message usage. */
@@ -213,6 +258,16 @@ export function formatTokenTotal(usage: TurnUsage | undefined): string {
213
258
  return total === undefined ? "" : `${formatNumber(total)} tokens`;
214
259
  }
215
260
 
261
+ /** Format the aggregate token count across conversation turns. */
262
+ export function formatUsageTotal(usages: Array<TurnUsage | undefined>): string {
263
+ const total = usages.reduce<number | undefined>((sum, usage) => {
264
+ const tokens = totalUsageTokens(usage);
265
+ if (tokens === undefined) return sum;
266
+ return (sum ?? 0) + tokens;
267
+ }, undefined);
268
+ return total === undefined ? "" : `${formatNumber(total)} tokens`;
269
+ }
270
+
216
271
  /** Format known token counters with available input/output detail. */
217
272
  export function formatUsage(usage: TurnUsage | undefined): string {
218
273
  const total = totalUsageTokens(usage);
@@ -8,6 +8,7 @@ import {
8
8
  formatConversationDuration,
9
9
  formatRelativeTime,
10
10
  formatTime,
11
+ formatUsageTotal,
11
12
  slackLocationLabel,
12
13
  turnMessageCount,
13
14
  turnToolCallCount,
@@ -123,11 +124,17 @@ function ConversationStats(props: {
123
124
  0,
124
125
  )
125
126
  : undefined;
127
+ const tokens = formatUsageTotal(
128
+ (props.detail?.turns ?? props.conversation.turns).map(
129
+ (turn) => turn.cumulativeUsage,
130
+ ),
131
+ );
126
132
  const stats = [
127
133
  slackLocationLabel(props.conversation, { includeId: false }),
128
134
  `${props.conversation.turns.length} turns`,
129
135
  messages === undefined ? "messages loading" : `${messages} messages`,
130
136
  toolCalls === undefined ? "tool calls loading" : `${toolCalls} tool calls`,
137
+ tokens,
131
138
  formatConversationDuration(props.conversation),
132
139
  `started ${formatTime(props.conversation.startedAt)}`,
133
140
  ].filter(Boolean);