wikimem 0.3.0 → 0.8.1

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 (228) hide show
  1. package/CHANGELOG.md +138 -29
  2. package/README.md +171 -309
  3. package/dist/cli/commands/ask.d.ts +3 -0
  4. package/dist/cli/commands/ask.d.ts.map +1 -0
  5. package/dist/cli/commands/ask.js +63 -0
  6. package/dist/cli/commands/ask.js.map +1 -0
  7. package/dist/cli/commands/export.d.ts +3 -0
  8. package/dist/cli/commands/export.d.ts.map +1 -0
  9. package/dist/cli/commands/export.js +108 -0
  10. package/dist/cli/commands/export.js.map +1 -0
  11. package/dist/cli/commands/history.d.ts +3 -0
  12. package/dist/cli/commands/history.d.ts.map +1 -0
  13. package/dist/cli/commands/history.js +61 -0
  14. package/dist/cli/commands/history.js.map +1 -0
  15. package/dist/cli/commands/improve.d.ts.map +1 -1
  16. package/dist/cli/commands/improve.js +4 -3
  17. package/dist/cli/commands/improve.js.map +1 -1
  18. package/dist/cli/commands/ingest.d.ts.map +1 -1
  19. package/dist/cli/commands/ingest.js +5 -4
  20. package/dist/cli/commands/ingest.js.map +1 -1
  21. package/dist/cli/commands/init.d.ts.map +1 -1
  22. package/dist/cli/commands/init.js +337 -81
  23. package/dist/cli/commands/init.js.map +1 -1
  24. package/dist/cli/commands/lint.d.ts.map +1 -1
  25. package/dist/cli/commands/lint.js +4 -3
  26. package/dist/cli/commands/lint.js.map +1 -1
  27. package/dist/cli/commands/mcp.d.ts +3 -0
  28. package/dist/cli/commands/mcp.d.ts.map +1 -0
  29. package/dist/cli/commands/mcp.js +11 -0
  30. package/dist/cli/commands/mcp.js.map +1 -0
  31. package/dist/cli/commands/open.d.ts +3 -0
  32. package/dist/cli/commands/open.d.ts.map +1 -0
  33. package/dist/cli/commands/open.js +36 -0
  34. package/dist/cli/commands/open.js.map +1 -0
  35. package/dist/cli/commands/query.d.ts.map +1 -1
  36. package/dist/cli/commands/query.js +5 -4
  37. package/dist/cli/commands/query.js.map +1 -1
  38. package/dist/cli/commands/search.d.ts +3 -0
  39. package/dist/cli/commands/search.d.ts.map +1 -0
  40. package/dist/cli/commands/search.js +61 -0
  41. package/dist/cli/commands/search.js.map +1 -0
  42. package/dist/cli/commands/serve.d.ts.map +1 -1
  43. package/dist/cli/commands/serve.js +41 -2
  44. package/dist/cli/commands/serve.js.map +1 -1
  45. package/dist/cli/commands/watch.d.ts.map +1 -1
  46. package/dist/cli/commands/watch.js +4 -3
  47. package/dist/cli/commands/watch.js.map +1 -1
  48. package/dist/cli/index.d.ts.map +1 -1
  49. package/dist/cli/index.js +27 -1
  50. package/dist/cli/index.js.map +1 -1
  51. package/dist/core/audit-trail.d.ts +15 -0
  52. package/dist/core/audit-trail.d.ts.map +1 -0
  53. package/dist/core/audit-trail.js +43 -0
  54. package/dist/core/audit-trail.js.map +1 -0
  55. package/dist/core/claude-code.d.ts +10 -0
  56. package/dist/core/claude-code.d.ts.map +1 -0
  57. package/dist/core/claude-code.js +81 -0
  58. package/dist/core/claude-code.js.map +1 -0
  59. package/dist/core/config.d.ts +23 -0
  60. package/dist/core/config.d.ts.map +1 -1
  61. package/dist/core/config.js.map +1 -1
  62. package/dist/core/connectors.d.ts +58 -0
  63. package/dist/core/connectors.d.ts.map +1 -0
  64. package/dist/core/connectors.js +189 -0
  65. package/dist/core/connectors.js.map +1 -0
  66. package/dist/core/folder-scanner.d.ts +10 -0
  67. package/dist/core/folder-scanner.d.ts.map +1 -0
  68. package/dist/core/folder-scanner.js +84 -0
  69. package/dist/core/folder-scanner.js.map +1 -0
  70. package/dist/core/git.d.ts +137 -0
  71. package/dist/core/git.d.ts.map +1 -0
  72. package/dist/core/git.js +520 -0
  73. package/dist/core/git.js.map +1 -0
  74. package/dist/core/history.d.ts +21 -0
  75. package/dist/core/history.d.ts.map +1 -0
  76. package/dist/core/history.js +107 -0
  77. package/dist/core/history.js.map +1 -0
  78. package/dist/core/improve.d.ts.map +1 -1
  79. package/dist/core/improve.js +9 -0
  80. package/dist/core/improve.js.map +1 -1
  81. package/dist/core/ingest.d.ts +1 -0
  82. package/dist/core/ingest.d.ts.map +1 -1
  83. package/dist/core/ingest.js +151 -7
  84. package/dist/core/ingest.js.map +1 -1
  85. package/dist/core/lint.d.ts.map +1 -1
  86. package/dist/core/lint.js +23 -4
  87. package/dist/core/lint.js.map +1 -1
  88. package/dist/core/oauth-defaults.d.ts +31 -0
  89. package/dist/core/oauth-defaults.d.ts.map +1 -0
  90. package/dist/core/oauth-defaults.js +77 -0
  91. package/dist/core/oauth-defaults.js.map +1 -0
  92. package/dist/core/observer.d.ts +94 -0
  93. package/dist/core/observer.d.ts.map +1 -0
  94. package/dist/core/observer.js +492 -0
  95. package/dist/core/observer.js.map +1 -0
  96. package/dist/core/pipeline-events.d.ts +63 -0
  97. package/dist/core/pipeline-events.d.ts.map +1 -0
  98. package/dist/core/pipeline-events.js +109 -0
  99. package/dist/core/pipeline-events.js.map +1 -0
  100. package/dist/core/query.d.ts.map +1 -1
  101. package/dist/core/query.js +16 -8
  102. package/dist/core/query.js.map +1 -1
  103. package/dist/core/scraper.d.ts +41 -0
  104. package/dist/core/scraper.d.ts.map +1 -0
  105. package/dist/core/scraper.js +277 -0
  106. package/dist/core/scraper.js.map +1 -0
  107. package/dist/core/sync/gdrive.d.ts +14 -0
  108. package/dist/core/sync/gdrive.d.ts.map +1 -0
  109. package/dist/core/sync/gdrive.js +205 -0
  110. package/dist/core/sync/gdrive.js.map +1 -0
  111. package/dist/core/sync/github.d.ts +20 -0
  112. package/dist/core/sync/github.d.ts.map +1 -0
  113. package/dist/core/sync/github.js +206 -0
  114. package/dist/core/sync/github.js.map +1 -0
  115. package/dist/core/sync/gmail.d.ts +15 -0
  116. package/dist/core/sync/gmail.d.ts.map +1 -0
  117. package/dist/core/sync/gmail.js +159 -0
  118. package/dist/core/sync/gmail.js.map +1 -0
  119. package/dist/core/sync/index.d.ts +47 -0
  120. package/dist/core/sync/index.d.ts.map +1 -0
  121. package/dist/core/sync/index.js +100 -0
  122. package/dist/core/sync/index.js.map +1 -0
  123. package/dist/core/sync/jira.d.ts +15 -0
  124. package/dist/core/sync/jira.d.ts.map +1 -0
  125. package/dist/core/sync/jira.js +176 -0
  126. package/dist/core/sync/jira.js.map +1 -0
  127. package/dist/core/sync/linear.d.ts +15 -0
  128. package/dist/core/sync/linear.d.ts.map +1 -0
  129. package/dist/core/sync/linear.js +111 -0
  130. package/dist/core/sync/linear.js.map +1 -0
  131. package/dist/core/sync/notion.d.ts +14 -0
  132. package/dist/core/sync/notion.d.ts.map +1 -0
  133. package/dist/core/sync/notion.js +168 -0
  134. package/dist/core/sync/notion.js.map +1 -0
  135. package/dist/core/sync/rss.d.ts +20 -0
  136. package/dist/core/sync/rss.d.ts.map +1 -0
  137. package/dist/core/sync/rss.js +165 -0
  138. package/dist/core/sync/rss.js.map +1 -0
  139. package/dist/core/sync/scheduler.d.ts +31 -0
  140. package/dist/core/sync/scheduler.d.ts.map +1 -0
  141. package/dist/core/sync/scheduler.js +129 -0
  142. package/dist/core/sync/scheduler.js.map +1 -0
  143. package/dist/core/sync/slack.d.ts +16 -0
  144. package/dist/core/sync/slack.d.ts.map +1 -0
  145. package/dist/core/sync/slack.js +173 -0
  146. package/dist/core/sync/slack.js.map +1 -0
  147. package/dist/core/vault.d.ts +22 -0
  148. package/dist/core/vault.d.ts.map +1 -1
  149. package/dist/core/vault.js +65 -0
  150. package/dist/core/vault.js.map +1 -1
  151. package/dist/core/webhooks.d.ts +13 -0
  152. package/dist/core/webhooks.d.ts.map +1 -0
  153. package/dist/core/webhooks.js +206 -0
  154. package/dist/core/webhooks.js.map +1 -0
  155. package/dist/index.js +3 -2
  156. package/dist/index.js.map +1 -1
  157. package/dist/mcp-entry.d.ts +10 -0
  158. package/dist/mcp-entry.d.ts.map +1 -0
  159. package/dist/mcp-entry.js +21 -0
  160. package/dist/mcp-entry.js.map +1 -0
  161. package/dist/mcp-server.d.ts +20 -0
  162. package/dist/mcp-server.d.ts.map +1 -0
  163. package/dist/mcp-server.js +483 -0
  164. package/dist/mcp-server.js.map +1 -0
  165. package/dist/mcp-tools-extended.d.ts +15 -0
  166. package/dist/mcp-tools-extended.d.ts.map +1 -0
  167. package/dist/mcp-tools-extended.js +277 -0
  168. package/dist/mcp-tools-extended.js.map +1 -0
  169. package/dist/processors/audio.d.ts.map +1 -1
  170. package/dist/processors/audio.js +42 -4
  171. package/dist/processors/audio.js.map +1 -1
  172. package/dist/processors/csv.d.ts +18 -0
  173. package/dist/processors/csv.d.ts.map +1 -0
  174. package/dist/processors/csv.js +230 -0
  175. package/dist/processors/csv.js.map +1 -0
  176. package/dist/processors/image.d.ts.map +1 -1
  177. package/dist/processors/image.js +55 -27
  178. package/dist/processors/image.js.map +1 -1
  179. package/dist/processors/pdf.d.ts.map +1 -1
  180. package/dist/processors/pdf.js +6 -3
  181. package/dist/processors/pdf.js.map +1 -1
  182. package/dist/processors/pptx.d.ts +3 -1
  183. package/dist/processors/pptx.d.ts.map +1 -1
  184. package/dist/processors/pptx.js +236 -95
  185. package/dist/processors/pptx.js.map +1 -1
  186. package/dist/processors/url.js +4 -1
  187. package/dist/processors/url.js.map +1 -1
  188. package/dist/processors/xlsx.d.ts +2 -0
  189. package/dist/processors/xlsx.d.ts.map +1 -1
  190. package/dist/processors/xlsx.js +182 -46
  191. package/dist/processors/xlsx.js.map +1 -1
  192. package/dist/providers/claude.d.ts +1 -0
  193. package/dist/providers/claude.d.ts.map +1 -1
  194. package/dist/providers/claude.js +5 -3
  195. package/dist/providers/claude.js.map +1 -1
  196. package/dist/providers/index.d.ts +17 -1
  197. package/dist/providers/index.d.ts.map +1 -1
  198. package/dist/providers/index.js +144 -0
  199. package/dist/providers/index.js.map +1 -1
  200. package/dist/providers/openai.d.ts +1 -0
  201. package/dist/providers/openai.d.ts.map +1 -1
  202. package/dist/providers/openai.js +5 -3
  203. package/dist/providers/openai.js.map +1 -1
  204. package/dist/providers/types.d.ts +18 -0
  205. package/dist/providers/types.d.ts.map +1 -1
  206. package/dist/templates/config-yaml.d.ts.map +1 -1
  207. package/dist/templates/config-yaml.js +12 -1
  208. package/dist/templates/config-yaml.js.map +1 -1
  209. package/dist/templates/source-types.d.ts +33 -0
  210. package/dist/templates/source-types.d.ts.map +1 -0
  211. package/dist/templates/source-types.js +178 -0
  212. package/dist/templates/source-types.js.map +1 -0
  213. package/dist/web/public/index.html +9836 -742
  214. package/dist/web/server.d.ts.map +1 -1
  215. package/dist/web/server.js +2823 -43
  216. package/dist/web/server.js.map +1 -1
  217. package/package.json +10 -4
  218. package/scripts/install.sh +54 -0
  219. package/src/web/public/index.html +9836 -742
  220. package/templates/mcp-config.json +9 -0
  221. package/templates/source-types/article.md +21 -0
  222. package/templates/source-types/book.md +21 -0
  223. package/templates/source-types/paper.md +23 -0
  224. package/templates/source-types/podcast.md +21 -0
  225. package/templates/source-types/raw-notes.md +17 -0
  226. package/templates/source-types/tweet-thread.md +19 -0
  227. package/templates/source-types/video.md +21 -0
  228. package/dist/web/public/public/index.html +0 -946
@@ -1,946 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>wikimem — Mission Control</title>
7
- <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Instrument+Serif&display=swap" rel="stylesheet">
8
- <script src="https://d3js.org/d3.v7.min.js"></script>
9
- <!-- Markdown rendering is built-in, no external deps needed -->
10
- <style>
11
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
12
-
13
- :root {
14
- --bg: #141312;
15
- --bg-card: #1c1b1a;
16
- --bg-hover: #242322;
17
- --border: #2a2928;
18
- --text: #e8e4df;
19
- --text-dim: #8a8580;
20
- --text-muted: #5a5550;
21
- --purple: #6b21a8;
22
- --purple-light: #8B5CF6;
23
- --purple-glow: rgba(139, 92, 246, 0.15);
24
- --green: #22c55e;
25
- --amber: #f59e0b;
26
- --red: #ef4444;
27
- --radius: 12px;
28
- }
29
-
30
- body {
31
- font-family: 'Poppins', system-ui, sans-serif;
32
- background: var(--bg);
33
- color: var(--text);
34
- line-height: 1.6;
35
- min-height: 100vh;
36
- }
37
-
38
- /* Header */
39
- .header {
40
- display: flex;
41
- align-items: center;
42
- justify-content: space-between;
43
- padding: 20px 32px;
44
- border-bottom: 1px solid var(--border);
45
- }
46
-
47
- .header h1 {
48
- font-family: 'Instrument Serif', serif;
49
- font-size: 28px;
50
- font-weight: 400;
51
- letter-spacing: -0.5px;
52
- }
53
-
54
- .header h1 span { color: var(--purple-light); }
55
-
56
- .header-status {
57
- display: flex;
58
- align-items: center;
59
- gap: 8px;
60
- font-size: 13px;
61
- color: var(--text-dim);
62
- }
63
-
64
- .status-dot {
65
- width: 8px;
66
- height: 8px;
67
- border-radius: 50%;
68
- background: var(--green);
69
- animation: pulse 2s infinite;
70
- }
71
-
72
- @keyframes pulse {
73
- 0%, 100% { opacity: 1; }
74
- 50% { opacity: 0.5; }
75
- }
76
-
77
- /* Nav */
78
- .nav {
79
- display: flex;
80
- gap: 4px;
81
- padding: 12px 32px;
82
- border-bottom: 1px solid var(--border);
83
- }
84
-
85
- .nav button {
86
- background: transparent;
87
- border: none;
88
- color: var(--text-dim);
89
- font-family: 'Poppins', sans-serif;
90
- font-size: 13px;
91
- font-weight: 500;
92
- padding: 8px 16px;
93
- border-radius: 8px;
94
- cursor: pointer;
95
- transition: all 0.2s;
96
- }
97
-
98
- .nav button:hover { background: var(--bg-hover); color: var(--text); }
99
- .nav button.active { background: var(--purple-glow); color: var(--purple-light); }
100
-
101
- /* Main Layout */
102
- .main { padding: 24px 32px; }
103
-
104
- /* Cards */
105
- .card {
106
- background: var(--bg-card);
107
- border: 1px solid var(--border);
108
- border-radius: var(--radius);
109
- padding: 24px;
110
- margin-bottom: 20px;
111
- }
112
-
113
- .card-title {
114
- font-family: 'Instrument Serif', serif;
115
- font-size: 20px;
116
- margin-bottom: 16px;
117
- color: var(--text);
118
- }
119
-
120
- /* Dashboard Grid */
121
- .stats-grid {
122
- display: grid;
123
- grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
124
- gap: 16px;
125
- margin-bottom: 24px;
126
- }
127
-
128
- .stat-card {
129
- background: var(--bg-card);
130
- border: 1px solid var(--border);
131
- border-radius: var(--radius);
132
- padding: 20px;
133
- text-align: center;
134
- transition: border-color 0.2s;
135
- }
136
-
137
- .stat-card:hover { border-color: var(--purple); }
138
-
139
- .stat-value {
140
- font-size: 32px;
141
- font-weight: 700;
142
- color: var(--purple-light);
143
- line-height: 1;
144
- }
145
-
146
- .stat-label {
147
- font-size: 12px;
148
- color: var(--text-dim);
149
- margin-top: 6px;
150
- text-transform: uppercase;
151
- letter-spacing: 0.5px;
152
- }
153
-
154
- /* Pages Table */
155
- .pages-table {
156
- width: 100%;
157
- border-collapse: collapse;
158
- }
159
-
160
- .pages-table th {
161
- text-align: left;
162
- font-size: 11px;
163
- font-weight: 600;
164
- text-transform: uppercase;
165
- letter-spacing: 0.5px;
166
- color: var(--text-muted);
167
- padding: 8px 12px;
168
- border-bottom: 1px solid var(--border);
169
- }
170
-
171
- .pages-table td {
172
- padding: 10px 12px;
173
- font-size: 13px;
174
- border-bottom: 1px solid var(--border);
175
- }
176
-
177
- .pages-table tr:hover td { background: var(--bg-hover); }
178
-
179
- .pages-table .title-cell { color: var(--purple-light); cursor: pointer; }
180
- .pages-table .title-cell:hover { text-decoration: underline; }
181
-
182
- .category-badge {
183
- display: inline-block;
184
- padding: 2px 8px;
185
- border-radius: 4px;
186
- font-size: 11px;
187
- font-weight: 500;
188
- background: var(--purple-glow);
189
- color: var(--purple-light);
190
- }
191
-
192
- /* Upload Zone */
193
- .upload-zone {
194
- border: 2px dashed var(--border);
195
- border-radius: var(--radius);
196
- padding: 48px;
197
- text-align: center;
198
- transition: all 0.3s;
199
- cursor: pointer;
200
- }
201
-
202
- .upload-zone.dragover {
203
- border-color: var(--purple-light);
204
- background: var(--purple-glow);
205
- }
206
-
207
- .upload-zone h3 {
208
- font-family: 'Instrument Serif', serif;
209
- font-size: 20px;
210
- margin-bottom: 8px;
211
- }
212
-
213
- .upload-zone p { color: var(--text-dim); font-size: 13px; }
214
-
215
- .upload-list {
216
- margin-top: 16px;
217
- display: flex;
218
- flex-direction: column;
219
- gap: 8px;
220
- }
221
-
222
- .upload-item {
223
- display: flex;
224
- align-items: center;
225
- justify-content: space-between;
226
- padding: 10px 16px;
227
- background: var(--bg-hover);
228
- border-radius: 8px;
229
- font-size: 13px;
230
- }
231
-
232
- .upload-item .status-ok { color: var(--green); }
233
- .upload-item .status-err { color: var(--red); }
234
-
235
- /* Graph Container */
236
- #graph-container {
237
- width: 100%;
238
- height: 500px;
239
- background: var(--bg-card);
240
- border: 1px solid var(--border);
241
- border-radius: var(--radius);
242
- overflow: hidden;
243
- position: relative;
244
- }
245
-
246
- #graph-container svg { width: 100%; height: 100%; }
247
-
248
- .graph-controls {
249
- position: absolute;
250
- top: 12px;
251
- right: 12px;
252
- display: flex;
253
- gap: 6px;
254
- z-index: 10;
255
- }
256
-
257
- .graph-controls button {
258
- background: var(--bg);
259
- border: 1px solid var(--border);
260
- color: var(--text-dim);
261
- width: 32px;
262
- height: 32px;
263
- border-radius: 6px;
264
- cursor: pointer;
265
- font-size: 16px;
266
- display: flex;
267
- align-items: center;
268
- justify-content: center;
269
- transition: all 0.2s;
270
- }
271
-
272
- .graph-controls button:hover { border-color: var(--purple); color: var(--text); }
273
-
274
- .node-tooltip {
275
- position: absolute;
276
- background: var(--bg);
277
- border: 1px solid var(--purple);
278
- border-radius: 8px;
279
- padding: 10px 14px;
280
- font-size: 12px;
281
- pointer-events: none;
282
- display: none;
283
- z-index: 20;
284
- max-width: 200px;
285
- }
286
-
287
- .node-tooltip .tt-title { font-weight: 600; color: var(--purple-light); }
288
- .node-tooltip .tt-meta { color: var(--text-dim); margin-top: 4px; }
289
-
290
- /* Query Interface */
291
- .query-form {
292
- display: flex;
293
- gap: 12px;
294
- margin-bottom: 20px;
295
- }
296
-
297
- .query-input {
298
- flex: 1;
299
- background: var(--bg);
300
- border: 1px solid var(--border);
301
- border-radius: 8px;
302
- padding: 12px 16px;
303
- color: var(--text);
304
- font-family: 'Poppins', sans-serif;
305
- font-size: 14px;
306
- outline: none;
307
- transition: border-color 0.2s;
308
- }
309
-
310
- .query-input:focus { border-color: var(--purple); }
311
-
312
- .query-input::placeholder { color: var(--text-muted); }
313
-
314
- .btn {
315
- background: var(--purple);
316
- border: none;
317
- color: white;
318
- font-family: 'Poppins', sans-serif;
319
- font-size: 13px;
320
- font-weight: 500;
321
- padding: 12px 24px;
322
- border-radius: 8px;
323
- cursor: pointer;
324
- transition: all 0.2s;
325
- }
326
-
327
- .btn:hover { background: #7c3aed; }
328
- .btn:disabled { opacity: 0.5; cursor: not-allowed; }
329
-
330
- .query-result {
331
- background: var(--bg);
332
- border: 1px solid var(--border);
333
- border-radius: var(--radius);
334
- padding: 20px;
335
- font-size: 14px;
336
- line-height: 1.7;
337
- white-space: pre-wrap;
338
- display: none;
339
- }
340
-
341
- .query-result.visible { display: block; }
342
-
343
- .query-sources {
344
- margin-top: 12px;
345
- padding-top: 12px;
346
- border-top: 1px solid var(--border);
347
- font-size: 12px;
348
- color: var(--text-dim);
349
- }
350
-
351
- .query-sources span {
352
- display: inline-block;
353
- margin-right: 8px;
354
- padding: 2px 8px;
355
- background: var(--bg-hover);
356
- border-radius: 4px;
357
- }
358
-
359
- /* Sections */
360
- .section { display: none; }
361
- .section.active { display: block; }
362
-
363
- /* Raw files table */
364
- .raw-table { width: 100%; border-collapse: collapse; margin-top: 16px; }
365
- .raw-table th {
366
- text-align: left;
367
- font-size: 11px;
368
- font-weight: 600;
369
- text-transform: uppercase;
370
- letter-spacing: 0.5px;
371
- color: var(--text-muted);
372
- padding: 8px 12px;
373
- border-bottom: 1px solid var(--border);
374
- }
375
- .raw-table td {
376
- padding: 8px 12px;
377
- font-size: 13px;
378
- border-bottom: 1px solid var(--border);
379
- }
380
-
381
- /* Page Detail Modal */
382
- .modal-overlay {
383
- position: fixed;
384
- inset: 0;
385
- background: rgba(0,0,0,0.6);
386
- display: none;
387
- z-index: 100;
388
- align-items: center;
389
- justify-content: center;
390
- }
391
-
392
- .modal-overlay.visible { display: flex; }
393
-
394
- .modal {
395
- background: var(--bg-card);
396
- border: 1px solid var(--border);
397
- border-radius: var(--radius);
398
- width: 90%;
399
- max-width: 700px;
400
- max-height: 80vh;
401
- overflow-y: auto;
402
- padding: 28px;
403
- }
404
-
405
- .modal-header {
406
- display: flex;
407
- justify-content: space-between;
408
- align-items: center;
409
- margin-bottom: 16px;
410
- }
411
-
412
- .modal-header h2 {
413
- font-family: 'Instrument Serif', serif;
414
- font-size: 24px;
415
- }
416
-
417
- .modal-close {
418
- background: transparent;
419
- border: none;
420
- color: var(--text-dim);
421
- font-size: 20px;
422
- cursor: pointer;
423
- }
424
-
425
- .modal-body {
426
- font-size: 14px;
427
- line-height: 1.7;
428
- color: var(--text);
429
- }
430
- .modal-body h1 { font-family: 'Instrument Serif', serif; font-size: 24px; margin: 24px 0 12px; color: var(--text); border-bottom: 1px solid var(--border); padding-bottom: 8px; }
431
- .modal-body h2 { font-family: 'Instrument Serif', serif; font-size: 20px; margin: 20px 0 10px; color: var(--text); }
432
- .modal-body h3 { font-size: 16px; margin: 16px 0 8px; color: var(--text); }
433
- .modal-body p { margin: 8px 0; color: var(--text-dim); }
434
- .modal-body ul, .modal-body ol { margin: 8px 0 8px 24px; color: var(--text-dim); }
435
- .modal-body li { margin: 4px 0; }
436
- .modal-body code { background: var(--bg); padding: 2px 6px; border-radius: 4px; font-size: 13px; color: var(--purple-light); }
437
- .modal-body pre { background: var(--bg); padding: 16px; border-radius: 8px; overflow-x: auto; margin: 12px 0; }
438
- .modal-body pre code { padding: 0; background: none; }
439
- .modal-body blockquote { border-left: 3px solid var(--purple); padding: 8px 16px; margin: 12px 0; background: var(--purple-glow); border-radius: 0 8px 8px 0; }
440
- .modal-body a { color: var(--purple-light); text-decoration: none; }
441
- .modal-body a:hover { text-decoration: underline; }
442
- .modal-body strong { color: var(--text); }
443
- .wikilink { color: var(--purple-light); cursor: pointer; border-bottom: 1px dashed var(--purple-light); }
444
- .wikilink:hover { color: #a78bfa; }
445
- .tag { background: var(--purple-glow); color: var(--purple-light); padding: 2px 8px; border-radius: 12px; font-size: 11px; }
446
- .type-badge { background: var(--bg); color: var(--amber); padding: 2px 8px; border-radius: 12px; font-size: 11px; border: 1px solid var(--border); }
447
- .source-badge { color: var(--text-muted); font-size: 11px; max-width: 200px; overflow: hidden; text-overflow: ellipsis; }
448
-
449
- .modal-meta {
450
- display: flex;
451
- flex-wrap: wrap;
452
- gap: 8px;
453
- margin-bottom: 16px;
454
- font-size: 12px;
455
- color: var(--text-muted);
456
- align-items: center;
457
- }
458
-
459
- /* Loading spinner */
460
- .spinner {
461
- display: inline-block;
462
- width: 16px;
463
- height: 16px;
464
- border: 2px solid var(--border);
465
- border-top-color: var(--purple-light);
466
- border-radius: 50%;
467
- animation: spin 0.6s linear infinite;
468
- }
469
-
470
- @keyframes spin { to { transform: rotate(360deg); } }
471
-
472
- /* Responsive */
473
- @media (max-width: 768px) {
474
- .header, .nav, .main { padding-left: 16px; padding-right: 16px; }
475
- .stats-grid { grid-template-columns: repeat(2, 1fr); }
476
- #graph-container { height: 350px; }
477
- .query-form { flex-direction: column; }
478
- }
479
- </style>
480
- </head>
481
- <body>
482
- <div class="header">
483
- <h1>wiki<span>mem</span></h1>
484
- <div class="header-status">
485
- <div class="status-dot"></div>
486
- <span id="header-page-count">Loading...</span>
487
- </div>
488
- </div>
489
-
490
- <div class="nav">
491
- <button class="active" data-section="dashboard">Dashboard</button>
492
- <button data-section="pages">Pages</button>
493
- <button data-section="graph">Knowledge Graph</button>
494
- <button data-section="query">Query</button>
495
- <button data-section="upload">Upload</button>
496
- </div>
497
-
498
- <div class="main">
499
- <!-- Dashboard -->
500
- <div class="section active" id="section-dashboard">
501
- <div class="stats-grid" id="stats-grid"></div>
502
- <div class="card">
503
- <div class="card-title">Recent Pages</div>
504
- <table class="pages-table">
505
- <thead><tr><th>Title</th><th>Category</th><th>Words</th><th>Links</th></tr></thead>
506
- <tbody id="recent-pages"></tbody>
507
- </table>
508
- </div>
509
- <div class="card">
510
- <div class="card-title">Raw Sources</div>
511
- <table class="raw-table">
512
- <thead><tr><th>File</th><th>Size</th><th>Modified</th></tr></thead>
513
- <tbody id="raw-files"></tbody>
514
- </table>
515
- </div>
516
- </div>
517
-
518
- <!-- Pages -->
519
- <div class="section" id="section-pages">
520
- <div class="card">
521
- <div class="card-title">All Wiki Pages</div>
522
- <table class="pages-table">
523
- <thead><tr><th>Title</th><th>Category</th><th>Words</th><th>Links</th></tr></thead>
524
- <tbody id="all-pages"></tbody>
525
- </table>
526
- </div>
527
- </div>
528
-
529
- <!-- Graph -->
530
- <div class="section" id="section-graph">
531
- <div id="graph-container">
532
- <div class="graph-controls">
533
- <button id="graph-zoom-in" title="Zoom in">+</button>
534
- <button id="graph-zoom-out" title="Zoom out">−</button>
535
- <button id="graph-reset" title="Reset">⟲</button>
536
- </div>
537
- <div class="node-tooltip" id="tooltip"></div>
538
- <svg id="graph-svg"></svg>
539
- </div>
540
- </div>
541
-
542
- <!-- Query -->
543
- <div class="section" id="section-query">
544
- <div class="card">
545
- <div class="card-title">Ask your knowledge base</div>
546
- <div class="query-form">
547
- <input type="text" class="query-input" id="query-input" placeholder="What do you want to know?" />
548
- <button class="btn" id="query-btn">Ask</button>
549
- </div>
550
- <div class="query-result" id="query-result"></div>
551
- </div>
552
- </div>
553
-
554
- <!-- Upload -->
555
- <div class="section" id="section-upload">
556
- <div class="upload-zone" id="upload-zone">
557
- <h3>Drop files here</h3>
558
- <p>Markdown, PDF, DOCX, XLSX, PPTX, text, or URLs</p>
559
- <p style="margin-top:8px;font-size:12px;color:var(--text-muted);">Click to browse or drag &amp; drop</p>
560
- <input type="file" id="file-input" multiple style="display:none" accept=".md,.txt,.pdf,.docx,.xlsx,.pptx,.csv,.json,.yaml,.yml" />
561
- </div>
562
- <div class="upload-list" id="upload-list"></div>
563
- </div>
564
- </div>
565
-
566
- <!-- Page Detail Modal -->
567
- <div class="modal-overlay" id="page-modal">
568
- <div class="modal">
569
- <div class="modal-header">
570
- <h2 id="modal-title"></h2>
571
- <button class="modal-close" id="modal-close">&times;</button>
572
- </div>
573
- <div class="modal-meta" id="modal-meta"></div>
574
- <div class="modal-body" id="modal-body"></div>
575
- </div>
576
- </div>
577
-
578
- <script>
579
- // ── State ──
580
- let currentSection = 'dashboard';
581
-
582
- // ── Navigation ──
583
- document.querySelectorAll('.nav button').forEach(btn => {
584
- btn.addEventListener('click', () => {
585
- document.querySelectorAll('.nav button').forEach(b => b.classList.remove('active'));
586
- btn.classList.add('active');
587
- document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
588
- const section = btn.dataset.section;
589
- document.getElementById('section-' + section).classList.add('active');
590
- currentSection = section;
591
- if (section === 'graph') initGraph();
592
- });
593
- });
594
-
595
- // ── API Helpers ──
596
- async function api(path) {
597
- const res = await fetch(path);
598
- return res.json();
599
- }
600
-
601
- function formatBytes(bytes) {
602
- if (bytes < 1024) return bytes + ' B';
603
- if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
604
- return (bytes / 1048576).toFixed(1) + ' MB';
605
- }
606
-
607
- function formatDate(iso) {
608
- return new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
609
- }
610
-
611
- // ── Dashboard ──
612
- async function loadDashboard() {
613
- const [stats, pages, raw] = await Promise.all([
614
- api('/api/status'),
615
- api('/api/pages'),
616
- api('/api/raw'),
617
- ]);
618
-
619
- document.getElementById('header-page-count').textContent = stats.pageCount + ' pages · ' + stats.wordCount.toLocaleString() + ' words';
620
-
621
- const statsGrid = document.getElementById('stats-grid');
622
- const statItems = [
623
- { value: stats.pageCount, label: 'Wiki Pages' },
624
- { value: stats.wordCount.toLocaleString(), label: 'Total Words' },
625
- { value: stats.sourceCount, label: 'Raw Sources' },
626
- { value: stats.wikilinks, label: 'Wiki Links' },
627
- { value: stats.orphanPages, label: 'Orphan Pages' },
628
- { value: stats.lastUpdated, label: 'Last Updated' },
629
- ];
630
- statsGrid.innerHTML = statItems.map(s =>
631
- `<div class="stat-card"><div class="stat-value">${s.value}</div><div class="stat-label">${s.label}</div></div>`
632
- ).join('');
633
-
634
- // Recent pages (last 10)
635
- const recentPages = pages.slice(-10).reverse();
636
- document.getElementById('recent-pages').innerHTML = recentPages.map(p =>
637
- `<tr>
638
- <td class="title-cell" data-title="${esc(p.title)}">${esc(p.title)}</td>
639
- <td><span class="category-badge">${esc(p.category)}</span></td>
640
- <td>${p.wordCount}</td>
641
- <td>${p.wikilinks.length}</td>
642
- </tr>`
643
- ).join('');
644
-
645
- // All pages
646
- document.getElementById('all-pages').innerHTML = pages.map(p =>
647
- `<tr>
648
- <td class="title-cell" data-title="${esc(p.title)}">${esc(p.title)}</td>
649
- <td><span class="category-badge">${esc(p.category)}</span></td>
650
- <td>${p.wordCount}</td>
651
- <td>${p.wikilinks.length}</td>
652
- </tr>`
653
- ).join('');
654
-
655
- // Raw files
656
- document.getElementById('raw-files').innerHTML = raw.length === 0
657
- ? '<tr><td colspan="3" style="color:var(--text-muted);text-align:center;padding:20px;">No raw sources yet. Upload files to get started.</td></tr>'
658
- : raw.map(f =>
659
- `<tr><td>${esc(f.name)}</td><td>${formatBytes(f.size)}</td><td>${formatDate(f.modified)}</td></tr>`
660
- ).join('');
661
-
662
- // Click handlers for page titles
663
- document.querySelectorAll('.title-cell').forEach(td => {
664
- td.addEventListener('click', () => openPage(td.dataset.title));
665
- });
666
- }
667
-
668
- function esc(s) { return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
669
-
670
- // ── Simple Markdown Renderer (no external deps) ──
671
- function renderMarkdown(md) {
672
- let html = esc(md);
673
- // Code blocks (fenced)
674
- html = html.replace(/```(\w*)\n([\s\S]*?)```/g, '<pre><code class="lang-$1">$2</code></pre>');
675
- // Headings
676
- html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
677
- html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
678
- html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
679
- // Bold + italic
680
- html = html.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>');
681
- html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
682
- html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
683
- // Inline code
684
- html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
685
- // Blockquotes
686
- html = html.replace(/^&gt; (.+)$/gm, '<blockquote>$1</blockquote>');
687
- // Unordered lists
688
- html = html.replace(/^- (.+)$/gm, '<li>$1</li>');
689
- html = html.replace(/(<li>.*<\/li>\n?)+/g, '<ul>$&</ul>');
690
- // Ordered lists
691
- html = html.replace(/^\d+\. (.+)$/gm, '<li>$1</li>');
692
- // Links
693
- html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
694
- // Horizontal rules
695
- html = html.replace(/^---$/gm, '<hr>');
696
- // Paragraphs (double newline)
697
- html = html.replace(/\n\n/g, '</p><p>');
698
- html = '<p>' + html + '</p>';
699
- // Clean up empty paragraphs
700
- html = html.replace(/<p>\s*<\/p>/g, '');
701
- html = html.replace(/<p>\s*(<h[123]>)/g, '$1');
702
- html = html.replace(/(<\/h[123]>)\s*<\/p>/g, '$1');
703
- html = html.replace(/<p>\s*(<ul>)/g, '$1');
704
- html = html.replace(/(<\/ul>)\s*<\/p>/g, '$1');
705
- html = html.replace(/<p>\s*(<blockquote>)/g, '$1');
706
- html = html.replace(/<p>\s*(<pre>)/g, '$1');
707
- html = html.replace(/<p>\s*(<hr>)/g, '$1');
708
- return html;
709
- }
710
-
711
- function renderWikilinks(html) {
712
- return html.replace(/\[\[([^\]]+)\]\]/g, (_, name) => {
713
- return `<a href="#" class="wikilink" onclick="event.preventDefault(); openPage('${esc(name)}')">${esc(name)}</a>`;
714
- });
715
- }
716
-
717
- async function openPage(title) {
718
- const page = await api('/api/pages/' + encodeURIComponent(title));
719
- document.getElementById('modal-title').textContent = page.title;
720
-
721
- // Show frontmatter as badges
722
- const tags = (page.frontmatter.tags || []).map(t => `<span class="tag">${esc(t)}</span>`).join('');
723
- document.getElementById('modal-meta').innerHTML =
724
- `<span>${page.wordCount} words</span>` +
725
- `<span>${page.wikilinks.length} links</span>` +
726
- (page.frontmatter.type ? `<span class="type-badge">${page.frontmatter.type}</span>` : '') +
727
- (page.frontmatter.created ? `<span>${page.frontmatter.created}</span>` : '') +
728
- tags;
729
-
730
- // Render markdown + wikilinks
731
- const rendered = renderMarkdown(page.content);
732
- document.getElementById('modal-body').innerHTML = renderWikilinks(rendered);
733
- document.getElementById('page-modal').classList.add('visible');
734
- }
735
-
736
- document.getElementById('modal-close').addEventListener('click', () => {
737
- document.getElementById('page-modal').classList.remove('visible');
738
- });
739
- document.getElementById('page-modal').addEventListener('click', (e) => {
740
- if (e.target === e.currentTarget) e.currentTarget.classList.remove('visible');
741
- });
742
-
743
- // ── Knowledge Graph (d3-force) ──
744
- let graphInitialized = false;
745
-
746
- async function initGraph() {
747
- if (graphInitialized) return;
748
- graphInitialized = true;
749
-
750
- const data = await api('/api/graph');
751
- if (data.nodes.length === 0) {
752
- document.getElementById('graph-container').innerHTML =
753
- '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-muted);font-size:14px;">No pages yet. Ingest some content first.</div>';
754
- return;
755
- }
756
-
757
- const container = document.getElementById('graph-container');
758
- const width = container.clientWidth;
759
- const height = container.clientHeight;
760
- const svg = d3.select('#graph-svg');
761
- const tooltip = document.getElementById('tooltip');
762
-
763
- // Category colors
764
- const catColors = {
765
- uncategorized: '#6b21a8',
766
- concept: '#2563eb',
767
- reference: '#059669',
768
- project: '#d97706',
769
- person: '#dc2626',
770
- tool: '#7c3aed',
771
- note: '#6366f1',
772
- };
773
-
774
- function nodeColor(cat) { return catColors[cat] || '#6b21a8'; }
775
-
776
- const g = svg.append('g');
777
-
778
- const zoom = d3.zoom()
779
- .scaleExtent([0.2, 5])
780
- .on('zoom', (e) => g.attr('transform', e.transform));
781
-
782
- svg.call(zoom);
783
-
784
- // Zoom controls
785
- document.getElementById('graph-zoom-in').addEventListener('click', () => svg.transition().call(zoom.scaleBy, 1.3));
786
- document.getElementById('graph-zoom-out').addEventListener('click', () => svg.transition().call(zoom.scaleBy, 0.7));
787
- document.getElementById('graph-reset').addEventListener('click', () => svg.transition().call(zoom.transform, d3.zoomIdentity));
788
-
789
- const simulation = d3.forceSimulation(data.nodes)
790
- .force('link', d3.forceLink(data.links).id(d => d.id).distance(80))
791
- .force('charge', d3.forceManyBody().strength(-200))
792
- .force('center', d3.forceCenter(width / 2, height / 2))
793
- .force('collision', d3.forceCollide().radius(20));
794
-
795
- // Links
796
- const link = g.append('g')
797
- .selectAll('line')
798
- .data(data.links)
799
- .join('line')
800
- .attr('stroke', '#2a2928')
801
- .attr('stroke-width', 1)
802
- .attr('stroke-opacity', 0.6);
803
-
804
- // Nodes
805
- const node = g.append('g')
806
- .selectAll('circle')
807
- .data(data.nodes)
808
- .join('circle')
809
- .attr('r', d => Math.max(6, Math.min(20, Math.sqrt(d.wordCount / 50))))
810
- .attr('fill', d => nodeColor(d.category))
811
- .attr('stroke', '#141312')
812
- .attr('stroke-width', 1.5)
813
- .attr('cursor', 'pointer')
814
- .call(d3.drag()
815
- .on('start', (e, d) => { if (!e.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; })
816
- .on('drag', (e, d) => { d.fx = e.x; d.fy = e.y; })
817
- .on('end', (e, d) => { if (!e.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; })
818
- );
819
-
820
- // Labels
821
- const label = g.append('g')
822
- .selectAll('text')
823
- .data(data.nodes)
824
- .join('text')
825
- .text(d => d.title.length > 20 ? d.title.slice(0, 18) + '…' : d.title)
826
- .attr('font-size', 10)
827
- .attr('font-family', 'Poppins, sans-serif')
828
- .attr('fill', '#8a8580')
829
- .attr('text-anchor', 'middle')
830
- .attr('dy', d => Math.max(6, Math.min(20, Math.sqrt(d.wordCount / 50))) + 14);
831
-
832
- // Tooltip
833
- node.on('mouseover', (e, d) => {
834
- tooltip.style.display = 'block';
835
- tooltip.innerHTML = `<div class="tt-title">${esc(d.title)}</div><div class="tt-meta">${d.wordCount} words · ${d.linksOut} out · ${d.linksIn} in<br>${d.category}</div>`;
836
- }).on('mousemove', (e) => {
837
- tooltip.style.left = (e.offsetX + 12) + 'px';
838
- tooltip.style.top = (e.offsetY - 10) + 'px';
839
- }).on('mouseout', () => {
840
- tooltip.style.display = 'none';
841
- }).on('click', (e, d) => {
842
- openPage(d.id);
843
- });
844
-
845
- simulation.on('tick', () => {
846
- link.attr('x1', d => d.source.x).attr('y1', d => d.source.y)
847
- .attr('x2', d => d.target.x).attr('y2', d => d.target.y);
848
- node.attr('cx', d => d.x).attr('cy', d => d.y);
849
- label.attr('x', d => d.x).attr('y', d => d.y);
850
- });
851
- }
852
-
853
- // ── Query ──
854
- document.getElementById('query-btn').addEventListener('click', submitQuery);
855
- document.getElementById('query-input').addEventListener('keydown', (e) => {
856
- if (e.key === 'Enter') submitQuery();
857
- });
858
-
859
- async function submitQuery() {
860
- const input = document.getElementById('query-input');
861
- const resultEl = document.getElementById('query-result');
862
- const btn = document.getElementById('query-btn');
863
- const question = input.value.trim();
864
- if (!question) return;
865
-
866
- btn.disabled = true;
867
- btn.innerHTML = '<span class="spinner"></span>';
868
- resultEl.classList.add('visible');
869
- resultEl.innerHTML = '<span class="spinner"></span> Searching knowledge base...';
870
-
871
- try {
872
- const res = await fetch('/api/query?q=' + encodeURIComponent(question));
873
- const data = await res.json();
874
- if (data.error) {
875
- resultEl.textContent = 'Error: ' + data.error;
876
- } else {
877
- resultEl.innerHTML = esc(data.answer || 'No answer found.');
878
- if (data.sourcesConsulted && data.sourcesConsulted.length > 0) {
879
- resultEl.innerHTML += '<div class="query-sources">Sources: ' +
880
- data.sourcesConsulted.map(s => '<span>' + esc(s) + '</span>').join('') +
881
- '</div>';
882
- }
883
- }
884
- } catch (err) {
885
- resultEl.textContent = 'Query failed. Make sure you have an LLM provider configured.';
886
- }
887
-
888
- btn.disabled = false;
889
- btn.textContent = 'Ask';
890
- }
891
-
892
- // ── Upload ──
893
- const uploadZone = document.getElementById('upload-zone');
894
- const fileInput = document.getElementById('file-input');
895
- const uploadList = document.getElementById('upload-list');
896
-
897
- uploadZone.addEventListener('click', () => fileInput.click());
898
-
899
- uploadZone.addEventListener('dragover', (e) => {
900
- e.preventDefault();
901
- uploadZone.classList.add('dragover');
902
- });
903
-
904
- uploadZone.addEventListener('dragleave', () => {
905
- uploadZone.classList.remove('dragover');
906
- });
907
-
908
- uploadZone.addEventListener('drop', (e) => {
909
- e.preventDefault();
910
- uploadZone.classList.remove('dragover');
911
- if (e.dataTransfer.files.length > 0) uploadFiles(e.dataTransfer.files);
912
- });
913
-
914
- fileInput.addEventListener('change', () => {
915
- if (fileInput.files.length > 0) uploadFiles(fileInput.files);
916
- });
917
-
918
- async function uploadFiles(files) {
919
- for (const file of files) {
920
- const item = document.createElement('div');
921
- item.className = 'upload-item';
922
- item.innerHTML = `<span>${esc(file.name)}</span><span class="spinner"></span>`;
923
- uploadList.prepend(item);
924
-
925
- try {
926
- const buf = await file.arrayBuffer();
927
- const res = await fetch('/api/upload', {
928
- method: 'POST',
929
- headers: { 'Content-Type': 'application/octet-stream', 'X-Filename': file.name },
930
- body: buf,
931
- });
932
- const data = await res.json();
933
- item.innerHTML = `<span>${esc(file.name)} (${formatBytes(file.size)})</span><span class="status-ok">uploaded</span>`;
934
- } catch (err) {
935
- item.innerHTML = `<span>${esc(file.name)}</span><span class="status-err">failed</span>`;
936
- }
937
- }
938
- // Refresh dashboard
939
- loadDashboard();
940
- }
941
-
942
- // ── Init ──
943
- loadDashboard();
944
- </script>
945
- </body>
946
- </html>