kiro-memory 1.2.2 → 1.4.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.
@@ -1083,7 +1083,7 @@ var require_react_development = __commonJS({
1083
1083
  }
1084
1084
  return dispatcher.useContext(Context);
1085
1085
  }
1086
- function useState5(initialState) {
1086
+ function useState8(initialState) {
1087
1087
  var dispatcher = resolveDispatcher();
1088
1088
  return dispatcher.useState(initialState);
1089
1089
  }
@@ -1091,11 +1091,11 @@ var require_react_development = __commonJS({
1091
1091
  var dispatcher = resolveDispatcher();
1092
1092
  return dispatcher.useReducer(reducer, initialArg, init);
1093
1093
  }
1094
- function useRef3(initialValue) {
1094
+ function useRef4(initialValue) {
1095
1095
  var dispatcher = resolveDispatcher();
1096
1096
  return dispatcher.useRef(initialValue);
1097
1097
  }
1098
- function useEffect5(create, deps) {
1098
+ function useEffect7(create, deps) {
1099
1099
  var dispatcher = resolveDispatcher();
1100
1100
  return dispatcher.useEffect(create, deps);
1101
1101
  }
@@ -1107,7 +1107,7 @@ var require_react_development = __commonJS({
1107
1107
  var dispatcher = resolveDispatcher();
1108
1108
  return dispatcher.useLayoutEffect(create, deps);
1109
1109
  }
1110
- function useCallback3(callback, deps) {
1110
+ function useCallback4(callback, deps) {
1111
1111
  var dispatcher = resolveDispatcher();
1112
1112
  return dispatcher.useCallback(callback, deps);
1113
1113
  }
@@ -1874,19 +1874,19 @@ var require_react_development = __commonJS({
1874
1874
  exports.memo = memo;
1875
1875
  exports.startTransition = startTransition;
1876
1876
  exports.unstable_act = act;
1877
- exports.useCallback = useCallback3;
1877
+ exports.useCallback = useCallback4;
1878
1878
  exports.useContext = useContext;
1879
1879
  exports.useDebugValue = useDebugValue;
1880
1880
  exports.useDeferredValue = useDeferredValue;
1881
- exports.useEffect = useEffect5;
1881
+ exports.useEffect = useEffect7;
1882
1882
  exports.useId = useId;
1883
1883
  exports.useImperativeHandle = useImperativeHandle;
1884
1884
  exports.useInsertionEffect = useInsertionEffect;
1885
1885
  exports.useLayoutEffect = useLayoutEffect;
1886
1886
  exports.useMemo = useMemo2;
1887
1887
  exports.useReducer = useReducer;
1888
- exports.useRef = useRef3;
1889
- exports.useState = useState5;
1888
+ exports.useRef = useRef4;
1889
+ exports.useState = useState8;
1890
1890
  exports.useSyncExternalStore = useSyncExternalStore;
1891
1891
  exports.useTransition = useTransition;
1892
1892
  exports.version = ReactVersion;
@@ -23581,11 +23581,11 @@ var require_client = __commonJS({
23581
23581
  });
23582
23582
 
23583
23583
  // src/ui/viewer/index.tsx
23584
- var import_react8 = __toESM(require_react(), 1);
23584
+ var import_react9 = __toESM(require_react(), 1);
23585
23585
  var import_client = __toESM(require_client(), 1);
23586
23586
 
23587
23587
  // src/ui/viewer/App.tsx
23588
- var import_react7 = __toESM(require_react(), 1);
23588
+ var import_react8 = __toESM(require_react(), 1);
23589
23589
 
23590
23590
  // src/ui/viewer/components/Header.tsx
23591
23591
  var import_react2 = __toESM(require_react(), 1);
@@ -23594,40 +23594,40 @@ var import_react2 = __toESM(require_react(), 1);
23594
23594
  var import_react = __toESM(require_react(), 1);
23595
23595
 
23596
23596
  // src/ui/viewer/utils/format.ts
23597
- function getBadgeClass(type) {
23597
+ function getTypeBadgeClasses(type) {
23598
23598
  const map = {
23599
- "file-write": "badge--file-write",
23600
- "file-read": "badge--file-read",
23601
- "command": "badge--command",
23602
- "research": "badge--research",
23603
- "delegation": "badge--delegation",
23604
- "tool-use": "badge--tool-use"
23599
+ "file-write": { bg: "bg-emerald-500/10 dark:bg-emerald-500/10", text: "text-emerald-600 dark:text-emerald-400", dot: "bg-emerald-500" },
23600
+ "file-read": { bg: "bg-cyan-500/10 dark:bg-cyan-500/10", text: "text-cyan-600 dark:text-cyan-400", dot: "bg-cyan-500" },
23601
+ "command": { bg: "bg-amber-500/10 dark:bg-amber-500/10", text: "text-amber-600 dark:text-amber-400", dot: "bg-amber-500" },
23602
+ "research": { bg: "bg-blue-500/10 dark:bg-blue-500/10", text: "text-blue-600 dark:text-blue-400", dot: "bg-blue-500" },
23603
+ "delegation": { bg: "bg-violet-500/10 dark:bg-violet-500/10", text: "text-violet-600 dark:text-violet-400", dot: "bg-violet-500" },
23604
+ "tool-use": { bg: "bg-zinc-500/10 dark:bg-zinc-500/10", text: "text-zinc-600 dark:text-zinc-400", dot: "bg-zinc-500" }
23605
23605
  };
23606
- return map[type] || "badge--default";
23606
+ return map[type] || { bg: "bg-zinc-500/10 dark:bg-zinc-500/10", text: "text-zinc-600 dark:text-zinc-400", dot: "bg-zinc-500" };
23607
23607
  }
23608
23608
  function timeAgo(epochSeconds) {
23609
23609
  const now = Date.now() / 1e3;
23610
23610
  const diff = Math.max(0, now - epochSeconds);
23611
- if (diff < 60) return "ora";
23611
+ if (diff < 60) return "just now";
23612
23612
  if (diff < 3600) {
23613
23613
  const m = Math.floor(diff / 60);
23614
- return `${m} min fa`;
23614
+ return `${m}m ago`;
23615
23615
  }
23616
23616
  if (diff < 86400) {
23617
23617
  const h = Math.floor(diff / 3600);
23618
- return `${h}h fa`;
23618
+ return `${h}h ago`;
23619
23619
  }
23620
- if (diff < 172800) return "ieri";
23620
+ if (diff < 172800) return "yesterday";
23621
23621
  if (diff < 604800) {
23622
23622
  const d = Math.floor(diff / 86400);
23623
- return `${d} giorni fa`;
23623
+ return `${d}d ago`;
23624
23624
  }
23625
23625
  if (diff < 2592e3) {
23626
23626
  const w = Math.floor(diff / 604800);
23627
- return `${w} sett. fa`;
23627
+ return `${w}w ago`;
23628
23628
  }
23629
23629
  const date = new Date(epochSeconds * 1e3);
23630
- return date.toLocaleDateString("it-IT", { day: "numeric", month: "short" });
23630
+ return date.toLocaleDateString("en-US", { day: "numeric", month: "short" });
23631
23631
  }
23632
23632
 
23633
23633
  // src/ui/viewer/components/SearchBar.tsx
@@ -23636,25 +23636,23 @@ function SearchBar() {
23636
23636
  const [results, setResults] = (0, import_react.useState)(null);
23637
23637
  const [isOpen, setIsOpen] = (0, import_react.useState)(false);
23638
23638
  const [isSearching, setIsSearching] = (0, import_react.useState)(false);
23639
+ const [selectedIndex, setSelectedIndex] = (0, import_react.useState)(0);
23639
23640
  const inputRef = (0, import_react.useRef)(null);
23640
- const containerRef = (0, import_react.useRef)(null);
23641
23641
  const debounceRef = (0, import_react.useRef)();
23642
23642
  const doSearch = (0, import_react.useCallback)(async (q) => {
23643
23643
  if (!q.trim()) {
23644
23644
  setResults(null);
23645
- setIsOpen(false);
23646
23645
  return;
23647
23646
  }
23648
23647
  setIsSearching(true);
23649
23648
  try {
23650
- const res = await fetch(`/api/search?q=${encodeURIComponent(q)}&limit=10`);
23649
+ const res = await fetch(`/api/search?q=${encodeURIComponent(q)}&limit=8`);
23651
23650
  if (res.ok) {
23652
- const data = await res.json();
23653
- setResults(data);
23654
- setIsOpen(true);
23651
+ setResults(await res.json());
23652
+ setSelectedIndex(0);
23655
23653
  }
23656
- } catch (error) {
23657
- console.error("Ricerca fallita:", error);
23654
+ } catch (err) {
23655
+ console.error("Search failed:", err);
23658
23656
  } finally {
23659
23657
  setIsSearching(false);
23660
23658
  }
@@ -23663,136 +23661,257 @@ function SearchBar() {
23663
23661
  const value = e.target.value;
23664
23662
  setQuery(value);
23665
23663
  if (debounceRef.current) clearTimeout(debounceRef.current);
23666
- debounceRef.current = setTimeout(() => doSearch(value), 300);
23664
+ debounceRef.current = setTimeout(() => doSearch(value), 250);
23667
23665
  }, [doSearch]);
23666
+ const close = (0, import_react.useCallback)(() => {
23667
+ setIsOpen(false);
23668
+ setQuery("");
23669
+ setResults(null);
23670
+ setSelectedIndex(0);
23671
+ }, []);
23668
23672
  (0, import_react.useEffect)(() => {
23669
23673
  const handler = (e) => {
23670
23674
  if ((e.metaKey || e.ctrlKey) && e.key === "k") {
23671
23675
  e.preventDefault();
23672
- inputRef.current?.focus();
23673
- }
23674
- if (e.key === "Escape") {
23675
- setIsOpen(false);
23676
- inputRef.current?.blur();
23676
+ setIsOpen(true);
23677
+ setTimeout(() => inputRef.current?.focus(), 50);
23677
23678
  }
23679
+ if (e.key === "Escape") close();
23678
23680
  };
23679
23681
  window.addEventListener("keydown", handler);
23680
23682
  return () => window.removeEventListener("keydown", handler);
23681
- }, []);
23682
- (0, import_react.useEffect)(() => {
23683
- const handler = (e) => {
23684
- if (containerRef.current && !containerRef.current.contains(e.target)) {
23685
- setIsOpen(false);
23686
- }
23687
- };
23688
- document.addEventListener("mousedown", handler);
23689
- return () => document.removeEventListener("mousedown", handler);
23690
- }, []);
23691
- const totalResults = results ? results.observations.length + results.summaries.length : 0;
23692
- return /* @__PURE__ */ import_react.default.createElement("div", { className: "search-container", ref: containerRef }, /* @__PURE__ */ import_react.default.createElement("div", { className: "search-input-wrapper" }, /* @__PURE__ */ import_react.default.createElement("svg", { className: "search-icon", viewBox: "0 0 16 16", fill: "currentColor" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z" })), /* @__PURE__ */ import_react.default.createElement(
23683
+ }, [close]);
23684
+ const total = results ? results.observations.length + results.summaries.length : 0;
23685
+ return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(
23686
+ "button",
23687
+ {
23688
+ onClick: () => {
23689
+ setIsOpen(true);
23690
+ setTimeout(() => inputRef.current?.focus(), 50);
23691
+ },
23692
+ className: "flex items-center gap-2.5 flex-1 max-w-md px-3 py-2 rounded-lg bg-surface-2 border border-border text-zinc-500 hover:text-zinc-300 hover:border-border-hover transition-all cursor-text"
23693
+ },
23694
+ /* @__PURE__ */ import_react.default.createElement("svg", { className: "w-4 h-4 flex-shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "11", cy: "11", r: "8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "m21 21-4.3-4.3" })),
23695
+ /* @__PURE__ */ import_react.default.createElement("span", { className: "text-sm" }, "Search memories..."),
23696
+ /* @__PURE__ */ import_react.default.createElement("kbd", { className: "ml-auto hidden sm:inline text-[11px] text-zinc-600 bg-surface-3 px-1.5 py-0.5 rounded font-mono border border-border" }, typeof navigator !== "undefined" && navigator.platform?.includes("Mac") ? "\u2318K" : "Ctrl+K")
23697
+ ), isOpen && /* @__PURE__ */ import_react.default.createElement("div", { className: "fixed inset-0 z-[999] bg-black/60 backdrop-blur-sm animate-fade-in", onClick: close }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mx-auto mt-[12vh] w-full max-w-xl animate-scale-in px-4", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react.default.createElement("div", { className: "bg-surface-1 border border-border rounded-xl shadow-2xl overflow-hidden" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-3 px-4 py-3.5 border-b border-border" }, /* @__PURE__ */ import_react.default.createElement("svg", { className: "w-5 h-5 text-accent-violet flex-shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "11", cy: "11", r: "8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "m21 21-4.3-4.3" })), /* @__PURE__ */ import_react.default.createElement(
23693
23698
  "input",
23694
23699
  {
23695
23700
  ref: inputRef,
23696
23701
  type: "text",
23697
- className: "search-input",
23698
- placeholder: "Search observations, summaries...",
23702
+ className: "flex-1 bg-transparent border-none outline-none text-sm text-zinc-100 cmd-input",
23703
+ placeholder: "Search observations, summaries, concepts...",
23699
23704
  value: query,
23700
23705
  onChange: handleChange,
23701
- onFocus: () => {
23702
- if (results && totalResults > 0) setIsOpen(true);
23703
- }
23706
+ autoFocus: true
23704
23707
  }
23705
- ), /* @__PURE__ */ import_react.default.createElement("span", { className: "search-shortcut" }, navigator.platform.includes("Mac") ? "\u2318K" : "Ctrl+K")), isOpen && results && /* @__PURE__ */ import_react.default.createElement("div", { className: "search-results" }, totalResults === 0 && !isSearching && /* @__PURE__ */ import_react.default.createElement("div", { className: "search-empty" }, "No results for \u201C", query, "\u201D"), results.observations.map((obs) => /* @__PURE__ */ import_react.default.createElement("div", { key: `obs-${obs.id}`, className: "search-result-item" }, /* @__PURE__ */ import_react.default.createElement("span", { className: `badge ${getBadgeClass(obs.type)}` }, obs.type), /* @__PURE__ */ import_react.default.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ import_react.default.createElement("div", { className: "search-result-title" }, obs.title), obs.text && /* @__PURE__ */ import_react.default.createElement("div", { className: "search-result-snippet" }, obs.text.substring(0, 120)), /* @__PURE__ */ import_react.default.createElement("div", { className: "search-result-meta" }, obs.project, " \xB7 ", timeAgo(obs.created_at_epoch))))), results.summaries.map((sum) => /* @__PURE__ */ import_react.default.createElement("div", { key: `sum-${sum.id}`, className: "search-result-item" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "badge badge--summary" }, "summary"), /* @__PURE__ */ import_react.default.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ import_react.default.createElement("div", { className: "search-result-title" }, sum.request || "Session Summary"), sum.completed && /* @__PURE__ */ import_react.default.createElement("div", { className: "search-result-snippet" }, sum.completed.substring(0, 120)), /* @__PURE__ */ import_react.default.createElement("div", { className: "search-result-meta" }, sum.project, " \xB7 ", timeAgo(sum.created_at_epoch)))))));
23708
+ ), isSearching && /* @__PURE__ */ import_react.default.createElement("div", { className: "w-4 h-4 border-2 border-accent-violet/30 border-t-accent-violet rounded-full animate-spin" }), /* @__PURE__ */ import_react.default.createElement("kbd", { className: "text-[10px] text-zinc-500 bg-surface-3 px-1.5 py-0.5 rounded font-mono border border-border cursor-pointer", onClick: close }, "ESC")), results && /* @__PURE__ */ import_react.default.createElement("div", { className: "max-h-[360px] overflow-y-auto" }, total === 0 && !isSearching && query.trim() && /* @__PURE__ */ import_react.default.createElement("div", { className: "px-4 py-10 text-center" }, /* @__PURE__ */ import_react.default.createElement("p", { className: "text-sm text-zinc-500" }, 'No results for "', query, '"')), results.observations.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("div", { className: "px-4 py-2 text-[11px] font-semibold uppercase tracking-wider text-zinc-500" }, "Observations"), results.observations.map((obs, idx) => {
23709
+ const badge = getTypeBadgeClasses(obs.type);
23710
+ return /* @__PURE__ */ import_react.default.createElement("div", { key: `obs-${obs.id}`, className: `flex items-start gap-3 px-4 py-2.5 transition-colors ${idx === selectedIndex ? "bg-surface-2" : "hover:bg-surface-2/50"}` }, /* @__PURE__ */ import_react.default.createElement("div", { className: `w-2 h-2 rounded-full mt-1.5 flex-shrink-0 ${badge.dot}` }), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "text-sm text-zinc-200 truncate" }, obs.title), obs.text && /* @__PURE__ */ import_react.default.createElement("div", { className: "text-xs text-zinc-500 truncate mt-0.5" }, obs.text.substring(0, 100)), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 mt-1" }, /* @__PURE__ */ import_react.default.createElement("span", { className: `text-[10px] font-medium px-1.5 py-0.5 rounded ${badge.bg} ${badge.text}` }, obs.type), /* @__PURE__ */ import_react.default.createElement("span", { className: "text-[10px] text-zinc-600 font-mono" }, timeAgo(obs.created_at_epoch)))));
23711
+ })), results.summaries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("div", { className: "px-4 py-2 text-[11px] font-semibold uppercase tracking-wider text-zinc-500 border-t border-border" }, "Summaries"), results.summaries.map((sum) => /* @__PURE__ */ import_react.default.createElement("div", { key: `sum-${sum.id}`, className: "flex items-start gap-3 px-4 py-2.5 hover:bg-surface-2/50 transition-colors" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "w-2 h-2 rounded-full mt-1.5 flex-shrink-0 bg-accent-cyan" }), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "text-sm text-zinc-200 truncate" }, sum.request || "Session Summary"), sum.completed && /* @__PURE__ */ import_react.default.createElement("div", { className: "text-xs text-zinc-500 truncate mt-0.5" }, sum.completed.substring(0, 100)), /* @__PURE__ */ import_react.default.createElement("span", { className: "text-[10px] text-zinc-600 font-mono" }, timeAgo(sum.created_at_epoch))))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-4 px-4 py-2.5 border-t border-border text-[11px] text-zinc-600" }, /* @__PURE__ */ import_react.default.createElement("span", null, /* @__PURE__ */ import_react.default.createElement("kbd", { className: "px-1 py-0.5 rounded bg-surface-3 border border-border font-mono mr-1" }, "\u2191\u2193"), "navigate"), /* @__PURE__ */ import_react.default.createElement("span", null, /* @__PURE__ */ import_react.default.createElement("kbd", { className: "px-1 py-0.5 rounded bg-surface-3 border border-border font-mono mr-1" }, "\u21B5"), "open"), /* @__PURE__ */ import_react.default.createElement("span", null, /* @__PURE__ */ import_react.default.createElement("kbd", { className: "px-1 py-0.5 rounded bg-surface-3 border border-border font-mono mr-1" }, "esc"), "close"))))));
23706
23712
  }
23707
23713
 
23708
23714
  // src/ui/viewer/components/Header.tsx
23709
23715
  function Header({ isConnected, resolvedTheme, onThemeToggle }) {
23710
- return /* @__PURE__ */ import_react2.default.createElement("header", { className: "header" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "header-brand" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: `status-dot ${isConnected ? "connected" : "disconnected"}` }), /* @__PURE__ */ import_react2.default.createElement("h1", null, "Kiro Memory")), /* @__PURE__ */ import_react2.default.createElement(SearchBar, null), /* @__PURE__ */ import_react2.default.createElement("div", { className: "header-controls" }, /* @__PURE__ */ import_react2.default.createElement(
23716
+ return /* @__PURE__ */ import_react2.default.createElement("header", { className: "flex items-center gap-4 px-6 h-14 bg-surface-1 border-b border-border z-50" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex items-center gap-3 flex-shrink-0" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "w-8 h-8 rounded-lg bg-accent-violet flex items-center justify-center" }, /* @__PURE__ */ import_react2.default.createElement("svg", { className: "w-[18px] h-[18px] text-white", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("path", { d: "M12 2a4 4 0 0 1 4 4c0 1.95-1.4 3.58-3.25 3.93a1 1 0 0 0-.75.97V13" }), /* @__PURE__ */ import_react2.default.createElement("path", { d: "M12 2a4 4 0 0 0-4 4c0 1.95 1.4 3.58 3.25 3.93a1 1 0 0 1 .75.97V13" }), /* @__PURE__ */ import_react2.default.createElement("path", { d: "M9 18h6" }), /* @__PURE__ */ import_react2.default.createElement("path", { d: "M10 22h4" }), /* @__PURE__ */ import_react2.default.createElement("path", { d: "M12 13v5" }))), /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h1", { className: "text-[15px] font-bold text-zinc-100 leading-none" }, "ContextKit"), /* @__PURE__ */ import_react2.default.createElement("span", { className: "text-[11px] text-zinc-500 mt-0.5 block" }, "Memory Dashboard"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "hidden md:block w-px h-6 bg-border" }), /* @__PURE__ */ import_react2.default.createElement(SearchBar, null), /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex-1" }), /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 rounded-lg bg-surface-2 border border-border" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: `w-2 h-2 rounded-full ${isConnected ? "bg-accent-green animate-pulse-dot" : "bg-zinc-500"}` }), /* @__PURE__ */ import_react2.default.createElement("span", { className: `text-xs font-medium ${isConnected ? "text-accent-green" : "text-zinc-500"}` }, isConnected ? "Live" : "Offline")), /* @__PURE__ */ import_react2.default.createElement(
23711
23717
  "button",
23712
23718
  {
23713
- className: "theme-btn",
23714
23719
  onClick: onThemeToggle,
23715
- title: resolvedTheme === "dark" ? "Switch to light mode" : "Switch to dark mode"
23720
+ className: "w-8 h-8 rounded-lg bg-surface-2 border border-border text-zinc-400 hover:text-zinc-100 hover:bg-surface-3 hover:border-border-hover transition-all flex items-center justify-center",
23721
+ title: resolvedTheme === "dark" ? "Light mode" : "Dark mode"
23716
23722
  },
23717
- resolvedTheme === "dark" ? "\u2600" : "\u263E"
23718
- )));
23723
+ resolvedTheme === "dark" ? /* @__PURE__ */ import_react2.default.createElement("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "12", cy: "12", r: "4" }), /* @__PURE__ */ import_react2.default.createElement("path", { d: "M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" })) : /* @__PURE__ */ import_react2.default.createElement("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" }))
23724
+ ));
23719
23725
  }
23720
23726
 
23721
23727
  // src/ui/viewer/components/Sidebar.tsx
23722
23728
  var import_react3 = __toESM(require_react(), 1);
23723
- var TYPE_COLORS = {
23724
- "file-write": "var(--green)",
23725
- "command": "var(--amber)",
23726
- "research": "var(--blue)",
23727
- "delegation": "var(--accent)",
23728
- "tool-use": "var(--text-muted)"
23729
+ var TYPE_CONFIG = {
23730
+ "file-write": { color: "bg-accent-green", label: "File writes" },
23731
+ "file-read": { color: "bg-accent-cyan", label: "File reads" },
23732
+ "command": { color: "bg-accent-amber", label: "Commands" },
23733
+ "research": { color: "bg-accent-blue", label: "Research" },
23734
+ "delegation": { color: "bg-accent-violet", label: "Delegations" },
23735
+ "tool-use": { color: "bg-zinc-400", label: "Tool usage" }
23729
23736
  };
23737
+ var PROJECT_COLORS = [
23738
+ { bg: "bg-accent-violet/15", text: "text-accent-violet", ring: "ring-accent-violet/30" },
23739
+ { bg: "bg-accent-blue/15", text: "text-accent-blue", ring: "ring-accent-blue/30" },
23740
+ { bg: "bg-accent-green/15", text: "text-accent-green", ring: "ring-accent-green/30" },
23741
+ { bg: "bg-accent-amber/15", text: "text-accent-amber", ring: "ring-accent-amber/30" },
23742
+ { bg: "bg-accent-rose/15", text: "text-accent-rose", ring: "ring-accent-rose/30" },
23743
+ { bg: "bg-accent-cyan/15", text: "text-accent-cyan", ring: "ring-accent-cyan/30" },
23744
+ { bg: "bg-accent-orange/15", text: "text-accent-orange", ring: "ring-accent-orange/30" }
23745
+ ];
23746
+ function getProjectColorByName(name) {
23747
+ let hash = 0;
23748
+ for (let i = 0; i < name.length; i++) hash = (hash << 5) - hash + name.charCodeAt(i) | 0;
23749
+ return PROJECT_COLORS[Math.abs(hash) % PROJECT_COLORS.length];
23750
+ }
23730
23751
  function Sidebar({
23731
23752
  projects,
23732
23753
  currentFilter,
23733
23754
  onFilterChange,
23734
23755
  activeTypes,
23735
23756
  onToggleType,
23736
- stats
23757
+ stats,
23758
+ getDisplayName,
23759
+ onRenameProject
23737
23760
  }) {
23738
- return /* @__PURE__ */ import_react3.default.createElement("aside", { className: "sidebar" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "sidebar-section" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "sidebar-title" }, "Projects"), /* @__PURE__ */ import_react3.default.createElement(
23739
- "div",
23740
- {
23741
- className: `sidebar-item ${currentFilter === "" ? "active" : ""}`,
23742
- onClick: () => onFilterChange("")
23743
- },
23744
- "All Projects"
23745
- ), projects.map((project) => /* @__PURE__ */ import_react3.default.createElement(
23746
- "div",
23761
+ const [editingProject, setEditingProject] = (0, import_react3.useState)(null);
23762
+ const [editValue, setEditValue] = (0, import_react3.useState)("");
23763
+ const editInputRef = (0, import_react3.useRef)(null);
23764
+ (0, import_react3.useEffect)(() => {
23765
+ if (editingProject && editInputRef.current) {
23766
+ editInputRef.current.focus();
23767
+ editInputRef.current.select();
23768
+ }
23769
+ }, [editingProject]);
23770
+ const startEditing = (project, e) => {
23771
+ e.stopPropagation();
23772
+ setEditingProject(project);
23773
+ setEditValue(getDisplayName(project));
23774
+ };
23775
+ const confirmEdit = async () => {
23776
+ if (editingProject && editValue.trim()) {
23777
+ await onRenameProject(editingProject, editValue.trim());
23778
+ }
23779
+ setEditingProject(null);
23780
+ };
23781
+ const cancelEdit = () => {
23782
+ setEditingProject(null);
23783
+ setEditValue("");
23784
+ };
23785
+ const handleKeyDown = (e) => {
23786
+ if (e.key === "Enter") confirmEdit();
23787
+ if (e.key === "Escape") cancelEdit();
23788
+ };
23789
+ return /* @__PURE__ */ import_react3.default.createElement("aside", { className: "h-full overflow-y-auto bg-surface-1 border-r border-border flex flex-col" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "p-4" }, /* @__PURE__ */ import_react3.default.createElement("h3", { className: "text-[11px] font-semibold uppercase tracking-wider text-zinc-500 mb-3 px-2" }, "Projects"), /* @__PURE__ */ import_react3.default.createElement(
23790
+ "button",
23747
23791
  {
23748
- key: project,
23749
- className: `sidebar-item ${currentFilter === project ? "active" : ""}`,
23750
- onClick: () => onFilterChange(project)
23792
+ onClick: () => onFilterChange(""),
23793
+ className: `w-full flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-all text-left mb-0.5 ${currentFilter === "" ? "bg-accent-violet/10 text-accent-violet font-semibold" : "text-zinc-400 hover:text-zinc-200 hover:bg-surface-2"}`
23751
23794
  },
23752
- project
23753
- ))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "sidebar-section" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "sidebar-title" }, "Type Filters"), Object.entries(TYPE_COLORS).map(([type, color]) => /* @__PURE__ */ import_react3.default.createElement("label", { key: type, className: "filter-checkbox" }, /* @__PURE__ */ import_react3.default.createElement(
23754
- "input",
23755
- {
23756
- type: "checkbox",
23757
- checked: activeTypes.has(type),
23758
- onChange: () => onToggleType(type)
23759
- }
23760
- ), /* @__PURE__ */ import_react3.default.createElement("span", { className: "filter-dot", style: { background: color } }), type))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "sidebar-section" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "sidebar-title" }, "Stats"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "stats-grid" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-card" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-value" }, stats.observations), /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-label" }, "Observations")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-card" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-value" }, stats.summaries), /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-label" }, "Summaries")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-card" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-value" }, stats.prompts), /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-label" }, "Prompts")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-card" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-value" }, projects.length), /* @__PURE__ */ import_react3.default.createElement("div", { className: "stat-label" }, "Projects")))));
23795
+ /* @__PURE__ */ import_react3.default.createElement("div", { className: "w-7 h-7 rounded-md bg-accent-violet/15 flex items-center justify-center flex-shrink-0" }, /* @__PURE__ */ import_react3.default.createElement("svg", { className: "w-3.5 h-3.5 text-accent-violet", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react3.default.createElement("rect", { x: "3", y: "3", width: "7", height: "7" }), /* @__PURE__ */ import_react3.default.createElement("rect", { x: "14", y: "3", width: "7", height: "7" }), /* @__PURE__ */ import_react3.default.createElement("rect", { x: "3", y: "14", width: "7", height: "7" }), /* @__PURE__ */ import_react3.default.createElement("rect", { x: "14", y: "14", width: "7", height: "7" }))),
23796
+ /* @__PURE__ */ import_react3.default.createElement("span", { className: "flex-1" }, "All projects"),
23797
+ /* @__PURE__ */ import_react3.default.createElement("span", { className: "text-xs text-zinc-600 font-mono tabular-nums" }, projects.length)
23798
+ ), /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex flex-col gap-0.5 mt-1" }, projects.map((project) => {
23799
+ const pc = getProjectColorByName(project);
23800
+ const isEditing = editingProject === project;
23801
+ const isActive = currentFilter === project;
23802
+ const initials = getDisplayName(project).substring(0, 2).toUpperCase();
23803
+ return /* @__PURE__ */ import_react3.default.createElement("div", { key: project, className: "group" }, isEditing ? /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 rounded-lg bg-surface-3 border border-border" }, /* @__PURE__ */ import_react3.default.createElement(
23804
+ "input",
23805
+ {
23806
+ ref: editInputRef,
23807
+ type: "text",
23808
+ value: editValue,
23809
+ onChange: (e) => setEditValue(e.target.value),
23810
+ onKeyDown: handleKeyDown,
23811
+ onBlur: confirmEdit,
23812
+ className: "flex-1 min-w-0 bg-transparent border-none outline-none text-sm text-zinc-200"
23813
+ }
23814
+ ), /* @__PURE__ */ import_react3.default.createElement("button", { onClick: confirmEdit, className: "text-accent-green hover:text-accent-green/80 p-0.5" }, /* @__PURE__ */ import_react3.default.createElement("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5" }, /* @__PURE__ */ import_react3.default.createElement("polyline", { points: "20 6 9 17 4 12" }))), /* @__PURE__ */ import_react3.default.createElement("button", { onClick: cancelEdit, className: "text-zinc-500 hover:text-zinc-300 p-0.5" }, /* @__PURE__ */ import_react3.default.createElement("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5" }, /* @__PURE__ */ import_react3.default.createElement("path", { d: "M18 6 6 18M6 6l12 12" })))) : /* @__PURE__ */ import_react3.default.createElement(
23815
+ "button",
23816
+ {
23817
+ onClick: () => onFilterChange(project),
23818
+ className: `w-full flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-all text-left ${isActive ? "bg-surface-3 text-zinc-100 font-medium" : "text-zinc-400 hover:text-zinc-200 hover:bg-surface-2"}`
23819
+ },
23820
+ /* @__PURE__ */ import_react3.default.createElement("div", { className: `w-7 h-7 rounded-md flex items-center justify-center flex-shrink-0 text-[11px] font-bold ${pc.bg} ${pc.text}` }, initials),
23821
+ /* @__PURE__ */ import_react3.default.createElement("span", { className: "flex-1 truncate" }, getDisplayName(project)),
23822
+ /* @__PURE__ */ import_react3.default.createElement(
23823
+ "span",
23824
+ {
23825
+ onClick: (e) => startEditing(project, e),
23826
+ className: "opacity-0 group-hover:opacity-100 text-zinc-600 hover:text-zinc-300 transition-all p-0.5",
23827
+ title: "Rename"
23828
+ },
23829
+ /* @__PURE__ */ import_react3.default.createElement("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react3.default.createElement("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" }))
23830
+ )
23831
+ ));
23832
+ }))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mx-4 h-px bg-border" }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "p-4" }, /* @__PURE__ */ import_react3.default.createElement("h3", { className: "text-[11px] font-semibold uppercase tracking-wider text-zinc-500 mb-3 px-2" }, "Filters"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex flex-col gap-0.5" }, Object.entries(TYPE_CONFIG).map(([type, config]) => {
23833
+ const isActive = activeTypes.has(type);
23834
+ return /* @__PURE__ */ import_react3.default.createElement(
23835
+ "label",
23836
+ {
23837
+ key: type,
23838
+ className: `flex items-center gap-3 px-3 py-2 rounded-lg text-sm cursor-pointer transition-all ${isActive ? "text-zinc-300 hover:text-zinc-100" : "text-zinc-600 hover:text-zinc-400"}`
23839
+ },
23840
+ /* @__PURE__ */ import_react3.default.createElement("div", { className: `w-4 h-4 rounded border flex items-center justify-center flex-shrink-0 transition-all ${isActive ? "bg-accent-violet border-accent-violet" : "bg-transparent border-zinc-600"}` }, isActive && /* @__PURE__ */ import_react3.default.createElement("svg", { className: "w-2.5 h-2.5 text-white", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3" }, /* @__PURE__ */ import_react3.default.createElement("polyline", { points: "20 6 9 17 4 12" }))),
23841
+ /* @__PURE__ */ import_react3.default.createElement("input", { type: "checkbox", checked: isActive, onChange: () => onToggleType(type), className: "sr-only" }),
23842
+ /* @__PURE__ */ import_react3.default.createElement("div", { className: `w-2.5 h-2.5 rounded-full flex-shrink-0 ${config.color} ${isActive ? "opacity-100" : "opacity-30"}` }),
23843
+ /* @__PURE__ */ import_react3.default.createElement("span", { className: "flex-1" }, config.label)
23844
+ );
23845
+ }))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mx-4 h-px bg-border" }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "p-4" }, /* @__PURE__ */ import_react3.default.createElement("h3", { className: "text-[11px] font-semibold uppercase tracking-wider text-zinc-500 mb-3 px-2" }, "Statistics"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "grid grid-cols-2 gap-2" }, [
23846
+ { label: "Observations", value: stats.observations, color: "text-accent-violet" },
23847
+ { label: "Summaries", value: stats.summaries, color: "text-accent-cyan" },
23848
+ { label: "Prompts", value: stats.prompts, color: "text-accent-amber" },
23849
+ { label: "Projects", value: projects.length, color: "text-accent-green" }
23850
+ ].map((item) => /* @__PURE__ */ import_react3.default.createElement("div", { key: item.label, className: "rounded-lg bg-surface-2 border border-border px-3 py-3 text-center" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: `text-xl font-bold tabular-nums ${item.color}` }, item.value), /* @__PURE__ */ import_react3.default.createElement("div", { className: "text-[10px] uppercase tracking-wider text-zinc-600 mt-1" }, item.label))))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mt-auto px-4 py-4" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "text-[10px] text-zinc-700 font-mono text-center" }, "ContextKit v1.4.0")));
23761
23851
  }
23762
23852
 
23763
23853
  // src/ui/viewer/components/Feed.tsx
23764
23854
  var import_react4 = __toESM(require_react(), 1);
23765
- function Feed({ observations, summaries, prompts, onLoadMore, isLoading, hasMore }) {
23855
+ var TYPE_STYLES = {
23856
+ "file-write": { border: "border-l-emerald-500", bg: "bg-emerald-500/10", text: "text-emerald-400", dot: "bg-emerald-500", label: "file-write" },
23857
+ "file-read": { border: "border-l-cyan-500", bg: "bg-cyan-500/10", text: "text-cyan-400", dot: "bg-cyan-500", label: "file-read" },
23858
+ "command": { border: "border-l-amber-500", bg: "bg-amber-500/10", text: "text-amber-400", dot: "bg-amber-500", label: "command" },
23859
+ "research": { border: "border-l-blue-500", bg: "bg-blue-500/10", text: "text-blue-400", dot: "bg-blue-500", label: "research" },
23860
+ "delegation": { border: "border-l-violet-500", bg: "bg-violet-500/10", text: "text-violet-400", dot: "bg-violet-500", label: "delegation" },
23861
+ "tool-use": { border: "border-l-zinc-500", bg: "bg-zinc-500/10", text: "text-zinc-400", dot: "bg-zinc-500", label: "tool-use" }
23862
+ };
23863
+ function getTypeStyle(type) {
23864
+ return TYPE_STYLES[type] || TYPE_STYLES["tool-use"];
23865
+ }
23866
+ function Feed({ observations, summaries, prompts, onLoadMore, isLoading, hasMore, getDisplayName }) {
23766
23867
  const items = [...observations, ...summaries, ...prompts].sort(
23767
23868
  (a, b) => b.created_at_epoch - a.created_at_epoch
23768
23869
  );
23769
- return /* @__PURE__ */ import_react4.default.createElement("div", { className: "feed" }, items.map((item) => {
23870
+ return /* @__PURE__ */ import_react4.default.createElement("div", { className: "space-y-3" }, items.map((item, index) => {
23871
+ const stagger = index < 8 ? `stagger-${index + 1}` : "";
23770
23872
  if ("type" in item && "title" in item) {
23771
- return /* @__PURE__ */ import_react4.default.createElement(ObservationCard, { key: `obs-${item.id}`, obs: item });
23873
+ return /* @__PURE__ */ import_react4.default.createElement("div", { key: `obs-${item.id}`, className: `opacity-0 animate-slide-up ${stagger}` }, /* @__PURE__ */ import_react4.default.createElement(ObservationCard, { obs: item, getDisplayName }));
23772
23874
  } else if ("request" in item) {
23773
- return /* @__PURE__ */ import_react4.default.createElement(SummaryCard, { key: `sum-${item.id}`, summary: item });
23875
+ return /* @__PURE__ */ import_react4.default.createElement("div", { key: `sum-${item.id}`, className: `opacity-0 animate-slide-up ${stagger}` }, /* @__PURE__ */ import_react4.default.createElement(SummaryCard, { summary: item, getDisplayName }));
23774
23876
  } else {
23775
- return /* @__PURE__ */ import_react4.default.createElement(PromptCard, { key: `prompt-${item.id}`, prompt: item });
23877
+ return /* @__PURE__ */ import_react4.default.createElement("div", { key: `prompt-${item.id}`, className: `opacity-0 animate-slide-up ${stagger}` }, /* @__PURE__ */ import_react4.default.createElement(PromptCard, { prompt: item, getDisplayName }));
23776
23878
  }
23777
- }), hasMore && /* @__PURE__ */ import_react4.default.createElement(
23879
+ }), hasMore && items.length > 0 && /* @__PURE__ */ import_react4.default.createElement("div", { className: "pt-2" }, /* @__PURE__ */ import_react4.default.createElement(
23778
23880
  "button",
23779
23881
  {
23780
- className: "load-more-btn",
23781
23882
  onClick: onLoadMore,
23782
- disabled: isLoading
23883
+ disabled: isLoading,
23884
+ className: "flex items-center justify-center gap-2 w-full py-3 rounded-lg transition-all text-sm font-medium bg-surface-2 border border-border text-zinc-400 hover:bg-surface-3 hover:text-zinc-200 hover:border-border-hover disabled:opacity-40 disabled:cursor-not-allowed"
23783
23885
  },
23784
- isLoading ? "Caricamento..." : "Carica altri"
23785
- ), items.length === 0 && !isLoading && /* @__PURE__ */ import_react4.default.createElement("div", { className: "empty-state" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "empty-state-icon" }, /* @__PURE__ */ import_react4.default.createElement("svg", { viewBox: "0 0 24 24", width: "48", height: "48", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }, /* @__PURE__ */ import_react4.default.createElement("path", { d: "M12 6v6l4 2", strokeLinecap: "round" }), /* @__PURE__ */ import_react4.default.createElement("circle", { cx: "12", cy: "12", r: "10" }))), /* @__PURE__ */ import_react4.default.createElement("p", null, "Nessun dato disponibile."), /* @__PURE__ */ import_react4.default.createElement("p", { className: "empty-state-hint" }, "Avvia una sessione con Kiro per iniziare a raccogliere contesto.")));
23886
+ isLoading ? /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement("div", { className: "w-4 h-4 border-2 border-accent-violet/30 border-t-accent-violet rounded-full animate-spin" }), "Loading...") : /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react4.default.createElement("path", { d: "m6 9 6 6 6-6" })), "Load more")
23887
+ )), items.length === 0 && !isLoading && /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex flex-col items-center justify-center py-20 text-center" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "w-16 h-16 rounded-2xl bg-surface-2 border border-border flex items-center justify-center mb-5" }, /* @__PURE__ */ import_react4.default.createElement("svg", { className: "w-7 h-7 text-zinc-600", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }, /* @__PURE__ */ import_react4.default.createElement("path", { d: "M12 2a4 4 0 0 1 4 4c0 1.95-1.4 3.58-3.25 3.93a1 1 0 0 0-.75.97V13" }), /* @__PURE__ */ import_react4.default.createElement("path", { d: "M12 2a4 4 0 0 0-4 4c0 1.95 1.4 3.58 3.25 3.93a1 1 0 0 1 .75.97V13" }), /* @__PURE__ */ import_react4.default.createElement("path", { d: "M9 18h6" }), /* @__PURE__ */ import_react4.default.createElement("path", { d: "M10 22h4" }), /* @__PURE__ */ import_react4.default.createElement("path", { d: "M12 13v5" }))), /* @__PURE__ */ import_react4.default.createElement("p", { className: "text-base font-semibold text-zinc-300 mb-2" }, "No memories yet"), /* @__PURE__ */ import_react4.default.createElement("p", { className: "text-sm text-zinc-500 max-w-xs leading-relaxed" }, "Start a coding session to begin capturing context automatically.")), items.length === 0 && isLoading && /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex flex-col items-center justify-center py-20" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "w-6 h-6 border-2 border-accent-violet/30 border-t-accent-violet rounded-full animate-spin mb-4" }), /* @__PURE__ */ import_react4.default.createElement("p", { className: "text-sm text-zinc-500" }, "Loading memories...")));
23786
23888
  }
23787
- function ObservationCard({ obs }) {
23788
- const cardClass = `card card--${obs.type.replace(/\s+/g, "-")}`;
23789
- return /* @__PURE__ */ import_react4.default.createElement("div", { className: cardClass }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-header" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: `badge ${getBadgeClass(obs.type)}` }, obs.type), /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-project" }, obs.project), /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-time" }, timeAgo(obs.created_at_epoch))), /* @__PURE__ */ import_react4.default.createElement("h3", { className: "card-title" }, obs.title), obs.subtitle && /* @__PURE__ */ import_react4.default.createElement("p", { className: "card-subtitle" }, obs.subtitle), obs.text && /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-content" }, /* @__PURE__ */ import_react4.default.createElement("p", null, obs.text)), obs.narrative && /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-section" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-section-label" }, "Narrativa"), /* @__PURE__ */ import_react4.default.createElement("p", null, obs.narrative)), obs.facts && /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-section" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-section-label" }, "Fatti"), /* @__PURE__ */ import_react4.default.createElement("p", null, obs.facts)), (obs.files_modified || obs.files_read) && /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-files" }, obs.files_modified && /* @__PURE__ */ import_react4.default.createElement("span", { className: "file-pill file-pill--modified", title: "File modificati" }, "\u270E ", obs.files_modified.split(",").length, " file"), obs.files_read && /* @__PURE__ */ import_react4.default.createElement("span", { className: "file-pill file-pill--read", title: "File letti" }, "\u25C9 ", obs.files_read.split(",").length, " file")), obs.concepts && /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-tags" }, obs.concepts.split(", ").map((concept, i) => /* @__PURE__ */ import_react4.default.createElement("span", { key: i, className: "tag" }, concept))));
23889
+ function ObservationCard({ obs, getDisplayName }) {
23890
+ const style = getTypeStyle(obs.type);
23891
+ const [expanded, setExpanded] = (0, import_react4.useState)(false);
23892
+ const longContent = obs.text && obs.text.length > 300;
23893
+ const displayText = longContent && !expanded ? obs.text.substring(0, 280) + "..." : obs.text;
23894
+ return /* @__PURE__ */ import_react4.default.createElement("div", { className: `bg-surface-1 border border-border rounded-lg border-l-[3px] ${style.border} shadow-card hover:shadow-card-hover hover:border-border-hover transition-all` }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "px-4 pt-4 pb-2" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: `inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide px-2 py-0.5 rounded-md ${style.bg} ${style.text}` }, /* @__PURE__ */ import_react4.default.createElement("span", { className: `w-1.5 h-1.5 rounded-full ${style.dot}` }), style.label), /* @__PURE__ */ import_react4.default.createElement("span", { className: "inline-flex items-center text-[11px] font-medium px-2 py-0.5 rounded-md bg-accent-violet/10 text-accent-violet" }, getDisplayName(obs.project)), /* @__PURE__ */ import_react4.default.createElement("span", { className: "text-xs text-zinc-600 font-mono ml-auto" }, timeAgo(obs.created_at_epoch))), /* @__PURE__ */ import_react4.default.createElement("h3", { className: "text-[15px] font-semibold text-zinc-100 leading-snug" }, obs.title)), obs.subtitle && /* @__PURE__ */ import_react4.default.createElement("div", { className: "px-4 pb-1" }, /* @__PURE__ */ import_react4.default.createElement("p", { className: "text-xs italic text-zinc-500" }, obs.subtitle)), obs.text && /* @__PURE__ */ import_react4.default.createElement("div", { className: "px-4 pb-3" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "text-sm text-zinc-400 leading-relaxed whitespace-pre-wrap break-words" }, displayText), longContent && /* @__PURE__ */ import_react4.default.createElement(
23895
+ "button",
23896
+ {
23897
+ onClick: () => setExpanded(!expanded),
23898
+ className: "text-xs text-accent-violet hover:text-accent-violet/80 mt-1.5 font-medium transition-colors"
23899
+ },
23900
+ expanded ? "Show less" : "Show more"
23901
+ )), obs.narrative && /* @__PURE__ */ import_react4.default.createElement("div", { className: "mx-4 mb-3 p-3 rounded-md bg-surface-2 border border-border" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "block text-[10px] font-semibold uppercase tracking-wider text-blue-400 mb-1" }, "Narrative"), /* @__PURE__ */ import_react4.default.createElement("p", { className: "text-xs text-zinc-400 leading-relaxed" }, obs.narrative)), obs.facts && /* @__PURE__ */ import_react4.default.createElement("div", { className: "mx-4 mb-3 p-3 rounded-md bg-surface-2 border border-border" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "block text-[10px] font-semibold uppercase tracking-wider text-cyan-400 mb-1" }, "Facts"), /* @__PURE__ */ import_react4.default.createElement("p", { className: "text-xs text-zinc-400 leading-relaxed" }, obs.facts)), (obs.files_modified || obs.files_read) && /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex flex-wrap gap-2 px-4 pb-3" }, obs.files_modified && /* @__PURE__ */ import_react4.default.createElement("span", { className: "inline-flex items-center gap-1.5 text-[11px] font-medium text-emerald-400 bg-emerald-500/10 px-2 py-0.5 rounded-md" }, /* @__PURE__ */ import_react4.default.createElement("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, /* @__PURE__ */ import_react4.default.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" }), /* @__PURE__ */ import_react4.default.createElement("path", { d: "M14 2v6h6" })), obs.files_modified.split(",").length, " modified"), obs.files_read && /* @__PURE__ */ import_react4.default.createElement("span", { className: "inline-flex items-center gap-1.5 text-[11px] font-medium text-blue-400 bg-blue-500/10 px-2 py-0.5 rounded-md" }, /* @__PURE__ */ import_react4.default.createElement("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, /* @__PURE__ */ import_react4.default.createElement("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7Z" })), obs.files_read.split(",").length, " read")), obs.concepts && /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex flex-wrap gap-1.5 px-4 pb-4" }, obs.concepts.split(", ").map((concept, i) => /* @__PURE__ */ import_react4.default.createElement("span", { key: i, className: "text-[11px] text-zinc-500 bg-surface-3 px-2 py-0.5 rounded-md border border-border" }, concept))));
23790
23902
  }
23791
- function SummaryCard({ summary }) {
23792
- return /* @__PURE__ */ import_react4.default.createElement("div", { className: "card card--summary" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-header" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "badge badge--summary" }, "summary"), /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-project" }, summary.project), /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-time" }, timeAgo(summary.created_at_epoch))), summary.request && /* @__PURE__ */ import_react4.default.createElement("h3", { className: "card-title" }, summary.request), summary.investigated && /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-section" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-section-label" }, "Indagato"), /* @__PURE__ */ import_react4.default.createElement("p", null, summary.investigated)), summary.learned && /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-section" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-section-label" }, "Appreso"), /* @__PURE__ */ import_react4.default.createElement("p", null, summary.learned)), summary.completed && /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-section" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-section-label" }, "Completato"), /* @__PURE__ */ import_react4.default.createElement("p", null, summary.completed)), summary.next_steps && /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-section" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-section-label" }, "Prossimi passi"), /* @__PURE__ */ import_react4.default.createElement("p", null, summary.next_steps)), summary.notes && /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-section" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-section-label" }, "Note"), /* @__PURE__ */ import_react4.default.createElement("p", null, summary.notes)));
23903
+ function SummaryCard({ summary, getDisplayName }) {
23904
+ const sections = [
23905
+ { label: "Investigated", value: summary.investigated, color: "text-blue-400" },
23906
+ { label: "Learned", value: summary.learned, color: "text-emerald-400" },
23907
+ { label: "Completed", value: summary.completed, color: "text-violet-400" },
23908
+ { label: "Next steps", value: summary.next_steps, color: "text-amber-400" },
23909
+ { label: "Notes", value: summary.notes, color: "text-zinc-400" }
23910
+ ].filter((s) => s.value);
23911
+ return /* @__PURE__ */ import_react4.default.createElement("div", { className: "bg-surface-1 border border-border rounded-lg border-l-[3px] border-l-cyan-500 shadow-card hover:shadow-card-hover hover:border-border-hover transition-all" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "px-4 pt-4 pb-2" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide px-2 py-0.5 rounded-md bg-cyan-500/10 text-cyan-400" }, /* @__PURE__ */ import_react4.default.createElement("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react4.default.createElement("path", { d: "M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" }), /* @__PURE__ */ import_react4.default.createElement("path", { d: "M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" })), "Summary"), /* @__PURE__ */ import_react4.default.createElement("span", { className: "inline-flex items-center text-[11px] font-medium px-2 py-0.5 rounded-md bg-accent-violet/10 text-accent-violet" }, getDisplayName(summary.project)), /* @__PURE__ */ import_react4.default.createElement("span", { className: "text-xs text-zinc-600 font-mono ml-auto" }, timeAgo(summary.created_at_epoch))), summary.request && /* @__PURE__ */ import_react4.default.createElement("h3", { className: "text-[15px] font-semibold text-zinc-100 leading-snug" }, summary.request)), /* @__PURE__ */ import_react4.default.createElement("div", { className: "px-4 pb-4 space-y-2" }, sections.map(({ label, value, color }) => /* @__PURE__ */ import_react4.default.createElement("div", { key: label, className: "p-3 rounded-md bg-surface-2 border border-border" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: `block text-[10px] font-semibold uppercase tracking-wider mb-1 ${color}` }, label), /* @__PURE__ */ import_react4.default.createElement("p", { className: "text-xs text-zinc-400 leading-relaxed" }, value)))));
23793
23912
  }
23794
- function PromptCard({ prompt }) {
23795
- return /* @__PURE__ */ import_react4.default.createElement("div", { className: "card card--prompt" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-header" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "badge badge--prompt" }, "prompt #", prompt.prompt_number), /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-project" }, prompt.project), /* @__PURE__ */ import_react4.default.createElement("span", { className: "card-time" }, timeAgo(prompt.created_at_epoch))), /* @__PURE__ */ import_react4.default.createElement("div", { className: "card-content card-content--mono" }, /* @__PURE__ */ import_react4.default.createElement("p", null, prompt.prompt_text)));
23913
+ function PromptCard({ prompt, getDisplayName }) {
23914
+ return /* @__PURE__ */ import_react4.default.createElement("div", { className: "bg-surface-1 border border-border rounded-lg border-l-[3px] border-l-rose-500 shadow-card hover:shadow-card-hover hover:border-border-hover transition-all" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "px-4 pt-4 pb-2" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide px-2 py-0.5 rounded-md bg-rose-500/10 text-rose-400" }, /* @__PURE__ */ import_react4.default.createElement("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react4.default.createElement("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })), "Prompt #", prompt.prompt_number), /* @__PURE__ */ import_react4.default.createElement("span", { className: "inline-flex items-center text-[11px] font-medium px-2 py-0.5 rounded-md bg-accent-violet/10 text-accent-violet" }, getDisplayName(prompt.project)), /* @__PURE__ */ import_react4.default.createElement("span", { className: "text-xs text-zinc-600 font-mono ml-auto" }, timeAgo(prompt.created_at_epoch)))), /* @__PURE__ */ import_react4.default.createElement("div", { className: "px-4 pb-4" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "p-3 rounded-md bg-surface-0 border border-border font-mono text-sm text-zinc-300 leading-relaxed whitespace-pre-wrap break-words" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "text-rose-400 select-none mr-2" }, "$"), prompt.prompt_text)));
23796
23915
  }
23797
23916
 
23798
23917
  // src/ui/viewer/hooks/useSSE.ts
@@ -23820,7 +23939,7 @@ function useSSE() {
23820
23939
  setState((prev) => ({ ...prev, observations }));
23821
23940
  }
23822
23941
  } catch (err) {
23823
- console.error("Fetch observations fallito:", err);
23942
+ console.error("Failed to fetch observations:", err);
23824
23943
  }
23825
23944
  };
23826
23945
  const fetchSummaries = async () => {
@@ -23831,7 +23950,7 @@ function useSSE() {
23831
23950
  setState((prev) => ({ ...prev, summaries }));
23832
23951
  }
23833
23952
  } catch (err) {
23834
- console.error("Fetch summaries fallito:", err);
23953
+ console.error("Failed to fetch summaries:", err);
23835
23954
  }
23836
23955
  };
23837
23956
  const fetchPrompts = async () => {
@@ -23842,7 +23961,7 @@ function useSSE() {
23842
23961
  setState((prev) => ({ ...prev, prompts }));
23843
23962
  }
23844
23963
  } catch (err) {
23845
- console.error("Fetch prompts fallito:", err);
23964
+ console.error("Failed to fetch prompts:", err);
23846
23965
  }
23847
23966
  };
23848
23967
  const fetchProjects = async () => {
@@ -23853,7 +23972,7 @@ function useSSE() {
23853
23972
  setState((prev) => ({ ...prev, projects }));
23854
23973
  }
23855
23974
  } catch (err) {
23856
- console.error("Fetch projects fallito:", err);
23975
+ console.error("Failed to fetch projects:", err);
23857
23976
  }
23858
23977
  };
23859
23978
  const connect = () => {
@@ -23901,8 +24020,8 @@ function useSSE() {
23901
24020
  // src/ui/viewer/hooks/useTheme.ts
23902
24021
  var import_react6 = __toESM(require_react(), 1);
23903
24022
  function useTheme() {
23904
- const [preference, setPreference] = (0, import_react6.useState)("system");
23905
- const [resolvedTheme, setResolvedTheme] = (0, import_react6.useState)("light");
24023
+ const [preference, setPreference] = (0, import_react6.useState)("dark");
24024
+ const [resolvedTheme, setResolvedTheme] = (0, import_react6.useState)("dark");
23906
24025
  (0, import_react6.useEffect)(() => {
23907
24026
  const saved = localStorage.getItem("kiro-memory-theme");
23908
24027
  if (saved) {
@@ -23910,19 +24029,31 @@ function useTheme() {
23910
24029
  }
23911
24030
  }, []);
23912
24031
  (0, import_react6.useEffect)(() => {
23913
- const resolveTheme = () => {
23914
- if (preference === "system") {
23915
- const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
23916
- setResolvedTheme(prefersDark ? "dark" : "light");
23917
- } else {
23918
- setResolvedTheme(preference);
23919
- }
23920
- };
23921
- resolveTheme();
24032
+ let resolved;
24033
+ if (preference === "system") {
24034
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
24035
+ resolved = prefersDark ? "dark" : "light";
24036
+ } else {
24037
+ resolved = preference;
24038
+ }
24039
+ setResolvedTheme(resolved);
24040
+ if (resolved === "dark") {
24041
+ document.documentElement.classList.add("dark");
24042
+ } else {
24043
+ document.documentElement.classList.remove("dark");
24044
+ }
24045
+ }, [preference]);
24046
+ (0, import_react6.useEffect)(() => {
23922
24047
  const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
23923
24048
  const handler = (e) => {
23924
24049
  if (preference === "system") {
23925
- setResolvedTheme(e.matches ? "dark" : "light");
24050
+ const resolved = e.matches ? "dark" : "light";
24051
+ setResolvedTheme(resolved);
24052
+ if (resolved === "dark") {
24053
+ document.documentElement.classList.add("dark");
24054
+ } else {
24055
+ document.documentElement.classList.remove("dark");
24056
+ }
23926
24057
  }
23927
24058
  };
23928
24059
  mediaQuery.addEventListener("change", handler);
@@ -23935,6 +24066,48 @@ function useTheme() {
23935
24066
  return { preference, resolvedTheme, setThemePreference };
23936
24067
  }
23937
24068
 
24069
+ // src/ui/viewer/hooks/useProjectAliases.ts
24070
+ var import_react7 = __toESM(require_react(), 1);
24071
+ function useProjectAliases() {
24072
+ const [aliases, setAliases] = (0, import_react7.useState)({});
24073
+ const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
24074
+ (0, import_react7.useEffect)(() => {
24075
+ const fetchAliases = async () => {
24076
+ try {
24077
+ const res = await fetch("/api/project-aliases");
24078
+ if (res.ok) {
24079
+ const data = await res.json();
24080
+ setAliases(data);
24081
+ }
24082
+ } catch (err) {
24083
+ console.error("Failed to fetch project aliases:", err);
24084
+ }
24085
+ };
24086
+ fetchAliases();
24087
+ }, []);
24088
+ const getDisplayName = (0, import_react7.useCallback)((project) => {
24089
+ return aliases[project] || project;
24090
+ }, [aliases]);
24091
+ const updateAlias = (0, import_react7.useCallback)(async (project, displayName) => {
24092
+ setIsLoading(true);
24093
+ try {
24094
+ const res = await fetch(`/api/project-aliases/${encodeURIComponent(project)}`, {
24095
+ method: "PUT",
24096
+ headers: { "Content-Type": "application/json" },
24097
+ body: JSON.stringify({ displayName })
24098
+ });
24099
+ if (res.ok) {
24100
+ setAliases((prev) => ({ ...prev, [project]: displayName }));
24101
+ }
24102
+ } catch (err) {
24103
+ console.error("Failed to update project alias:", err);
24104
+ } finally {
24105
+ setIsLoading(false);
24106
+ }
24107
+ }, []);
24108
+ return { aliases, getDisplayName, updateAlias, isLoading };
24109
+ }
24110
+
23938
24111
  // src/ui/viewer/utils/data.ts
23939
24112
  function mergeAndDeduplicateByProject(liveData, paginatedData) {
23940
24113
  const seen = /* @__PURE__ */ new Set();
@@ -23959,39 +24132,40 @@ function mergeAndDeduplicateByProject(liveData, paginatedData) {
23959
24132
  }
23960
24133
 
23961
24134
  // src/ui/viewer/App.tsx
23962
- var TYPE_FILTERS = ["file-write", "command", "research", "delegation", "tool-use"];
24135
+ var TYPE_FILTERS = ["file-write", "file-read", "command", "research", "delegation", "tool-use"];
23963
24136
  function App() {
23964
- const [currentFilter, setCurrentFilter] = (0, import_react7.useState)("");
23965
- const [activeTypes, setActiveTypes] = (0, import_react7.useState)(new Set(TYPE_FILTERS));
23966
- const [paginatedObservations, setPaginatedObservations] = (0, import_react7.useState)([]);
23967
- const [paginatedSummaries, setPaginatedSummaries] = (0, import_react7.useState)([]);
23968
- const [paginatedPrompts, setPaginatedPrompts] = (0, import_react7.useState)([]);
23969
- const [isLoadingMore, setIsLoadingMore] = (0, import_react7.useState)(false);
23970
- const [hasMore, setHasMore] = (0, import_react7.useState)(true);
24137
+ const [currentFilter, setCurrentFilter] = (0, import_react8.useState)("");
24138
+ const [activeTypes, setActiveTypes] = (0, import_react8.useState)(new Set(TYPE_FILTERS));
24139
+ const [paginatedObservations, setPaginatedObservations] = (0, import_react8.useState)([]);
24140
+ const [paginatedSummaries, setPaginatedSummaries] = (0, import_react8.useState)([]);
24141
+ const [paginatedPrompts, setPaginatedPrompts] = (0, import_react8.useState)([]);
24142
+ const [isLoadingMore, setIsLoadingMore] = (0, import_react8.useState)(false);
24143
+ const [hasMore, setHasMore] = (0, import_react8.useState)(true);
23971
24144
  const { observations, summaries, prompts, projects, isConnected } = useSSE();
23972
24145
  const { resolvedTheme, setThemePreference } = useTheme();
23973
- const allObservations = (0, import_react7.useMemo)(() => {
24146
+ const { getDisplayName, updateAlias } = useProjectAliases();
24147
+ const allObservations = (0, import_react8.useMemo)(() => {
23974
24148
  if (currentFilter) return paginatedObservations;
23975
24149
  return mergeAndDeduplicateByProject(observations, paginatedObservations);
23976
24150
  }, [observations, paginatedObservations, currentFilter]);
23977
- const allSummaries = (0, import_react7.useMemo)(() => {
24151
+ const allSummaries = (0, import_react8.useMemo)(() => {
23978
24152
  if (currentFilter) return paginatedSummaries;
23979
24153
  return mergeAndDeduplicateByProject(summaries, paginatedSummaries);
23980
24154
  }, [summaries, paginatedSummaries, currentFilter]);
23981
- const allPrompts = (0, import_react7.useMemo)(() => {
24155
+ const allPrompts = (0, import_react8.useMemo)(() => {
23982
24156
  if (currentFilter) return paginatedPrompts;
23983
24157
  return mergeAndDeduplicateByProject(prompts, paginatedPrompts);
23984
24158
  }, [prompts, paginatedPrompts, currentFilter]);
23985
- const filteredObservations = (0, import_react7.useMemo)(
24159
+ const filteredObservations = (0, import_react8.useMemo)(
23986
24160
  () => allObservations.filter((o) => activeTypes.has(o.type)),
23987
24161
  [allObservations, activeTypes]
23988
24162
  );
23989
- const stats = (0, import_react7.useMemo)(() => ({
24163
+ const stats = (0, import_react8.useMemo)(() => ({
23990
24164
  observations: allObservations.length,
23991
24165
  summaries: allSummaries.length,
23992
24166
  prompts: allPrompts.length
23993
24167
  }), [allObservations, allSummaries, allPrompts]);
23994
- const toggleType = (0, import_react7.useCallback)((type) => {
24168
+ const toggleType = (0, import_react8.useCallback)((type) => {
23995
24169
  setActiveTypes((prev) => {
23996
24170
  const next = new Set(prev);
23997
24171
  if (next.has(type)) next.delete(type);
@@ -23999,15 +24173,36 @@ function App() {
23999
24173
  return next;
24000
24174
  });
24001
24175
  }, []);
24002
- const handleLoadMore = (0, import_react7.useCallback)(async () => {
24176
+ const fetchForProject = (0, import_react8.useCallback)(async (project) => {
24177
+ setIsLoadingMore(true);
24178
+ try {
24179
+ const params = new URLSearchParams({
24180
+ offset: "0",
24181
+ limit: "30",
24182
+ ...project && { project }
24183
+ });
24184
+ const [obsRes, sumRes, promptRes] = await Promise.all([
24185
+ fetch(`/api/observations?${params}`),
24186
+ fetch(`/api/summaries?${params}`),
24187
+ fetch(`/api/prompts?${params}`)
24188
+ ]);
24189
+ if (obsRes.ok) setPaginatedObservations(await obsRes.json());
24190
+ if (sumRes.ok) setPaginatedSummaries(await sumRes.json());
24191
+ if (promptRes.ok) setPaginatedPrompts(await promptRes.json());
24192
+ } catch (error) {
24193
+ console.error("Failed to load data:", error);
24194
+ } finally {
24195
+ setIsLoadingMore(false);
24196
+ }
24197
+ }, []);
24198
+ const handleLoadMore = (0, import_react8.useCallback)(async () => {
24003
24199
  if (isLoadingMore) return;
24004
24200
  setIsLoadingMore(true);
24005
24201
  try {
24006
24202
  const offset = paginatedObservations.length;
24007
- const limit = 20;
24008
24203
  const params = new URLSearchParams({
24009
24204
  offset: String(offset),
24010
- limit: String(limit),
24205
+ limit: "20",
24011
24206
  ...currentFilter && { project: currentFilter }
24012
24207
  });
24013
24208
  const [obsRes, sumRes, promptRes] = await Promise.all([
@@ -24033,25 +24228,28 @@ function App() {
24033
24228
  }
24034
24229
  if (newItems === 0) setHasMore(false);
24035
24230
  } catch (error) {
24036
- console.error("Errore caricamento dati:", error);
24231
+ console.error("Failed to load more data:", error);
24037
24232
  } finally {
24038
24233
  setIsLoadingMore(false);
24039
24234
  }
24040
24235
  }, [currentFilter, paginatedObservations.length, isLoadingMore]);
24041
- (0, import_react7.useEffect)(() => {
24236
+ (0, import_react8.useEffect)(() => {
24042
24237
  setPaginatedObservations([]);
24043
24238
  setPaginatedSummaries([]);
24044
24239
  setPaginatedPrompts([]);
24045
24240
  setHasMore(true);
24046
- }, [currentFilter]);
24047
- return /* @__PURE__ */ import_react7.default.createElement("div", { className: "app", "data-theme": resolvedTheme }, /* @__PURE__ */ import_react7.default.createElement(
24241
+ if (currentFilter) {
24242
+ fetchForProject(currentFilter);
24243
+ }
24244
+ }, [currentFilter, fetchForProject]);
24245
+ return /* @__PURE__ */ import_react8.default.createElement("div", { className: "h-screen overflow-hidden flex flex-col bg-surface-0" }, /* @__PURE__ */ import_react8.default.createElement(
24048
24246
  Header,
24049
24247
  {
24050
24248
  isConnected,
24051
24249
  resolvedTheme,
24052
24250
  onThemeToggle: () => setThemePreference(resolvedTheme === "dark" ? "light" : "dark")
24053
24251
  }
24054
- ), /* @__PURE__ */ import_react7.default.createElement(
24252
+ ), /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex flex-1 overflow-hidden" }, /* @__PURE__ */ import_react8.default.createElement("div", { className: "hidden md:flex w-[260px] flex-shrink-0" }, /* @__PURE__ */ import_react8.default.createElement(
24055
24253
  Sidebar,
24056
24254
  {
24057
24255
  projects,
@@ -24059,9 +24257,19 @@ function App() {
24059
24257
  onFilterChange: setCurrentFilter,
24060
24258
  activeTypes,
24061
24259
  onToggleType: toggleType,
24062
- stats
24260
+ stats,
24261
+ getDisplayName,
24262
+ onRenameProject: updateAlias
24063
24263
  }
24064
- ), /* @__PURE__ */ import_react7.default.createElement("div", { className: "main" }, /* @__PURE__ */ import_react7.default.createElement(
24264
+ )), /* @__PURE__ */ import_react8.default.createElement("main", { className: "flex-1 overflow-y-auto bg-surface-0" }, /* @__PURE__ */ import_react8.default.createElement("div", { className: "max-w-3xl mx-auto px-6 py-6" }, currentFilter && /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex items-center gap-3 mb-6 animate-fade-in" }, /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ import_react8.default.createElement("div", { className: "w-9 h-9 rounded-lg bg-accent-violet/15 flex items-center justify-center" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-xs font-bold text-accent-violet" }, getDisplayName(currentFilter).substring(0, 2).toUpperCase())), /* @__PURE__ */ import_react8.default.createElement("div", null, /* @__PURE__ */ import_react8.default.createElement("h2", { className: "text-lg font-bold text-zinc-100" }, getDisplayName(currentFilter)), currentFilter !== getDisplayName(currentFilter) && /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[11px] font-mono text-zinc-600" }, currentFilter))), /* @__PURE__ */ import_react8.default.createElement(
24265
+ "button",
24266
+ {
24267
+ onClick: () => setCurrentFilter(""),
24268
+ className: "ml-auto flex items-center gap-1.5 text-xs text-zinc-500 hover:text-zinc-300 transition-colors px-3 py-1.5 rounded-lg hover:bg-surface-2 border border-transparent hover:border-border"
24269
+ },
24270
+ /* @__PURE__ */ import_react8.default.createElement("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, /* @__PURE__ */ import_react8.default.createElement("path", { d: "M18 6 6 18M6 6l12 12" })),
24271
+ "Clear filter"
24272
+ )), /* @__PURE__ */ import_react8.default.createElement(
24065
24273
  Feed,
24066
24274
  {
24067
24275
  observations: filteredObservations,
@@ -24069,15 +24277,16 @@ function App() {
24069
24277
  prompts: allPrompts,
24070
24278
  onLoadMore: handleLoadMore,
24071
24279
  isLoading: isLoadingMore,
24072
- hasMore
24280
+ hasMore,
24281
+ getDisplayName
24073
24282
  }
24074
- )));
24283
+ )))));
24075
24284
  }
24076
24285
 
24077
24286
  // src/ui/viewer/index.tsx
24078
24287
  var root = import_client.default.createRoot(document.getElementById("root"));
24079
24288
  root.render(
24080
- /* @__PURE__ */ import_react8.default.createElement(import_react8.default.StrictMode, null, /* @__PURE__ */ import_react8.default.createElement(App, null))
24289
+ /* @__PURE__ */ import_react9.default.createElement(import_react9.default.StrictMode, null, /* @__PURE__ */ import_react9.default.createElement(App, null))
24081
24290
  );
24082
24291
  /*! Bundled license information:
24083
24292