@powerhousedao/knowledge-note 1.0.5 → 1.0.7

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.
Files changed (32) hide show
  1. package/dist/editors/knowledge-note-editor/components/links-section.d.ts.map +1 -1
  2. package/dist/editors/knowledge-note-editor/components/links-section.js +18 -2
  3. package/dist/editors/knowledge-vault/components/ActivityView.d.ts +2 -0
  4. package/dist/editors/knowledge-vault/components/ActivityView.d.ts.map +1 -0
  5. package/dist/editors/knowledge-vault/components/ActivityView.js +188 -0
  6. package/dist/editors/knowledge-vault/components/DriveExplorer.d.ts.map +1 -1
  7. package/dist/editors/knowledge-vault/components/DriveExplorer.js +8 -1
  8. package/dist/editors/knowledge-vault/components/GraphView.d.ts +1 -0
  9. package/dist/editors/knowledge-vault/components/GraphView.d.ts.map +1 -1
  10. package/dist/editors/knowledge-vault/components/GraphView.js +27 -9
  11. package/dist/editors/knowledge-vault/components/SearchView.d.ts.map +1 -1
  12. package/dist/editors/knowledge-vault/components/SearchView.js +18 -11
  13. package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts +2 -1
  14. package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts.map +1 -1
  15. package/dist/editors/knowledge-vault/hooks/use-graph-search.js +33 -2
  16. package/dist/editors/moc-editor/editor.d.ts.map +1 -1
  17. package/dist/editors/moc-editor/editor.js +1 -1
  18. package/dist/package.json +1 -1
  19. package/dist/processors/graph-indexer/index.d.ts.map +1 -1
  20. package/dist/processors/graph-indexer/index.js +80 -0
  21. package/dist/processors/graph-indexer/migrations.d.ts.map +1 -1
  22. package/dist/processors/graph-indexer/migrations.js +46 -0
  23. package/dist/processors/graph-indexer/query.d.ts +29 -0
  24. package/dist/processors/graph-indexer/query.d.ts.map +1 -1
  25. package/dist/processors/graph-indexer/query.js +104 -0
  26. package/dist/processors/graph-indexer/schema.d.ts +13 -0
  27. package/dist/processors/graph-indexer/schema.d.ts.map +1 -1
  28. package/dist/style.css +4 -4
  29. package/dist/subgraphs/knowledge-graph/subgraph.d.ts +55 -0
  30. package/dist/subgraphs/knowledge-graph/subgraph.d.ts.map +1 -1
  31. package/dist/subgraphs/knowledge-graph/subgraph.js +72 -0
  32. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"links-section.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-note-editor/components/links-section.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,QAAQ,EACR,QAAQ,EACT,MAAM,gEAAgE,CAAC;AAExE,KAAK,iBAAiB,GAAG;IACvB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,CACT,EAAE,EAAE,MAAM,EACV,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,QAAQ,KACf,IAAI,CAAC;IACV,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;CAC5D,CAAC;AA0BF,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,gBAAgB,GACjB,EAAE,iBAAiB,2CA8FnB"}
1
+ {"version":3,"file":"links-section.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-note-editor/components/links-section.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,QAAQ,EACR,QAAQ,EACT,MAAM,gEAAgE,CAAC;AAExE,KAAK,iBAAiB,GAAG;IACvB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,CACT,EAAE,EAAE,MAAM,EACV,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,QAAQ,KACf,IAAI,CAAC;IACV,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;CAC5D,CAAC;AA0BF,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,gBAAgB,GACjB,EAAE,iBAAiB,2CA8FnB"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState, useMemo, useRef, useEffect } from "react";
3
3
  import { generateId } from "document-model/core";
4
+ import { setSelectedNode } from "@powerhousedao/reactor-browser";
4
5
  import { useKnowledgeNoteDocumentsInSelectedDrive } from "@powerhousedao/knowledge-note/document-models/knowledge-note";
5
6
  const LINK_TYPES = [
6
7
  "RELATES_TO",
@@ -59,10 +60,25 @@ export function LinksSection({ links, currentDocId, onAddLink, onRemoveLink, onU
59
60
  } }, link.id))), isAdding ? (_jsx(AddLinkForm, { docOptions: docOptions, onAdd: handleAdd, onCancel: () => setIsAdding(false) })) : (_jsx("button", { type: "button", onClick: () => setIsAdding(true), className: "w-full rounded-lg border border-dashed border-white/10 py-1.5 text-xs text-gray-500 hover:border-[#cba6f7]/30 hover:text-[#cba6f7]", children: "+ Add link" }))] }));
60
61
  }
61
62
  function LinkCard({ link, isEditing, allDocOptions, onStartEdit, onCancelEdit, onUpdateLinkType, onRemoveLink, onChangeTarget, }) {
63
+ const editRef = useRef(null);
64
+ useEffect(() => {
65
+ if (!isEditing)
66
+ return;
67
+ function handleClickOutside(e) {
68
+ if (editRef.current && !editRef.current.contains(e.target)) {
69
+ onCancelEdit();
70
+ }
71
+ }
72
+ document.addEventListener("mousedown", handleClickOutside);
73
+ return () => document.removeEventListener("mousedown", handleClickOutside);
74
+ }, [isEditing, onCancelEdit]);
62
75
  if (isEditing) {
63
- return (_jsxs("div", { className: "rounded-lg border border-[#cba6f7]/20 bg-[#cba6f7]/5 p-2", children: [_jsx("p", { className: "mb-1.5 text-[10px] font-medium uppercase text-gray-500", children: "Change target document" }), _jsx(DocumentSearch, { options: allDocOptions, onSelect: (doc) => onChangeTarget(doc.id, doc.title), onCancel: onCancelEdit, autoFocus: true })] }));
76
+ return (_jsxs("div", { ref: editRef, className: "rounded-lg border border-[#cba6f7]/20 bg-[#cba6f7]/5 p-2", children: [_jsx("p", { className: "mb-1.5 text-[10px] font-medium uppercase text-gray-500", children: "Change target document" }), _jsx(DocumentSearch, { options: allDocOptions, onSelect: (doc) => onChangeTarget(doc.id, doc.title), onCancel: onCancelEdit, autoFocus: true })] }));
64
77
  }
65
- return (_jsxs("div", { className: "group flex items-center gap-2 rounded-lg border border-white/5 bg-[#1e1e2e] px-3 py-2", children: [_jsx("select", { value: link.linkType ?? "RELATES_TO", onChange: (e) => onUpdateLinkType(link.id, e.target.value), className: `rounded-md border-0 px-2 py-0.5 text-xs font-medium ${LINK_TYPE_COLORS[link.linkType ?? "RELATES_TO"]} bg-transparent`, children: LINK_TYPES.map((lt) => (_jsx("option", { value: lt, children: LINK_TYPE_LABELS[lt] }, lt))) }), _jsx("button", { type: "button", onClick: onStartEdit, className: "flex-1 truncate text-left text-sm text-gray-300 hover:text-[#cba6f7]", title: "Click to change target document", children: link.targetTitle || link.targetDocumentId || "Untitled" }), link.targetDocumentId && (_jsx("span", { className: "hidden text-[10px] font-mono text-gray-600 group-hover:inline", title: link.targetDocumentId, children: link.targetDocumentId.slice(0, 8) })), _jsx("button", { type: "button", onClick: () => onRemoveLink(link.id), className: "text-gray-600 opacity-0 transition-opacity hover:text-red-400 group-hover:opacity-100", "aria-label": "Remove link", children: "\u00D7" })] }));
78
+ return (_jsxs("div", { className: "group flex items-center gap-2 rounded-lg border border-white/5 bg-[#1e1e2e] px-3 py-2", children: [_jsx("select", { value: link.linkType ?? "RELATES_TO", onChange: (e) => onUpdateLinkType(link.id, e.target.value), className: `rounded-md border-0 px-2 py-0.5 text-xs font-medium ${LINK_TYPE_COLORS[link.linkType ?? "RELATES_TO"]} bg-transparent`, children: LINK_TYPES.map((lt) => (_jsx("option", { value: lt, children: LINK_TYPE_LABELS[lt] }, lt))) }), _jsxs("button", { type: "button", onClick: () => {
79
+ if (link.targetDocumentId)
80
+ setSelectedNode(link.targetDocumentId);
81
+ }, className: "flex-1 truncate text-left text-sm text-gray-300 hover:text-[#cba6f7] hover:underline", title: `Open: ${link.targetTitle || link.targetDocumentId || "Untitled"}`, children: [link.targetTitle || link.targetDocumentId || "Untitled", _jsxs("svg", { className: "ml-1 inline-block h-3 w-3 opacity-0 group-hover:opacity-50", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }), _jsx("polyline", { points: "15 3 21 3 21 9" }), _jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] })] }), _jsx("button", { type: "button", onClick: onStartEdit, className: "text-gray-600 opacity-0 transition-opacity hover:text-[#cba6f7] group-hover:opacity-100", "aria-label": "Change target", title: "Change target document", children: _jsxs("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" }), _jsx("path", { d: "M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" })] }) }), _jsx("button", { type: "button", onClick: () => onRemoveLink(link.id), className: "text-gray-600 opacity-0 transition-opacity hover:text-red-400 group-hover:opacity-100", "aria-label": "Remove link", children: "\u00D7" })] }));
66
82
  }
67
83
  function AddLinkForm({ docOptions, onAdd, onCancel, }) {
68
84
  const [selectedDoc, setSelectedDoc] = useState(null);
@@ -0,0 +1,2 @@
1
+ export declare function ActivityView(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=ActivityView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActivityView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/ActivityView.tsx"],"names":[],"mappings":"AAuHA,wBAAgB,YAAY,4CAkJ3B"}
@@ -0,0 +1,188 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo, useCallback } from "react";
3
+ import { setSelectedNode, useSelectedDriveId, } from "@powerhousedao/reactor-browser";
4
+ const FILTER_TYPES = {
5
+ all: { label: "All", ops: [] },
6
+ content: {
7
+ label: "Content",
8
+ ops: ["SET_TITLE", "SET_DESCRIPTION", "SET_CONTENT", "SET_NOTE_TYPE"],
9
+ },
10
+ links: {
11
+ label: "Links",
12
+ ops: ["ADD_LINK", "REMOVE_LINK", "UPDATE_LINK_TYPE"],
13
+ },
14
+ lifecycle: {
15
+ label: "Lifecycle",
16
+ ops: [
17
+ "SET_STATUS",
18
+ "SUBMIT_FOR_REVIEW",
19
+ "APPROVE_NOTE",
20
+ "REJECT_NOTE",
21
+ "ARCHIVE_NOTE",
22
+ "RESTORE_NOTE",
23
+ ],
24
+ },
25
+ topics: { label: "Topics", ops: ["ADD_TOPIC", "REMOVE_TOPIC"] },
26
+ };
27
+ const OP_ICONS = {
28
+ SET_TITLE: "T",
29
+ SET_DESCRIPTION: "D",
30
+ SET_CONTENT: "C",
31
+ SET_NOTE_TYPE: "N",
32
+ ADD_LINK: "+L",
33
+ REMOVE_LINK: "-L",
34
+ ADD_TOPIC: "+#",
35
+ REMOVE_TOPIC: "-#",
36
+ SET_STATUS: "S",
37
+ SUBMIT_FOR_REVIEW: "R",
38
+ APPROVE_NOTE: "A",
39
+ REJECT_NOTE: "X",
40
+ ARCHIVE_NOTE: "Z",
41
+ RESTORE_NOTE: "U",
42
+ SET_PROVENANCE: "P",
43
+ SET_METADATA_FIELD: "M",
44
+ };
45
+ /* ------------------------------------------------------------------ */
46
+ /* GraphQL */
47
+ /* ------------------------------------------------------------------ */
48
+ const SUBGRAPH_PATH = "/graphql/knowledgeGraph";
49
+ function getEndpoint() {
50
+ const envUrl = typeof import.meta !== "undefined" &&
51
+ import.meta.env?.VITE_SUBGRAPH_URL;
52
+ if (envUrl)
53
+ return envUrl;
54
+ const port = globalThis.window?.location?.port;
55
+ if (port === "3000" || port === "3001") {
56
+ return `http://localhost:4001${SUBGRAPH_PATH}`;
57
+ }
58
+ return SUBGRAPH_PATH;
59
+ }
60
+ const ACTIVITY_QUERY = `
61
+ query Activity($driveId: ID!, $limit: Int) {
62
+ knowledgeGraphActivity(driveId: $driveId, limit: $limit) {
63
+ id documentId operationType timestamp index summary signerAddress signerApp
64
+ }
65
+ }
66
+ `;
67
+ async function fetchActivity(driveId, limit) {
68
+ try {
69
+ const res = await fetch(getEndpoint(), {
70
+ method: "POST",
71
+ headers: { "Content-Type": "application/json" },
72
+ body: JSON.stringify({
73
+ query: ACTIVITY_QUERY,
74
+ variables: { driveId, limit },
75
+ }),
76
+ });
77
+ if (!res.ok)
78
+ return [];
79
+ const json = (await res.json());
80
+ return json.data?.knowledgeGraphActivity ?? [];
81
+ }
82
+ catch {
83
+ return [];
84
+ }
85
+ }
86
+ /* ------------------------------------------------------------------ */
87
+ /* Component */
88
+ /* ------------------------------------------------------------------ */
89
+ export function ActivityView() {
90
+ const driveId = useSelectedDriveId();
91
+ const [operations, setOperations] = useState([]);
92
+ const [loading, setLoading] = useState(true);
93
+ const [filter, setFilter] = useState("all");
94
+ const endpoint = useMemo(() => getEndpoint(), []);
95
+ const loadActivity = useCallback(async () => {
96
+ if (!driveId)
97
+ return;
98
+ setLoading(true);
99
+ const data = await fetchActivity(driveId, 200);
100
+ setOperations(data);
101
+ setLoading(false);
102
+ }, [driveId]);
103
+ useEffect(() => {
104
+ void loadActivity();
105
+ }, [loadActivity]);
106
+ const filtered = useMemo(() => {
107
+ if (filter === "all")
108
+ return operations;
109
+ const allowedOps = FILTER_TYPES[filter].ops;
110
+ return operations.filter((op) => allowedOps.includes(op.operationType));
111
+ }, [operations, filter]);
112
+ // Group by date
113
+ const grouped = useMemo(() => {
114
+ const groups = new Map();
115
+ for (const op of filtered) {
116
+ const date = op.timestamp.slice(0, 10);
117
+ const existing = groups.get(date) ?? [];
118
+ existing.push(op);
119
+ groups.set(date, existing);
120
+ }
121
+ return groups;
122
+ }, [filtered]);
123
+ if (loading) {
124
+ return (_jsx("div", { className: "flex h-full items-center justify-center text-sm", style: { color: "var(--bai-text-muted)" }, children: "Loading activity..." }));
125
+ }
126
+ if (operations.length === 0) {
127
+ return (_jsxs("div", { className: "flex h-full flex-col items-center justify-center", children: [_jsxs("svg", { className: "mb-4 h-12 w-12", style: { color: "var(--bai-text-faint)", opacity: 0.5 }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 6v6l4 2" })] }), _jsx("p", { className: "text-sm", style: { color: "var(--bai-text-muted)" }, children: "No activity recorded yet" }), _jsx("p", { className: "mt-1 text-xs", style: { color: "var(--bai-text-faint)" }, children: "Edit notes to start tracking changes" })] }));
128
+ }
129
+ return (_jsxs("div", { className: "flex h-full flex-col p-4", children: [_jsxs("div", { className: "mb-4 flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold", style: { color: "var(--bai-text)" }, children: "Vault Activity" }), _jsxs("p", { className: "text-xs", style: { color: "var(--bai-text-muted)" }, children: [filtered.length, " operation", filtered.length !== 1 ? "s" : "", filter !== "all" ? ` (${FILTER_TYPES[filter].label})` : ""] })] }), _jsx("div", { className: "flex items-center gap-2", children: _jsx("button", { type: "button", onClick: () => void loadActivity(), className: "rounded-md border px-3 py-1.5 text-xs transition-colors", style: {
130
+ borderColor: "var(--bai-border)",
131
+ color: "var(--bai-text-muted)",
132
+ backgroundColor: "var(--bai-surface)",
133
+ }, children: "Refresh" }) })] }), _jsx("div", { className: "mb-4 flex flex-wrap gap-1.5", children: Object.keys(FILTER_TYPES).map((key) => (_jsx("button", { type: "button", onClick: () => setFilter(key), className: "rounded-full px-3 py-1 text-xs transition-colors", style: {
134
+ backgroundColor: filter === key ? "var(--bai-accent)" : "var(--bai-hover)",
135
+ color: filter === key ? "#fff" : "var(--bai-text-muted)",
136
+ }, children: FILTER_TYPES[key].label }, key))) }), _jsx("div", { className: "flex-1 overflow-auto", children: [...grouped.entries()].map(([date, ops]) => (_jsxs("div", { className: "mb-6", children: [_jsx("h3", { className: "sticky top-0 z-10 mb-2 py-1 text-xs font-semibold uppercase tracking-wider", style: {
137
+ color: "var(--bai-text-muted)",
138
+ backgroundColor: "var(--bai-bg)",
139
+ }, children: formatDate(date) }), _jsx("div", { className: "space-y-1", children: ops.map((op) => (_jsx(OperationRow, { op: op }, op.id))) })] }, date))) })] }));
140
+ }
141
+ /* ------------------------------------------------------------------ */
142
+ /* Operation row */
143
+ /* ------------------------------------------------------------------ */
144
+ function OperationRow({ op }) {
145
+ const icon = OP_ICONS[op.operationType] ?? "?";
146
+ const time = op.timestamp.slice(11, 16);
147
+ return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(op.documentId), className: "group flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left transition-colors hover:bg-[var(--bai-hover)]", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[10px] font-bold", style: {
148
+ backgroundColor: opColor(op.operationType) + "20",
149
+ color: opColor(op.operationType),
150
+ }, children: icon }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-xs", style: { color: "var(--bai-text)" }, children: op.summary ?? op.operationType }), _jsxs("p", { className: "truncate text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [op.documentId.slice(0, 8), "...", op.signerAddress || op.signerApp ? (_jsxs("span", { title: op.signerAddress ?? op.signerApp ?? "", className: "cursor-pointer", style: { color: "var(--bai-accent)", opacity: 0.7 }, onClick: (e) => {
151
+ e.stopPropagation();
152
+ const text = op.signerAddress ?? op.signerApp ?? "";
153
+ void navigator.clipboard.writeText(text);
154
+ }, children: [" by ", op.signerAddress
155
+ ? `${op.signerAddress.slice(0, 6)}...${op.signerAddress.slice(-4)}`
156
+ : op.signerApp, " ", _jsxs("svg", { className: "inline-block h-2.5 w-2.5 opacity-0 group-hover:opacity-50", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }), _jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })] })] })) : null] })] }), _jsx("span", { className: "shrink-0 text-[10px] font-mono", style: { color: "var(--bai-text-faint)" }, children: time }), _jsx("svg", { className: "h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-50", style: { color: "var(--bai-text-muted)" }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M9 18l6-6-6-6" }) })] }));
157
+ }
158
+ /* ------------------------------------------------------------------ */
159
+ /* Helpers */
160
+ /* ------------------------------------------------------------------ */
161
+ function opColor(type) {
162
+ if (type.startsWith("SET_TITLE") || type.startsWith("SET_CONTENT"))
163
+ return "#f59e0b";
164
+ if (type.startsWith("ADD_LINK") || type.startsWith("REMOVE_LINK"))
165
+ return "#3b82f6";
166
+ if (type.startsWith("ADD_TOPIC") || type.startsWith("REMOVE_TOPIC"))
167
+ return "#10b981";
168
+ if (type === "SUBMIT_FOR_REVIEW" ||
169
+ type === "APPROVE_NOTE" ||
170
+ type === "REJECT_NOTE")
171
+ return "#a855f7";
172
+ if (type === "ARCHIVE_NOTE" || type === "RESTORE_NOTE")
173
+ return "#6b7280";
174
+ return "#64748b";
175
+ }
176
+ function formatDate(dateStr) {
177
+ const today = new Date().toISOString().slice(0, 10);
178
+ const yesterday = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
179
+ if (dateStr === today)
180
+ return "Today";
181
+ if (dateStr === yesterday)
182
+ return "Yesterday";
183
+ return new Date(dateStr + "T00:00:00").toLocaleDateString("en-US", {
184
+ weekday: "short",
185
+ month: "short",
186
+ day: "numeric",
187
+ });
188
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/DriveExplorer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAiClD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAiWtD"}
1
+ {"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/DriveExplorer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAmClD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAoXtD"}
@@ -8,6 +8,7 @@ import { NoteList } from "./NoteList.js";
8
8
  import { SourceList } from "./SourceList.js";
9
9
  import { HealthDashboard } from "./HealthDashboard.js";
10
10
  import { SearchView } from "./SearchView.js";
11
+ import { ActivityView } from "./ActivityView.js";
11
12
  import { GettingStartedButton } from "./GettingStarted.js";
12
13
  import { useKnowledgeNotes } from "../hooks/use-knowledge-notes.js";
13
14
  import { useKnowledgeGraph, buildSyncPayload, } from "../hooks/use-knowledge-graph.js";
@@ -36,6 +37,7 @@ export function DriveExplorer({ children }) {
36
37
  noteRef: ci.noteRef,
37
38
  contextPhrase: ci.contextPhrase,
38
39
  })),
40
+ childRefs: state.childRefs ?? [],
39
41
  };
40
42
  });
41
43
  }, [allDocuments]);
@@ -129,6 +131,11 @@ export function DriveExplorer({ children }) {
129
131
  badge: sourceCount > 0 ? sourceCount : undefined,
130
132
  icon: (_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M4 19.5A2.5 2.5 0 016.5 17H20" }), _jsx("path", { d: "M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z" })] })),
131
133
  },
134
+ {
135
+ key: "activity",
136
+ label: "Activity",
137
+ icon: (_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 6v6l4 2" })] })),
138
+ },
132
139
  {
133
140
  key: "pipeline",
134
141
  label: "Pipeline",
@@ -161,7 +168,7 @@ export function DriveExplorer({ children }) {
161
168
  }, children: tab.badge }))] }, tab.key))), showDocumentEditor && (_jsxs("span", { className: "flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium", style: {
162
169
  backgroundColor: "var(--bai-hover)",
163
170
  color: "var(--bai-accent)",
164
- }, children: [_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" }), _jsx("path", { d: "M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" })] }), "Editing"] }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [hasGraphDoc && graphState && (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [graphState.nodes.length, "n / ", graphState.edges.length, "e"] })), _jsx(ThemeToggle, {}), _jsx(GettingStartedButton, {}), _jsx(CreateMenu, {})] })] }), _jsx("div", { className: "flex-1 overflow-auto", children: showDocumentEditor ? (_jsx("div", { className: "h-full", children: children })) : viewMode === "graph" ? (_jsx(GraphView, { notes: notes, graphState: graphState, mocs: mocs, tensions: tensions })) : viewMode === "search" ? (_jsx(SearchView, {})) : viewMode === "sources" ? (_jsx(SourceList, {})) : viewMode === "health" ? (_jsx(HealthDashboard, {})) : (_jsx(NoteList, { notes: notes })) })] })] }));
171
+ }, children: [_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" }), _jsx("path", { d: "M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" })] }), "Editing"] }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [hasGraphDoc && graphState && (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [graphState.nodes.length, "n / ", graphState.edges.length, "e"] })), _jsx(ThemeToggle, {}), _jsx(GettingStartedButton, {}), _jsx(CreateMenu, {})] })] }), _jsx("div", { className: "flex-1 overflow-auto", children: showDocumentEditor ? (_jsx("div", { className: "h-full", children: children })) : viewMode === "graph" ? (_jsx(GraphView, { notes: notes, graphState: graphState, mocs: mocs, tensions: tensions })) : viewMode === "search" ? (_jsx(SearchView, {})) : viewMode === "activity" ? (_jsx(ActivityView, {})) : viewMode === "sources" ? (_jsx(SourceList, {})) : viewMode === "health" ? (_jsx(HealthDashboard, {})) : (_jsx(NoteList, { notes: notes })) })] })] }));
165
172
  }
166
173
  const CREATE_ITEMS = [
167
174
  {
@@ -23,6 +23,7 @@ type MocInfo = {
23
23
  noteRef: string;
24
24
  contextPhrase: string;
25
25
  }[];
26
+ childRefs: string[];
26
27
  };
27
28
  type TensionInfo = {
28
29
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"GraphView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/GraphView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAUzE,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,EAAE,CAAC;IACJ,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,EAAE,CAAC;IACJ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,IAAI,CAAC;AAET,KAAK,OAAO,GAAG;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACzD,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;CAC1B,CAAC;AA6fF,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,UAAU,EACV,IAAI,EACJ,QAAQ,GACT,EAAE,cAAc,2CAmtBhB"}
1
+ {"version":3,"file":"GraphView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/GraphView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAUzE,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,EAAE,CAAC;IACJ,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,EAAE,CAAC;IACJ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,IAAI,CAAC;AAET,KAAK,OAAO,GAAG;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;CAC1B,CAAC;AAghBF,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,UAAU,EACV,IAAI,EACJ,QAAQ,GACT,EAAE,cAAc,2CAotBhB"}
@@ -184,8 +184,12 @@ function buildElements(notes, graphState, mocs, tensions) {
184
184
  }
185
185
  // Track existing node IDs for MOC + tension edge targets
186
186
  const existingNodeIds = new Set(elements.filter((e) => !e.data.source).map((e) => e.data.id));
187
- // Add MOC nodes as compound parents + edges for non-parented refs
187
+ // Add MOC nodes + edges (coreIdeas notes, childRefs child MOCs)
188
188
  if (mocs?.length) {
189
+ // Pre-register MOC IDs so childRef edges between MOCs can resolve
190
+ for (const moc of mocs) {
191
+ existingNodeIds.add(moc.id);
192
+ }
189
193
  for (const moc of mocs) {
190
194
  elements.push({
191
195
  data: {
@@ -195,7 +199,7 @@ function buildElements(notes, graphState, mocs, tensions) {
195
199
  noteType: `MOC (${moc.tier ?? "TOPIC"})`,
196
200
  description: null,
197
201
  topics: [],
198
- linkCount: moc.coreIdeas.length,
202
+ linkCount: moc.coreIdeas.length + moc.childRefs.length,
199
203
  color: MOC_NODE_COLOR,
200
204
  isMoc: true,
201
205
  },
@@ -213,6 +217,19 @@ function buildElements(notes, graphState, mocs, tensions) {
213
217
  });
214
218
  }
215
219
  }
220
+ for (const childRef of moc.childRefs) {
221
+ if (existingNodeIds.has(childRef)) {
222
+ elements.push({
223
+ data: {
224
+ id: `moc-child-${moc.id}-${childRef}`,
225
+ source: moc.id,
226
+ target: childRef,
227
+ linkType: "CORE_IDEA",
228
+ color: MOC_EDGE_COLOR,
229
+ },
230
+ });
231
+ }
232
+ }
216
233
  }
217
234
  }
218
235
  // Add tension nodes and edges to involved notes
@@ -572,16 +589,17 @@ export function GraphView({ notes, graphState, mocs, tensions, }) {
572
589
  const dx = curr.x - prev.x;
573
590
  const dy = curr.y - prev.y;
574
591
  mocDragState.set(moc.id(), { x: curr.x, y: curr.y });
575
- // Move all CORE_IDEA-connected notes along with the MOC
592
+ // Move CORE_IDEA-connected notes along with the MOC,
593
+ // but skip MOC-to-MOC connections (parent ↔ child MOCs move independently)
576
594
  moc.connectedEdges().forEach((edge) => {
577
595
  if (edge.data("linkType") !== "CORE_IDEA")
578
596
  return;
579
- const child = edge.source().id() === moc.id()
580
- ? edge.target()
581
- : edge.source();
582
- if (child.grabbed())
583
- return; // don't fight if user is dragging it
584
- child.shift({ x: dx, y: dy });
597
+ const other = edge.source().id() === moc.id() ? edge.target() : edge.source();
598
+ if (other.grabbed())
599
+ return;
600
+ if (other.data("isMoc"))
601
+ return; // don't drag other MOCs
602
+ other.shift({ x: dx, y: dy });
585
603
  });
586
604
  });
587
605
  // Persist position after manual drag
@@ -1 +1 @@
1
- {"version":3,"file":"SearchView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/SearchView.tsx"],"names":[],"mappings":"AA+CA,wBAAgB,UAAU,4CAmGzB"}
1
+ {"version":3,"file":"SearchView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/SearchView.tsx"],"names":[],"mappings":"AA+CA,wBAAgB,UAAU,4CAsGzB"}
@@ -43,19 +43,19 @@ export function SearchView() {
43
43
  backgroundColor: "var(--bai-surface)",
44
44
  borderColor: "var(--bai-border)",
45
45
  color: "var(--bai-text)",
46
- } }), query && (_jsx("button", { type: "button", onClick: () => setQuery(""), className: "absolute right-3 top-1/2 -translate-y-1/2 rounded p-0.5 transition-colors hover:opacity-80", style: { color: "var(--bai-text-muted)" }, children: _jsx("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }))] }), _jsx(ModeToggle, { mode: searchMode, onChange: setSearchMode })] }), query.trim() && (_jsxs("div", { className: "mb-3 flex items-center gap-2 text-xs", style: { color: "var(--bai-text-muted)" }, children: [loading ? (_jsx("span", { children: "Searching..." })) : (_jsxs(_Fragment, { children: [_jsxs("span", { children: [results.length, " result", results.length !== 1 ? "s" : ""] }), searchMode === "semantic" && results.length > 0 && (_jsx("span", { style: { opacity: 0.6 }, children: "\u2014 match % shows how closely each note relates to your query by meaning" }))] })), error && _jsx("span", { style: { color: "#ef4444" }, children: error })] })), _jsx("div", { className: "flex-1 overflow-auto", children: !query.trim() ? (_jsx(EmptyState, { topics: topics, onTopicClick: setQuery })) : (_jsx(ResultList, { results: results, searchMode: searchMode })) })] }));
46
+ } }), query && (_jsx("button", { type: "button", onClick: () => setQuery(""), className: "absolute right-3 top-1/2 -translate-y-1/2 rounded p-0.5 transition-colors hover:opacity-80", style: { color: "var(--bai-text-muted)" }, children: _jsx("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }))] }), _jsx(ModeToggle, { mode: searchMode, onChange: setSearchMode })] }), query.trim() && (_jsxs("div", { className: "mb-3 flex items-center gap-2 text-xs", style: { color: "var(--bai-text-muted)" }, children: [loading ? (_jsx("span", { children: "Searching..." })) : (_jsxs(_Fragment, { children: [_jsxs("span", { children: [results.length, " result", results.length !== 1 ? "s" : ""] }), (searchMode === "semantic" || searchMode === "hybrid") &&
47
+ results.length > 0 && (_jsxs("span", { style: { opacity: 0.6 }, children: ["\u2014 score shows relevance", searchMode === "hybrid"
48
+ ? " combining keyword + semantic matching"
49
+ : " by meaning"] }))] })), error && _jsx("span", { style: { color: "#ef4444" }, children: error })] })), _jsx("div", { className: "flex-1 overflow-auto", children: !query.trim() ? (_jsx(EmptyState, { topics: topics, onTopicClick: setQuery })) : (_jsx(ResultList, { results: results, searchMode: searchMode })) })] }));
47
50
  }
48
51
  /* ------------------------------------------------------------------ */
49
52
  /* Mode toggle */
50
53
  /* ------------------------------------------------------------------ */
51
54
  function ModeToggle({ mode, onChange, }) {
52
- return (_jsxs("div", { className: "flex shrink-0 overflow-hidden rounded-lg border text-xs", style: { borderColor: "var(--bai-border)" }, children: [_jsx("button", { type: "button", onClick: () => onChange("semantic"), className: "px-3 py-2 transition-colors", style: {
53
- backgroundColor: mode === "semantic" ? "var(--bai-accent)" : "var(--bai-surface)",
54
- color: mode === "semantic" ? "#fff" : "var(--bai-text-muted)",
55
- }, children: "Semantic" }), _jsx("button", { type: "button", onClick: () => onChange("keyword"), className: "px-3 py-2 transition-colors", style: {
56
- backgroundColor: mode === "keyword" ? "var(--bai-accent)" : "var(--bai-surface)",
57
- color: mode === "keyword" ? "#fff" : "var(--bai-text-muted)",
58
- }, children: "Keyword" })] }));
55
+ return (_jsx("div", { className: "flex shrink-0 overflow-hidden rounded-lg border text-xs", style: { borderColor: "var(--bai-border)" }, children: ["hybrid", "semantic", "keyword"].map((m) => (_jsx("button", { type: "button", onClick: () => onChange(m), className: "px-3 py-2 transition-colors capitalize", style: {
56
+ backgroundColor: mode === m ? "var(--bai-accent)" : "var(--bai-surface)",
57
+ color: mode === m ? "#fff" : "var(--bai-text-muted)",
58
+ }, children: m }, m))) }));
59
59
  }
60
60
  /* ------------------------------------------------------------------ */
61
61
  /* Empty state */
@@ -78,7 +78,7 @@ function ResultList({ results, searchMode, }) {
78
78
  if (results.length === 0) {
79
79
  return (_jsx("div", { className: "py-12 text-center text-sm", style: { color: "var(--bai-text-muted)" }, children: "No results found" }));
80
80
  }
81
- return (_jsx("div", { className: "space-y-2", children: results.map((result) => (_jsx(ResultCard, { result: result, showSimilarity: searchMode === "semantic" }, result.documentId))) }));
81
+ return (_jsx("div", { className: "space-y-2", children: results.map((result) => (_jsx(ResultCard, { result: result, showSimilarity: searchMode !== "keyword" }, result.documentId))) }));
82
82
  }
83
83
  /* ------------------------------------------------------------------ */
84
84
  /* Result card */
@@ -86,11 +86,18 @@ function ResultList({ results, searchMode, }) {
86
86
  function ResultCard({ result, showSimilarity, }) {
87
87
  const status = result.status ?? "DRAFT";
88
88
  const badgeStyle = STATUS_COLORS[status] ?? STATUS_COLORS.DRAFT;
89
- return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(result.documentId), className: "group flex w-full items-start gap-3 rounded-xl border p-4 text-left transition-all border-[var(--bai-border)] bg-[var(--bai-surface)] hover:border-[var(--bai-accent)] hover:bg-[var(--bai-hover)]", children: [showSimilarity && result.similarity != null && (_jsxs("div", { className: "shrink-0 rounded-md px-2 py-1 text-center font-mono text-xs font-bold", title: "Semantic similarity \u2014 how closely this note's meaning matches your query (higher is better)", style: {
89
+ return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(result.documentId), className: "group flex w-full items-start gap-3 rounded-xl border p-4 text-left transition-all border-[var(--bai-border)] bg-[var(--bai-surface)] hover:border-[var(--bai-accent)] hover:bg-[var(--bai-hover)]", children: [showSimilarity && result.similarity != null && (_jsxs("div", { className: "shrink-0 rounded-md px-2 py-1 text-center font-mono text-xs font-bold", title: "Relevance score \u2014 how closely this note matches your query (higher is better)", style: {
90
90
  color: similarityColor(result.similarity),
91
91
  backgroundColor: `${similarityColor(result.similarity)}15`,
92
92
  }, children: [(result.similarity * 100).toFixed(0), "%"] })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "mb-1 flex items-start justify-between gap-2", children: [_jsx("h4", { className: "truncate text-sm font-semibold text-[var(--bai-text)] group-hover:text-[var(--bai-accent)]", children: result.title ?? "Untitled" }), _jsx("span", { className: "shrink-0 rounded-full border px-2 py-0.5 text-[10px] font-medium", style: badgeStyle, children: status.replace("_", " ") })] }), result.description && (_jsx("p", { className: "mb-2 line-clamp-2 text-xs leading-relaxed", style: { color: "var(--bai-text-muted)" }, children: result.description })), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [result.noteType && (_jsx("span", { className: "rounded px-1.5 py-0.5 text-[10px] font-medium", style: {
93
93
  background: "var(--bai-hover)",
94
94
  color: "var(--bai-text-muted)",
95
- }, children: result.noteType })), result.topics?.slice(0, 4).map((topic) => (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-accent)", opacity: 0.6 }, children: ["#", topic] }, topic)))] })] }), _jsx("svg", { className: "mt-1 h-4 w-4 shrink-0 opacity-0 transition-opacity group-hover:opacity-60", style: { color: "var(--bai-text-muted)" }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M9 18l6-6-6-6" }) })] }));
95
+ }, children: result.noteType })), result.topics?.slice(0, 4).map((topic) => (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-accent)", opacity: 0.6 }, children: ["#", topic] }, topic))), result.matchedBy && result.matchedBy.length > 0 && (_jsx("span", { className: "ml-auto flex gap-1", children: result.matchedBy.map((m) => (_jsx("span", { className: "rounded px-1.5 py-0.5 text-[9px] font-medium", style: {
96
+ background: m === "semantic"
97
+ ? "rgba(139, 92, 246, 0.15)"
98
+ : "rgba(59, 130, 246, 0.15)",
99
+ color: m === "semantic"
100
+ ? "rgba(167, 139, 250, 1)"
101
+ : "rgba(147, 197, 253, 1)",
102
+ }, children: m }, m))) }))] })] }), _jsx("svg", { className: "mt-1 h-4 w-4 shrink-0 opacity-0 transition-opacity group-hover:opacity-60", style: { color: "var(--bai-text-muted)" }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M9 18l6-6-6-6" }) })] }));
96
103
  }
@@ -6,12 +6,13 @@ export type SearchResult = {
6
6
  status: string | null;
7
7
  topics: string[];
8
8
  similarity?: number;
9
+ matchedBy?: string[];
9
10
  };
10
11
  export type TopicInfo = {
11
12
  name: string;
12
13
  noteCount: number;
13
14
  };
14
- export type SearchMode = "semantic" | "keyword";
15
+ export type SearchMode = "hybrid" | "semantic" | "keyword";
15
16
  export declare function useGraphSearch(): {
16
17
  query: string;
17
18
  setQuery: (q: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"use-graph-search.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/hooks/use-graph-search.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAkIhD,wBAAgB,cAAc;;kBAgBtB,MAAM;;;;;;uBAQN,UAAU;EAoGjB"}
1
+ {"version":3,"file":"use-graph-search.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/hooks/use-graph-search.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAkJ3D,wBAAgB,cAAc;;kBAgBtB,MAAM;;;;;;uBAQN,UAAU;EA4HjB"}
@@ -79,6 +79,15 @@ const KEYWORD_SEARCH_QUERY = `
79
79
  }
80
80
  }
81
81
  `;
82
+ const HYBRID_SEARCH_QUERY = `
83
+ query HybridSearch($driveId: ID!, $query: String!, $limit: Int) {
84
+ knowledgeGraphHybridSearch(driveId: $driveId, query: $query, limit: $limit) {
85
+ node { documentId title description noteType status topics }
86
+ score
87
+ matchedBy
88
+ }
89
+ }
90
+ `;
82
91
  const TOPICS_QUERY = `
83
92
  query Topics($driveId: ID!) {
84
93
  knowledgeGraphTopics(driveId: $driveId) { name noteCount }
@@ -93,9 +102,14 @@ function loadSearchState() {
93
102
  const raw = sessionStorage.getItem(STORAGE_KEY);
94
103
  if (raw) {
95
104
  const parsed = JSON.parse(raw);
105
+ const mode = parsed.mode;
96
106
  return {
97
107
  query: parsed.query ?? "",
98
- mode: parsed.mode === "keyword" ? "keyword" : "semantic",
108
+ mode: mode === "keyword"
109
+ ? "keyword"
110
+ : mode === "semantic"
111
+ ? "semantic"
112
+ : "hybrid",
99
113
  };
100
114
  }
101
115
  }
@@ -151,7 +165,24 @@ export function useGraphSearch() {
151
165
  }
152
166
  setLoading(true);
153
167
  setError(null);
154
- if (mode === "semantic") {
168
+ if (mode === "hybrid") {
169
+ const data = await graphqlFetch(endpoint, HYBRID_SEARCH_QUERY, { driveId, query: q, limit: 20 });
170
+ if (data?.knowledgeGraphHybridSearch) {
171
+ const raw = data.knowledgeGraphHybridSearch;
172
+ // Normalize RRF scores to 0-1 range (top result = 1.0)
173
+ const maxScore = raw.length > 0 ? raw[0].score : 1;
174
+ setResults(raw.map((r) => ({
175
+ ...r.node,
176
+ similarity: maxScore > 0 ? r.score / maxScore : 0,
177
+ matchedBy: r.matchedBy,
178
+ })));
179
+ }
180
+ else {
181
+ setResults([]);
182
+ setError("Hybrid search unavailable. Try keyword mode.");
183
+ }
184
+ }
185
+ else if (mode === "semantic") {
155
186
  const data = await graphqlFetch(endpoint, SEMANTIC_SEARCH_QUERY, { driveId, query: q, limit: 20 });
156
187
  if (data?.knowledgeGraphSemanticSearch) {
157
188
  setResults(data.knowledgeGraphSemanticSearch.map((r) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../editors/moc-editor/editor.tsx"],"names":[],"mappings":"AAuBA,MAAM,CAAC,OAAO,UAAU,MAAM,4CA6X7B"}
1
+ {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../editors/moc-editor/editor.tsx"],"names":[],"mappings":"AAuBA,MAAM,CAAC,OAAO,UAAU,MAAM,4CAgY7B"}
@@ -58,7 +58,7 @@ export default function Editor() {
58
58
  backgroundColor: "var(--bai-bg)",
59
59
  boxShadow: "0 0 0 1px var(--bai-ring)",
60
60
  }, children: [_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-xs", style: { color: "var(--bai-text-secondary)" }, children: idea.contextPhrase }), idea.noteRef &&
61
- (noteTitle ? (_jsxs("button", { type: "button", onClick: () => setSelectedNode(idea.noteRef), className: "mt-0.5 text-[10px] text-left truncate max-w-full transition-colors hover:underline", style: { color: "var(--bai-accent)" }, title: `Open note: ${noteTitle}`, children: [noteTitle, _jsxs("svg", { className: "ml-1 inline h-2.5 w-2.5", style: { color: "var(--bai-text-faint)" }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }), _jsx("path", { d: "M15 3h6v6" }), _jsx("path", { d: "M10 14L21 3" })] })] })) : (_jsx("p", { className: "text-[10px] font-mono mt-0.5", style: { color: "var(--bai-text-faint)" }, children: idea.noteRef.slice(0, 12) })))] }), _jsx("button", { type: "button", onClick: () => dispatch(actions.removeCoreIdea({ id: idea.id })), className: "opacity-0 hover:text-red-400 group-hover:opacity-100", style: { color: "var(--bai-text-faint)" }, children: "\u00D7" })] }, idea.id));
61
+ (noteTitle ? (_jsxs("button", { type: "button", onClick: () => setSelectedNode(idea.noteRef), className: "mt-0.5 text-[10px] text-left truncate max-w-full transition-colors hover:underline", style: { color: "var(--bai-accent)" }, title: `Open note: ${noteTitle}`, children: [noteTitle, _jsxs("svg", { className: "ml-1 inline h-2.5 w-2.5", style: { color: "var(--bai-text-faint)" }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }), _jsx("path", { d: "M15 3h6v6" }), _jsx("path", { d: "M10 14L21 3" })] })] })) : (_jsxs("button", { type: "button", onClick: () => setSelectedNode(idea.noteRef), className: "mt-0.5 text-[10px] font-mono text-left truncate max-w-full transition-colors hover:underline", style: { color: "var(--bai-text-faint)" }, title: `Open document: ${idea.noteRef}`, children: [idea.noteRef.slice(0, 12), "..."] })))] }), _jsx("button", { type: "button", onClick: () => dispatch(actions.removeCoreIdea({ id: idea.id })), className: "opacity-0 hover:text-red-400 group-hover:opacity-100", style: { color: "var(--bai-text-faint)" }, children: "\u00D7" })] }, idea.id));
62
62
  }), _jsxs("form", { className: "flex gap-2", onSubmit: (e) => {
63
63
  e.preventDefault();
64
64
  if (!newIdeaRef.trim() || !newIdeaPhrase.trim())
package/dist/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/knowledge-note",
3
3
  "description": "Knowledge Note document model package for Powerhouse",
4
- "version": "1.0.5",
4
+ "version": "1.0.7",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "files": [
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAItC,qBAAa,qBAAsB,SAAQ,qBAAqB,CAAC,EAAE,CAAC;WAClD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,YAAY,CACzB,UAAU,EAAE,oBAAoB,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC;IAmKV,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAQrB,UAAU;CA8BzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AA+DtC,qBAAa,qBAAsB,SAAQ,qBAAqB,CAAC,EAAE,CAAC;WAClD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,YAAY,CACzB,UAAU,EAAE,oBAAoB,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC;IAgMV,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAQrB,UAAU;CA8BzB"}
@@ -2,6 +2,62 @@ import { RelationalDbProcessor } from "@powerhousedao/shared/processors";
2
2
  import { up } from "./migrations.js";
3
3
  import { generateEmbedding } from "./embedder.js";
4
4
  import { upsertEmbedding, deleteEmbedding } from "./embedding-store.js";
5
+ function summarizeOperation(type, input) {
6
+ switch (type) {
7
+ case "SET_TITLE":
8
+ return `Title changed to "${truncate(input.title)}"`;
9
+ case "SET_DESCRIPTION":
10
+ return `Description updated`;
11
+ case "SET_CONTENT": {
12
+ const len = typeof input.content === "string" ? input.content.length : 0;
13
+ return `Content updated (${len} chars)`;
14
+ }
15
+ case "SET_NOTE_TYPE":
16
+ return `Type set to ${String(input.noteType)}`;
17
+ case "SET_STATUS":
18
+ return `Status changed to ${String(input.status)}`;
19
+ case "ADD_LINK":
20
+ return `Linked to "${truncate(input.targetTitle)}" (${s(input.linkType, "RELATES_TO")})`;
21
+ case "REMOVE_LINK":
22
+ return `Removed link ${s(input.id)}`;
23
+ case "UPDATE_LINK_TYPE":
24
+ return `Link type changed to ${s(input.linkType)}`;
25
+ case "ADD_TOPIC":
26
+ return `Added topic #${s(input.name)}`;
27
+ case "REMOVE_TOPIC":
28
+ return `Removed topic`;
29
+ case "SET_PROVENANCE":
30
+ return `Provenance set: ${s(input.author, "unknown")}, ${s(input.sourceOrigin)}`;
31
+ case "SUBMIT_FOR_REVIEW":
32
+ return `Submitted for review`;
33
+ case "APPROVE_NOTE":
34
+ return `Approved by ${s(input.actor, "unknown")}`;
35
+ case "REJECT_NOTE":
36
+ return `Rejected: ${truncate(input.comment)}`;
37
+ case "ARCHIVE_NOTE":
38
+ return `Archived`;
39
+ case "RESTORE_NOTE":
40
+ return `Restored from archive`;
41
+ case "SET_METADATA_FIELD":
42
+ return `Metadata: ${String(input.field)} = ${truncate(input.value)}`;
43
+ default:
44
+ return type;
45
+ }
46
+ }
47
+ /** Safely stringify an unknown value with optional fallback */
48
+ function s(val, fallback = "") {
49
+ if (val == null)
50
+ return fallback;
51
+ if (typeof val === "string")
52
+ return val;
53
+ if (typeof val === "number" || typeof val === "boolean")
54
+ return String(val);
55
+ return fallback;
56
+ }
57
+ function truncate(val, max = 60) {
58
+ const str = s(val);
59
+ return str.length > max ? str.slice(0, max) + "..." : str;
60
+ }
5
61
  export class GraphIndexerProcessor extends RelationalDbProcessor {
6
62
  static getNamespace(driveId) {
7
63
  return super.getNamespace(driveId);
@@ -28,6 +84,30 @@ export class GraphIndexerProcessor extends RelationalDbProcessor {
28
84
  // Only process knowledge-note documents
29
85
  if (context.documentType !== "bai/knowledge-note")
30
86
  continue;
87
+ // Index operation for history tracking
88
+ try {
89
+ const input = operation.action.input;
90
+ const signer = operation.action.context?.signer;
91
+ await this.relationalDb
92
+ .insertInto("graph_operations")
93
+ .values({
94
+ id: `${documentId}-${operation.index}`,
95
+ document_id: documentId,
96
+ operation_type: operation.action.type,
97
+ timestamp: operation.timestampUtcMs ?? new Date().toISOString(),
98
+ index: operation.index,
99
+ scope: context.scope ?? "global",
100
+ summary: summarizeOperation(operation.action.type, input),
101
+ input_json: JSON.stringify(input),
102
+ signer_address: signer?.user?.address || null,
103
+ signer_app: signer?.app?.name || null,
104
+ })
105
+ .onConflict((oc) => oc.column("id").doNothing())
106
+ .execute();
107
+ }
108
+ catch {
109
+ // non-critical — don't block state reconciliation
110
+ }
31
111
  // Collect last state per document
32
112
  if (context.resultingState) {
33
113
  lastByDocument.set(documentId, entry);
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,wBAAsB,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAmF9D;AAED,wBAAsB,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE"}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,wBAAsB,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAqI9D;AAED,wBAAsB,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhE"}
@@ -73,8 +73,54 @@ export async function up(db) {
73
73
  .column("name")
74
74
  .ifNotExists()
75
75
  .execute();
76
+ // --- Operation history ---
77
+ await db.schema
78
+ .createTable("graph_operations")
79
+ .addColumn("id", "varchar(255)", (col) => col.primaryKey())
80
+ .addColumn("document_id", "varchar(255)", (col) => col.notNull())
81
+ .addColumn("operation_type", "varchar(100)", (col) => col.notNull())
82
+ .addColumn("timestamp", "varchar(50)", (col) => col.notNull())
83
+ .addColumn("index", "integer", (col) => col.notNull())
84
+ .addColumn("scope", "varchar(20)", (col) => col.notNull())
85
+ .addColumn("summary", "text")
86
+ .addColumn("input_json", "text")
87
+ .addColumn("signer_address", "varchar(255)")
88
+ .addColumn("signer_app", "varchar(255)")
89
+ .ifNotExists()
90
+ .execute();
91
+ await db.schema
92
+ .createIndex("idx_graph_ops_doc")
93
+ .on("graph_operations")
94
+ .column("document_id")
95
+ .ifNotExists()
96
+ .execute();
97
+ await db.schema
98
+ .createIndex("idx_graph_ops_timestamp")
99
+ .on("graph_operations")
100
+ .column("timestamp")
101
+ .ifNotExists()
102
+ .execute();
103
+ await db.schema
104
+ .createIndex("idx_graph_ops_type")
105
+ .on("graph_operations")
106
+ .column("operation_type")
107
+ .ifNotExists()
108
+ .execute();
109
+ // Add signer columns to graph_operations (idempotent)
110
+ for (const col of ["signer_address", "signer_app"]) {
111
+ try {
112
+ await db.schema
113
+ .alterTable("graph_operations")
114
+ .addColumn(col, "varchar(255)")
115
+ .execute();
116
+ }
117
+ catch {
118
+ // column likely already exists — ignore
119
+ }
120
+ }
76
121
  }
77
122
  export async function down(db) {
123
+ await db.schema.dropTable("graph_operations").ifExists().execute();
78
124
  await db.schema.dropTable("graph_topics").ifExists().execute();
79
125
  await db.schema.dropTable("graph_edges").ifExists().execute();
80
126
  await db.schema.dropTable("graph_nodes").ifExists().execute();
@@ -40,6 +40,22 @@ export interface RelatedByTopicResult {
40
40
  sharedTopics: string[];
41
41
  sharedTopicCount: number;
42
42
  }
43
+ export interface HybridSearchResult {
44
+ node: GraphNodeResult;
45
+ score: number;
46
+ matchedBy: string[];
47
+ }
48
+ export interface OperationRecord {
49
+ id: string;
50
+ documentId: string;
51
+ operationType: string;
52
+ timestamp: string;
53
+ index: number;
54
+ scope: string;
55
+ summary: string | null;
56
+ signerAddress: string | null;
57
+ signerApp: string | null;
58
+ }
43
59
  export declare function createGraphQuery(db: Kysely<DB>): {
44
60
  allNodes(): Promise<GraphNodeResult[]>;
45
61
  allEdges(): Promise<GraphEdgeResult[]>;
@@ -66,5 +82,18 @@ export declare function createGraphQuery(db: Kysely<DB>): {
66
82
  nodesByAuthor(author: string): Promise<GraphNodeResult[]>;
67
83
  nodesByOrigin(origin: string): Promise<GraphNodeResult[]>;
68
84
  recentNodes(limit?: number, since?: string): Promise<GraphNodeResult[]>;
85
+ /**
86
+ * Hybrid search: run keyword + semantic in parallel, merge via
87
+ * Reciprocal Rank Fusion (RRF). semanticResults must be pre-fetched
88
+ * from the embedding store by the caller.
89
+ */
90
+ hybridSearch(keywordQuery: string, semanticResults: Array<{
91
+ documentId: string;
92
+ similarity: number;
93
+ }>, limit?: number): Promise<HybridSearchResult[]>;
94
+ history(documentId: string, limit?: number): Promise<OperationRecord[]>;
95
+ activity(limit?: number, since?: string): Promise<OperationRecord[]>;
96
+ activityByType(operationType: string, limit?: number): Promise<OperationRecord[]>;
97
+ staleNodes(since: string, limit?: number): Promise<GraphNodeResult[]>;
69
98
  };
70
99
  //# sourceMappingURL=query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/query.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,19 @@ function rowToEdge(row) {
24
24
  updatedAt: row.updated_at,
25
25
  };
26
26
  }
27
+ function rowToOperation(row) {
28
+ return {
29
+ id: row.id,
30
+ documentId: row.document_id,
31
+ operationType: row.operation_type,
32
+ timestamp: row.timestamp,
33
+ index: row.index,
34
+ scope: row.scope,
35
+ summary: row.summary,
36
+ signerAddress: row.signer_address,
37
+ signerApp: row.signer_app,
38
+ };
39
+ }
27
40
  export function createGraphQuery(db) {
28
41
  return {
29
42
  async allNodes() {
@@ -372,5 +385,96 @@ export function createGraphQuery(db) {
372
385
  const rows = await query.execute();
373
386
  return rows.map(rowToNode);
374
387
  },
388
+ /**
389
+ * Hybrid search: run keyword + semantic in parallel, merge via
390
+ * Reciprocal Rank Fusion (RRF). semanticResults must be pre-fetched
391
+ * from the embedding store by the caller.
392
+ */
393
+ async hybridSearch(keywordQuery, semanticResults, limit = 20) {
394
+ const K = 60;
395
+ const scores = new Map();
396
+ // Keyword leg
397
+ const keywordResults = await this.fullSearch(keywordQuery, limit * 2);
398
+ keywordResults.forEach((node, rank) => {
399
+ const existing = scores.get(node.documentId) ?? {
400
+ score: 0,
401
+ matchedBy: [],
402
+ };
403
+ existing.score += 1 / (K + rank);
404
+ existing.matchedBy.push("keyword");
405
+ existing.node = node;
406
+ scores.set(node.documentId, existing);
407
+ });
408
+ // Semantic leg
409
+ for (let rank = 0; rank < semanticResults.length; rank++) {
410
+ const sr = semanticResults[rank];
411
+ const existing = scores.get(sr.documentId) ?? {
412
+ score: 0,
413
+ matchedBy: [],
414
+ };
415
+ existing.score += 1 / (K + rank);
416
+ if (!existing.matchedBy.includes("semantic")) {
417
+ existing.matchedBy.push("semantic");
418
+ }
419
+ // Fetch node data if we don't have it from keyword results
420
+ if (!existing.node) {
421
+ existing.node = await this.nodeByDocumentId(sr.documentId);
422
+ }
423
+ scores.set(sr.documentId, existing);
424
+ }
425
+ return [...scores.values()]
426
+ .filter((e) => !!e.node)
427
+ .sort((a, b) => b.score - a.score)
428
+ .slice(0, limit)
429
+ .map(({ score, matchedBy, node }) => ({ node, score, matchedBy }));
430
+ },
431
+ // ---- Operation history queries ----
432
+ async history(documentId, limit = 50) {
433
+ const rows = await db
434
+ .selectFrom("graph_operations")
435
+ .where("document_id", "=", documentId)
436
+ .orderBy("index", "desc")
437
+ .selectAll()
438
+ .limit(limit)
439
+ .execute();
440
+ return rows.map(rowToOperation);
441
+ },
442
+ async activity(limit = 50, since) {
443
+ let query = db
444
+ .selectFrom("graph_operations")
445
+ .orderBy("timestamp", "desc")
446
+ .selectAll()
447
+ .limit(limit);
448
+ if (since) {
449
+ query = query.where("timestamp", ">", since);
450
+ }
451
+ const rows = await query.execute();
452
+ return rows.map(rowToOperation);
453
+ },
454
+ async activityByType(operationType, limit = 50) {
455
+ const rows = await db
456
+ .selectFrom("graph_operations")
457
+ .where("operation_type", "=", operationType)
458
+ .orderBy("timestamp", "desc")
459
+ .selectAll()
460
+ .limit(limit)
461
+ .execute();
462
+ return rows.map(rowToOperation);
463
+ },
464
+ async staleNodes(since, limit = 50) {
465
+ // Nodes with no operations after `since` but that have neighbors
466
+ // who DO have operations after `since`
467
+ const staleRows = await db
468
+ .selectFrom("graph_nodes")
469
+ .where("document_id", "not in", db
470
+ .selectFrom("graph_operations")
471
+ .where("timestamp", ">", since)
472
+ .select("document_id"))
473
+ .where("updated_at", "<", since)
474
+ .selectAll()
475
+ .limit(limit)
476
+ .execute();
477
+ return staleRows.map(rowToNode);
478
+ },
375
479
  };
376
480
  }
@@ -25,9 +25,22 @@ export interface GraphEdge {
25
25
  target_title: string | null;
26
26
  updated_at: string;
27
27
  }
28
+ export interface GraphOperation {
29
+ id: string;
30
+ document_id: string;
31
+ operation_type: string;
32
+ timestamp: string;
33
+ index: number;
34
+ scope: string;
35
+ summary: string | null;
36
+ input_json: string | null;
37
+ signer_address: string | null;
38
+ signer_app: string | null;
39
+ }
28
40
  export interface DB {
29
41
  graph_nodes: GraphNode;
30
42
  graph_edges: GraphEdge;
31
43
  graph_topics: GraphTopic;
44
+ graph_operations: GraphOperation;
32
45
  }
33
46
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/schema.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,EAAE;IACjB,WAAW,EAAE,SAAS,CAAC;IACvB,WAAW,EAAE,SAAS,CAAC;IACvB,YAAY,EAAE,UAAU,CAAC;CAC1B"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/schema.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,EAAE;IACjB,WAAW,EAAE,SAAS,CAAC;IACvB,WAAW,EAAE,SAAS,CAAC;IACvB,YAAY,EAAE,UAAU,CAAC;IACzB,gBAAgB,EAAE,cAAc,CAAC;CAClC"}
package/dist/style.css CHANGED
@@ -1495,17 +1495,17 @@
1495
1495
  --tw-outline-style: none;
1496
1496
  outline-style: none;
1497
1497
  }
1498
- .group-hover\:inline {
1498
+ .group-hover\:text-\[var\(--bai-accent\)\] {
1499
1499
  &:is(:where(.group):hover *) {
1500
1500
  @media (hover: hover) {
1501
- display: inline;
1501
+ color: var(--bai-accent);
1502
1502
  }
1503
1503
  }
1504
1504
  }
1505
- .group-hover\:text-\[var\(--bai-accent\)\] {
1505
+ .group-hover\:opacity-50 {
1506
1506
  &:is(:where(.group):hover *) {
1507
1507
  @media (hover: hover) {
1508
- color: var(--bai-accent);
1508
+ opacity: 50%;
1509
1509
  }
1510
1510
  }
1511
1511
  }
@@ -360,6 +360,61 @@ export declare class KnowledgeGraphSubgraph extends BaseSubgraph {
360
360
  };
361
361
  similarity: number;
362
362
  }[]>;
363
+ knowledgeGraphHybridSearch: (_: unknown, args: {
364
+ driveId: string;
365
+ query: string;
366
+ limit?: number;
367
+ }) => Promise<{
368
+ node: {
369
+ _driveId: string;
370
+ id: string;
371
+ documentId: string;
372
+ title: string | null;
373
+ description: string | null;
374
+ noteType: string | null;
375
+ status: string | null;
376
+ content: string | null;
377
+ author: string | null;
378
+ sourceOrigin: string | null;
379
+ createdAt: string | null;
380
+ updatedAt: string;
381
+ };
382
+ score: number;
383
+ matchedBy: string[];
384
+ }[]>;
385
+ knowledgeGraphHistory: (_: unknown, args: {
386
+ driveId: string;
387
+ documentId: string;
388
+ limit?: number;
389
+ }) => Promise<import("../../processors/graph-indexer/query.js").OperationRecord[]>;
390
+ knowledgeGraphActivity: (_: unknown, args: {
391
+ driveId: string;
392
+ limit?: number;
393
+ since?: string;
394
+ }) => Promise<import("../../processors/graph-indexer/query.js").OperationRecord[]>;
395
+ knowledgeGraphActivityByType: (_: unknown, args: {
396
+ driveId: string;
397
+ operationType: string;
398
+ limit?: number;
399
+ }) => Promise<import("../../processors/graph-indexer/query.js").OperationRecord[]>;
400
+ knowledgeGraphStale: (_: unknown, args: {
401
+ driveId: string;
402
+ since: string;
403
+ limit?: number;
404
+ }) => Promise<{
405
+ _driveId: string;
406
+ id: string;
407
+ documentId: string;
408
+ title: string | null;
409
+ description: string | null;
410
+ noteType: string | null;
411
+ status: string | null;
412
+ content: string | null;
413
+ author: string | null;
414
+ sourceOrigin: string | null;
415
+ createdAt: string | null;
416
+ updatedAt: string;
417
+ }[]>;
363
418
  knowledgeGraphDebug: (_: unknown, args: {
364
419
  driveId: string;
365
420
  }) => Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"subgraph.d.ts","sourceRoot":"","sources":["../../../subgraphs/knowledge-graph/subgraph.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"subgraph.d.ts","sourceRoot":"","sources":["../../../subgraphs/knowledge-graph/subgraph.ts"],"names":[],"mappings":"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"}
@@ -129,6 +129,32 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
129
129
  documentId: String!
130
130
  limit: Int
131
131
  ): [SemanticResult!]!
132
+ knowledgeGraphHybridSearch(
133
+ driveId: ID!
134
+ query: String!
135
+ limit: Int
136
+ ): [HybridResult!]!
137
+
138
+ knowledgeGraphHistory(
139
+ driveId: ID!
140
+ documentId: String!
141
+ limit: Int
142
+ ): [OperationRecord!]!
143
+ knowledgeGraphActivity(
144
+ driveId: ID!
145
+ limit: Int
146
+ since: String
147
+ ): [OperationRecord!]!
148
+ knowledgeGraphActivityByType(
149
+ driveId: ID!
150
+ operationType: String!
151
+ limit: Int
152
+ ): [OperationRecord!]!
153
+ knowledgeGraphStale(
154
+ driveId: ID!
155
+ since: String!
156
+ limit: Int
157
+ ): [KnowledgeGraphNode!]!
132
158
 
133
159
  """
134
160
  Debug: raw processor DB tables
@@ -147,6 +173,24 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
147
173
  similarity: Float!
148
174
  }
149
175
 
176
+ type HybridResult {
177
+ node: KnowledgeGraphNode!
178
+ score: Float!
179
+ matchedBy: [String!]!
180
+ }
181
+
182
+ type OperationRecord {
183
+ id: String!
184
+ documentId: String!
185
+ operationType: String!
186
+ timestamp: String!
187
+ index: Int!
188
+ scope: String!
189
+ summary: String
190
+ signerAddress: String
191
+ signerApp: String
192
+ }
193
+
150
194
  type GraphDebugInfo {
151
195
  rawNodeCount: Int!
152
196
  rawEdgeCount: Int!
@@ -331,6 +375,34 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
331
375
  }
332
376
  return semanticResults.slice(0, args.limit ?? 10);
333
377
  },
378
+ knowledgeGraphHybridSearch: async (_, args) => {
379
+ const queryEmbedding = await generateEmbedding(args.query);
380
+ const semanticResults = await searchSimilar(queryEmbedding, (args.limit ?? 20) * 2);
381
+ const graphQuery = this.getQuery(args.driveId);
382
+ const hybridResults = await graphQuery.hybridSearch(args.query, semanticResults, args.limit ?? 20);
383
+ return hybridResults.map((r) => ({
384
+ node: { ...r.node, _driveId: args.driveId },
385
+ score: r.score,
386
+ matchedBy: r.matchedBy,
387
+ }));
388
+ },
389
+ knowledgeGraphHistory: async (_, args) => {
390
+ const query = this.getQuery(args.driveId);
391
+ return query.history(args.documentId, args.limit ?? 50);
392
+ },
393
+ knowledgeGraphActivity: async (_, args) => {
394
+ const query = this.getQuery(args.driveId);
395
+ return query.activity(args.limit ?? 50, args.since ?? undefined);
396
+ },
397
+ knowledgeGraphActivityByType: async (_, args) => {
398
+ const query = this.getQuery(args.driveId);
399
+ return query.activityByType(args.operationType, args.limit ?? 50);
400
+ },
401
+ knowledgeGraphStale: async (_, args) => {
402
+ const query = this.getQuery(args.driveId);
403
+ const nodes = await query.staleNodes(args.since, args.limit ?? 50);
404
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
405
+ },
334
406
  knowledgeGraphDebug: async (_, args) => {
335
407
  const namespace = GraphIndexerProcessor.getNamespace(args.driveId);
336
408
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/knowledge-note",
3
3
  "description": "Knowledge Note document model package for Powerhouse",
4
- "version": "1.0.5",
4
+ "version": "1.0.7",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "files": [