kiro-memory 1.0.4 → 1.1.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.
@@ -1091,7 +1091,7 @@ var require_react_development = __commonJS({
1091
1091
  var dispatcher = resolveDispatcher();
1092
1092
  return dispatcher.useReducer(reducer, initialArg, init);
1093
1093
  }
1094
- function useRef(initialValue) {
1094
+ function useRef3(initialValue) {
1095
1095
  var dispatcher = resolveDispatcher();
1096
1096
  return dispatcher.useRef(initialValue);
1097
1097
  }
@@ -1885,7 +1885,7 @@ var require_react_development = __commonJS({
1885
1885
  exports.useLayoutEffect = useLayoutEffect;
1886
1886
  exports.useMemo = useMemo2;
1887
1887
  exports.useReducer = useReducer;
1888
- exports.useRef = useRef;
1888
+ exports.useRef = useRef3;
1889
1889
  exports.useState = useState5;
1890
1890
  exports.useSyncExternalStore = useSyncExternalStore;
1891
1891
  exports.useTransition = useTransition;
@@ -2382,9 +2382,9 @@ var require_react_dom_development = __commonJS({
2382
2382
  if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined" && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart === "function") {
2383
2383
  __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
2384
2384
  }
2385
- var React5 = require_react();
2385
+ var React7 = require_react();
2386
2386
  var Scheduler = require_scheduler();
2387
- var ReactSharedInternals = React5.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
2387
+ var ReactSharedInternals = React7.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
2388
2388
  var suppressWarning = false;
2389
2389
  function setSuppressWarning(newSuppressWarning) {
2390
2390
  {
@@ -3991,7 +3991,7 @@ var require_react_dom_development = __commonJS({
3991
3991
  {
3992
3992
  if (props.value == null) {
3993
3993
  if (typeof props.children === "object" && props.children !== null) {
3994
- React5.Children.forEach(props.children, function(child) {
3994
+ React7.Children.forEach(props.children, function(child) {
3995
3995
  if (child == null) {
3996
3996
  return;
3997
3997
  }
@@ -23581,196 +23581,334 @@ var require_client = __commonJS({
23581
23581
  });
23582
23582
 
23583
23583
  // src/ui/viewer/index.tsx
23584
- var import_react7 = __toESM(require_react(), 1);
23584
+ var import_react8 = __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_react6 = __toESM(require_react(), 1);
23588
+ var import_react7 = __toESM(require_react(), 1);
23589
23589
 
23590
23590
  // src/ui/viewer/components/Header.tsx
23591
+ var import_react2 = __toESM(require_react(), 1);
23592
+
23593
+ // src/ui/viewer/components/SearchBar.tsx
23591
23594
  var import_react = __toESM(require_react(), 1);
23592
- function Header({
23593
- isConnected,
23595
+
23596
+ // src/ui/viewer/utils/format.ts
23597
+ function getBadgeClass(type) {
23598
+ const map = {
23599
+ "file-write": "badge--file-write",
23600
+ "command": "badge--command",
23601
+ "research": "badge--research",
23602
+ "delegation": "badge--delegation",
23603
+ "tool-use": "badge--tool-use"
23604
+ };
23605
+ return map[type] || "badge--default";
23606
+ }
23607
+ function timeAgo(epochSeconds) {
23608
+ const now = Date.now() / 1e3;
23609
+ const diff = Math.max(0, now - epochSeconds);
23610
+ if (diff < 60) return "ora";
23611
+ if (diff < 3600) {
23612
+ const m = Math.floor(diff / 60);
23613
+ return `${m} min fa`;
23614
+ }
23615
+ if (diff < 86400) {
23616
+ const h = Math.floor(diff / 3600);
23617
+ return `${h}h fa`;
23618
+ }
23619
+ if (diff < 172800) return "ieri";
23620
+ if (diff < 604800) {
23621
+ const d = Math.floor(diff / 86400);
23622
+ return `${d} giorni fa`;
23623
+ }
23624
+ if (diff < 2592e3) {
23625
+ const w = Math.floor(diff / 604800);
23626
+ return `${w} sett. fa`;
23627
+ }
23628
+ const date = new Date(epochSeconds * 1e3);
23629
+ return date.toLocaleDateString("it-IT", { day: "numeric", month: "short" });
23630
+ }
23631
+
23632
+ // src/ui/viewer/components/SearchBar.tsx
23633
+ function SearchBar() {
23634
+ const [query, setQuery] = (0, import_react.useState)("");
23635
+ const [results, setResults] = (0, import_react.useState)(null);
23636
+ const [isOpen, setIsOpen] = (0, import_react.useState)(false);
23637
+ const [isSearching, setIsSearching] = (0, import_react.useState)(false);
23638
+ const inputRef = (0, import_react.useRef)(null);
23639
+ const containerRef = (0, import_react.useRef)(null);
23640
+ const debounceRef = (0, import_react.useRef)();
23641
+ const doSearch = (0, import_react.useCallback)(async (q) => {
23642
+ if (!q.trim()) {
23643
+ setResults(null);
23644
+ setIsOpen(false);
23645
+ return;
23646
+ }
23647
+ setIsSearching(true);
23648
+ try {
23649
+ const res = await fetch(`/api/search?q=${encodeURIComponent(q)}&limit=10`);
23650
+ if (res.ok) {
23651
+ const data = await res.json();
23652
+ setResults(data);
23653
+ setIsOpen(true);
23654
+ }
23655
+ } catch (error) {
23656
+ console.error("Ricerca fallita:", error);
23657
+ } finally {
23658
+ setIsSearching(false);
23659
+ }
23660
+ }, []);
23661
+ const handleChange = (0, import_react.useCallback)((e) => {
23662
+ const value = e.target.value;
23663
+ setQuery(value);
23664
+ if (debounceRef.current) clearTimeout(debounceRef.current);
23665
+ debounceRef.current = setTimeout(() => doSearch(value), 300);
23666
+ }, [doSearch]);
23667
+ (0, import_react.useEffect)(() => {
23668
+ const handler = (e) => {
23669
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
23670
+ e.preventDefault();
23671
+ inputRef.current?.focus();
23672
+ }
23673
+ if (e.key === "Escape") {
23674
+ setIsOpen(false);
23675
+ inputRef.current?.blur();
23676
+ }
23677
+ };
23678
+ window.addEventListener("keydown", handler);
23679
+ return () => window.removeEventListener("keydown", handler);
23680
+ }, []);
23681
+ (0, import_react.useEffect)(() => {
23682
+ const handler = (e) => {
23683
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
23684
+ setIsOpen(false);
23685
+ }
23686
+ };
23687
+ document.addEventListener("mousedown", handler);
23688
+ return () => document.removeEventListener("mousedown", handler);
23689
+ }, []);
23690
+ const totalResults = results ? results.observations.length + results.summaries.length : 0;
23691
+ 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(
23692
+ "input",
23693
+ {
23694
+ ref: inputRef,
23695
+ type: "text",
23696
+ className: "search-input",
23697
+ placeholder: "Search observations, summaries...",
23698
+ value: query,
23699
+ onChange: handleChange,
23700
+ onFocus: () => {
23701
+ if (results && totalResults > 0) setIsOpen(true);
23702
+ }
23703
+ }
23704
+ ), /* @__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)))))));
23705
+ }
23706
+
23707
+ // src/ui/viewer/components/Header.tsx
23708
+ function Header({ isConnected, resolvedTheme, onThemeToggle }) {
23709
+ 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(
23710
+ "button",
23711
+ {
23712
+ className: "theme-btn",
23713
+ onClick: onThemeToggle,
23714
+ title: resolvedTheme === "dark" ? "Switch to light mode" : "Switch to dark mode"
23715
+ },
23716
+ resolvedTheme === "dark" ? "\u2600" : "\u263E"
23717
+ )));
23718
+ }
23719
+
23720
+ // src/ui/viewer/components/Sidebar.tsx
23721
+ var import_react3 = __toESM(require_react(), 1);
23722
+ var TYPE_COLORS = {
23723
+ "file-write": "var(--green)",
23724
+ "command": "var(--amber)",
23725
+ "research": "var(--blue)",
23726
+ "delegation": "var(--accent)",
23727
+ "tool-use": "var(--text-muted)"
23728
+ };
23729
+ function Sidebar({
23594
23730
  projects,
23595
23731
  currentFilter,
23596
23732
  onFilterChange,
23597
- themePreference,
23598
- onThemeChange
23733
+ activeTypes,
23734
+ onToggleType,
23735
+ stats
23599
23736
  }) {
23600
- return /* @__PURE__ */ import_react.default.createElement("header", { className: "header" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "header-left" }, /* @__PURE__ */ import_react.default.createElement("h1", null, "ContextKit"), /* @__PURE__ */ import_react.default.createElement("span", { className: `connection-status ${isConnected ? "connected" : "disconnected"}` }, isConnected ? "Connected" : "Disconnected")), /* @__PURE__ */ import_react.default.createElement("div", { className: "header-center" }, /* @__PURE__ */ import_react.default.createElement(
23601
- "select",
23737
+ 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(
23738
+ "div",
23602
23739
  {
23603
- value: currentFilter,
23604
- onChange: (e) => onFilterChange(e.target.value),
23605
- className: "project-filter"
23740
+ className: `sidebar-item ${currentFilter === "" ? "active" : ""}`,
23741
+ onClick: () => onFilterChange("")
23606
23742
  },
23607
- /* @__PURE__ */ import_react.default.createElement("option", { value: "" }, "All Projects"),
23608
- projects.map((project) => /* @__PURE__ */ import_react.default.createElement("option", { key: project, value: project }, project))
23609
- )), /* @__PURE__ */ import_react.default.createElement("div", { className: "header-right" }, /* @__PURE__ */ import_react.default.createElement(
23610
- "select",
23743
+ "All Projects"
23744
+ ), projects.map((project) => /* @__PURE__ */ import_react3.default.createElement(
23745
+ "div",
23611
23746
  {
23612
- value: themePreference,
23613
- onChange: (e) => onThemeChange(e.target.value),
23614
- className: "theme-selector"
23747
+ key: project,
23748
+ className: `sidebar-item ${currentFilter === project ? "active" : ""}`,
23749
+ onClick: () => onFilterChange(project)
23615
23750
  },
23616
- /* @__PURE__ */ import_react.default.createElement("option", { value: "system" }, "System"),
23617
- /* @__PURE__ */ import_react.default.createElement("option", { value: "light" }, "Light"),
23618
- /* @__PURE__ */ import_react.default.createElement("option", { value: "dark" }, "Dark")
23619
- )));
23751
+ project
23752
+ ))), /* @__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(
23753
+ "input",
23754
+ {
23755
+ type: "checkbox",
23756
+ checked: activeTypes.has(type),
23757
+ onChange: () => onToggleType(type)
23758
+ }
23759
+ ), /* @__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")))));
23620
23760
  }
23621
23761
 
23622
23762
  // src/ui/viewer/components/Feed.tsx
23623
- var import_react2 = __toESM(require_react(), 1);
23763
+ var import_react4 = __toESM(require_react(), 1);
23624
23764
  function Feed({ observations, summaries, prompts, onLoadMore, isLoading, hasMore }) {
23625
23765
  const items = [...observations, ...summaries, ...prompts].sort(
23626
23766
  (a, b) => b.created_at_epoch - a.created_at_epoch
23627
23767
  );
23628
- return /* @__PURE__ */ import_react2.default.createElement("div", { className: "feed" }, items.map((item) => {
23629
- if ("type" in item) {
23630
- return /* @__PURE__ */ import_react2.default.createElement(ObservationCard, { key: `obs-${item.id}`, observation: item });
23768
+ return /* @__PURE__ */ import_react4.default.createElement("div", { className: "feed" }, items.map((item) => {
23769
+ if ("type" in item && "title" in item) {
23770
+ return /* @__PURE__ */ import_react4.default.createElement(ObservationCard, { key: `obs-${item.id}`, obs: item });
23631
23771
  } else if ("request" in item) {
23632
- return /* @__PURE__ */ import_react2.default.createElement(SummaryCard, { key: `sum-${item.id}`, summary: item });
23772
+ return /* @__PURE__ */ import_react4.default.createElement(SummaryCard, { key: `sum-${item.id}`, summary: item });
23633
23773
  } else {
23634
- return /* @__PURE__ */ import_react2.default.createElement(PromptCard, { key: `prompt-${item.id}`, prompt: item });
23774
+ return /* @__PURE__ */ import_react4.default.createElement(PromptCard, { key: `prompt-${item.id}`, prompt: item });
23635
23775
  }
23636
- }), hasMore && /* @__PURE__ */ import_react2.default.createElement(
23776
+ }), hasMore && /* @__PURE__ */ import_react4.default.createElement(
23637
23777
  "button",
23638
23778
  {
23639
23779
  className: "load-more-btn",
23640
23780
  onClick: onLoadMore,
23641
23781
  disabled: isLoading
23642
23782
  },
23643
- isLoading ? "Loading..." : "Load More"
23644
- ), items.length === 0 && !isLoading && /* @__PURE__ */ import_react2.default.createElement("div", { className: "empty-state" }, /* @__PURE__ */ import_react2.default.createElement("p", null, "No data available. Start using ContextKit to see your context here.")));
23783
+ isLoading ? "Caricamento..." : "Carica altri"
23784
+ ), 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.")));
23645
23785
  }
23646
- function ObservationCard({ observation }) {
23647
- return /* @__PURE__ */ import_react2.default.createElement("div", { className: "card observation-card" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "card-header" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "card-type" }, observation.type), /* @__PURE__ */ import_react2.default.createElement("span", { className: "card-project" }, observation.project), /* @__PURE__ */ import_react2.default.createElement("span", { className: "card-date" }, new Date(observation.created_at).toLocaleString())), /* @__PURE__ */ import_react2.default.createElement("h3", { className: "card-title" }, observation.title), observation.subtitle && /* @__PURE__ */ import_react2.default.createElement("p", { className: "card-subtitle" }, observation.subtitle), observation.text && /* @__PURE__ */ import_react2.default.createElement("p", { className: "card-content" }, observation.text), observation.concepts && /* @__PURE__ */ import_react2.default.createElement("div", { className: "card-tags" }, observation.concepts.split(", ").map((concept, i) => /* @__PURE__ */ import_react2.default.createElement("span", { key: i, className: "tag" }, concept))));
23786
+ function ObservationCard({ obs }) {
23787
+ const cardClass = `card card--${obs.type.replace(/\s+/g, "-")}`;
23788
+ 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))));
23648
23789
  }
23649
23790
  function SummaryCard({ summary }) {
23650
- return /* @__PURE__ */ import_react2.default.createElement("div", { className: "card summary-card" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "card-header" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "card-type" }, "Summary"), /* @__PURE__ */ import_react2.default.createElement("span", { className: "card-project" }, summary.project), /* @__PURE__ */ import_react2.default.createElement("span", { className: "card-date" }, new Date(summary.created_at).toLocaleString())), summary.request && /* @__PURE__ */ import_react2.default.createElement("h3", { className: "card-title" }, summary.request), summary.learned && /* @__PURE__ */ import_react2.default.createElement("div", { className: "summary-section" }, /* @__PURE__ */ import_react2.default.createElement("strong", null, "Learned:"), /* @__PURE__ */ import_react2.default.createElement("p", null, summary.learned)), summary.completed && /* @__PURE__ */ import_react2.default.createElement("div", { className: "summary-section" }, /* @__PURE__ */ import_react2.default.createElement("strong", null, "Completed:"), /* @__PURE__ */ import_react2.default.createElement("p", null, summary.completed)), summary.next_steps && /* @__PURE__ */ import_react2.default.createElement("div", { className: "summary-section" }, /* @__PURE__ */ import_react2.default.createElement("strong", null, "Next Steps:"), /* @__PURE__ */ import_react2.default.createElement("p", null, summary.next_steps)));
23791
+ 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)));
23651
23792
  }
23652
23793
  function PromptCard({ prompt }) {
23653
- return /* @__PURE__ */ import_react2.default.createElement("div", { className: "card prompt-card" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "card-header" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "card-type" }, "Prompt #", prompt.prompt_number), /* @__PURE__ */ import_react2.default.createElement("span", { className: "card-project" }, prompt.project), /* @__PURE__ */ import_react2.default.createElement("span", { className: "card-date" }, new Date(prompt.created_at).toLocaleString())), /* @__PURE__ */ import_react2.default.createElement("p", { className: "card-content" }, prompt.prompt_text));
23794
+ 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)));
23654
23795
  }
23655
23796
 
23656
23797
  // src/ui/viewer/hooks/useSSE.ts
23657
- var import_react3 = __toESM(require_react(), 1);
23798
+ var import_react5 = __toESM(require_react(), 1);
23658
23799
  function useSSE() {
23659
- const [state, setState] = (0, import_react3.useState)({
23800
+ const [state, setState] = (0, import_react5.useState)({
23660
23801
  observations: [],
23661
23802
  summaries: [],
23662
23803
  prompts: [],
23663
23804
  projects: [],
23664
23805
  isConnected: false
23665
23806
  });
23666
- (0, import_react3.useEffect)(() => {
23667
- const eventSource = new EventSource("/events");
23668
- eventSource.onopen = () => {
23669
- setState((prev) => ({ ...prev, isConnected: true }));
23670
- };
23671
- eventSource.onerror = () => {
23672
- setState((prev) => ({ ...prev, isConnected: false }));
23673
- };
23674
- eventSource.addEventListener("observation-created", (event) => {
23675
- const data = JSON.parse(event.data);
23676
- fetchObservations();
23677
- });
23678
- eventSource.addEventListener("summary-created", (event) => {
23679
- const data = JSON.parse(event.data);
23680
- fetchSummaries();
23681
- });
23807
+ const mountedRef = (0, import_react5.useRef)(true);
23808
+ (0, import_react5.useEffect)(() => {
23809
+ mountedRef.current = true;
23810
+ let eventSource = null;
23811
+ let retryTimeout = null;
23812
+ let retryCount = 0;
23813
+ const MAX_RETRY_DELAY = 3e4;
23682
23814
  const fetchObservations = async () => {
23683
23815
  try {
23684
23816
  const res = await fetch("/api/observations?limit=50");
23685
- if (res.ok) {
23817
+ if (res.ok && mountedRef.current) {
23686
23818
  const observations = await res.json();
23687
23819
  setState((prev) => ({ ...prev, observations }));
23688
23820
  }
23689
- } catch (error) {
23690
- console.error("Failed to fetch observations:", error);
23821
+ } catch (err) {
23822
+ console.error("Fetch observations fallito:", err);
23691
23823
  }
23692
23824
  };
23693
23825
  const fetchSummaries = async () => {
23694
23826
  try {
23695
23827
  const res = await fetch("/api/summaries?limit=20");
23696
- if (res.ok) {
23828
+ if (res.ok && mountedRef.current) {
23697
23829
  const summaries = await res.json();
23698
23830
  setState((prev) => ({ ...prev, summaries }));
23699
23831
  }
23700
- } catch (error) {
23701
- console.error("Failed to fetch summaries:", error);
23832
+ } catch (err) {
23833
+ console.error("Fetch summaries fallito:", err);
23834
+ }
23835
+ };
23836
+ const fetchPrompts = async () => {
23837
+ try {
23838
+ const res = await fetch("/api/prompts?limit=50");
23839
+ if (res.ok && mountedRef.current) {
23840
+ const prompts = await res.json();
23841
+ setState((prev) => ({ ...prev, prompts }));
23842
+ }
23843
+ } catch (err) {
23844
+ console.error("Fetch prompts fallito:", err);
23702
23845
  }
23703
23846
  };
23704
23847
  const fetchProjects = async () => {
23705
23848
  try {
23706
23849
  const res = await fetch("/api/projects");
23707
- if (res.ok) {
23850
+ if (res.ok && mountedRef.current) {
23708
23851
  const projects = await res.json();
23709
23852
  setState((prev) => ({ ...prev, projects }));
23710
23853
  }
23711
- } catch (error) {
23712
- console.error("Failed to fetch projects:", error);
23854
+ } catch (err) {
23855
+ console.error("Fetch projects fallito:", err);
23713
23856
  }
23714
23857
  };
23858
+ const connect = () => {
23859
+ if (!mountedRef.current) return;
23860
+ eventSource = new EventSource("/events");
23861
+ eventSource.onopen = () => {
23862
+ if (!mountedRef.current) return;
23863
+ retryCount = 0;
23864
+ setState((prev) => ({ ...prev, isConnected: true }));
23865
+ };
23866
+ eventSource.onerror = () => {
23867
+ if (!mountedRef.current) return;
23868
+ setState((prev) => ({ ...prev, isConnected: false }));
23869
+ eventSource?.close();
23870
+ eventSource = null;
23871
+ const delay = Math.min(1e3 * Math.pow(2, retryCount), MAX_RETRY_DELAY);
23872
+ retryCount++;
23873
+ retryTimeout = setTimeout(connect, delay);
23874
+ };
23875
+ eventSource.addEventListener("observation-created", () => {
23876
+ fetchObservations();
23877
+ fetchProjects();
23878
+ });
23879
+ eventSource.addEventListener("summary-created", () => {
23880
+ fetchSummaries();
23881
+ });
23882
+ eventSource.addEventListener("prompt-created", () => {
23883
+ fetchPrompts();
23884
+ });
23885
+ };
23715
23886
  fetchObservations();
23716
23887
  fetchSummaries();
23888
+ fetchPrompts();
23717
23889
  fetchProjects();
23890
+ connect();
23718
23891
  return () => {
23719
- eventSource.close();
23892
+ mountedRef.current = false;
23893
+ eventSource?.close();
23894
+ if (retryTimeout) clearTimeout(retryTimeout);
23720
23895
  };
23721
23896
  }, []);
23722
23897
  return state;
23723
23898
  }
23724
23899
 
23725
- // src/ui/viewer/hooks/useSettings.ts
23726
- var import_react4 = __toESM(require_react(), 1);
23727
- function useSettings() {
23728
- const [settings, setSettings] = (0, import_react4.useState)({
23729
- sidebarOpen: true,
23730
- selectedProject: null,
23731
- theme: "system"
23732
- });
23733
- const [isSaving, setIsSaving] = (0, import_react4.useState)(false);
23734
- const [saveStatus, setSaveStatus] = (0, import_react4.useState)("idle");
23735
- (0, import_react4.useEffect)(() => {
23736
- const saved = localStorage.getItem("contextkit-settings");
23737
- if (saved) {
23738
- try {
23739
- setSettings(JSON.parse(saved));
23740
- } catch (error) {
23741
- console.error("Failed to parse settings:", error);
23742
- }
23743
- }
23744
- }, []);
23745
- const saveSettings = async (newSettings) => {
23746
- setIsSaving(true);
23747
- try {
23748
- localStorage.setItem("contextkit-settings", JSON.stringify(newSettings));
23749
- setSettings(newSettings);
23750
- setSaveStatus("success");
23751
- setTimeout(() => setSaveStatus("idle"), 2e3);
23752
- } catch (error) {
23753
- console.error("Failed to save settings:", error);
23754
- setSaveStatus("error");
23755
- } finally {
23756
- setIsSaving(false);
23757
- }
23758
- };
23759
- return { settings, saveSettings, isSaving, saveStatus };
23760
- }
23761
-
23762
23900
  // src/ui/viewer/hooks/useTheme.ts
23763
- var import_react5 = __toESM(require_react(), 1);
23901
+ var import_react6 = __toESM(require_react(), 1);
23764
23902
  function useTheme() {
23765
- const [preference, setPreference] = (0, import_react5.useState)("system");
23766
- const [resolvedTheme, setResolvedTheme] = (0, import_react5.useState)("light");
23767
- (0, import_react5.useEffect)(() => {
23768
- const saved = localStorage.getItem("contextkit-theme");
23903
+ const [preference, setPreference] = (0, import_react6.useState)("system");
23904
+ const [resolvedTheme, setResolvedTheme] = (0, import_react6.useState)("light");
23905
+ (0, import_react6.useEffect)(() => {
23906
+ const saved = localStorage.getItem("kiro-memory-theme");
23769
23907
  if (saved) {
23770
23908
  setPreference(saved);
23771
23909
  }
23772
23910
  }, []);
23773
- (0, import_react5.useEffect)(() => {
23911
+ (0, import_react6.useEffect)(() => {
23774
23912
  const resolveTheme = () => {
23775
23913
  if (preference === "system") {
23776
23914
  const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
@@ -23791,7 +23929,7 @@ function useTheme() {
23791
23929
  }, [preference]);
23792
23930
  const setThemePreference = (theme) => {
23793
23931
  setPreference(theme);
23794
- localStorage.setItem("contextkit-theme", theme);
23932
+ localStorage.setItem("kiro-memory-theme", theme);
23795
23933
  };
23796
23934
  return { preference, resolvedTheme, setThemePreference };
23797
23935
  }
@@ -23820,34 +23958,47 @@ function mergeAndDeduplicateByProject(liveData, paginatedData) {
23820
23958
  }
23821
23959
 
23822
23960
  // src/ui/viewer/App.tsx
23961
+ var TYPE_FILTERS = ["file-write", "command", "research", "delegation", "tool-use"];
23823
23962
  function App() {
23824
- const [currentFilter, setCurrentFilter] = (0, import_react6.useState)("");
23825
- const [paginatedObservations, setPaginatedObservations] = (0, import_react6.useState)([]);
23826
- const [paginatedSummaries, setPaginatedSummaries] = (0, import_react6.useState)([]);
23827
- const [paginatedPrompts, setPaginatedPrompts] = (0, import_react6.useState)([]);
23828
- const [isLoadingMore, setIsLoadingMore] = (0, import_react6.useState)(false);
23963
+ const [currentFilter, setCurrentFilter] = (0, import_react7.useState)("");
23964
+ const [activeTypes, setActiveTypes] = (0, import_react7.useState)(new Set(TYPE_FILTERS));
23965
+ const [paginatedObservations, setPaginatedObservations] = (0, import_react7.useState)([]);
23966
+ const [paginatedSummaries, setPaginatedSummaries] = (0, import_react7.useState)([]);
23967
+ const [paginatedPrompts, setPaginatedPrompts] = (0, import_react7.useState)([]);
23968
+ const [isLoadingMore, setIsLoadingMore] = (0, import_react7.useState)(false);
23969
+ const [hasMore, setHasMore] = (0, import_react7.useState)(true);
23829
23970
  const { observations, summaries, prompts, projects, isConnected } = useSSE();
23830
- const { settings, saveSettings } = useSettings();
23831
- const { preference, resolvedTheme, setThemePreference } = useTheme();
23832
- const allObservations = (0, import_react6.useMemo)(() => {
23833
- if (currentFilter) {
23834
- return paginatedObservations;
23835
- }
23971
+ const { resolvedTheme, setThemePreference } = useTheme();
23972
+ const allObservations = (0, import_react7.useMemo)(() => {
23973
+ if (currentFilter) return paginatedObservations;
23836
23974
  return mergeAndDeduplicateByProject(observations, paginatedObservations);
23837
23975
  }, [observations, paginatedObservations, currentFilter]);
23838
- const allSummaries = (0, import_react6.useMemo)(() => {
23839
- if (currentFilter) {
23840
- return paginatedSummaries;
23841
- }
23976
+ const allSummaries = (0, import_react7.useMemo)(() => {
23977
+ if (currentFilter) return paginatedSummaries;
23842
23978
  return mergeAndDeduplicateByProject(summaries, paginatedSummaries);
23843
23979
  }, [summaries, paginatedSummaries, currentFilter]);
23844
- const allPrompts = (0, import_react6.useMemo)(() => {
23845
- if (currentFilter) {
23846
- return paginatedPrompts;
23847
- }
23980
+ const allPrompts = (0, import_react7.useMemo)(() => {
23981
+ if (currentFilter) return paginatedPrompts;
23848
23982
  return mergeAndDeduplicateByProject(prompts, paginatedPrompts);
23849
23983
  }, [prompts, paginatedPrompts, currentFilter]);
23850
- const handleLoadMore = (0, import_react6.useCallback)(async () => {
23984
+ const filteredObservations = (0, import_react7.useMemo)(
23985
+ () => allObservations.filter((o) => activeTypes.has(o.type)),
23986
+ [allObservations, activeTypes]
23987
+ );
23988
+ const stats = (0, import_react7.useMemo)(() => ({
23989
+ observations: allObservations.length,
23990
+ summaries: allSummaries.length,
23991
+ prompts: allPrompts.length
23992
+ }), [allObservations, allSummaries, allPrompts]);
23993
+ const toggleType = (0, import_react7.useCallback)((type) => {
23994
+ setActiveTypes((prev) => {
23995
+ const next = new Set(prev);
23996
+ if (next.has(type)) next.delete(type);
23997
+ else next.add(type);
23998
+ return next;
23999
+ });
24000
+ }, []);
24001
+ const handleLoadMore = (0, import_react7.useCallback)(async () => {
23851
24002
  if (isLoadingMore) return;
23852
24003
  setIsLoadingMore(true);
23853
24004
  try {
@@ -23863,57 +24014,69 @@ function App() {
23863
24014
  fetch(`/api/summaries?${params}`),
23864
24015
  fetch(`/api/prompts?${params}`)
23865
24016
  ]);
24017
+ let newItems = 0;
23866
24018
  if (obsRes.ok) {
23867
24019
  const newObs = await obsRes.json();
24020
+ newItems += newObs.length;
23868
24021
  setPaginatedObservations((prev) => [...prev, ...newObs]);
23869
24022
  }
23870
24023
  if (sumRes.ok) {
23871
24024
  const newSum = await sumRes.json();
24025
+ newItems += newSum.length;
23872
24026
  setPaginatedSummaries((prev) => [...prev, ...newSum]);
23873
24027
  }
23874
24028
  if (promptRes.ok) {
23875
24029
  const newPrompts = await promptRes.json();
24030
+ newItems += newPrompts.length;
23876
24031
  setPaginatedPrompts((prev) => [...prev, ...newPrompts]);
23877
24032
  }
24033
+ if (newItems === 0) setHasMore(false);
23878
24034
  } catch (error) {
23879
- console.error("Failed to load more data:", error);
24035
+ console.error("Errore caricamento dati:", error);
23880
24036
  } finally {
23881
24037
  setIsLoadingMore(false);
23882
24038
  }
23883
24039
  }, [currentFilter, paginatedObservations.length, isLoadingMore]);
23884
- (0, import_react6.useEffect)(() => {
24040
+ (0, import_react7.useEffect)(() => {
23885
24041
  setPaginatedObservations([]);
23886
24042
  setPaginatedSummaries([]);
23887
24043
  setPaginatedPrompts([]);
23888
- handleLoadMore();
24044
+ setHasMore(true);
23889
24045
  }, [currentFilter]);
23890
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "app", "data-theme": resolvedTheme }, /* @__PURE__ */ import_react6.default.createElement(
24046
+ return /* @__PURE__ */ import_react7.default.createElement("div", { className: "app", "data-theme": resolvedTheme }, /* @__PURE__ */ import_react7.default.createElement(
23891
24047
  Header,
23892
24048
  {
23893
24049
  isConnected,
24050
+ resolvedTheme,
24051
+ onThemeToggle: () => setThemePreference(resolvedTheme === "dark" ? "light" : "dark")
24052
+ }
24053
+ ), /* @__PURE__ */ import_react7.default.createElement(
24054
+ Sidebar,
24055
+ {
23894
24056
  projects,
23895
24057
  currentFilter,
23896
24058
  onFilterChange: setCurrentFilter,
23897
- themePreference: preference,
23898
- onThemeChange: setThemePreference
24059
+ activeTypes,
24060
+ onToggleType: toggleType,
24061
+ stats
23899
24062
  }
23900
- ), /* @__PURE__ */ import_react6.default.createElement(
24063
+ ), /* @__PURE__ */ import_react7.default.createElement("div", { className: "main" }, /* @__PURE__ */ import_react7.default.createElement(
23901
24064
  Feed,
23902
24065
  {
23903
- observations: allObservations,
24066
+ observations: filteredObservations,
23904
24067
  summaries: allSummaries,
23905
24068
  prompts: allPrompts,
23906
24069
  onLoadMore: handleLoadMore,
23907
24070
  isLoading: isLoadingMore,
23908
- hasMore: true
24071
+ hasMore
23909
24072
  }
23910
- ));
24073
+ )));
23911
24074
  }
23912
24075
 
23913
24076
  // src/ui/viewer/index.tsx
23914
24077
  var root = import_client.default.createRoot(document.getElementById("root"));
23915
24078
  root.render(
23916
- /* @__PURE__ */ import_react7.default.createElement(import_react7.default.StrictMode, null, /* @__PURE__ */ import_react7.default.createElement(App, null))
24079
+ /* @__PURE__ */ import_react8.default.createElement(import_react8.default.StrictMode, null, /* @__PURE__ */ import_react8.default.createElement(App, null))
23917
24080
  );
23918
24081
  /*! Bundled license information:
23919
24082