@vheins/local-memory-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/DASHBOARD.md +129 -0
  2. package/HYBRID_SEARCH.md +204 -0
  3. package/IMPLEMENTATION.md +159 -0
  4. package/README.md +175 -0
  5. package/dist/capabilities.d.ts +22 -0
  6. package/dist/capabilities.d.ts.map +1 -0
  7. package/dist/capabilities.js +23 -0
  8. package/dist/capabilities.js.map +1 -0
  9. package/dist/dashboard/dashboard.test.d.ts +2 -0
  10. package/dist/dashboard/dashboard.test.d.ts.map +1 -0
  11. package/dist/dashboard/dashboard.test.js +362 -0
  12. package/dist/dashboard/dashboard.test.js.map +1 -0
  13. package/dist/dashboard/public/app.js +1187 -0
  14. package/dist/dashboard/public/chart.js +0 -0
  15. package/dist/dashboard/public/index.html +967 -0
  16. package/dist/dashboard/server.d.ts +3 -0
  17. package/dist/dashboard/server.d.ts.map +1 -0
  18. package/dist/dashboard/server.js +297 -0
  19. package/dist/dashboard/server.js.map +1 -0
  20. package/dist/mcp/client.d.ts +34 -0
  21. package/dist/mcp/client.d.ts.map +1 -0
  22. package/dist/mcp/client.js +181 -0
  23. package/dist/mcp/client.js.map +1 -0
  24. package/dist/mcp/client.test.d.ts +2 -0
  25. package/dist/mcp/client.test.d.ts.map +1 -0
  26. package/dist/mcp/client.test.js +130 -0
  27. package/dist/mcp/client.test.js.map +1 -0
  28. package/dist/prompts/registry.d.ts +39 -0
  29. package/dist/prompts/registry.d.ts.map +1 -0
  30. package/dist/prompts/registry.js +90 -0
  31. package/dist/prompts/registry.js.map +1 -0
  32. package/dist/resources/index.d.ts +17 -0
  33. package/dist/resources/index.d.ts.map +1 -0
  34. package/dist/resources/index.js +100 -0
  35. package/dist/resources/index.js.map +1 -0
  36. package/dist/resources/index.test.d.ts +2 -0
  37. package/dist/resources/index.test.d.ts.map +1 -0
  38. package/dist/resources/index.test.js +96 -0
  39. package/dist/resources/index.test.js.map +1 -0
  40. package/dist/router.d.ts +4 -0
  41. package/dist/router.d.ts.map +1 -0
  42. package/dist/router.js +60 -0
  43. package/dist/router.js.map +1 -0
  44. package/dist/router.test.d.ts +2 -0
  45. package/dist/router.test.d.ts.map +1 -0
  46. package/dist/router.test.js +113 -0
  47. package/dist/router.test.js.map +1 -0
  48. package/dist/search_memory_example.d.ts +3 -0
  49. package/dist/search_memory_example.d.ts.map +1 -0
  50. package/dist/search_memory_example.js +56 -0
  51. package/dist/search_memory_example.js.map +1 -0
  52. package/dist/server.d.ts +3 -0
  53. package/dist/server.d.ts.map +1 -0
  54. package/dist/server.js +91 -0
  55. package/dist/server.js.map +1 -0
  56. package/dist/storage/sqlite.d.ts +95 -0
  57. package/dist/storage/sqlite.d.ts.map +1 -0
  58. package/dist/storage/sqlite.js +537 -0
  59. package/dist/storage/sqlite.js.map +1 -0
  60. package/dist/storage/sqlite.test.d.ts +2 -0
  61. package/dist/storage/sqlite.test.d.ts.map +1 -0
  62. package/dist/storage/sqlite.test.js +358 -0
  63. package/dist/storage/sqlite.test.js.map +1 -0
  64. package/dist/storage/vectors.stub.d.ts +12 -0
  65. package/dist/storage/vectors.stub.d.ts.map +1 -0
  66. package/dist/storage/vectors.stub.js +88 -0
  67. package/dist/storage/vectors.stub.js.map +1 -0
  68. package/dist/store_memory_example.d.ts +3 -0
  69. package/dist/store_memory_example.d.ts.map +1 -0
  70. package/dist/store_memory_example.js +69 -0
  71. package/dist/store_memory_example.js.map +1 -0
  72. package/dist/test_quotes_client.d.ts +3 -0
  73. package/dist/test_quotes_client.d.ts.map +1 -0
  74. package/dist/test_quotes_client.js +72 -0
  75. package/dist/test_quotes_client.js.map +1 -0
  76. package/dist/tools/memory.delete.d.ts +9 -0
  77. package/dist/tools/memory.delete.d.ts.map +1 -0
  78. package/dist/tools/memory.delete.js +22 -0
  79. package/dist/tools/memory.delete.js.map +1 -0
  80. package/dist/tools/memory.recap.d.ts +4 -0
  81. package/dist/tools/memory.recap.d.ts.map +1 -0
  82. package/dist/tools/memory.recap.js +42 -0
  83. package/dist/tools/memory.recap.js.map +1 -0
  84. package/dist/tools/memory.search.d.ts +5 -0
  85. package/dist/tools/memory.search.d.ts.map +1 -0
  86. package/dist/tools/memory.search.js +192 -0
  87. package/dist/tools/memory.search.js.map +1 -0
  88. package/dist/tools/memory.search.test.d.ts +2 -0
  89. package/dist/tools/memory.search.test.d.ts.map +1 -0
  90. package/dist/tools/memory.search.test.js +181 -0
  91. package/dist/tools/memory.search.test.js.map +1 -0
  92. package/dist/tools/memory.store.d.ts +5 -0
  93. package/dist/tools/memory.store.d.ts.map +1 -0
  94. package/dist/tools/memory.store.js +41 -0
  95. package/dist/tools/memory.store.js.map +1 -0
  96. package/dist/tools/memory.summarize.d.ts +4 -0
  97. package/dist/tools/memory.summarize.d.ts.map +1 -0
  98. package/dist/tools/memory.summarize.js +13 -0
  99. package/dist/tools/memory.summarize.js.map +1 -0
  100. package/dist/tools/memory.update.d.ts +5 -0
  101. package/dist/tools/memory.update.d.ts.map +1 -0
  102. package/dist/tools/memory.update.js +31 -0
  103. package/dist/tools/memory.update.js.map +1 -0
  104. package/dist/tools/schemas.d.ts +334 -0
  105. package/dist/tools/schemas.d.ts.map +1 -0
  106. package/dist/tools/schemas.js +251 -0
  107. package/dist/tools/schemas.js.map +1 -0
  108. package/dist/types.d.ts +31 -0
  109. package/dist/types.d.ts.map +1 -0
  110. package/dist/types.js +3 -0
  111. package/dist/types.js.map +1 -0
  112. package/dist/utils/git-scope.d.ts +8 -0
  113. package/dist/utils/git-scope.d.ts.map +1 -0
  114. package/dist/utils/git-scope.js +38 -0
  115. package/dist/utils/git-scope.js.map +1 -0
  116. package/dist/utils/logger.d.ts +7 -0
  117. package/dist/utils/logger.d.ts.map +1 -0
  118. package/dist/utils/logger.js +40 -0
  119. package/dist/utils/logger.js.map +1 -0
  120. package/dist/utils/logger.test.d.ts +2 -0
  121. package/dist/utils/logger.test.d.ts.map +1 -0
  122. package/dist/utils/logger.test.js +84 -0
  123. package/dist/utils/logger.test.js.map +1 -0
  124. package/dist/utils/mcp-response.d.ts +44 -0
  125. package/dist/utils/mcp-response.d.ts.map +1 -0
  126. package/dist/utils/mcp-response.js +81 -0
  127. package/dist/utils/mcp-response.js.map +1 -0
  128. package/dist/utils/normalize.d.ts +4 -0
  129. package/dist/utils/normalize.d.ts.map +1 -0
  130. package/dist/utils/normalize.js +51 -0
  131. package/dist/utils/normalize.js.map +1 -0
  132. package/dist/utils/normalize.test.d.ts +2 -0
  133. package/dist/utils/normalize.test.d.ts.map +1 -0
  134. package/dist/utils/normalize.test.js +159 -0
  135. package/dist/utils/normalize.test.js.map +1 -0
  136. package/dist/utils/query-expander.d.ts +2 -0
  137. package/dist/utils/query-expander.d.ts.map +1 -0
  138. package/dist/utils/query-expander.js +50 -0
  139. package/dist/utils/query-expander.js.map +1 -0
  140. package/dist/utils/query-expander.test.d.ts +2 -0
  141. package/dist/utils/query-expander.test.d.ts.map +1 -0
  142. package/dist/utils/query-expander.test.js +35 -0
  143. package/dist/utils/query-expander.test.js.map +1 -0
  144. package/docs/PRD.md +199 -0
  145. package/docs/PROMPT-agent.md +139 -0
  146. package/docs/SPEC-git-scope.md +172 -0
  147. package/docs/SPEC-heuristics.md +199 -0
  148. package/docs/SPEC-server.md +243 -0
  149. package/docs/SPEC-skeleton.md +255 -0
  150. package/docs/SPEC-sqlite-schema.md +183 -0
  151. package/docs/SPEC-tool-schema.md +201 -0
  152. package/docs/SPEC-vector-search.md +198 -0
  153. package/docs/TEST-scenarios.md +179 -0
  154. package/package.json +43 -0
  155. package/scripts/update-null-titles-ai.mjs +272 -0
  156. package/scripts/update-titles-batch.mjs +71 -0
  157. package/scripts/update-titles.mjs +66 -0
  158. package/seed-data.mjs +151 -0
  159. package/src/capabilities.ts +22 -0
  160. package/src/dashboard/dashboard.test.ts +546 -0
  161. package/src/dashboard/public/app.js +1187 -0
  162. package/src/dashboard/public/chart.js +0 -0
  163. package/src/dashboard/public/index.html +967 -0
  164. package/src/dashboard/server.ts +347 -0
  165. package/src/mcp/client.test.ts +164 -0
  166. package/src/mcp/client.ts +212 -0
  167. package/src/prompts/registry.ts +89 -0
  168. package/src/resources/index.test.ts +132 -0
  169. package/src/resources/index.ts +113 -0
  170. package/src/router.test.ts +145 -0
  171. package/src/router.ts +80 -0
  172. package/src/server.ts +99 -0
  173. package/src/storage/sqlite.test.ts +504 -0
  174. package/src/storage/sqlite.ts +688 -0
  175. package/src/storage/vectors.stub.ts +101 -0
  176. package/src/tools/memory.delete.ts +37 -0
  177. package/src/tools/memory.recap.ts +61 -0
  178. package/src/tools/memory.search.test.ts +276 -0
  179. package/src/tools/memory.search.ts +244 -0
  180. package/src/tools/memory.store.ts +56 -0
  181. package/src/tools/memory.summarize.ts +23 -0
  182. package/src/tools/memory.update.ts +46 -0
  183. package/src/tools/schemas.ts +261 -0
  184. package/src/types.ts +36 -0
  185. package/src/utils/git-scope.ts +42 -0
  186. package/src/utils/logger.test.ts +125 -0
  187. package/src/utils/logger.ts +53 -0
  188. package/src/utils/mcp-response.ts +116 -0
  189. package/src/utils/normalize.test.ts +203 -0
  190. package/src/utils/normalize.ts +53 -0
  191. package/src/utils/query-expander.test.ts +40 -0
  192. package/src/utils/query-expander.ts +60 -0
  193. package/storage/.gitkeep +5 -0
  194. package/test.sh +48 -0
  195. package/tsconfig.json +21 -0
  196. package/vitest.config.ts +10 -0
@@ -0,0 +1,967 @@
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>MCP Memory Dashboard</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.js" onerror="window.Chart = null;"></script>
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script>
10
+ tailwind.config = {
11
+ darkMode: 'class',
12
+ theme: {
13
+ extend: {
14
+ colors: {
15
+ decision: { bg: '#e3f2fd', text: '#1976d2', dark: '#1a3a5c', 'dark-text': '#64b5f6' },
16
+ mistake: { bg: '#ffebee', text: '#d32f2f', dark: '#5c1a1a', 'dark-text': '#ef9a9a' },
17
+ code_fact: { bg: '#f3e5f5', text: '#7b1fa2', dark: '#3a1a4c', 'dark-text': '#ce93d8' },
18
+ pattern: { bg: '#e8f5e9', text: '#388e3c', dark: '#1a3a1a', 'dark-text': '#a5d6a7' },
19
+ }
20
+ }
21
+ }
22
+ }
23
+ </script>
24
+ <style>
25
+ :root {
26
+ --glass-bg: rgba(255, 255, 255, 0.58);
27
+ --glass-bg-strong: rgba(255, 255, 255, 0.76);
28
+ --glass-border: rgba(255, 255, 255, 0.34);
29
+ --glass-shadow: 0 28px 90px rgba(15, 23, 42, 0.14);
30
+ --glow-blue: rgba(56, 189, 248, 0.24);
31
+ --glow-cyan: rgba(34, 211, 238, 0.18);
32
+ --glow-violet: rgba(129, 140, 248, 0.14);
33
+ --panel-dark: rgba(7, 15, 28, 0.72);
34
+ --panel-dark-strong: rgba(6, 12, 24, 0.9);
35
+ --panel-dark-border: rgba(148, 163, 184, 0.12);
36
+ --panel-dark-highlight: rgba(125, 211, 252, 0.16);
37
+ }
38
+
39
+ html {
40
+ background:
41
+ radial-gradient(circle at 14% 16%, rgba(125, 211, 252, 0.4), transparent 0 24rem),
42
+ radial-gradient(circle at 88% 12%, rgba(59, 130, 246, 0.2), transparent 0 18rem),
43
+ radial-gradient(circle at 82% 82%, rgba(99, 102, 241, 0.14), transparent 0 20rem),
44
+ linear-gradient(180deg, #f4fbff 0%, #eef5ff 38%, #f7faff 100%);
45
+ }
46
+
47
+ body {
48
+ position: relative;
49
+ min-height: 100vh;
50
+ background: transparent !important;
51
+ color: #0f172a;
52
+ }
53
+
54
+ body::before,
55
+ body::after {
56
+ content: "";
57
+ position: fixed;
58
+ inset: auto;
59
+ pointer-events: none;
60
+ z-index: 0;
61
+ border-radius: 9999px;
62
+ filter: blur(70px);
63
+ opacity: 0.55;
64
+ }
65
+
66
+ body::before {
67
+ width: 28rem;
68
+ height: 28rem;
69
+ top: 3rem;
70
+ left: -7rem;
71
+ background: rgba(56, 189, 248, 0.16);
72
+ }
73
+
74
+ body::after {
75
+ width: 26rem;
76
+ height: 26rem;
77
+ right: -5rem;
78
+ bottom: 2rem;
79
+ background: rgba(99, 102, 241, 0.12);
80
+ }
81
+
82
+ .dark html {
83
+ background:
84
+ radial-gradient(circle at 14% 18%, rgba(34, 211, 238, 0.16), transparent 0 18rem),
85
+ radial-gradient(circle at 85% 12%, rgba(59, 130, 246, 0.14), transparent 0 18rem),
86
+ radial-gradient(circle at 78% 84%, rgba(99, 102, 241, 0.14), transparent 0 20rem),
87
+ linear-gradient(180deg, #020617 0%, #081120 34%, #0b1324 100%);
88
+ }
89
+
90
+ .dark body {
91
+ color: #e2e8f0;
92
+ }
93
+
94
+ .dark body::before {
95
+ background: rgba(34, 211, 238, 0.14);
96
+ opacity: 0.65;
97
+ }
98
+
99
+ .dark body::after {
100
+ background: rgba(99, 102, 241, 0.16);
101
+ opacity: 0.55;
102
+ }
103
+
104
+ header,
105
+ main .rounded-lg,
106
+ #bulkActionBar,
107
+ #memoryDrawer aside {
108
+ position: relative;
109
+ background: var(--glass-bg) !important;
110
+ border: 1px solid var(--glass-border) !important;
111
+ box-shadow: var(--glass-shadow);
112
+ backdrop-filter: blur(24px) saturate(1.15);
113
+ -webkit-backdrop-filter: blur(24px) saturate(1.15);
114
+ }
115
+
116
+ .dark header,
117
+ .dark main .rounded-lg,
118
+ .dark #bulkActionBar,
119
+ .dark #memoryDrawer aside {
120
+ background: var(--panel-dark) !important;
121
+ border-color: var(--panel-dark-border) !important;
122
+ box-shadow:
123
+ 0 28px 90px rgba(2, 6, 23, 0.55),
124
+ inset 0 1px 0 rgba(148, 163, 184, 0.05);
125
+ }
126
+
127
+ header {
128
+ background: var(--glass-bg-strong) !important;
129
+ }
130
+
131
+ .dark header {
132
+ background: var(--panel-dark-strong) !important;
133
+ }
134
+
135
+ main,
136
+ header {
137
+ z-index: 1;
138
+ }
139
+
140
+ canvas {
141
+ filter: saturate(1.08);
142
+ }
143
+
144
+ input,
145
+ select,
146
+ textarea {
147
+ background: rgba(255, 255, 255, 0.68) !important;
148
+ border-color: rgba(148, 163, 184, 0.24) !important;
149
+ backdrop-filter: blur(14px);
150
+ -webkit-backdrop-filter: blur(14px);
151
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
152
+ }
153
+
154
+ .dark input,
155
+ .dark select,
156
+ .dark textarea {
157
+ background: rgba(9, 17, 31, 0.86) !important;
158
+ border-color: rgba(125, 211, 252, 0.12) !important;
159
+ box-shadow:
160
+ inset 0 1px 0 rgba(255, 255, 255, 0.03),
161
+ 0 0 0 1px rgba(2, 132, 199, 0.06);
162
+ }
163
+
164
+ button {
165
+ transition: transform 160ms ease, box-shadow 160ms ease, filter 160ms ease;
166
+ }
167
+
168
+ button:hover {
169
+ transform: translateY(-1px);
170
+ box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);
171
+ }
172
+
173
+ .dark button:hover {
174
+ box-shadow: 0 16px 36px rgba(2, 6, 23, 0.4);
175
+ }
176
+
177
+ button[class*="bg-blue-600"],
178
+ button[class*="bg-gray-600"],
179
+ button[class*="bg-red-600"] {
180
+ border: 1px solid rgba(255, 255, 255, 0.16);
181
+ }
182
+
183
+ table {
184
+ border-spacing: 0 0.35rem;
185
+ border-collapse: separate;
186
+ }
187
+
188
+ tbody tr {
189
+ background: rgba(255, 255, 255, 0.52);
190
+ }
191
+
192
+ .dark tbody tr {
193
+ background: rgba(8, 15, 28, 0.8);
194
+ box-shadow: inset 0 1px 0 rgba(125, 211, 252, 0.04);
195
+ }
196
+
197
+ thead th {
198
+ background: rgba(255, 255, 255, 0.72) !important;
199
+ backdrop-filter: blur(18px) saturate(1.1);
200
+ -webkit-backdrop-filter: blur(18px) saturate(1.1);
201
+ }
202
+
203
+ .dark thead th {
204
+ background: rgba(5, 11, 22, 0.92) !important;
205
+ color: #cbd5e1;
206
+ border-bottom-color: rgba(125, 211, 252, 0.12);
207
+ }
208
+
209
+ .dark [data-theme="light"] { display: none; }
210
+ :not(.dark) [data-theme="dark"] { display: none; }
211
+
212
+ .type-decision { background: #e3f2fd; color: #1976d2; }
213
+ .type-mistake { background: #ffebee; color: #d32f2f; }
214
+ .type-code_fact { background: #f3e5f5; color: #7b1fa2; }
215
+ .type-pattern { background: #e8f5e9; color: #388e3c; }
216
+
217
+ .dark .type-decision { background: #1a3a5c; color: #64b5f6; }
218
+ .dark .type-mistake { background: #5c1a1a; color: #ef9a9a; }
219
+ .dark .type-code_fact { background: #3a1a4c; color: #ce93d8; }
220
+ .dark .type-pattern { background: #1a3a1a; color: #a5d6a7; }
221
+
222
+ .countdown-fill { transition: width 1s linear; }
223
+
224
+ tr.expanded { background: #f9f9f9; }
225
+ .dark tr.expanded { background: #333; }
226
+
227
+ tr.expanded td { border-bottom: none; }
228
+
229
+ mark { background: #ffeb3b; color: #333; padding: 0 2px; border-radius: 2px; }
230
+ .dark mark { background: #ffc107; color: #000; }
231
+
232
+ @keyframes slideIn {
233
+ from { transform: translateX(100%); opacity: 0; }
234
+ to { transform: translateX(0); opacity: 1; }
235
+ }
236
+ @keyframes slideOut {
237
+ from { transform: translateX(0); opacity: 1; }
238
+ to { transform: translateX(100%); opacity: 0; }
239
+ }
240
+ .toast.removing { animation: slideOut 0.3s ease forwards; }
241
+ .toast { animation: slideIn 0.3s ease; }
242
+ .memory-preview {
243
+ display: -webkit-box;
244
+ -webkit-line-clamp: 2;
245
+ -webkit-box-orient: vertical;
246
+ overflow: hidden;
247
+ }
248
+ .table-chip {
249
+ display: inline-flex;
250
+ align-items: center;
251
+ gap: 0.35rem;
252
+ border-radius: 9999px;
253
+ padding: 0.2rem 0.6rem;
254
+ font-size: 0.75rem;
255
+ font-weight: 600;
256
+ line-height: 1;
257
+ }
258
+ .metric-badge {
259
+ display: inline-flex;
260
+ align-items: center;
261
+ justify-content: center;
262
+ min-width: 2rem;
263
+ border-radius: 9999px;
264
+ padding: 0.2rem 0.55rem;
265
+ font-size: 0.75rem;
266
+ font-weight: 700;
267
+ }
268
+ .sticky-table-header thead th {
269
+ position: sticky;
270
+ top: 0;
271
+ z-index: 5;
272
+ }
273
+ .sticky-actions {
274
+ position: sticky;
275
+ right: 0;
276
+ z-index: 4;
277
+ background: rgba(255, 255, 255, 0.78);
278
+ }
279
+ .dark .sticky-actions {
280
+ background: rgba(5, 11, 22, 0.94);
281
+ }
282
+ .app-shell {
283
+ position: relative;
284
+ z-index: 1;
285
+ }
286
+ .app-layout {
287
+ display: grid;
288
+ grid-template-columns: 280px minmax(0, 1fr);
289
+ gap: 1.5rem;
290
+ align-items: start;
291
+ }
292
+ .app-layout.repo-sidebar-collapsed {
293
+ grid-template-columns: 88px minmax(0, 1fr);
294
+ }
295
+ .repo-sidebar {
296
+ position: sticky;
297
+ top: 6.5rem;
298
+ max-height: calc(100vh - 8rem);
299
+ display: flex;
300
+ flex-direction: column;
301
+ min-height: 0;
302
+ transition: width 180ms ease, padding 180ms ease, transform 180ms ease;
303
+ }
304
+ .repo-sidebar-list {
305
+ flex: 1 1 auto;
306
+ min-height: 0;
307
+ overflow-y: auto;
308
+ padding-right: 0.35rem;
309
+ scrollbar-width: thin;
310
+ scrollbar-color: rgba(56, 189, 248, 0.65) rgba(255, 255, 255, 0.08);
311
+ }
312
+ .repo-sidebar-list::-webkit-scrollbar {
313
+ width: 12px;
314
+ }
315
+ .repo-sidebar-list::-webkit-scrollbar-track {
316
+ background: linear-gradient(180deg, rgba(255,255,255,0.16), rgba(255,255,255,0.04));
317
+ border-radius: 9999px;
318
+ border: 1px solid rgba(255,255,255,0.18);
319
+ box-shadow: inset 0 0 0 1px rgba(255,255,255,0.05);
320
+ }
321
+ .repo-sidebar-list::-webkit-scrollbar-thumb {
322
+ background: linear-gradient(180deg, rgba(34,211,238,0.92), rgba(59,130,246,0.78));
323
+ border-radius: 9999px;
324
+ border: 2px solid rgba(245, 250, 255, 0.72);
325
+ box-shadow:
326
+ inset 0 1px 0 rgba(255,255,255,0.42),
327
+ 0 6px 18px rgba(56, 189, 248, 0.28);
328
+ }
329
+ .repo-sidebar-list::-webkit-scrollbar-thumb:hover {
330
+ background: linear-gradient(180deg, rgba(103,232,249,0.96), rgba(37,99,235,0.86));
331
+ }
332
+ .repo-sidebar-list::-webkit-scrollbar-corner {
333
+ background: transparent;
334
+ }
335
+ .dark .repo-sidebar-list {
336
+ scrollbar-color: rgba(34, 211, 238, 0.72) rgba(15, 23, 42, 0.24);
337
+ }
338
+ .dark .repo-sidebar-list::-webkit-scrollbar-track {
339
+ background: linear-gradient(180deg, rgba(15,23,42,0.72), rgba(8,15,28,0.42));
340
+ border-radius: 9999px;
341
+ border: 1px solid rgba(125, 211, 252, 0.08);
342
+ box-shadow:
343
+ inset 0 0 0 1px rgba(125, 211, 252, 0.04),
344
+ inset 0 10px 24px rgba(2, 6, 23, 0.24);
345
+ }
346
+ .dark .repo-sidebar-list::-webkit-scrollbar-thumb {
347
+ background: linear-gradient(180deg, rgba(34,211,238,0.86), rgba(79,70,229,0.72));
348
+ border: 2px solid rgba(8, 15, 28, 0.86);
349
+ box-shadow:
350
+ inset 0 1px 0 rgba(255,255,255,0.14),
351
+ 0 8px 22px rgba(34, 211, 238, 0.18);
352
+ }
353
+ .dark .repo-sidebar-list::-webkit-scrollbar-thumb:hover {
354
+ background: linear-gradient(180deg, rgba(103,232,249,0.92), rgba(99,102,241,0.82));
355
+ }
356
+ .repo-collapse-trigger {
357
+ width: 2rem;
358
+ height: 2rem;
359
+ border-radius: 9999px;
360
+ display: inline-flex;
361
+ align-items: center;
362
+ justify-content: center;
363
+ border: 1px solid rgba(148, 163, 184, 0.2);
364
+ background: rgba(255,255,255,0.42);
365
+ }
366
+ .dark .repo-collapse-trigger {
367
+ background: rgba(8, 15, 28, 0.76);
368
+ border-color: rgba(125, 211, 252, 0.12);
369
+ }
370
+ .repo-collapsed-summary {
371
+ display: none;
372
+ align-items: center;
373
+ justify-content: center;
374
+ margin-bottom: 0.85rem;
375
+ }
376
+ .repo-collapsed-summary-button {
377
+ position: relative;
378
+ width: 3rem;
379
+ height: 3rem;
380
+ border-radius: 1rem;
381
+ display: inline-flex;
382
+ align-items: center;
383
+ justify-content: center;
384
+ color: white;
385
+ font-size: 0.9rem;
386
+ font-weight: 800;
387
+ letter-spacing: 0.05em;
388
+ background: linear-gradient(135deg, rgba(34,211,238,0.96), rgba(59,130,246,0.78));
389
+ box-shadow:
390
+ inset 0 1px 0 rgba(255,255,255,0.32),
391
+ 0 18px 32px rgba(56, 189, 248, 0.2);
392
+ }
393
+ .repo-collapsed-summary-count {
394
+ position: absolute;
395
+ right: -0.15rem;
396
+ bottom: -0.15rem;
397
+ min-width: 1.15rem;
398
+ height: 1.15rem;
399
+ padding: 0 0.22rem;
400
+ border-radius: 9999px;
401
+ display: inline-flex;
402
+ align-items: center;
403
+ justify-content: center;
404
+ font-size: 0.6rem;
405
+ font-weight: 800;
406
+ color: #0f172a;
407
+ background: rgba(255,255,255,0.9);
408
+ box-shadow: 0 4px 12px rgba(15, 23, 42, 0.16);
409
+ }
410
+ .repo-sidebar-mobile {
411
+ display: none;
412
+ }
413
+ .repo-item {
414
+ position: relative;
415
+ width: 100%;
416
+ display: flex;
417
+ align-items: center;
418
+ gap: 0.85rem;
419
+ padding: 0.85rem 0.95rem;
420
+ border-radius: 1rem;
421
+ border: 1px solid transparent;
422
+ background: rgba(255,255,255,0.3);
423
+ text-align: left;
424
+ }
425
+ .repo-item:hover {
426
+ transform: translateY(0);
427
+ border-color: rgba(125, 211, 252, 0.28);
428
+ background: rgba(255,255,255,0.46);
429
+ }
430
+ .repo-item.active {
431
+ border-color: rgba(56, 189, 248, 0.34);
432
+ background: linear-gradient(135deg, rgba(56,189,248,0.16), rgba(99,102,241,0.12));
433
+ box-shadow: 0 18px 42px rgba(56, 189, 248, 0.16);
434
+ }
435
+ .repo-item.pinned-item {
436
+ cursor: grab;
437
+ }
438
+ .repo-item.dragging {
439
+ opacity: 0.58;
440
+ transform: scale(0.985);
441
+ }
442
+ .repo-item.drag-target {
443
+ border-color: rgba(14, 165, 233, 0.42);
444
+ box-shadow:
445
+ inset 0 0 0 1px rgba(56, 189, 248, 0.22),
446
+ 0 18px 38px rgba(56, 189, 248, 0.12);
447
+ }
448
+ .repo-drag-handle {
449
+ display: inline-flex;
450
+ align-items: center;
451
+ justify-content: center;
452
+ color: rgba(100, 116, 139, 0.9);
453
+ cursor: grab;
454
+ flex-shrink: 0;
455
+ }
456
+ .dark .repo-drag-handle {
457
+ color: rgba(148, 163, 184, 0.82);
458
+ }
459
+ .repo-item-pin {
460
+ position: absolute;
461
+ top: 0.55rem;
462
+ right: 0.55rem;
463
+ width: 1.7rem;
464
+ height: 1.7rem;
465
+ border-radius: 9999px;
466
+ display: inline-flex;
467
+ align-items: center;
468
+ justify-content: center;
469
+ color: rgba(71, 85, 105, 0.82);
470
+ background: rgba(255,255,255,0.68);
471
+ border: 1px solid rgba(148, 163, 184, 0.18);
472
+ opacity: 0;
473
+ transition: opacity 160ms ease, transform 160ms ease, color 160ms ease, background 160ms ease;
474
+ }
475
+ .repo-item:hover .repo-item-pin,
476
+ .repo-item-pin.pinned {
477
+ opacity: 1;
478
+ }
479
+ .repo-item-pin:hover {
480
+ transform: scale(1.05);
481
+ color: #0f172a;
482
+ background: rgba(255,255,255,0.92);
483
+ }
484
+ .repo-item-pin.pinned {
485
+ color: #f59e0b;
486
+ background: rgba(255,255,255,0.94);
487
+ box-shadow: 0 10px 22px rgba(245, 158, 11, 0.18);
488
+ }
489
+ .dark .repo-item {
490
+ background: rgba(8, 15, 28, 0.72);
491
+ }
492
+ .dark .repo-item:hover {
493
+ background: rgba(10, 18, 32, 0.82);
494
+ border-color: rgba(125, 211, 252, 0.16);
495
+ }
496
+ .dark .repo-item-pin {
497
+ color: rgba(148, 163, 184, 0.92);
498
+ background: rgba(8, 15, 28, 0.88);
499
+ border-color: rgba(125, 211, 252, 0.08);
500
+ }
501
+ .dark .repo-item-pin:hover {
502
+ color: #e2e8f0;
503
+ background: rgba(15, 23, 42, 0.95);
504
+ }
505
+ .dark .repo-item-pin.pinned {
506
+ color: #fbbf24;
507
+ background: rgba(15, 23, 42, 0.96);
508
+ box-shadow: 0 10px 20px rgba(245, 158, 11, 0.12);
509
+ }
510
+ .dark .repo-item.active {
511
+ background: linear-gradient(135deg, rgba(14,165,233,0.2), rgba(79,70,229,0.18));
512
+ border-color: rgba(125, 211, 252, 0.2);
513
+ box-shadow: 0 20px 44px rgba(2, 132, 199, 0.2);
514
+ }
515
+ .repo-avatar {
516
+ width: 2.35rem;
517
+ height: 2.35rem;
518
+ border-radius: 0.85rem;
519
+ display: inline-flex;
520
+ align-items: center;
521
+ justify-content: center;
522
+ flex-shrink: 0;
523
+ font-size: 0.78rem;
524
+ font-weight: 800;
525
+ letter-spacing: 0.04em;
526
+ color: white;
527
+ background: linear-gradient(135deg, rgba(34,211,238,0.94), rgba(59,130,246,0.76));
528
+ box-shadow: inset 0 1px 0 rgba(255,255,255,0.34);
529
+ }
530
+ .repo-active-dot {
531
+ position: absolute;
532
+ right: 0.72rem;
533
+ top: 50%;
534
+ width: 0.55rem;
535
+ height: 0.55rem;
536
+ border-radius: 9999px;
537
+ background: #22c55e;
538
+ box-shadow:
539
+ 0 0 0 3px rgba(255,255,255,0.86),
540
+ 0 0 18px rgba(34, 197, 94, 0.45);
541
+ transform: translateY(-50%);
542
+ }
543
+ .dark .repo-active-dot {
544
+ box-shadow:
545
+ 0 0 0 3px rgba(8,15,28,0.92),
546
+ 0 0 18px rgba(74, 222, 128, 0.36);
547
+ }
548
+ .app-layout.repo-sidebar-collapsed .repo-sidebar {
549
+ padding-left: 0.75rem;
550
+ padding-right: 0.75rem;
551
+ }
552
+ .app-layout.repo-sidebar-collapsed .repo-sidebar-header-copy,
553
+ .app-layout.repo-sidebar-collapsed #repoSearchInput,
554
+ .app-layout.repo-sidebar-collapsed #repoCountBadge {
555
+ display: none;
556
+ }
557
+ .app-layout.repo-sidebar-collapsed .repo-sidebar-header {
558
+ justify-content: center;
559
+ }
560
+ .app-layout.repo-sidebar-collapsed .repo-collapsed-summary {
561
+ display: flex;
562
+ }
563
+ .app-layout.repo-sidebar-collapsed .repo-item {
564
+ justify-content: center;
565
+ padding: 0.7rem 0.4rem;
566
+ }
567
+ .app-layout.repo-sidebar-collapsed .repo-item .repo-item-copy {
568
+ display: none;
569
+ }
570
+ .app-layout.repo-sidebar-collapsed .repo-item .repo-drag-handle {
571
+ display: none;
572
+ }
573
+ .app-layout.repo-sidebar-collapsed .repo-item .repo-item-pin {
574
+ display: none;
575
+ }
576
+ .app-layout.repo-sidebar-collapsed .repo-item .repo-active-dot {
577
+ right: 0.4rem;
578
+ top: auto;
579
+ bottom: 0.4rem;
580
+ transform: none;
581
+ }
582
+ .app-layout.repo-sidebar-collapsed .repo-avatar {
583
+ width: 2.6rem;
584
+ height: 2.6rem;
585
+ border-radius: 0.95rem;
586
+ }
587
+ .app-layout.repo-sidebar-collapsed .repo-sidebar-list {
588
+ padding-right: 0;
589
+ scrollbar-width: none;
590
+ }
591
+ .app-layout.repo-sidebar-collapsed .repo-sidebar-list::-webkit-scrollbar {
592
+ width: 0;
593
+ }
594
+ .repo-group + .repo-group {
595
+ margin-top: 1rem;
596
+ }
597
+ .repo-group-label {
598
+ margin-bottom: 0.45rem;
599
+ padding: 0 0.35rem;
600
+ font-size: 0.68rem;
601
+ font-weight: 800;
602
+ letter-spacing: 0.16em;
603
+ text-transform: uppercase;
604
+ color: rgb(100 116 139);
605
+ }
606
+ .dark .repo-group-label {
607
+ color: rgb(148 163 184);
608
+ }
609
+ .repo-pinned-mark {
610
+ position: absolute;
611
+ left: -0.1rem;
612
+ top: -0.08rem;
613
+ width: 0.95rem;
614
+ height: 0.95rem;
615
+ border-radius: 9999px;
616
+ display: inline-flex;
617
+ align-items: center;
618
+ justify-content: center;
619
+ font-size: 0.52rem;
620
+ color: #0f172a;
621
+ background: rgba(255,255,255,0.92);
622
+ box-shadow: 0 6px 16px rgba(15, 23, 42, 0.14);
623
+ }
624
+ @media (max-width: 1023px) {
625
+ .app-layout {
626
+ grid-template-columns: 1fr;
627
+ }
628
+ .repo-sidebar-desktop {
629
+ display: none;
630
+ }
631
+ .repo-sidebar-mobile {
632
+ display: block;
633
+ }
634
+ }
635
+ .drawer-open {
636
+ overflow: hidden;
637
+ }
638
+ .glass-panel {
639
+ animation: panelFadeIn 380ms cubic-bezier(0.2, 0.8, 0.2, 1);
640
+ }
641
+ .table-animate tbody tr {
642
+ animation: rowRise 360ms cubic-bezier(0.2, 0.8, 0.2, 1);
643
+ animation-fill-mode: both;
644
+ }
645
+ .table-animate tbody tr:nth-child(1) { animation-delay: 30ms; }
646
+ .table-animate tbody tr:nth-child(2) { animation-delay: 60ms; }
647
+ .table-animate tbody tr:nth-child(3) { animation-delay: 90ms; }
648
+ .table-animate tbody tr:nth-child(4) { animation-delay: 120ms; }
649
+ .table-animate tbody tr:nth-child(5) { animation-delay: 150ms; }
650
+ .table-animate tbody tr:nth-child(6) { animation-delay: 180ms; }
651
+ .recent-action-item {
652
+ position: relative;
653
+ overflow: hidden;
654
+ border: 1px solid rgba(255,255,255,0.22);
655
+ background: rgba(255,255,255,0.44);
656
+ backdrop-filter: blur(14px);
657
+ -webkit-backdrop-filter: blur(14px);
658
+ }
659
+ .dark .recent-action-item {
660
+ background: linear-gradient(180deg, rgba(7, 15, 28, 0.84), rgba(10, 18, 32, 0.78));
661
+ border-color: rgba(125, 211, 252, 0.08);
662
+ box-shadow: inset 0 1px 0 rgba(125, 211, 252, 0.04);
663
+ }
664
+ .recent-action-item::before {
665
+ content: "";
666
+ position: absolute;
667
+ inset: 0 auto 0 0;
668
+ width: 3px;
669
+ background: linear-gradient(180deg, rgba(34,211,238,0.95), rgba(56,189,248,0.25));
670
+ }
671
+ @keyframes panelFadeIn {
672
+ from { opacity: 0; transform: translateY(10px); }
673
+ to { opacity: 1; transform: translateY(0); }
674
+ }
675
+ @keyframes rowRise {
676
+ from { opacity: 0; transform: translateY(8px); }
677
+ to { opacity: 1; transform: translateY(0); }
678
+ }
679
+ .skeleton {
680
+ position: relative;
681
+ overflow: hidden;
682
+ background: rgb(229 231 235);
683
+ border-radius: 0.5rem;
684
+ }
685
+ .dark .skeleton {
686
+ background: rgb(55 65 81);
687
+ }
688
+ .skeleton::after {
689
+ content: "";
690
+ position: absolute;
691
+ inset: 0;
692
+ transform: translateX(-100%);
693
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.45), transparent);
694
+ animation: shimmer 1.4s infinite;
695
+ }
696
+ @keyframes shimmer {
697
+ 100% { transform: translateX(100%); }
698
+ }
699
+ </style>
700
+ </head>
701
+ <body class="bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 transition-colors duration-300">
702
+ <!-- Sticky Header -->
703
+ <header class="sticky top-0 z-50 bg-white dark:bg-gray-800 border-b-2 border-gray-200 dark:border-gray-700 px-4 md:px-8 py-4 shadow-sm">
704
+ <div class="max-w-7xl mx-auto flex flex-col md:flex-row md:items-center md:justify-between gap-4">
705
+ <h1 class="text-xl md:text-2xl font-semibold">MCP Memory Dashboard</h1>
706
+
707
+ <div class="flex flex-wrap items-center gap-3 md:gap-4">
708
+ <div class="flex items-center gap-2 px-3 py-2 rounded-full bg-white/35 dark:bg-slate-900/40 border border-white/30 dark:border-slate-700/60">
709
+ <span class="text-sm text-gray-600 dark:text-gray-400">Repo:</span>
710
+ <span id="currentRepoLabel" class="text-sm font-semibold text-gray-900 dark:text-gray-100">Loading...</span>
711
+ </div>
712
+
713
+ <button id="repoNavToggle" class="repo-sidebar-mobile px-3 py-2 rounded-full bg-white/35 dark:bg-slate-900/40 border border-white/30 dark:border-slate-700/60 text-sm font-medium">
714
+ Repositories
715
+ </button>
716
+
717
+ <div class="flex items-center gap-2">
718
+ <span id="syncStatus" class="text-sm text-gray-600 dark:text-gray-400 whitespace-nowrap">Synced 0s ago</span>
719
+ <div class="w-14 h-1 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
720
+ <div id="countdownFill" class="h-full bg-blue-500 rounded-full" style="width: 100%"></div>
721
+ </div>
722
+ </div>
723
+
724
+ <div class="flex items-center gap-2">
725
+ <span class="text-sm text-gray-600 dark:text-gray-400">Status:</span>
726
+ <div id="statusDot" class="w-3 h-3 rounded-full bg-gray-400"></div>
727
+ <span id="statusText" class="text-sm">Checking...</span>
728
+ </div>
729
+
730
+ <button id="themeToggle" class="p-2 rounded-md bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors" title="Toggle dark mode">🌙</button>
731
+ </div>
732
+ </div>
733
+ </header>
734
+
735
+ <main class="app-shell max-w-[1600px] mx-auto px-4 md:px-8 py-6">
736
+ <div id="appLayout" class="app-layout">
737
+ <aside class="repo-sidebar repo-sidebar-desktop rounded-2xl p-4 glass-panel">
738
+ <div class="repo-sidebar-header flex items-center justify-between mb-4 gap-3">
739
+ <div class="repo-sidebar-header-copy min-w-0">
740
+ <div class="text-xs uppercase tracking-[0.24em] text-gray-500 dark:text-gray-400">Repositories</div>
741
+ <div class="text-lg font-semibold text-gray-900 dark:text-gray-100">Context Switcher</div>
742
+ <div id="memorySummaryLabel" class="mt-1 text-xs text-gray-500 dark:text-gray-400">0 memories indexed</div>
743
+ </div>
744
+ <div class="flex items-center gap-2">
745
+ <span id="repoCountBadge" class="px-2.5 py-1 rounded-full text-xs font-semibold bg-sky-100 text-sky-700 dark:bg-sky-900/40 dark:text-sky-200">0</span>
746
+ <button id="repoSidebarCollapseToggle" class="repo-collapse-trigger" title="Collapse repositories" aria-label="Collapse repositories">
747
+ <svg id="repoSidebarCollapseIcon" class="w-4 h-4 text-gray-700 dark:text-gray-200 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
748
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
749
+ </svg>
750
+ </button>
751
+ </div>
752
+ </div>
753
+ <div id="repoCollapsedSummary" class="repo-collapsed-summary">
754
+ <button id="repoCollapsedSummaryButton" class="repo-collapsed-summary-button" title="Active repository">
755
+ <span id="repoCollapsedSummaryInitials">RP</span>
756
+ <span id="repoCollapsedSummaryCount" class="repo-collapsed-summary-count">0</span>
757
+ </button>
758
+ </div>
759
+ <input id="repoSearchInput" type="text" placeholder="Search repositories..." class="w-full px-3 py-2.5 rounded-xl text-sm mb-4">
760
+ <div id="repoSidebarList" class="repo-sidebar-list space-y-2">
761
+ <div class="text-sm text-gray-500 dark:text-gray-400 px-3 py-4">Loading repositories...</div>
762
+ </div>
763
+ </aside>
764
+
765
+ <div class="min-w-0">
766
+ <!-- Summary Cards + Charts + Recent Queries (2/3 + 1/3) -->
767
+ <div class="grid lg:grid-cols-3 gap-6 mb-6" style="min-height: 60vh;">
768
+ <!-- Main Content: 2/3 -->
769
+ <div class="lg:col-span-2 flex flex-col">
770
+ <!-- Summary Cards -->
771
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
772
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
773
+ <div class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Total Memories</div>
774
+ <div id="totalCount" class="text-2xl font-bold">0</div>
775
+ </div>
776
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
777
+ <div class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Avg Importance</div>
778
+ <div id="avgImportance" class="text-2xl font-bold">0</div>
779
+ </div>
780
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
781
+ <div class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Total Hits</div>
782
+ <div id="totalHits" class="text-2xl font-bold">0</div>
783
+ </div>
784
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
785
+ <div class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Expiring Soon</div>
786
+ <div id="expiringSoon" class="text-2xl font-bold">0</div>
787
+ </div>
788
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
789
+ <div class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Code Facts</div>
790
+ <div id="codeFactCount" class="text-2xl font-bold">0</div>
791
+ </div>
792
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
793
+ <div class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Decisions</div>
794
+ <div id="decisionCount" class="text-2xl font-bold">0</div>
795
+ </div>
796
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
797
+ <div class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Mistakes</div>
798
+ <div id="mistakeCount" class="text-2xl font-bold">0</div>
799
+ </div>
800
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
801
+ <div class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Patterns</div>
802
+ <div id="patternCount" class="text-2xl font-bold">0</div>
803
+ </div>
804
+ </div>
805
+
806
+ <!-- Charts Row 1: 2 columns -->
807
+ <div class="grid md:grid-cols-2 gap-4 mb-4 flex-1">
808
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
809
+ <div class="font-semibold mb-2">Memory by Type</div>
810
+ <canvas id="typeChart"></canvas>
811
+ </div>
812
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
813
+ <div class="font-semibold mb-2">Memories Over Time (30 days)</div>
814
+ <canvas id="timeSeriesChart"></canvas>
815
+ </div>
816
+ </div>
817
+
818
+ <!-- Charts Row 2: 2 columns -->
819
+ <div class="grid md:grid-cols-2 gap-4 flex-1">
820
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
821
+ <div class="font-semibold mb-2">Importance vs Hit Count</div>
822
+ <canvas id="scatterChart"></canvas>
823
+ </div>
824
+ <div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md">
825
+ <div class="font-semibold mb-2">Top 10 Memories by Importance</div>
826
+ <canvas id="topMemoriesChart"></canvas>
827
+ </div>
828
+ </div>
829
+ </div>
830
+
831
+ <!-- Sidebar: 1/3 - Recent Queries (Full Height) -->
832
+ <div class="lg:col-span-1 flex flex-col">
833
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-4 flex-1 overflow-hidden flex flex-col">
834
+ <h3 class="font-semibold mb-4 text-lg">Recent Actions</h3>
835
+ <div id="recentQueries" class="flex-1 overflow-y-auto space-y-2">
836
+ <div class="text-gray-500 text-sm">No recent queries</div>
837
+ </div>
838
+ </div>
839
+ </div>
840
+ </div>
841
+
842
+ <!-- Memory Table Full Width -->
843
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-4 md:p-6 mb-6">
844
+ <div class="flex flex-wrap gap-3 mb-4">
845
+ <input type="text" id="searchInput" placeholder="Search title, content, or ID..." class="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-sm w-60">
846
+
847
+ <div class="flex items-center gap-2">
848
+ <label class="text-sm text-gray-600 dark:text-gray-400">Type:</label>
849
+ <select id="typeFilter" class="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-sm">
850
+ <option value="">All Types</option>
851
+ <option value="decision">Decision</option>
852
+ <option value="mistake">Mistake</option>
853
+ <option value="code_fact">Code Fact</option>
854
+ <option value="pattern">Pattern</option>
855
+ </select>
856
+ </div>
857
+
858
+ <div class="flex items-center gap-2">
859
+ <label class="text-sm text-gray-600 dark:text-gray-400">Min Importance:</label>
860
+ <select id="minImportanceFilter" class="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-sm">
861
+ <option value="">Any</option>
862
+ <option value="1">1+</option>
863
+ <option value="2">2+</option>
864
+ <option value="3">3+</option>
865
+ <option value="4">4+</option>
866
+ <option value="5">5</option>
867
+ </select>
868
+ </div>
869
+
870
+ <div class="flex items-center gap-2">
871
+ <label class="text-sm text-gray-600 dark:text-gray-400">Max Importance:</label>
872
+ <select id="maxImportanceFilter" class="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-sm">
873
+ <option value="">Any</option>
874
+ <option value="1">1</option>
875
+ <option value="2">2</option>
876
+ <option value="3">3</option>
877
+ <option value="4">4</option>
878
+ <option value="5">5 or less</option>
879
+ </select>
880
+ </div>
881
+
882
+ <button onclick="loadData()" class="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-md text-sm transition-colors">Refresh</button>
883
+ <button onclick="exportData('json')" class="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-md text-sm transition-colors">Export JSON</button>
884
+ <button onclick="exportData('csv')" class="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-md text-sm transition-colors">Export CSV</button>
885
+ <button onclick="archiveExpired()" class="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-md text-sm transition-colors">Archive Expired</button>
886
+ </div>
887
+
888
+ <div id="errorMessage"></div>
889
+ <div id="tableContainer" class="text-center py-8 text-gray-500">Loading memories...</div>
890
+
891
+ <!-- Pagination -->
892
+ <div class="flex flex-wrap justify-between items-center mt-4 pt-4 border-t border-gray-200 dark:border-gray-700 gap-4">
893
+ <div id="paginationInfo" class="text-sm text-gray-600 dark:text-gray-400"></div>
894
+ <div class="flex items-center gap-2">
895
+ <button id="firstPageBtn" onclick="goToPage(1)" class="px-3 py-1 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed text-sm">First</button>
896
+ <button id="prevPageBtn" onclick="goToPage(currentPage - 1)" class="px-3 py-1 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed text-sm">Prev</button>
897
+ <span id="pageNumbers"></span>
898
+ <button id="nextPageBtn" onclick="goToPage(currentPage + 1)" class="px-3 py-1 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed text-sm">Next</button>
899
+ <button id="lastPageBtn" onclick="goToPage(totalPages)" class="px-3 py-1 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed text-sm">Last</button>
900
+ </div>
901
+ <div class="flex items-center gap-2">
902
+ <label class="text-sm text-gray-600 dark:text-gray-400">Per page:</label>
903
+ <select id="pageSizeSelect" onchange="changePageSize()" class="px-2 py-1 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-sm">
904
+ <option value="10">10</option>
905
+ <option value="25">25</option>
906
+ <option value="50">50</option>
907
+ <option value="100">100</option>
908
+ </select>
909
+ </div>
910
+ </div>
911
+ </div>
912
+ </div>
913
+ </div>
914
+ </main>
915
+
916
+ <div id="memoryDrawer" class="hidden fixed inset-0 z-40">
917
+ <div class="absolute inset-0 bg-black/40" onclick="closeDrawer()"></div>
918
+ <aside class="absolute right-0 top-0 h-full w-full max-w-xl bg-white dark:bg-gray-800 shadow-2xl border-l border-gray-200 dark:border-gray-700 overflow-y-auto">
919
+ <div class="sticky top-0 z-10 bg-white/95 dark:bg-gray-800/95 backdrop-blur border-b border-gray-200 dark:border-gray-700 px-6 py-4 flex items-center justify-between">
920
+ <div>
921
+ <div class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400">Memory Details</div>
922
+ <h2 id="drawerTitle" class="text-lg font-semibold text-gray-900 dark:text-gray-100">Loading...</h2>
923
+ </div>
924
+ <button onclick="closeDrawer()" class="text-2xl text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">&times;</button>
925
+ </div>
926
+ <div id="drawerBody" class="p-6"></div>
927
+ </aside>
928
+ </div>
929
+
930
+ <div id="repoSidebarDrawer" class="hidden fixed inset-0 z-40 repo-sidebar-mobile">
931
+ <div class="absolute inset-0 bg-black/40" onclick="closeRepoSidebarDrawer()"></div>
932
+ <aside class="absolute left-0 top-0 h-full w-full max-w-sm bg-white dark:bg-gray-900 shadow-2xl border-r border-gray-200 dark:border-gray-700 overflow-y-auto">
933
+ <div class="sticky top-0 z-10 bg-white/95 dark:bg-gray-900/95 backdrop-blur border-b border-gray-200 dark:border-gray-700 px-5 py-4 flex items-center justify-between">
934
+ <div>
935
+ <div class="text-xs uppercase tracking-[0.24em] text-gray-500 dark:text-gray-400">Repositories</div>
936
+ <div class="text-lg font-semibold text-gray-900 dark:text-gray-100">Context Switcher</div>
937
+ </div>
938
+ <button onclick="closeRepoSidebarDrawer()" class="text-2xl text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">&times;</button>
939
+ </div>
940
+ <div class="p-4">
941
+ <input id="repoSearchInputMobile" type="text" placeholder="Search repositories..." class="w-full px-3 py-2.5 rounded-xl text-sm mb-4">
942
+ <div id="repoSidebarListMobile" class="repo-sidebar-list space-y-2"></div>
943
+ </div>
944
+ </aside>
945
+ </div>
946
+
947
+ <!-- Toast Container -->
948
+ <div id="toastContainer" class="fixed bottom-5 right-5 z-[100] flex flex-col gap-2"></div>
949
+
950
+ <!-- Bulk Action Bar -->
951
+ <div id="bulkActionBar" class="hidden fixed bottom-5 left-1/2 -translate-x-1/2 bg-white dark:bg-gray-800 px-6 py-4 rounded-lg shadow-xl z-[90] gap-4 items-center">
952
+ <span id="selectedCount" class="font-semibold">0 selected</span>
953
+ <select id="bulkImportanceSelect" class="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700">
954
+ <option value="1">Importance 1</option>
955
+ <option value="2">Importance 2</option>
956
+ <option value="3" selected>Importance 3</option>
957
+ <option value="4">Importance 4</option>
958
+ <option value="5">Importance 5</option>
959
+ </select>
960
+ <button onclick="bulkUpdateImportance()" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors">Update</button>
961
+ <button onclick="showBulkDeleteConfirm()" class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded transition-colors">Delete</button>
962
+ <button onclick="clearSelection()" class="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded transition-colors">Clear</button>
963
+ </div>
964
+
965
+ <script src="/app.js"></script>
966
+ </body>
967
+ </html>