@wrongstack/webui 0.73.1 → 0.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -408,6 +408,10 @@ var WrongStackWebSocketClient = class {
408
408
  clearTodos() {
409
409
  this.send({ type: "todos.clear" });
410
410
  }
411
+ removeTodo(idOrIndex) {
412
+ const payload = typeof idOrIndex === "number" ? { index: idOrIndex } : { id: idOrIndex };
413
+ this.send({ type: "todos.remove", payload });
414
+ }
411
415
  listSessions(limit = 50) {
412
416
  this.send({ type: "sessions.list", payload: { limit } });
413
417
  }
@@ -1605,7 +1609,7 @@ function useWebSocket() {
1605
1609
  }
1606
1610
 
1607
1611
  // src/App.tsx
1608
- import { useEffect as useEffect21 } from "react";
1612
+ import { useEffect as useEffect22 } from "react";
1609
1613
 
1610
1614
  // src/components/ChatView/index.tsx
1611
1615
  import {
@@ -7812,8 +7816,98 @@ function Sidebar() {
7812
7816
  ] });
7813
7817
  }
7814
7818
 
7819
+ // src/components/TodosPanel.tsx
7820
+ import { CheckCircle2 as CheckCircle27, Circle as Circle2, Clock as Clock3, X as X8 } from "lucide-react";
7821
+ import { useCallback as useCallback6, useEffect as useEffect21, useRef as useRef12, useState as useState26 } from "react";
7822
+ import { jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
7823
+ function TodosPanel() {
7824
+ const [todos, setTodos] = useState26([]);
7825
+ const ws = getWSClient();
7826
+ const offRef = useRef12(null);
7827
+ useEffect21(() => {
7828
+ ws.send({ type: "todos.get" });
7829
+ offRef.current = ws.on("todos.updated", (msg) => {
7830
+ const payload = msg?.payload;
7831
+ if (payload?.todos) setTodos(payload.todos);
7832
+ });
7833
+ return () => {
7834
+ offRef.current?.();
7835
+ };
7836
+ }, [ws]);
7837
+ const handleRemove = useCallback6(
7838
+ (id) => {
7839
+ ws.removeTodo(id);
7840
+ },
7841
+ [ws]
7842
+ );
7843
+ const pending = todos.filter((t) => t.status === "pending");
7844
+ const inProgress = todos.filter((t) => t.status === "in_progress");
7845
+ const completed = todos.filter((t) => t.status === "completed");
7846
+ return /* @__PURE__ */ jsxs38("div", { className: "flex flex-col h-full bg-background", children: [
7847
+ /* @__PURE__ */ jsxs38("div", { className: "px-4 py-3 border-b border-border flex items-center justify-between shrink-0", children: [
7848
+ /* @__PURE__ */ jsxs38("div", { className: "flex items-center gap-2", children: [
7849
+ /* @__PURE__ */ jsx43("h2", { className: "text-sm font-semibold text-foreground", children: "TODOS" }),
7850
+ /* @__PURE__ */ jsxs38("span", { className: "text-xs text-muted-foreground tabular-nums", children: [
7851
+ completed.length,
7852
+ "/",
7853
+ todos.length
7854
+ ] })
7855
+ ] }),
7856
+ /* @__PURE__ */ jsxs38("div", { className: "flex items-center gap-2 text-xs", children: [
7857
+ inProgress.length > 0 && /* @__PURE__ */ jsxs38("span", { className: "flex items-center gap-1 text-yellow-600 dark:text-yellow-400", children: [
7858
+ /* @__PURE__ */ jsx43(Clock3, { className: "w-3 h-3" }),
7859
+ inProgress.length
7860
+ ] }),
7861
+ pending.length > 0 && /* @__PURE__ */ jsxs38("span", { className: "flex items-center gap-1 text-muted-foreground", children: [
7862
+ /* @__PURE__ */ jsx43(Circle2, { className: "w-3 h-3" }),
7863
+ pending.length
7864
+ ] }),
7865
+ completed.length > 0 && /* @__PURE__ */ jsxs38("span", { className: "flex items-center gap-1 text-emerald-600 dark:text-emerald-400", children: [
7866
+ /* @__PURE__ */ jsx43(CheckCircle27, { className: "w-3 h-3" }),
7867
+ completed.length
7868
+ ] })
7869
+ ] })
7870
+ ] }),
7871
+ /* @__PURE__ */ jsx43("div", { className: "flex-1 overflow-y-auto", children: todos.length === 0 ? /* @__PURE__ */ jsx43("p", { className: "px-4 py-8 text-xs text-muted-foreground text-center", children: "No todos yet. The agent will create them as it plans work." }) : /* @__PURE__ */ jsx43("div", { className: "py-1", children: todos.map((t) => {
7872
+ const label = t.status === "in_progress" && t.activeForm ? t.activeForm : t.content;
7873
+ const isInProgress = t.status === "in_progress";
7874
+ const isCompleted = t.status === "completed";
7875
+ return /* @__PURE__ */ jsxs38(
7876
+ "div",
7877
+ {
7878
+ className: `px-4 py-2 flex items-start gap-2.5 text-sm border-l-2 group ${isInProgress ? "border-l-yellow-500 bg-yellow-50/40 dark:bg-yellow-950/20" : isCompleted ? "border-l-emerald-500 bg-emerald-50/30 dark:bg-emerald-950/15" : "border-l-transparent"}`,
7879
+ children: [
7880
+ /* @__PURE__ */ jsx43("span", { className: "mt-0.5 shrink-0", children: isCompleted ? /* @__PURE__ */ jsx43(CheckCircle27, { className: "w-3.5 h-3.5 text-emerald-500" }) : isInProgress ? /* @__PURE__ */ jsx43(Clock3, { className: "w-3.5 h-3.5 text-yellow-500 animate-spin" }) : /* @__PURE__ */ jsx43(Circle2, { className: "w-3.5 h-3.5 text-muted-foreground/50" }) }),
7881
+ /* @__PURE__ */ jsx43(
7882
+ "span",
7883
+ {
7884
+ className: `leading-snug flex-1 ${isInProgress ? "text-yellow-800 dark:text-yellow-200 font-medium" : isCompleted ? "text-muted-foreground line-through" : "text-foreground"}`,
7885
+ children: label
7886
+ }
7887
+ ),
7888
+ /* @__PURE__ */ jsx43(
7889
+ "button",
7890
+ {
7891
+ type: "button",
7892
+ onClick: (e) => {
7893
+ e.stopPropagation();
7894
+ handleRemove(t.id);
7895
+ },
7896
+ className: "shrink-0 p-0.5 rounded opacity-0 group-hover:opacity-60 hover:opacity-100 hover:bg-muted transition-all",
7897
+ title: "Remove",
7898
+ children: /* @__PURE__ */ jsx43(X8, { className: "w-3 h-3 text-muted-foreground" })
7899
+ }
7900
+ )
7901
+ ]
7902
+ },
7903
+ t.id
7904
+ );
7905
+ }) }) })
7906
+ ] });
7907
+ }
7908
+
7815
7909
  // src/App.tsx
7816
- import { Fragment as Fragment11, jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
7910
+ import { Fragment as Fragment11, jsx as jsx44, jsxs as jsxs39 } from "react/jsx-runtime";
7817
7911
  function AppInner() {
7818
7912
  const { theme } = useTheme();
7819
7913
  const { currentView, sidebarOpen, toggleSidebar, setSearchOpen, setSidebarOpen } = useUIStore();
@@ -7823,7 +7917,7 @@ function AppInner() {
7823
7917
  const sessionTitle = useSessionStore((s) => s.session?.title);
7824
7918
  const sessionId = useSessionStore((s) => s.session?.id);
7825
7919
  const nickname = useUIStore((s) => sessionId ? s.sessionNicknames[sessionId] : void 0);
7826
- useEffect21(() => {
7920
+ useEffect22(() => {
7827
7921
  if (typeof window === "undefined") return;
7828
7922
  const mq = window.matchMedia("(max-width: 768px)");
7829
7923
  const apply = () => {
@@ -7836,7 +7930,7 @@ function AppInner() {
7836
7930
  return () => mq.removeEventListener("change", apply);
7837
7931
  }, [setSidebarOpen]);
7838
7932
  useWebSocketBootstrap();
7839
- useEffect21(() => {
7933
+ useEffect22(() => {
7840
7934
  const parts = [];
7841
7935
  if (isLoading) {
7842
7936
  const it = iteration ? ` iter ${iteration.index}${iteration.max ? `/${iteration.max}` : ""}` : "";
@@ -7853,7 +7947,7 @@ function AppInner() {
7853
7947
  document.title = "WrongStack";
7854
7948
  };
7855
7949
  }, [isLoading, iteration, projectName, sessionTitle, nickname]);
7856
- useEffect21(() => {
7950
+ useEffect22(() => {
7857
7951
  const onKey = (e) => {
7858
7952
  const t = e.target;
7859
7953
  const tag = t?.tagName?.toLowerCase();
@@ -7949,33 +8043,34 @@ function AppInner() {
7949
8043
  window.addEventListener("keydown", onKey);
7950
8044
  return () => window.removeEventListener("keydown", onKey);
7951
8045
  }, [toggleSidebar, setSearchOpen]);
7952
- return /* @__PURE__ */ jsxs38("div", { className: cn("flex h-screen", theme), children: [
7953
- sidebarOpen && /* @__PURE__ */ jsx43(Sidebar, {}),
7954
- /* @__PURE__ */ jsxs38("main", { className: "flex-1 flex flex-col overflow-hidden", children: [
7955
- /* @__PURE__ */ jsx43(ConnectionBanner, {}),
7956
- currentView === "chat" && /* @__PURE__ */ jsxs38(Fragment11, { children: [
7957
- sessionId && /* @__PURE__ */ jsxs38("div", { className: "px-4 pt-2 space-y-2", children: [
7958
- /* @__PURE__ */ jsx43(CollabPanel, { sessionId }),
7959
- /* @__PURE__ */ jsx43(FleetPanel, {})
8046
+ return /* @__PURE__ */ jsxs39("div", { className: cn("flex h-screen", theme), children: [
8047
+ sidebarOpen && /* @__PURE__ */ jsx44(Sidebar, {}),
8048
+ /* @__PURE__ */ jsxs39("main", { className: "flex-1 flex flex-col overflow-hidden", children: [
8049
+ /* @__PURE__ */ jsx44(ConnectionBanner, {}),
8050
+ currentView === "chat" && /* @__PURE__ */ jsxs39(Fragment11, { children: [
8051
+ sessionId && /* @__PURE__ */ jsxs39("div", { className: "px-4 pt-2 space-y-2", children: [
8052
+ /* @__PURE__ */ jsx44(CollabPanel, { sessionId }),
8053
+ /* @__PURE__ */ jsx44(FleetPanel, {}),
8054
+ /* @__PURE__ */ jsx44(TodosPanel, {})
7960
8055
  ] }),
7961
- /* @__PURE__ */ jsx43(ChatView, {})
8056
+ /* @__PURE__ */ jsx44(ChatView, {})
7962
8057
  ] }),
7963
- currentView === "settings" && /* @__PURE__ */ jsx43(SettingsPanel, {})
8058
+ currentView === "settings" && /* @__PURE__ */ jsx44(SettingsPanel, {})
7964
8059
  ] }),
7965
- /* @__PURE__ */ jsx43(ConfirmDialog, {}),
7966
- /* @__PURE__ */ jsx43(CommandPalette, {}),
7967
- /* @__PURE__ */ jsx43(ShortcutsOverlay, {}),
7968
- /* @__PURE__ */ jsx43(QuickModelSwitcher, {}),
7969
- /* @__PURE__ */ jsx43(Toaster, {})
8060
+ /* @__PURE__ */ jsx44(ConfirmDialog, {}),
8061
+ /* @__PURE__ */ jsx44(CommandPalette, {}),
8062
+ /* @__PURE__ */ jsx44(ShortcutsOverlay, {}),
8063
+ /* @__PURE__ */ jsx44(QuickModelSwitcher, {}),
8064
+ /* @__PURE__ */ jsx44(Toaster, {})
7970
8065
  ] });
7971
8066
  }
7972
8067
  function App() {
7973
- return /* @__PURE__ */ jsx43(ErrorBoundary, { children: /* @__PURE__ */ jsx43(ThemeProvider, { defaultTheme: "system", children: /* @__PURE__ */ jsx43(AppInner, {}) }) });
8068
+ return /* @__PURE__ */ jsx44(ErrorBoundary, { children: /* @__PURE__ */ jsx44(ThemeProvider, { defaultTheme: "system", children: /* @__PURE__ */ jsx44(AppInner, {}) }) });
7974
8069
  }
7975
8070
 
7976
8071
  // src/main.tsx
7977
- import { jsx as jsx44 } from "react/jsx-runtime";
8072
+ import { jsx as jsx45 } from "react/jsx-runtime";
7978
8073
  ReactDOM.createRoot(document.getElementById("root")).render(
7979
- /* @__PURE__ */ jsx44(React6.StrictMode, { children: /* @__PURE__ */ jsx44(App, {}) })
8074
+ /* @__PURE__ */ jsx45(React6.StrictMode, { children: /* @__PURE__ */ jsx45(App, {}) })
7980
8075
  );
7981
8076
  //# sourceMappingURL=index.js.map