kiro-memory 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -101
- package/package.json +18 -8
- package/plugin/dist/cli/contextkit.js +2609 -593
- package/plugin/dist/hooks/agentSpawn.js +1399 -279
- package/plugin/dist/hooks/kiro-hooks.js +1307 -222
- package/plugin/dist/hooks/postToolUse.js +1404 -264
- package/plugin/dist/hooks/stop.js +1381 -261
- package/plugin/dist/hooks/userPromptSubmit.js +1369 -258
- package/plugin/dist/index.js +1352 -255
- package/plugin/dist/sdk/index.js +1306 -220
- package/plugin/dist/servers/mcp-server.js +203 -2
- package/plugin/dist/services/search/EmbeddingService.js +363 -0
- package/plugin/dist/services/search/HybridSearch.js +703 -151
- package/plugin/dist/services/search/ScoringEngine.js +75 -0
- package/plugin/dist/services/search/VectorSearch.js +512 -0
- package/plugin/dist/services/search/index.js +776 -64
- package/plugin/dist/services/sqlite/Database.js +73 -6
- package/plugin/dist/services/sqlite/Observations.js +70 -6
- package/plugin/dist/services/sqlite/Search.js +98 -8
- package/plugin/dist/services/sqlite/Summaries.js +8 -5
- package/plugin/dist/services/sqlite/index.js +413 -24
- package/plugin/dist/shared/paths.js +6 -3
- package/plugin/dist/types/worker-types.js +6 -0
- package/plugin/dist/viewer.js +381 -73
- package/plugin/dist/worker-service.js +4585 -195
package/plugin/dist/viewer.js
CHANGED
|
@@ -1083,7 +1083,7 @@ var require_react_development = __commonJS({
|
|
|
1083
1083
|
}
|
|
1084
1084
|
return dispatcher.useContext(Context);
|
|
1085
1085
|
}
|
|
1086
|
-
function
|
|
1086
|
+
function useState10(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
|
|
1094
|
+
function useRef5(initialValue) {
|
|
1095
1095
|
var dispatcher = resolveDispatcher();
|
|
1096
1096
|
return dispatcher.useRef(initialValue);
|
|
1097
1097
|
}
|
|
1098
|
-
function
|
|
1098
|
+
function useEffect8(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
|
|
1110
|
+
function useCallback5(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 =
|
|
1877
|
+
exports.useCallback = useCallback5;
|
|
1878
1878
|
exports.useContext = useContext;
|
|
1879
1879
|
exports.useDebugValue = useDebugValue;
|
|
1880
1880
|
exports.useDeferredValue = useDeferredValue;
|
|
1881
|
-
exports.useEffect =
|
|
1881
|
+
exports.useEffect = useEffect8;
|
|
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 =
|
|
1889
|
-
exports.useState =
|
|
1888
|
+
exports.useRef = useRef5;
|
|
1889
|
+
exports.useState = useState10;
|
|
1890
1890
|
exports.useSyncExternalStore = useSyncExternalStore;
|
|
1891
1891
|
exports.useTransition = useTransition;
|
|
1892
1892
|
exports.version = ReactVersion;
|
|
@@ -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
|
|
2385
|
+
var React8 = require_react();
|
|
2386
2386
|
var Scheduler = require_scheduler();
|
|
2387
|
-
var ReactSharedInternals =
|
|
2387
|
+
var ReactSharedInternals = React8.__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
|
-
|
|
3994
|
+
React8.Children.forEach(props.children, function(child) {
|
|
3995
3995
|
if (child == null) {
|
|
3996
3996
|
return;
|
|
3997
3997
|
}
|
|
@@ -23581,11 +23581,11 @@ var require_client = __commonJS({
|
|
|
23581
23581
|
});
|
|
23582
23582
|
|
|
23583
23583
|
// src/ui/viewer/index.tsx
|
|
23584
|
-
var
|
|
23584
|
+
var import_react11 = __toESM(require_react(), 1);
|
|
23585
23585
|
var import_client = __toESM(require_client(), 1);
|
|
23586
23586
|
|
|
23587
23587
|
// src/ui/viewer/App.tsx
|
|
23588
|
-
var
|
|
23588
|
+
var import_react10 = __toESM(require_react(), 1);
|
|
23589
23589
|
|
|
23590
23590
|
// src/ui/viewer/components/Header.tsx
|
|
23591
23591
|
var import_react2 = __toESM(require_react(), 1);
|
|
@@ -23601,7 +23601,11 @@ function getTypeBadgeClasses(type) {
|
|
|
23601
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
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
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" }
|
|
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
|
+
"constraint": { bg: "bg-red-500/10 dark:bg-red-500/10", text: "text-red-600 dark:text-red-400", dot: "bg-red-500" },
|
|
23606
|
+
"decision": { bg: "bg-orange-500/10 dark:bg-orange-500/10", text: "text-orange-600 dark:text-orange-400", dot: "bg-orange-500" },
|
|
23607
|
+
"heuristic": { bg: "bg-indigo-500/10 dark:bg-indigo-500/10", text: "text-indigo-600 dark:text-indigo-400", dot: "bg-indigo-500" },
|
|
23608
|
+
"rejected": { bg: "bg-slate-500/10 dark:bg-slate-500/10", text: "text-slate-600 dark:text-slate-400", dot: "bg-slate-500" }
|
|
23605
23609
|
};
|
|
23606
23610
|
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
23611
|
}
|
|
@@ -23712,8 +23716,26 @@ function SearchBar() {
|
|
|
23712
23716
|
}
|
|
23713
23717
|
|
|
23714
23718
|
// src/ui/viewer/components/Header.tsx
|
|
23715
|
-
function Header({ isConnected, resolvedTheme, onThemeToggle }) {
|
|
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" }, "Kiro Memory"), /* @__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(
|
|
23719
|
+
function Header({ isConnected, resolvedTheme, onThemeToggle, currentView, onViewChange }) {
|
|
23720
|
+
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" }, "Kiro Memory"), /* @__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("div", { className: "flex items-center rounded-lg bg-surface-2 border border-border p-0.5" }, /* @__PURE__ */ import_react2.default.createElement(
|
|
23721
|
+
"button",
|
|
23722
|
+
{
|
|
23723
|
+
onClick: () => onViewChange("feed"),
|
|
23724
|
+
className: `flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs font-medium transition-all ${currentView === "feed" ? "bg-surface-3 text-zinc-100 shadow-sm" : "text-zinc-500 hover:text-zinc-300"}`,
|
|
23725
|
+
title: "Memory Feed"
|
|
23726
|
+
},
|
|
23727
|
+
/* @__PURE__ */ import_react2.default.createElement("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("line", { x1: "8", y1: "6", x2: "21", y2: "6" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "8", y1: "12", x2: "21", y2: "12" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "8", y1: "18", x2: "21", y2: "18" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "3", y1: "6", x2: "3.01", y2: "6" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "3", y1: "12", x2: "3.01", y2: "12" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "3", y1: "18", x2: "3.01", y2: "18" })),
|
|
23728
|
+
"Feed"
|
|
23729
|
+
), /* @__PURE__ */ import_react2.default.createElement(
|
|
23730
|
+
"button",
|
|
23731
|
+
{
|
|
23732
|
+
onClick: () => onViewChange("analytics"),
|
|
23733
|
+
className: `flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs font-medium transition-all ${currentView === "analytics" ? "bg-surface-3 text-zinc-100 shadow-sm" : "text-zinc-500 hover:text-zinc-300"}`,
|
|
23734
|
+
title: "Analytics Dashboard"
|
|
23735
|
+
},
|
|
23736
|
+
/* @__PURE__ */ import_react2.default.createElement("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("line", { x1: "18", y1: "20", x2: "18", y2: "10" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "12", y1: "20", x2: "12", y2: "4" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "6", y1: "20", x2: "6", y2: "14" })),
|
|
23737
|
+
"Analytics"
|
|
23738
|
+
)), /* @__PURE__ */ import_react2.default.createElement(
|
|
23717
23739
|
"button",
|
|
23718
23740
|
{
|
|
23719
23741
|
onClick: onThemeToggle,
|
|
@@ -23732,7 +23754,11 @@ var TYPE_CONFIG = {
|
|
|
23732
23754
|
"command": { color: "bg-accent-amber", label: "Commands" },
|
|
23733
23755
|
"research": { color: "bg-accent-blue", label: "Research" },
|
|
23734
23756
|
"delegation": { color: "bg-accent-violet", label: "Delegations" },
|
|
23735
|
-
"tool-use": { color: "bg-zinc-400", label: "Tool usage" }
|
|
23757
|
+
"tool-use": { color: "bg-zinc-400", label: "Tool usage" },
|
|
23758
|
+
"constraint": { color: "bg-red-500", label: "Constraints" },
|
|
23759
|
+
"decision": { color: "bg-orange-500", label: "Decisions" },
|
|
23760
|
+
"heuristic": { color: "bg-indigo-500", label: "Heuristics" },
|
|
23761
|
+
"rejected": { color: "bg-slate-500", label: "Rejected" }
|
|
23736
23762
|
};
|
|
23737
23763
|
var PROJECT_COLORS = [
|
|
23738
23764
|
{ bg: "bg-accent-violet/15", text: "text-accent-violet", ring: "ring-accent-violet/30" },
|
|
@@ -23858,7 +23884,11 @@ var TYPE_STYLES = {
|
|
|
23858
23884
|
"command": { border: "border-l-amber-500", bg: "bg-amber-500/10", text: "text-amber-400", dot: "bg-amber-500", label: "command" },
|
|
23859
23885
|
"research": { border: "border-l-blue-500", bg: "bg-blue-500/10", text: "text-blue-400", dot: "bg-blue-500", label: "research" },
|
|
23860
23886
|
"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" }
|
|
23887
|
+
"tool-use": { border: "border-l-zinc-500", bg: "bg-zinc-500/10", text: "text-zinc-400", dot: "bg-zinc-500", label: "tool-use" },
|
|
23888
|
+
"constraint": { border: "border-l-red-500", bg: "bg-red-500/10", text: "text-red-400", dot: "bg-red-500", label: "constraint" },
|
|
23889
|
+
"decision": { border: "border-l-orange-500", bg: "bg-orange-500/10", text: "text-orange-400", dot: "bg-orange-500", label: "decision" },
|
|
23890
|
+
"heuristic": { border: "border-l-indigo-500", bg: "bg-indigo-500/10", text: "text-indigo-400", dot: "bg-indigo-500", label: "heuristic" },
|
|
23891
|
+
"rejected": { border: "border-l-slate-500", bg: "bg-slate-500/10", text: "text-slate-400", dot: "bg-slate-500", label: "rejected" }
|
|
23862
23892
|
};
|
|
23863
23893
|
function getTypeStyle(type) {
|
|
23864
23894
|
return TYPE_STYLES[type] || TYPE_STYLES["tool-use"];
|
|
@@ -23914,18 +23944,266 @@ function PromptCard({ prompt, getDisplayName }) {
|
|
|
23914
23944
|
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)));
|
|
23915
23945
|
}
|
|
23916
23946
|
|
|
23917
|
-
// src/ui/viewer/
|
|
23947
|
+
// src/ui/viewer/components/Analytics.tsx
|
|
23948
|
+
var import_react6 = __toESM(require_react(), 1);
|
|
23949
|
+
|
|
23950
|
+
// src/ui/viewer/hooks/useAnalytics.ts
|
|
23918
23951
|
var import_react5 = __toESM(require_react(), 1);
|
|
23952
|
+
function useAnalytics(project) {
|
|
23953
|
+
const [data, setData] = (0, import_react5.useState)({
|
|
23954
|
+
overview: null,
|
|
23955
|
+
timeline: [],
|
|
23956
|
+
typeDistribution: [],
|
|
23957
|
+
sessionStats: null,
|
|
23958
|
+
isLoading: true
|
|
23959
|
+
});
|
|
23960
|
+
const mountedRef = (0, import_react5.useRef)(true);
|
|
23961
|
+
const debounceRef = (0, import_react5.useRef)(null);
|
|
23962
|
+
const fetchAnalytics = (0, import_react5.useCallback)(async () => {
|
|
23963
|
+
if (!mountedRef.current) return;
|
|
23964
|
+
const params = project ? `?project=${encodeURIComponent(project)}` : "";
|
|
23965
|
+
try {
|
|
23966
|
+
const [overviewRes, timelineRes, typesRes, sessionsRes] = await Promise.all([
|
|
23967
|
+
fetch(`/api/analytics/overview${params}`),
|
|
23968
|
+
fetch(`/api/analytics/timeline${params}`),
|
|
23969
|
+
fetch(`/api/analytics/types${params}`),
|
|
23970
|
+
fetch(`/api/analytics/sessions${params}`)
|
|
23971
|
+
]);
|
|
23972
|
+
if (!mountedRef.current) return;
|
|
23973
|
+
const overview = overviewRes.ok ? await overviewRes.json() : null;
|
|
23974
|
+
const timeline = timelineRes.ok ? await timelineRes.json() : [];
|
|
23975
|
+
const typeDistribution = typesRes.ok ? await typesRes.json() : [];
|
|
23976
|
+
const sessionStats = sessionsRes.ok ? await sessionsRes.json() : null;
|
|
23977
|
+
setData({ overview, timeline, typeDistribution, sessionStats, isLoading: false });
|
|
23978
|
+
} catch (err) {
|
|
23979
|
+
console.error("Analytics fetch failed:", err);
|
|
23980
|
+
if (mountedRef.current) {
|
|
23981
|
+
setData((prev) => ({ ...prev, isLoading: false }));
|
|
23982
|
+
}
|
|
23983
|
+
}
|
|
23984
|
+
}, [project]);
|
|
23985
|
+
const debouncedRefresh = (0, import_react5.useCallback)(() => {
|
|
23986
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
23987
|
+
debounceRef.current = setTimeout(() => {
|
|
23988
|
+
fetchAnalytics();
|
|
23989
|
+
}, 2e3);
|
|
23990
|
+
}, [fetchAnalytics]);
|
|
23991
|
+
(0, import_react5.useEffect)(() => {
|
|
23992
|
+
mountedRef.current = true;
|
|
23993
|
+
setData((prev) => ({ ...prev, isLoading: true }));
|
|
23994
|
+
fetchAnalytics();
|
|
23995
|
+
const eventSource = new EventSource("/events");
|
|
23996
|
+
const onUpdate = () => debouncedRefresh();
|
|
23997
|
+
eventSource.addEventListener("observation-created", onUpdate);
|
|
23998
|
+
eventSource.addEventListener("summary-created", onUpdate);
|
|
23999
|
+
eventSource.addEventListener("session-created", onUpdate);
|
|
24000
|
+
return () => {
|
|
24001
|
+
mountedRef.current = false;
|
|
24002
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
24003
|
+
eventSource.removeEventListener("observation-created", onUpdate);
|
|
24004
|
+
eventSource.removeEventListener("summary-created", onUpdate);
|
|
24005
|
+
eventSource.removeEventListener("session-created", onUpdate);
|
|
24006
|
+
eventSource.close();
|
|
24007
|
+
};
|
|
24008
|
+
}, [fetchAnalytics, debouncedRefresh]);
|
|
24009
|
+
return data;
|
|
24010
|
+
}
|
|
24011
|
+
|
|
24012
|
+
// src/ui/viewer/components/Analytics.tsx
|
|
24013
|
+
var TYPE_COLORS = {
|
|
24014
|
+
"file-write": { bar: "bg-emerald-500", text: "text-emerald-400" },
|
|
24015
|
+
"file-read": { bar: "bg-cyan-500", text: "text-cyan-400" },
|
|
24016
|
+
"command": { bar: "bg-amber-500", text: "text-amber-400" },
|
|
24017
|
+
"research": { bar: "bg-blue-500", text: "text-blue-400" },
|
|
24018
|
+
"delegation": { bar: "bg-violet-500", text: "text-violet-400" },
|
|
24019
|
+
"tool-use": { bar: "bg-zinc-500", text: "text-zinc-400" },
|
|
24020
|
+
"constraint": { bar: "bg-red-500", text: "text-red-400" },
|
|
24021
|
+
"decision": { bar: "bg-orange-500", text: "text-orange-400" },
|
|
24022
|
+
"heuristic": { bar: "bg-indigo-500", text: "text-indigo-400" },
|
|
24023
|
+
"rejected": { bar: "bg-slate-500", text: "text-slate-400" }
|
|
24024
|
+
};
|
|
24025
|
+
function getTypeColor(type) {
|
|
24026
|
+
return TYPE_COLORS[type] || { bar: "bg-zinc-500", text: "text-zinc-400" };
|
|
24027
|
+
}
|
|
24028
|
+
function Analytics({ currentFilter, getDisplayName }) {
|
|
24029
|
+
const { overview, timeline, typeDistribution, sessionStats, isLoading } = useAnalytics(currentFilter);
|
|
24030
|
+
if (isLoading) {
|
|
24031
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex flex-col items-center justify-center py-20" }, /* @__PURE__ */ import_react6.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_react6.default.createElement("p", { className: "text-sm text-zinc-500" }, "Loading analytics..."));
|
|
24032
|
+
}
|
|
24033
|
+
if (!overview) {
|
|
24034
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex flex-col items-center justify-center py-20 text-center" }, /* @__PURE__ */ import_react6.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_react6.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_react6.default.createElement("line", { x1: "18", y1: "20", x2: "18", y2: "10" }), /* @__PURE__ */ import_react6.default.createElement("line", { x1: "12", y1: "20", x2: "12", y2: "4" }), /* @__PURE__ */ import_react6.default.createElement("line", { x1: "6", y1: "20", x2: "6", y2: "14" }))), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-base font-semibold text-zinc-300 mb-2" }, "No data available"), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-sm text-zinc-500 max-w-xs leading-relaxed" }, "Start a coding session to begin collecting analytics data."));
|
|
24035
|
+
}
|
|
24036
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "grid grid-cols-2 lg:grid-cols-4 gap-3" }, /* @__PURE__ */ import_react6.default.createElement(
|
|
24037
|
+
StatCard,
|
|
24038
|
+
{
|
|
24039
|
+
label: "Observations",
|
|
24040
|
+
value: overview.observations,
|
|
24041
|
+
sub: `${overview.observationsToday} today`,
|
|
24042
|
+
color: "text-accent-violet"
|
|
24043
|
+
}
|
|
24044
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
24045
|
+
StatCard,
|
|
24046
|
+
{
|
|
24047
|
+
label: "This Week",
|
|
24048
|
+
value: overview.observationsThisWeek,
|
|
24049
|
+
sub: "observations",
|
|
24050
|
+
color: "text-accent-blue"
|
|
24051
|
+
}
|
|
24052
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
24053
|
+
StatCard,
|
|
24054
|
+
{
|
|
24055
|
+
label: "Sessions",
|
|
24056
|
+
value: sessionStats?.total || overview.sessions,
|
|
24057
|
+
sub: sessionStats ? `${sessionStats.avgDurationMinutes}m avg` : "",
|
|
24058
|
+
color: "text-accent-cyan"
|
|
24059
|
+
}
|
|
24060
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
24061
|
+
StatCard,
|
|
24062
|
+
{
|
|
24063
|
+
label: "Knowledge",
|
|
24064
|
+
value: overview.knowledgeCount,
|
|
24065
|
+
sub: `${overview.staleCount} stale`,
|
|
24066
|
+
color: "text-accent-amber"
|
|
24067
|
+
}
|
|
24068
|
+
)), /* @__PURE__ */ import_react6.default.createElement("div", { className: "grid grid-cols-3 gap-3" }, /* @__PURE__ */ import_react6.default.createElement(MiniStat, { label: "Summaries", value: overview.summaries, color: "text-accent-cyan" }), /* @__PURE__ */ import_react6.default.createElement(MiniStat, { label: "Prompts", value: overview.prompts, color: "text-accent-rose" }), /* @__PURE__ */ import_react6.default.createElement(
|
|
24069
|
+
MiniStat,
|
|
24070
|
+
{
|
|
24071
|
+
label: "Completion",
|
|
24072
|
+
value: sessionStats ? `${sessionStats.total > 0 ? Math.round(sessionStats.completed / sessionStats.total * 100) : 0}%` : "\u2014",
|
|
24073
|
+
color: "text-accent-green"
|
|
24074
|
+
}
|
|
24075
|
+
)), timeline.length > 0 && /* @__PURE__ */ import_react6.default.createElement("div", { className: "bg-surface-1 border border-border rounded-lg p-5" }, /* @__PURE__ */ import_react6.default.createElement("h3", { className: "text-[11px] font-semibold uppercase tracking-wider text-zinc-500 mb-4" }, "Activity Timeline (30 days)"), /* @__PURE__ */ import_react6.default.createElement(TimelineChart, { entries: timeline })), typeDistribution.length > 0 && /* @__PURE__ */ import_react6.default.createElement("div", { className: "bg-surface-1 border border-border rounded-lg p-5" }, /* @__PURE__ */ import_react6.default.createElement("h3", { className: "text-[11px] font-semibold uppercase tracking-wider text-zinc-500 mb-4" }, "Observation Types"), /* @__PURE__ */ import_react6.default.createElement(TypeDistributionChart, { entries: typeDistribution })), sessionStats && sessionStats.total > 0 && /* @__PURE__ */ import_react6.default.createElement("div", { className: "bg-surface-1 border border-border rounded-lg p-5" }, /* @__PURE__ */ import_react6.default.createElement("h3", { className: "text-[11px] font-semibold uppercase tracking-wider text-zinc-500 mb-4" }, "Sessions"), /* @__PURE__ */ import_react6.default.createElement(SessionStatsPanel, { stats: sessionStats })));
|
|
24076
|
+
}
|
|
24077
|
+
function StatCard({ label, value, sub, color }) {
|
|
24078
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { className: "rounded-lg bg-surface-1 border border-border px-4 py-4" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: `text-2xl font-bold tabular-nums ${color}` }, value.toLocaleString()), /* @__PURE__ */ import_react6.default.createElement("div", { className: "text-[11px] font-semibold uppercase tracking-wider text-zinc-500 mt-1" }, label), sub && /* @__PURE__ */ import_react6.default.createElement("div", { className: "text-[10px] text-zinc-600 mt-0.5" }, sub));
|
|
24079
|
+
}
|
|
24080
|
+
function MiniStat({ label, value, color }) {
|
|
24081
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { className: "rounded-lg bg-surface-1 border border-border px-3 py-3 text-center" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: `text-lg font-bold tabular-nums ${color}` }, typeof value === "number" ? value.toLocaleString() : value), /* @__PURE__ */ import_react6.default.createElement("div", { className: "text-[10px] uppercase tracking-wider text-zinc-600 mt-0.5" }, label));
|
|
24082
|
+
}
|
|
24083
|
+
function TimelineChart({ entries }) {
|
|
24084
|
+
const [hoveredIndex, setHoveredIndex] = (0, import_react6.useState)(null);
|
|
24085
|
+
const maxCount = Math.max(...entries.map((e) => e.count), 1);
|
|
24086
|
+
const barWidth = Math.max(4, Math.min(16, Math.floor(600 / entries.length) - 2));
|
|
24087
|
+
const chartHeight = 120;
|
|
24088
|
+
const chartWidth = entries.length * (barWidth + 2);
|
|
24089
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { className: "relative overflow-x-auto" }, /* @__PURE__ */ import_react6.default.createElement(
|
|
24090
|
+
"svg",
|
|
24091
|
+
{
|
|
24092
|
+
width: Math.max(chartWidth, 200),
|
|
24093
|
+
height: chartHeight + 24,
|
|
24094
|
+
className: "w-full",
|
|
24095
|
+
viewBox: `0 0 ${Math.max(chartWidth, 200)} ${chartHeight + 24}`,
|
|
24096
|
+
preserveAspectRatio: "none"
|
|
24097
|
+
},
|
|
24098
|
+
entries.map((entry, i) => {
|
|
24099
|
+
const barHeight = Math.max(2, entry.count / maxCount * chartHeight);
|
|
24100
|
+
const x = i * (barWidth + 2);
|
|
24101
|
+
const y = chartHeight - barHeight;
|
|
24102
|
+
const isHovered = hoveredIndex === i;
|
|
24103
|
+
return /* @__PURE__ */ import_react6.default.createElement(
|
|
24104
|
+
"g",
|
|
24105
|
+
{
|
|
24106
|
+
key: entry.day,
|
|
24107
|
+
onMouseEnter: () => setHoveredIndex(i),
|
|
24108
|
+
onMouseLeave: () => setHoveredIndex(null)
|
|
24109
|
+
},
|
|
24110
|
+
/* @__PURE__ */ import_react6.default.createElement(
|
|
24111
|
+
"rect",
|
|
24112
|
+
{
|
|
24113
|
+
x,
|
|
24114
|
+
y,
|
|
24115
|
+
width: barWidth,
|
|
24116
|
+
height: barHeight,
|
|
24117
|
+
rx: 2,
|
|
24118
|
+
className: `transition-all ${isHovered ? "fill-accent-violet" : "fill-accent-violet/60"}`
|
|
24119
|
+
}
|
|
24120
|
+
),
|
|
24121
|
+
isHovered && /* @__PURE__ */ import_react6.default.createElement("g", null, /* @__PURE__ */ import_react6.default.createElement(
|
|
24122
|
+
"rect",
|
|
24123
|
+
{
|
|
24124
|
+
x: Math.max(0, x - 30),
|
|
24125
|
+
y: Math.max(0, y - 28),
|
|
24126
|
+
width: 70,
|
|
24127
|
+
height: 22,
|
|
24128
|
+
rx: 4,
|
|
24129
|
+
className: "fill-surface-3"
|
|
24130
|
+
}
|
|
24131
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
24132
|
+
"text",
|
|
24133
|
+
{
|
|
24134
|
+
x: Math.max(35, x + barWidth / 2),
|
|
24135
|
+
y: Math.max(15, y - 13),
|
|
24136
|
+
textAnchor: "middle",
|
|
24137
|
+
className: "fill-zinc-200 text-[10px] font-mono"
|
|
24138
|
+
},
|
|
24139
|
+
entry.count,
|
|
24140
|
+
" \xB7 ",
|
|
24141
|
+
entry.day.slice(5)
|
|
24142
|
+
))
|
|
24143
|
+
);
|
|
24144
|
+
}),
|
|
24145
|
+
entries.map((entry, i) => {
|
|
24146
|
+
if (i % Math.max(1, Math.floor(entries.length / 6)) !== 0) return null;
|
|
24147
|
+
return /* @__PURE__ */ import_react6.default.createElement(
|
|
24148
|
+
"text",
|
|
24149
|
+
{
|
|
24150
|
+
key: `label-${i}`,
|
|
24151
|
+
x: i * (barWidth + 2) + barWidth / 2,
|
|
24152
|
+
y: chartHeight + 16,
|
|
24153
|
+
textAnchor: "middle",
|
|
24154
|
+
className: "fill-zinc-600 text-[8px] font-mono"
|
|
24155
|
+
},
|
|
24156
|
+
entry.day.slice(5)
|
|
24157
|
+
);
|
|
24158
|
+
})
|
|
24159
|
+
));
|
|
24160
|
+
}
|
|
24161
|
+
function TypeDistributionChart({ entries }) {
|
|
24162
|
+
const total = entries.reduce((sum, e) => sum + e.count, 0);
|
|
24163
|
+
const maxCount = Math.max(...entries.map((e) => e.count), 1);
|
|
24164
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { className: "space-y-2" }, entries.map((entry) => {
|
|
24165
|
+
const pct = total > 0 ? Math.round(entry.count / total * 100) : 0;
|
|
24166
|
+
const widthPct = Math.max(2, entry.count / maxCount * 100);
|
|
24167
|
+
const colors = getTypeColor(entry.type);
|
|
24168
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { key: entry.type, className: "flex items-center gap-3" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-24 text-right" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: `text-xs font-medium ${colors.text}` }, entry.type)), /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex-1 h-5 bg-surface-2 rounded-md overflow-hidden" }, /* @__PURE__ */ import_react6.default.createElement(
|
|
24169
|
+
"div",
|
|
24170
|
+
{
|
|
24171
|
+
className: `h-full rounded-md ${colors.bar} transition-all duration-500`,
|
|
24172
|
+
style: { width: `${widthPct}%` }
|
|
24173
|
+
}
|
|
24174
|
+
)), /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-16 text-right" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "text-xs text-zinc-400 font-mono tabular-nums" }, entry.count), /* @__PURE__ */ import_react6.default.createElement("span", { className: "text-[10px] text-zinc-600 ml-1" }, pct, "%")));
|
|
24175
|
+
}));
|
|
24176
|
+
}
|
|
24177
|
+
function SessionStatsPanel({ stats }) {
|
|
24178
|
+
const completionRate = stats.total > 0 ? Math.round(stats.completed / stats.total * 100) : 0;
|
|
24179
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ import_react6.default.createElement("div", null, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center justify-between mb-2" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "text-xs text-zinc-400" }, "Completion rate"), /* @__PURE__ */ import_react6.default.createElement("span", { className: "text-xs font-bold text-accent-green tabular-nums" }, completionRate, "%")), /* @__PURE__ */ import_react6.default.createElement("div", { className: "h-2.5 bg-surface-2 rounded-full overflow-hidden" }, /* @__PURE__ */ import_react6.default.createElement(
|
|
24180
|
+
"div",
|
|
24181
|
+
{
|
|
24182
|
+
className: "h-full bg-accent-green rounded-full transition-all duration-500",
|
|
24183
|
+
style: { width: `${completionRate}%` }
|
|
24184
|
+
}
|
|
24185
|
+
)), /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center justify-between mt-1" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "text-[10px] text-zinc-600" }, stats.completed, " completed"), /* @__PURE__ */ import_react6.default.createElement("span", { className: "text-[10px] text-zinc-600" }, stats.total, " total"))), /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-3 p-3 rounded-lg bg-surface-2 border border-border" }, /* @__PURE__ */ import_react6.default.createElement("svg", { className: "w-4 h-4 text-accent-cyan flex-shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react6.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react6.default.createElement("polyline", { points: "12 6 12 12 16 14" })), /* @__PURE__ */ import_react6.default.createElement("div", null, /* @__PURE__ */ import_react6.default.createElement("span", { className: "text-sm font-semibold text-zinc-200" }, formatDuration(stats.avgDurationMinutes)), /* @__PURE__ */ import_react6.default.createElement("span", { className: "text-xs text-zinc-500 ml-2" }, "avg session duration"))));
|
|
24186
|
+
}
|
|
24187
|
+
function formatDuration(minutes) {
|
|
24188
|
+
if (minutes < 1) return "<1m";
|
|
24189
|
+
if (minutes < 60) return `${Math.round(minutes)}m`;
|
|
24190
|
+
const h = Math.floor(minutes / 60);
|
|
24191
|
+
const m = Math.round(minutes % 60);
|
|
24192
|
+
return m > 0 ? `${h}h ${m}m` : `${h}h`;
|
|
24193
|
+
}
|
|
24194
|
+
|
|
24195
|
+
// src/ui/viewer/hooks/useSSE.ts
|
|
24196
|
+
var import_react7 = __toESM(require_react(), 1);
|
|
23919
24197
|
function useSSE() {
|
|
23920
|
-
const [state, setState] = (0,
|
|
24198
|
+
const [state, setState] = (0, import_react7.useState)({
|
|
23921
24199
|
observations: [],
|
|
23922
24200
|
summaries: [],
|
|
23923
24201
|
prompts: [],
|
|
23924
24202
|
projects: [],
|
|
23925
24203
|
isConnected: false
|
|
23926
24204
|
});
|
|
23927
|
-
const mountedRef = (0,
|
|
23928
|
-
(0,
|
|
24205
|
+
const mountedRef = (0, import_react7.useRef)(true);
|
|
24206
|
+
(0, import_react7.useEffect)(() => {
|
|
23929
24207
|
mountedRef.current = true;
|
|
23930
24208
|
let eventSource = null;
|
|
23931
24209
|
let retryTimeout = null;
|
|
@@ -23975,42 +24253,63 @@ function useSSE() {
|
|
|
23975
24253
|
console.error("Failed to fetch projects:", err);
|
|
23976
24254
|
}
|
|
23977
24255
|
};
|
|
24256
|
+
const fetchAll = () => {
|
|
24257
|
+
fetchObservations();
|
|
24258
|
+
fetchSummaries();
|
|
24259
|
+
fetchPrompts();
|
|
24260
|
+
fetchProjects();
|
|
24261
|
+
};
|
|
24262
|
+
const onObservation = () => {
|
|
24263
|
+
fetchObservations();
|
|
24264
|
+
fetchProjects();
|
|
24265
|
+
};
|
|
24266
|
+
const onSummary = () => {
|
|
24267
|
+
fetchSummaries();
|
|
24268
|
+
};
|
|
24269
|
+
const onPrompt = () => {
|
|
24270
|
+
fetchPrompts();
|
|
24271
|
+
};
|
|
24272
|
+
let wasConnected = false;
|
|
23978
24273
|
const connect = () => {
|
|
23979
24274
|
if (!mountedRef.current) return;
|
|
23980
24275
|
eventSource = new EventSource("/events");
|
|
23981
24276
|
eventSource.onopen = () => {
|
|
23982
24277
|
if (!mountedRef.current) return;
|
|
24278
|
+
if (wasConnected) {
|
|
24279
|
+
fetchAll();
|
|
24280
|
+
}
|
|
24281
|
+
wasConnected = true;
|
|
23983
24282
|
retryCount = 0;
|
|
23984
24283
|
setState((prev) => ({ ...prev, isConnected: true }));
|
|
23985
24284
|
};
|
|
23986
24285
|
eventSource.onerror = () => {
|
|
23987
24286
|
if (!mountedRef.current) return;
|
|
23988
24287
|
setState((prev) => ({ ...prev, isConnected: false }));
|
|
23989
|
-
eventSource
|
|
24288
|
+
if (eventSource) {
|
|
24289
|
+
eventSource.removeEventListener("observation-created", onObservation);
|
|
24290
|
+
eventSource.removeEventListener("summary-created", onSummary);
|
|
24291
|
+
eventSource.removeEventListener("prompt-created", onPrompt);
|
|
24292
|
+
eventSource.close();
|
|
24293
|
+
}
|
|
23990
24294
|
eventSource = null;
|
|
23991
24295
|
const delay = Math.min(1e3 * Math.pow(2, retryCount), MAX_RETRY_DELAY);
|
|
23992
24296
|
retryCount++;
|
|
23993
24297
|
retryTimeout = setTimeout(connect, delay);
|
|
23994
24298
|
};
|
|
23995
|
-
eventSource.addEventListener("observation-created",
|
|
23996
|
-
|
|
23997
|
-
|
|
23998
|
-
});
|
|
23999
|
-
eventSource.addEventListener("summary-created", () => {
|
|
24000
|
-
fetchSummaries();
|
|
24001
|
-
});
|
|
24002
|
-
eventSource.addEventListener("prompt-created", () => {
|
|
24003
|
-
fetchPrompts();
|
|
24004
|
-
});
|
|
24299
|
+
eventSource.addEventListener("observation-created", onObservation);
|
|
24300
|
+
eventSource.addEventListener("summary-created", onSummary);
|
|
24301
|
+
eventSource.addEventListener("prompt-created", onPrompt);
|
|
24005
24302
|
};
|
|
24006
|
-
|
|
24007
|
-
fetchSummaries();
|
|
24008
|
-
fetchPrompts();
|
|
24009
|
-
fetchProjects();
|
|
24303
|
+
fetchAll();
|
|
24010
24304
|
connect();
|
|
24011
24305
|
return () => {
|
|
24012
24306
|
mountedRef.current = false;
|
|
24013
|
-
eventSource
|
|
24307
|
+
if (eventSource) {
|
|
24308
|
+
eventSource.removeEventListener("observation-created", onObservation);
|
|
24309
|
+
eventSource.removeEventListener("summary-created", onSummary);
|
|
24310
|
+
eventSource.removeEventListener("prompt-created", onPrompt);
|
|
24311
|
+
eventSource.close();
|
|
24312
|
+
}
|
|
24014
24313
|
if (retryTimeout) clearTimeout(retryTimeout);
|
|
24015
24314
|
};
|
|
24016
24315
|
}, []);
|
|
@@ -24018,17 +24317,17 @@ function useSSE() {
|
|
|
24018
24317
|
}
|
|
24019
24318
|
|
|
24020
24319
|
// src/ui/viewer/hooks/useTheme.ts
|
|
24021
|
-
var
|
|
24320
|
+
var import_react8 = __toESM(require_react(), 1);
|
|
24022
24321
|
function useTheme() {
|
|
24023
|
-
const [preference, setPreference] = (0,
|
|
24024
|
-
const [resolvedTheme, setResolvedTheme] = (0,
|
|
24025
|
-
(0,
|
|
24322
|
+
const [preference, setPreference] = (0, import_react8.useState)("dark");
|
|
24323
|
+
const [resolvedTheme, setResolvedTheme] = (0, import_react8.useState)("dark");
|
|
24324
|
+
(0, import_react8.useEffect)(() => {
|
|
24026
24325
|
const saved = localStorage.getItem("kiro-memory-theme");
|
|
24027
24326
|
if (saved) {
|
|
24028
24327
|
setPreference(saved);
|
|
24029
24328
|
}
|
|
24030
24329
|
}, []);
|
|
24031
|
-
(0,
|
|
24330
|
+
(0, import_react8.useEffect)(() => {
|
|
24032
24331
|
let resolved;
|
|
24033
24332
|
if (preference === "system") {
|
|
24034
24333
|
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
@@ -24043,7 +24342,7 @@ function useTheme() {
|
|
|
24043
24342
|
document.documentElement.classList.remove("dark");
|
|
24044
24343
|
}
|
|
24045
24344
|
}, [preference]);
|
|
24046
|
-
(0,
|
|
24345
|
+
(0, import_react8.useEffect)(() => {
|
|
24047
24346
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
24048
24347
|
const handler = (e) => {
|
|
24049
24348
|
if (preference === "system") {
|
|
@@ -24067,11 +24366,11 @@ function useTheme() {
|
|
|
24067
24366
|
}
|
|
24068
24367
|
|
|
24069
24368
|
// src/ui/viewer/hooks/useProjectAliases.ts
|
|
24070
|
-
var
|
|
24369
|
+
var import_react9 = __toESM(require_react(), 1);
|
|
24071
24370
|
function useProjectAliases() {
|
|
24072
|
-
const [aliases, setAliases] = (0,
|
|
24073
|
-
const [isLoading, setIsLoading] = (0,
|
|
24074
|
-
(0,
|
|
24371
|
+
const [aliases, setAliases] = (0, import_react9.useState)({});
|
|
24372
|
+
const [isLoading, setIsLoading] = (0, import_react9.useState)(false);
|
|
24373
|
+
(0, import_react9.useEffect)(() => {
|
|
24075
24374
|
const fetchAliases = async () => {
|
|
24076
24375
|
try {
|
|
24077
24376
|
const res = await fetch("/api/project-aliases");
|
|
@@ -24085,10 +24384,10 @@ function useProjectAliases() {
|
|
|
24085
24384
|
};
|
|
24086
24385
|
fetchAliases();
|
|
24087
24386
|
}, []);
|
|
24088
|
-
const getDisplayName = (0,
|
|
24387
|
+
const getDisplayName = (0, import_react9.useCallback)((project) => {
|
|
24089
24388
|
return aliases[project] || project;
|
|
24090
24389
|
}, [aliases]);
|
|
24091
|
-
const updateAlias = (0,
|
|
24390
|
+
const updateAlias = (0, import_react9.useCallback)(async (project, displayName) => {
|
|
24092
24391
|
setIsLoading(true);
|
|
24093
24392
|
try {
|
|
24094
24393
|
const res = await fetch(`/api/project-aliases/${encodeURIComponent(project)}`, {
|
|
@@ -24134,38 +24433,39 @@ function mergeAndDeduplicateByProject(liveData, paginatedData) {
|
|
|
24134
24433
|
// src/ui/viewer/App.tsx
|
|
24135
24434
|
var TYPE_FILTERS = ["file-write", "file-read", "command", "research", "delegation", "tool-use"];
|
|
24136
24435
|
function App() {
|
|
24137
|
-
const [currentFilter, setCurrentFilter] = (0,
|
|
24138
|
-
const [
|
|
24139
|
-
const [
|
|
24140
|
-
const [
|
|
24141
|
-
const [
|
|
24142
|
-
const [
|
|
24143
|
-
const [
|
|
24436
|
+
const [currentFilter, setCurrentFilter] = (0, import_react10.useState)("");
|
|
24437
|
+
const [currentView, setCurrentView] = (0, import_react10.useState)("feed");
|
|
24438
|
+
const [activeTypes, setActiveTypes] = (0, import_react10.useState)(new Set(TYPE_FILTERS));
|
|
24439
|
+
const [paginatedObservations, setPaginatedObservations] = (0, import_react10.useState)([]);
|
|
24440
|
+
const [paginatedSummaries, setPaginatedSummaries] = (0, import_react10.useState)([]);
|
|
24441
|
+
const [paginatedPrompts, setPaginatedPrompts] = (0, import_react10.useState)([]);
|
|
24442
|
+
const [isLoadingMore, setIsLoadingMore] = (0, import_react10.useState)(false);
|
|
24443
|
+
const [hasMore, setHasMore] = (0, import_react10.useState)(true);
|
|
24144
24444
|
const { observations, summaries, prompts, projects, isConnected } = useSSE();
|
|
24145
24445
|
const { resolvedTheme, setThemePreference } = useTheme();
|
|
24146
24446
|
const { getDisplayName, updateAlias } = useProjectAliases();
|
|
24147
|
-
const allObservations = (0,
|
|
24447
|
+
const allObservations = (0, import_react10.useMemo)(() => {
|
|
24148
24448
|
if (currentFilter) return paginatedObservations;
|
|
24149
24449
|
return mergeAndDeduplicateByProject(observations, paginatedObservations);
|
|
24150
24450
|
}, [observations, paginatedObservations, currentFilter]);
|
|
24151
|
-
const allSummaries = (0,
|
|
24451
|
+
const allSummaries = (0, import_react10.useMemo)(() => {
|
|
24152
24452
|
if (currentFilter) return paginatedSummaries;
|
|
24153
24453
|
return mergeAndDeduplicateByProject(summaries, paginatedSummaries);
|
|
24154
24454
|
}, [summaries, paginatedSummaries, currentFilter]);
|
|
24155
|
-
const allPrompts = (0,
|
|
24455
|
+
const allPrompts = (0, import_react10.useMemo)(() => {
|
|
24156
24456
|
if (currentFilter) return paginatedPrompts;
|
|
24157
24457
|
return mergeAndDeduplicateByProject(prompts, paginatedPrompts);
|
|
24158
24458
|
}, [prompts, paginatedPrompts, currentFilter]);
|
|
24159
|
-
const filteredObservations = (0,
|
|
24459
|
+
const filteredObservations = (0, import_react10.useMemo)(
|
|
24160
24460
|
() => allObservations.filter((o) => activeTypes.has(o.type)),
|
|
24161
24461
|
[allObservations, activeTypes]
|
|
24162
24462
|
);
|
|
24163
|
-
const stats = (0,
|
|
24463
|
+
const stats = (0, import_react10.useMemo)(() => ({
|
|
24164
24464
|
observations: allObservations.length,
|
|
24165
24465
|
summaries: allSummaries.length,
|
|
24166
24466
|
prompts: allPrompts.length
|
|
24167
24467
|
}), [allObservations, allSummaries, allPrompts]);
|
|
24168
|
-
const toggleType = (0,
|
|
24468
|
+
const toggleType = (0, import_react10.useCallback)((type) => {
|
|
24169
24469
|
setActiveTypes((prev) => {
|
|
24170
24470
|
const next = new Set(prev);
|
|
24171
24471
|
if (next.has(type)) next.delete(type);
|
|
@@ -24173,7 +24473,7 @@ function App() {
|
|
|
24173
24473
|
return next;
|
|
24174
24474
|
});
|
|
24175
24475
|
}, []);
|
|
24176
|
-
const fetchForProject = (0,
|
|
24476
|
+
const fetchForProject = (0, import_react10.useCallback)(async (project) => {
|
|
24177
24477
|
setIsLoadingMore(true);
|
|
24178
24478
|
try {
|
|
24179
24479
|
const params = new URLSearchParams({
|
|
@@ -24195,7 +24495,7 @@ function App() {
|
|
|
24195
24495
|
setIsLoadingMore(false);
|
|
24196
24496
|
}
|
|
24197
24497
|
}, []);
|
|
24198
|
-
const handleLoadMore = (0,
|
|
24498
|
+
const handleLoadMore = (0, import_react10.useCallback)(async () => {
|
|
24199
24499
|
if (isLoadingMore) return;
|
|
24200
24500
|
setIsLoadingMore(true);
|
|
24201
24501
|
try {
|
|
@@ -24233,7 +24533,7 @@ function App() {
|
|
|
24233
24533
|
setIsLoadingMore(false);
|
|
24234
24534
|
}
|
|
24235
24535
|
}, [currentFilter, paginatedObservations.length, isLoadingMore]);
|
|
24236
|
-
(0,
|
|
24536
|
+
(0, import_react10.useEffect)(() => {
|
|
24237
24537
|
setPaginatedObservations([]);
|
|
24238
24538
|
setPaginatedSummaries([]);
|
|
24239
24539
|
setPaginatedPrompts([]);
|
|
@@ -24242,14 +24542,16 @@ function App() {
|
|
|
24242
24542
|
fetchForProject(currentFilter);
|
|
24243
24543
|
}
|
|
24244
24544
|
}, [currentFilter, fetchForProject]);
|
|
24245
|
-
return /* @__PURE__ */
|
|
24545
|
+
return /* @__PURE__ */ import_react10.default.createElement("div", { className: "h-screen overflow-hidden flex flex-col bg-surface-0" }, /* @__PURE__ */ import_react10.default.createElement(
|
|
24246
24546
|
Header,
|
|
24247
24547
|
{
|
|
24248
24548
|
isConnected,
|
|
24249
24549
|
resolvedTheme,
|
|
24250
|
-
onThemeToggle: () => setThemePreference(resolvedTheme === "dark" ? "light" : "dark")
|
|
24550
|
+
onThemeToggle: () => setThemePreference(resolvedTheme === "dark" ? "light" : "dark"),
|
|
24551
|
+
currentView,
|
|
24552
|
+
onViewChange: setCurrentView
|
|
24251
24553
|
}
|
|
24252
|
-
), /* @__PURE__ */
|
|
24554
|
+
), /* @__PURE__ */ import_react10.default.createElement("div", { className: "flex flex-1 overflow-hidden" }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "hidden md:flex w-[260px] flex-shrink-0" }, /* @__PURE__ */ import_react10.default.createElement(
|
|
24253
24555
|
Sidebar,
|
|
24254
24556
|
{
|
|
24255
24557
|
projects,
|
|
@@ -24261,15 +24563,15 @@ function App() {
|
|
|
24261
24563
|
getDisplayName,
|
|
24262
24564
|
onRenameProject: updateAlias
|
|
24263
24565
|
}
|
|
24264
|
-
)), /* @__PURE__ */
|
|
24566
|
+
)), /* @__PURE__ */ import_react10.default.createElement("main", { className: "flex-1 overflow-y-auto bg-surface-0" }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "max-w-3xl mx-auto px-6 py-6" }, currentFilter && /* @__PURE__ */ import_react10.default.createElement("div", { className: "flex items-center gap-3 mb-6 animate-fade-in" }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "w-9 h-9 rounded-lg bg-accent-violet/15 flex items-center justify-center" }, /* @__PURE__ */ import_react10.default.createElement("span", { className: "text-xs font-bold text-accent-violet" }, getDisplayName(currentFilter).substring(0, 2).toUpperCase())), /* @__PURE__ */ import_react10.default.createElement("div", null, /* @__PURE__ */ import_react10.default.createElement("h2", { className: "text-lg font-bold text-zinc-100" }, getDisplayName(currentFilter)), currentFilter !== getDisplayName(currentFilter) && /* @__PURE__ */ import_react10.default.createElement("span", { className: "text-[11px] font-mono text-zinc-600" }, currentFilter))), /* @__PURE__ */ import_react10.default.createElement(
|
|
24265
24567
|
"button",
|
|
24266
24568
|
{
|
|
24267
24569
|
onClick: () => setCurrentFilter(""),
|
|
24268
24570
|
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
24571
|
},
|
|
24270
|
-
/* @__PURE__ */
|
|
24572
|
+
/* @__PURE__ */ import_react10.default.createElement("svg", { className: "w-3.5 h-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, /* @__PURE__ */ import_react10.default.createElement("path", { d: "M18 6 6 18M6 6l12 12" })),
|
|
24271
24573
|
"Clear filter"
|
|
24272
|
-
)), /* @__PURE__ */
|
|
24574
|
+
)), currentView === "feed" ? /* @__PURE__ */ import_react10.default.createElement(
|
|
24273
24575
|
Feed,
|
|
24274
24576
|
{
|
|
24275
24577
|
observations: filteredObservations,
|
|
@@ -24280,13 +24582,19 @@ function App() {
|
|
|
24280
24582
|
hasMore,
|
|
24281
24583
|
getDisplayName
|
|
24282
24584
|
}
|
|
24585
|
+
) : /* @__PURE__ */ import_react10.default.createElement(
|
|
24586
|
+
Analytics,
|
|
24587
|
+
{
|
|
24588
|
+
currentFilter,
|
|
24589
|
+
getDisplayName
|
|
24590
|
+
}
|
|
24283
24591
|
)))));
|
|
24284
24592
|
}
|
|
24285
24593
|
|
|
24286
24594
|
// src/ui/viewer/index.tsx
|
|
24287
24595
|
var root = import_client.default.createRoot(document.getElementById("root"));
|
|
24288
24596
|
root.render(
|
|
24289
|
-
/* @__PURE__ */
|
|
24597
|
+
/* @__PURE__ */ import_react11.default.createElement(import_react11.default.StrictMode, null, /* @__PURE__ */ import_react11.default.createElement(App, null))
|
|
24290
24598
|
);
|
|
24291
24599
|
/*! Bundled license information:
|
|
24292
24600
|
|