kiro-memory 1.6.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 +105 -99
- package/package.json +14 -7
- package/plugin/dist/cli/contextkit.js +2661 -497
- package/plugin/dist/hooks/agentSpawn.js +1455 -189
- package/plugin/dist/hooks/kiro-hooks.js +1389 -156
- package/plugin/dist/hooks/postToolUse.js +1451 -174
- package/plugin/dist/hooks/stop.js +1426 -170
- package/plugin/dist/hooks/userPromptSubmit.js +1418 -170
- package/plugin/dist/index.js +1406 -172
- package/plugin/dist/sdk/index.js +1389 -155
- 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 +49 -0
- package/plugin/dist/services/sqlite/Observations.js +70 -6
- package/plugin/dist/services/sqlite/Search.js +92 -8
- package/plugin/dist/services/sqlite/Summaries.js +8 -5
- package/plugin/dist/services/sqlite/index.js +384 -18
- package/plugin/dist/types/worker-types.js +6 -0
- package/plugin/dist/viewer.js +369 -69
- package/plugin/dist/worker-service.js +1493 -145
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;
|
|
@@ -23981,6 +24259,16 @@ function useSSE() {
|
|
|
23981
24259
|
fetchPrompts();
|
|
23982
24260
|
fetchProjects();
|
|
23983
24261
|
};
|
|
24262
|
+
const onObservation = () => {
|
|
24263
|
+
fetchObservations();
|
|
24264
|
+
fetchProjects();
|
|
24265
|
+
};
|
|
24266
|
+
const onSummary = () => {
|
|
24267
|
+
fetchSummaries();
|
|
24268
|
+
};
|
|
24269
|
+
const onPrompt = () => {
|
|
24270
|
+
fetchPrompts();
|
|
24271
|
+
};
|
|
23984
24272
|
let wasConnected = false;
|
|
23985
24273
|
const connect = () => {
|
|
23986
24274
|
if (!mountedRef.current) return;
|
|
@@ -23997,28 +24285,31 @@ function useSSE() {
|
|
|
23997
24285
|
eventSource.onerror = () => {
|
|
23998
24286
|
if (!mountedRef.current) return;
|
|
23999
24287
|
setState((prev) => ({ ...prev, isConnected: false }));
|
|
24000
|
-
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
|
+
}
|
|
24001
24294
|
eventSource = null;
|
|
24002
24295
|
const delay = Math.min(1e3 * Math.pow(2, retryCount), MAX_RETRY_DELAY);
|
|
24003
24296
|
retryCount++;
|
|
24004
24297
|
retryTimeout = setTimeout(connect, delay);
|
|
24005
24298
|
};
|
|
24006
|
-
eventSource.addEventListener("observation-created",
|
|
24007
|
-
|
|
24008
|
-
|
|
24009
|
-
});
|
|
24010
|
-
eventSource.addEventListener("summary-created", () => {
|
|
24011
|
-
fetchSummaries();
|
|
24012
|
-
});
|
|
24013
|
-
eventSource.addEventListener("prompt-created", () => {
|
|
24014
|
-
fetchPrompts();
|
|
24015
|
-
});
|
|
24299
|
+
eventSource.addEventListener("observation-created", onObservation);
|
|
24300
|
+
eventSource.addEventListener("summary-created", onSummary);
|
|
24301
|
+
eventSource.addEventListener("prompt-created", onPrompt);
|
|
24016
24302
|
};
|
|
24017
24303
|
fetchAll();
|
|
24018
24304
|
connect();
|
|
24019
24305
|
return () => {
|
|
24020
24306
|
mountedRef.current = false;
|
|
24021
|
-
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
|
+
}
|
|
24022
24313
|
if (retryTimeout) clearTimeout(retryTimeout);
|
|
24023
24314
|
};
|
|
24024
24315
|
}, []);
|
|
@@ -24026,17 +24317,17 @@ function useSSE() {
|
|
|
24026
24317
|
}
|
|
24027
24318
|
|
|
24028
24319
|
// src/ui/viewer/hooks/useTheme.ts
|
|
24029
|
-
var
|
|
24320
|
+
var import_react8 = __toESM(require_react(), 1);
|
|
24030
24321
|
function useTheme() {
|
|
24031
|
-
const [preference, setPreference] = (0,
|
|
24032
|
-
const [resolvedTheme, setResolvedTheme] = (0,
|
|
24033
|
-
(0,
|
|
24322
|
+
const [preference, setPreference] = (0, import_react8.useState)("dark");
|
|
24323
|
+
const [resolvedTheme, setResolvedTheme] = (0, import_react8.useState)("dark");
|
|
24324
|
+
(0, import_react8.useEffect)(() => {
|
|
24034
24325
|
const saved = localStorage.getItem("kiro-memory-theme");
|
|
24035
24326
|
if (saved) {
|
|
24036
24327
|
setPreference(saved);
|
|
24037
24328
|
}
|
|
24038
24329
|
}, []);
|
|
24039
|
-
(0,
|
|
24330
|
+
(0, import_react8.useEffect)(() => {
|
|
24040
24331
|
let resolved;
|
|
24041
24332
|
if (preference === "system") {
|
|
24042
24333
|
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
@@ -24051,7 +24342,7 @@ function useTheme() {
|
|
|
24051
24342
|
document.documentElement.classList.remove("dark");
|
|
24052
24343
|
}
|
|
24053
24344
|
}, [preference]);
|
|
24054
|
-
(0,
|
|
24345
|
+
(0, import_react8.useEffect)(() => {
|
|
24055
24346
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
24056
24347
|
const handler = (e) => {
|
|
24057
24348
|
if (preference === "system") {
|
|
@@ -24075,11 +24366,11 @@ function useTheme() {
|
|
|
24075
24366
|
}
|
|
24076
24367
|
|
|
24077
24368
|
// src/ui/viewer/hooks/useProjectAliases.ts
|
|
24078
|
-
var
|
|
24369
|
+
var import_react9 = __toESM(require_react(), 1);
|
|
24079
24370
|
function useProjectAliases() {
|
|
24080
|
-
const [aliases, setAliases] = (0,
|
|
24081
|
-
const [isLoading, setIsLoading] = (0,
|
|
24082
|
-
(0,
|
|
24371
|
+
const [aliases, setAliases] = (0, import_react9.useState)({});
|
|
24372
|
+
const [isLoading, setIsLoading] = (0, import_react9.useState)(false);
|
|
24373
|
+
(0, import_react9.useEffect)(() => {
|
|
24083
24374
|
const fetchAliases = async () => {
|
|
24084
24375
|
try {
|
|
24085
24376
|
const res = await fetch("/api/project-aliases");
|
|
@@ -24093,10 +24384,10 @@ function useProjectAliases() {
|
|
|
24093
24384
|
};
|
|
24094
24385
|
fetchAliases();
|
|
24095
24386
|
}, []);
|
|
24096
|
-
const getDisplayName = (0,
|
|
24387
|
+
const getDisplayName = (0, import_react9.useCallback)((project) => {
|
|
24097
24388
|
return aliases[project] || project;
|
|
24098
24389
|
}, [aliases]);
|
|
24099
|
-
const updateAlias = (0,
|
|
24390
|
+
const updateAlias = (0, import_react9.useCallback)(async (project, displayName) => {
|
|
24100
24391
|
setIsLoading(true);
|
|
24101
24392
|
try {
|
|
24102
24393
|
const res = await fetch(`/api/project-aliases/${encodeURIComponent(project)}`, {
|
|
@@ -24142,38 +24433,39 @@ function mergeAndDeduplicateByProject(liveData, paginatedData) {
|
|
|
24142
24433
|
// src/ui/viewer/App.tsx
|
|
24143
24434
|
var TYPE_FILTERS = ["file-write", "file-read", "command", "research", "delegation", "tool-use"];
|
|
24144
24435
|
function App() {
|
|
24145
|
-
const [currentFilter, setCurrentFilter] = (0,
|
|
24146
|
-
const [
|
|
24147
|
-
const [
|
|
24148
|
-
const [
|
|
24149
|
-
const [
|
|
24150
|
-
const [
|
|
24151
|
-
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);
|
|
24152
24444
|
const { observations, summaries, prompts, projects, isConnected } = useSSE();
|
|
24153
24445
|
const { resolvedTheme, setThemePreference } = useTheme();
|
|
24154
24446
|
const { getDisplayName, updateAlias } = useProjectAliases();
|
|
24155
|
-
const allObservations = (0,
|
|
24447
|
+
const allObservations = (0, import_react10.useMemo)(() => {
|
|
24156
24448
|
if (currentFilter) return paginatedObservations;
|
|
24157
24449
|
return mergeAndDeduplicateByProject(observations, paginatedObservations);
|
|
24158
24450
|
}, [observations, paginatedObservations, currentFilter]);
|
|
24159
|
-
const allSummaries = (0,
|
|
24451
|
+
const allSummaries = (0, import_react10.useMemo)(() => {
|
|
24160
24452
|
if (currentFilter) return paginatedSummaries;
|
|
24161
24453
|
return mergeAndDeduplicateByProject(summaries, paginatedSummaries);
|
|
24162
24454
|
}, [summaries, paginatedSummaries, currentFilter]);
|
|
24163
|
-
const allPrompts = (0,
|
|
24455
|
+
const allPrompts = (0, import_react10.useMemo)(() => {
|
|
24164
24456
|
if (currentFilter) return paginatedPrompts;
|
|
24165
24457
|
return mergeAndDeduplicateByProject(prompts, paginatedPrompts);
|
|
24166
24458
|
}, [prompts, paginatedPrompts, currentFilter]);
|
|
24167
|
-
const filteredObservations = (0,
|
|
24459
|
+
const filteredObservations = (0, import_react10.useMemo)(
|
|
24168
24460
|
() => allObservations.filter((o) => activeTypes.has(o.type)),
|
|
24169
24461
|
[allObservations, activeTypes]
|
|
24170
24462
|
);
|
|
24171
|
-
const stats = (0,
|
|
24463
|
+
const stats = (0, import_react10.useMemo)(() => ({
|
|
24172
24464
|
observations: allObservations.length,
|
|
24173
24465
|
summaries: allSummaries.length,
|
|
24174
24466
|
prompts: allPrompts.length
|
|
24175
24467
|
}), [allObservations, allSummaries, allPrompts]);
|
|
24176
|
-
const toggleType = (0,
|
|
24468
|
+
const toggleType = (0, import_react10.useCallback)((type) => {
|
|
24177
24469
|
setActiveTypes((prev) => {
|
|
24178
24470
|
const next = new Set(prev);
|
|
24179
24471
|
if (next.has(type)) next.delete(type);
|
|
@@ -24181,7 +24473,7 @@ function App() {
|
|
|
24181
24473
|
return next;
|
|
24182
24474
|
});
|
|
24183
24475
|
}, []);
|
|
24184
|
-
const fetchForProject = (0,
|
|
24476
|
+
const fetchForProject = (0, import_react10.useCallback)(async (project) => {
|
|
24185
24477
|
setIsLoadingMore(true);
|
|
24186
24478
|
try {
|
|
24187
24479
|
const params = new URLSearchParams({
|
|
@@ -24203,7 +24495,7 @@ function App() {
|
|
|
24203
24495
|
setIsLoadingMore(false);
|
|
24204
24496
|
}
|
|
24205
24497
|
}, []);
|
|
24206
|
-
const handleLoadMore = (0,
|
|
24498
|
+
const handleLoadMore = (0, import_react10.useCallback)(async () => {
|
|
24207
24499
|
if (isLoadingMore) return;
|
|
24208
24500
|
setIsLoadingMore(true);
|
|
24209
24501
|
try {
|
|
@@ -24241,7 +24533,7 @@ function App() {
|
|
|
24241
24533
|
setIsLoadingMore(false);
|
|
24242
24534
|
}
|
|
24243
24535
|
}, [currentFilter, paginatedObservations.length, isLoadingMore]);
|
|
24244
|
-
(0,
|
|
24536
|
+
(0, import_react10.useEffect)(() => {
|
|
24245
24537
|
setPaginatedObservations([]);
|
|
24246
24538
|
setPaginatedSummaries([]);
|
|
24247
24539
|
setPaginatedPrompts([]);
|
|
@@ -24250,14 +24542,16 @@ function App() {
|
|
|
24250
24542
|
fetchForProject(currentFilter);
|
|
24251
24543
|
}
|
|
24252
24544
|
}, [currentFilter, fetchForProject]);
|
|
24253
|
-
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(
|
|
24254
24546
|
Header,
|
|
24255
24547
|
{
|
|
24256
24548
|
isConnected,
|
|
24257
24549
|
resolvedTheme,
|
|
24258
|
-
onThemeToggle: () => setThemePreference(resolvedTheme === "dark" ? "light" : "dark")
|
|
24550
|
+
onThemeToggle: () => setThemePreference(resolvedTheme === "dark" ? "light" : "dark"),
|
|
24551
|
+
currentView,
|
|
24552
|
+
onViewChange: setCurrentView
|
|
24259
24553
|
}
|
|
24260
|
-
), /* @__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(
|
|
24261
24555
|
Sidebar,
|
|
24262
24556
|
{
|
|
24263
24557
|
projects,
|
|
@@ -24269,15 +24563,15 @@ function App() {
|
|
|
24269
24563
|
getDisplayName,
|
|
24270
24564
|
onRenameProject: updateAlias
|
|
24271
24565
|
}
|
|
24272
|
-
)), /* @__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(
|
|
24273
24567
|
"button",
|
|
24274
24568
|
{
|
|
24275
24569
|
onClick: () => setCurrentFilter(""),
|
|
24276
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"
|
|
24277
24571
|
},
|
|
24278
|
-
/* @__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" })),
|
|
24279
24573
|
"Clear filter"
|
|
24280
|
-
)), /* @__PURE__ */
|
|
24574
|
+
)), currentView === "feed" ? /* @__PURE__ */ import_react10.default.createElement(
|
|
24281
24575
|
Feed,
|
|
24282
24576
|
{
|
|
24283
24577
|
observations: filteredObservations,
|
|
@@ -24288,13 +24582,19 @@ function App() {
|
|
|
24288
24582
|
hasMore,
|
|
24289
24583
|
getDisplayName
|
|
24290
24584
|
}
|
|
24585
|
+
) : /* @__PURE__ */ import_react10.default.createElement(
|
|
24586
|
+
Analytics,
|
|
24587
|
+
{
|
|
24588
|
+
currentFilter,
|
|
24589
|
+
getDisplayName
|
|
24590
|
+
}
|
|
24291
24591
|
)))));
|
|
24292
24592
|
}
|
|
24293
24593
|
|
|
24294
24594
|
// src/ui/viewer/index.tsx
|
|
24295
24595
|
var root = import_client.default.createRoot(document.getElementById("root"));
|
|
24296
24596
|
root.render(
|
|
24297
|
-
/* @__PURE__ */
|
|
24597
|
+
/* @__PURE__ */ import_react11.default.createElement(import_react11.default.StrictMode, null, /* @__PURE__ */ import_react11.default.createElement(App, null))
|
|
24298
24598
|
);
|
|
24299
24599
|
/*! Bundled license information:
|
|
24300
24600
|
|