@wrongstack/webui 0.257.0 → 0.257.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/wrongstack.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>WrongStack WebUI</title>
8
- <script type="module" crossorigin src="/assets/index-BCG7KsrQ.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-DiFYgNpK.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-QTnfLwEv.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-BXyxPv8C.js">
11
11
  <link rel="stylesheet" crossorigin href="/assets/vendor-CEQg2uSG.css">
package/dist/index.js CHANGED
@@ -8133,8 +8133,13 @@ var NEXT_STEPS_RE = /💡\s*Next steps?\s*\n+((?:\d+\.\s+.+(?:\s+auto="true")?\n
8133
8133
  var ITEM_RE = /^(\d+)\.\s+(.+?)(?:\s+auto="true")?$/;
8134
8134
  function parseNextSteps(content) {
8135
8135
  const match = NEXT_STEPS_RE.exec(content);
8136
- if (!match?.[1]) return [];
8137
- const block = match[1];
8136
+ if (match?.[1]) {
8137
+ const block = match[1];
8138
+ return parseStepLines(block);
8139
+ }
8140
+ return parseStepLines(content);
8141
+ }
8142
+ function parseStepLines(block) {
8138
8143
  const lines = block.split("\n").filter(Boolean);
8139
8144
  const steps = [];
8140
8145
  for (const line of lines) {
@@ -8331,32 +8336,23 @@ function CodeCopyButton({ text }) {
8331
8336
  }
8332
8337
  var markdownComponents = {
8333
8338
  next_steps({ children }) {
8334
- const rawText = typeof children === "string" ? children : "";
8339
+ const rawText = typeof children === "string" ? children.trim() : "";
8340
+ if (!rawText) return null;
8335
8341
  const steps = parseNextSteps(rawText);
8336
8342
  if (steps.length === 0) return null;
8337
- return /* @__PURE__ */ jsxs22("div", { className: "mt-4 rounded-xl border border-primary/20 bg-primary/[0.03] overflow-hidden animate-message", children: [
8338
- /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2 px-3.5 py-2 border-b border-primary/10 bg-primary/[0.04]", children: [
8339
- /* @__PURE__ */ jsx24("span", { className: "flex items-center justify-center w-5 h-5 rounded-md bg-primary/15 text-primary text-xs", children: "\u{1F4A1}" }),
8340
- /* @__PURE__ */ jsx24("span", { className: "text-xs font-semibold text-foreground/90", children: "Next steps" }),
8341
- /* @__PURE__ */ jsx24("span", { className: "text-[10px] text-muted-foreground ml-auto", children: "click to fill input" })
8342
- ] }),
8343
- /* @__PURE__ */ jsx24("div", { className: "flex flex-col p-2 gap-1", children: steps.map((s) => /* @__PURE__ */ jsxs22(
8344
- "button",
8345
- {
8346
- type: "button",
8347
- onClick: () => fillInput(s.text),
8348
- className: "group flex items-center gap-2.5 w-full text-left px-3 py-2 rounded-lg transition-all\n hover:bg-primary/[0.08] hover:shadow-sm\n border border-transparent hover:border-primary/20 cursor-pointer",
8349
- title: `Click to fill: ${s.text}`,
8350
- children: [
8351
- /* @__PURE__ */ jsx24("span", { className: "flex items-center justify-center w-5 h-5 rounded-md bg-muted/80 group-hover:bg-primary/20\n text-[11px] font-mono font-semibold tabular-nums shrink-0\n text-muted-foreground group-hover:text-primary transition-colors", children: s.index }),
8352
- /* @__PURE__ */ jsx24("span", { className: "h-3.5 w-3.5 text-muted-foreground/60 group-hover:text-primary group-hover:translate-x-0.5 transition-all shrink-0 text-xs", children: "\u2192" }),
8353
- /* @__PURE__ */ jsx24("span", { className: "text-sm leading-snug text-foreground/80 group-hover:text-foreground transition-colors flex-1 min-w-0", children: s.text }),
8354
- /* @__PURE__ */ jsx24("span", { className: "opacity-0 group-hover:opacity-100 text-primary/60 transition-all shrink-0 text-xs", children: "\u2197" })
8355
- ]
8356
- },
8357
- s.index
8358
- )) })
8359
- ] });
8343
+ return /* @__PURE__ */ jsx24(
8344
+ NextStepsBar,
8345
+ {
8346
+ steps,
8347
+ onAutoSubmit: (text) => {
8348
+ fillInput(text);
8349
+ const form = document.querySelector('form[class*="flex items-end gap-2"]');
8350
+ if (form) {
8351
+ form.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
8352
+ }
8353
+ }
8354
+ }
8355
+ );
8360
8356
  },
8361
8357
  code({
8362
8358
  inline,
@@ -8577,6 +8573,7 @@ var MessageBubble = memo3(function MessageBubble2({
8577
8573
  setLoading(true);
8578
8574
  const client2 = getWSClient(wsUrl);
8579
8575
  client2.sendMessage(text);
8576
+ fillInput("");
8580
8577
  };
8581
8578
  const isLatestAssistant = (() => {
8582
8579
  if (message.role !== "assistant" || isLoading) return false;
@@ -9737,6 +9734,14 @@ function ChatView() {
9737
9734
  }
9738
9735
  setScrolledDeep(h.scrollOffset > h.viewportSize && h.scrollSize > h.viewportSize * 2.5);
9739
9736
  }, []);
9737
+ const handleHistorySelect = useCallback10(
9738
+ (sessionId2) => {
9739
+ const ws = getWSClient();
9740
+ ws?.resumeSession?.(sessionId2);
9741
+ setSwitcherOpen(false);
9742
+ },
9743
+ []
9744
+ );
9740
9745
  useEffect19(() => {
9741
9746
  const h = vlistRef.current;
9742
9747
  if (!h) return;
@@ -9774,6 +9779,41 @@ function ChatView() {
9774
9779
  const [runStartedAt, setRunStartedAt] = useState25(null);
9775
9780
  const [nowTick, setNowTick] = useState25(() => Date.now());
9776
9781
  const streamAnchor = useRef14(null);
9782
+ const runningStatus = useMemo7(() => {
9783
+ const last = messages[messages.length - 1];
9784
+ const runningTools = messages.filter((m) => m.role === "tool" && m.toolResult === void 0);
9785
+ let label = "Thinking\u2026";
9786
+ if (runningTools.length > 0) {
9787
+ const names = Array.from(new Set(runningTools.map((t) => t.toolName).filter(Boolean)));
9788
+ const preview = names.slice(0, 2).join(", ");
9789
+ const more = names.length > 2 ? ` +${names.length - 2}` : "";
9790
+ label = runningTools.length === 1 ? `Running ${preview || "tool"}\u2026` : `Running ${runningTools.length} tools (${preview}${more})\u2026`;
9791
+ } else if (last?.role === "assistant" && last.content) {
9792
+ label = "Writing reply\u2026";
9793
+ } else if (last?.role === "tool" && last.toolResult !== void 0) {
9794
+ label = "Thinking about the next step\u2026";
9795
+ }
9796
+ const elapsedSec = runStartedAt ? Math.max(0, Math.floor((nowTick - runStartedAt) / 1e3)) : 0;
9797
+ const elapsed = elapsedSec < 60 ? `${elapsedSec}s` : `${Math.floor(elapsedSec / 60)}m ${elapsedSec % 60}s`;
9798
+ let speedLabel = "";
9799
+ const streamingBubble = last?.role === "assistant" && last.streaming && last.content ? last : null;
9800
+ if (streamingBubble) {
9801
+ const anchor = streamAnchor.current;
9802
+ if (!anchor || anchor.id !== streamingBubble.id) {
9803
+ streamAnchor.current = { id: streamingBubble.id, at: Date.now(), len: streamingBubble.content.length };
9804
+ } else {
9805
+ const dt = Math.max(1, nowTick - anchor.at);
9806
+ const dl = Math.max(0, streamingBubble.content.length - anchor.len);
9807
+ if (dt > 500 && dl > 0) {
9808
+ const cps = dl * 1e3 / dt;
9809
+ speedLabel = cps >= 1e3 ? `${(cps / 1e3).toFixed(1)}k ch/s` : `${Math.round(cps)} ch/s`;
9810
+ }
9811
+ }
9812
+ } else if (streamAnchor.current) {
9813
+ streamAnchor.current = null;
9814
+ }
9815
+ return { label, elapsed, speedLabel };
9816
+ }, [messages, nowTick, runStartedAt]);
9777
9817
  useEffect19(() => {
9778
9818
  if (isLoading && runStartedAt === null) setRunStartedAt(Date.now());
9779
9819
  if (!isLoading && runStartedAt !== null) setRunStartedAt(null);
@@ -9888,11 +9928,7 @@ function ChatView() {
9888
9928
  "button",
9889
9929
  {
9890
9930
  type: "button",
9891
- onClick: () => {
9892
- const ws = getWSClient();
9893
- ws?.resumeSession?.(e.id);
9894
- setSwitcherOpen(false);
9895
- },
9931
+ onClick: () => handleHistorySelect(e.id),
9896
9932
  className: cn(
9897
9933
  "w-full text-left px-2 py-1.5 rounded text-xs hover:bg-accent transition-colors",
9898
9934
  e.isCurrent && "bg-primary/10"
@@ -10081,69 +10117,27 @@ function ChatView() {
10081
10117
  className: cn("mx-auto max-w-5xl w-full px-4", compactMode ? "pb-3" : "pb-8"),
10082
10118
  children: [
10083
10119
  /* @__PURE__ */ jsx35(ThinkingBubble, {}),
10084
- isLoading && (() => {
10085
- const last = messages[messages.length - 1];
10086
- const runningTools = messages.filter(
10087
- (m) => m.role === "tool" && m.toolResult === void 0
10088
- );
10089
- let label = "Thinking\u2026";
10090
- if (runningTools.length > 0) {
10091
- const names = Array.from(
10092
- new Set(runningTools.map((t) => t.toolName).filter(Boolean))
10093
- );
10094
- const preview = names.slice(0, 2).join(", ");
10095
- const more = names.length > 2 ? ` +${names.length - 2}` : "";
10096
- label = runningTools.length === 1 ? `Running ${preview || "tool"}\u2026` : `Running ${runningTools.length} tools (${preview}${more})\u2026`;
10097
- } else if (last?.role === "assistant" && last.content) {
10098
- label = "Writing reply\u2026";
10099
- } else if (last?.role === "tool" && last.toolResult !== void 0) {
10100
- label = "Thinking about the next step\u2026";
10101
- }
10102
- const elapsedSec = runStartedAt ? Math.max(0, Math.floor((nowTick - runStartedAt) / 1e3)) : 0;
10103
- const elapsed = elapsedSec < 60 ? `${elapsedSec}s` : `${Math.floor(elapsedSec / 60)}m ${elapsedSec % 60}s`;
10104
- let speedLabel = "";
10105
- const streamingBubble = last?.role === "assistant" && last.streaming && last.content ? last : null;
10106
- if (streamingBubble) {
10107
- const anchor = streamAnchor.current;
10108
- if (!anchor || anchor.id !== streamingBubble.id) {
10109
- streamAnchor.current = {
10110
- id: streamingBubble.id,
10111
- at: Date.now(),
10112
- len: streamingBubble.content.length
10113
- };
10114
- } else {
10115
- const dt = Math.max(1, nowTick - anchor.at);
10116
- const dl = Math.max(0, streamingBubble.content.length - anchor.len);
10117
- if (dt > 500 && dl > 0) {
10118
- const cps = dl * 1e3 / dt;
10119
- speedLabel = cps >= 1e3 ? `${(cps / 1e3).toFixed(1)}k ch/s` : `${Math.round(cps)} ch/s`;
10120
- }
10121
- }
10122
- } else if (streamAnchor.current) {
10123
- streamAnchor.current = null;
10124
- }
10125
- return /* @__PURE__ */ jsxs33("div", { className: "flex gap-3 animate-message", children: [
10126
- /* @__PURE__ */ jsx35("div", { className: "flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-accent text-accent-foreground ring-2 ring-offset-2 ring-offset-background ring-accent/20", children: /* @__PURE__ */ jsx35(Bot4, { className: "h-4 w-4" }) }),
10127
- /* @__PURE__ */ jsx35("div", { className: "flex flex-col gap-1.5", children: /* @__PURE__ */ jsx35("div", { className: "rounded-2xl px-4 py-3 bg-card border text-foreground", children: /* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-3 text-sm", children: [
10128
- /* @__PURE__ */ jsxs33("span", { className: "flex gap-1", children: [
10129
- /* @__PURE__ */ jsx35("span", { className: "h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce [animation-delay:-0.3s]" }),
10130
- /* @__PURE__ */ jsx35("span", { className: "h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce [animation-delay:-0.15s]" }),
10131
- /* @__PURE__ */ jsx35("span", { className: "h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce" })
10132
- ] }),
10133
- /* @__PURE__ */ jsx35("span", { className: "text-foreground/90", children: label }),
10134
- /* @__PURE__ */ jsx35("span", { className: "text-xs text-muted-foreground tabular-nums", children: elapsed }),
10135
- iteration && /* @__PURE__ */ jsxs33("span", { className: "text-xs text-muted-foreground tabular-nums", children: [
10136
- "\xB7 iter ",
10137
- iteration.index,
10138
- iteration.max > 0 ? `/${iteration.max}` : ""
10139
- ] }),
10140
- speedLabel && /* @__PURE__ */ jsxs33("span", { className: "text-xs text-muted-foreground/80 tabular-nums", children: [
10141
- "\xB7 ",
10142
- speedLabel
10143
- ] })
10144
- ] }) }) })
10145
- ] });
10146
- })()
10120
+ isLoading && /* @__PURE__ */ jsxs33("div", { className: "flex gap-3 animate-message", children: [
10121
+ /* @__PURE__ */ jsx35("div", { className: "flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-accent text-accent-foreground ring-2 ring-offset-2 ring-offset-background ring-accent/20", children: /* @__PURE__ */ jsx35(Bot4, { className: "h-4 w-4" }) }),
10122
+ /* @__PURE__ */ jsx35("div", { className: "flex flex-col gap-1.5", children: /* @__PURE__ */ jsx35("div", { className: "rounded-2xl px-4 py-3 bg-card border text-foreground", children: /* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-3 text-sm", children: [
10123
+ /* @__PURE__ */ jsxs33("span", { className: "flex gap-1", children: [
10124
+ /* @__PURE__ */ jsx35("span", { className: "h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce [animation-delay:-0.3s]" }),
10125
+ /* @__PURE__ */ jsx35("span", { className: "h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce [animation-delay:-0.15s]" }),
10126
+ /* @__PURE__ */ jsx35("span", { className: "h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce" })
10127
+ ] }),
10128
+ /* @__PURE__ */ jsx35("span", { className: "text-foreground/90", children: runningStatus.label }),
10129
+ /* @__PURE__ */ jsx35("span", { className: "text-xs text-muted-foreground tabular-nums", children: runningStatus.elapsed }),
10130
+ iteration && /* @__PURE__ */ jsxs33("span", { className: "text-xs text-muted-foreground tabular-nums", children: [
10131
+ "\xB7 iter ",
10132
+ iteration.index,
10133
+ iteration.max > 0 ? `/${iteration.max}` : ""
10134
+ ] }),
10135
+ runningStatus.speedLabel && /* @__PURE__ */ jsxs33("span", { className: "text-xs text-muted-foreground/80 tabular-nums", children: [
10136
+ "\xB7 ",
10137
+ runningStatus.speedLabel
10138
+ ] })
10139
+ ] }) }) })
10140
+ ] })
10147
10141
  ]
10148
10142
  },
10149
10143
  "__live"
@@ -14688,7 +14682,10 @@ function MailboxPanel({ className }) {
14688
14682
  const [deleting, setDeleting] = useState34(false);
14689
14683
  const { client: client2 } = useWebSocket();
14690
14684
  const [ready, setReady] = useState34(client2.status.state === "open");
14691
- useEffect31(() => client2.onStatus((s) => setReady(s.state === "open")), [client2]);
14685
+ useEffect31(() => {
14686
+ const off = client2.onStatus((s) => setReady(s.state === "open"));
14687
+ return () => off();
14688
+ }, [client2]);
14692
14689
  useEffect31(() => {
14693
14690
  if (!ready) return;
14694
14691
  client2.send({ type: "mailbox.messages", payload: { limit: 30 } });
@@ -18313,6 +18310,10 @@ function FleetMonitor({
18313
18310
  );
18314
18311
  const runningCount = fleetList.filter((a) => a.status === "running").length;
18315
18312
  const selectedAgent = selectedIdx !== null ? fleetList[selectedIdx] : null;
18313
+ const handleAgentClick = useCallback27(
18314
+ (i) => setSelectedIdx((prev) => prev === i ? null : i),
18315
+ []
18316
+ );
18316
18317
  const handleKeyDown = useCallback27(
18317
18318
  (e) => {
18318
18319
  if (e.key === "Escape") {
@@ -18428,7 +18429,7 @@ function FleetMonitor({
18428
18429
  agent,
18429
18430
  isSelected: i === selectedIdx,
18430
18431
  isLeader: agent.id === leaderId,
18431
- onClick: () => setSelectedIdx(i === selectedIdx ? null : i)
18432
+ onClick: () => handleAgentClick(i)
18432
18433
  },
18433
18434
  agent.id
18434
18435
  )) }) }),