@geminilight/mindos 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 (282) hide show
  1. package/.env.local.example +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +423 -0
  4. package/README_zh.md +423 -0
  5. package/app/README.md +152 -0
  6. package/app/app/api/ask/route.ts +170 -0
  7. package/app/app/api/ask-sessions/route.ts +90 -0
  8. package/app/app/api/auth/route.ts +37 -0
  9. package/app/app/api/backlinks/route.ts +22 -0
  10. package/app/app/api/bootstrap/route.ts +37 -0
  11. package/app/app/api/extract-pdf/route.ts +82 -0
  12. package/app/app/api/file/route.ts +138 -0
  13. package/app/app/api/files/route.ts +12 -0
  14. package/app/app/api/git/route.ts +42 -0
  15. package/app/app/api/graph/route.ts +113 -0
  16. package/app/app/api/recent-files/route.ts +10 -0
  17. package/app/app/api/search/route.ts +17 -0
  18. package/app/app/api/settings/reset-token/route.ts +21 -0
  19. package/app/app/api/settings/route.ts +123 -0
  20. package/app/app/error.tsx +33 -0
  21. package/app/app/globals.css +368 -0
  22. package/app/app/icon.svg +35 -0
  23. package/app/app/layout.tsx +103 -0
  24. package/app/app/login/page.tsx +120 -0
  25. package/app/app/page.tsx +12 -0
  26. package/app/app/view/[...path]/ViewPageClient.tsx +343 -0
  27. package/app/app/view/[...path]/error.tsx +33 -0
  28. package/app/app/view/[...path]/loading.tsx +15 -0
  29. package/app/app/view/[...path]/page.tsx +93 -0
  30. package/app/components/AskFab.tsx +59 -0
  31. package/app/components/AskModal.tsx +398 -0
  32. package/app/components/Backlinks.tsx +75 -0
  33. package/app/components/Breadcrumb.tsx +31 -0
  34. package/app/components/CsvView.tsx +325 -0
  35. package/app/components/DirView.tsx +138 -0
  36. package/app/components/Editor.tsx +124 -0
  37. package/app/components/EditorWrapper.tsx +17 -0
  38. package/app/components/ErrorBoundary.tsx +53 -0
  39. package/app/components/FileTree.tsx +369 -0
  40. package/app/components/HomeContent.tsx +262 -0
  41. package/app/components/JsonView.tsx +27 -0
  42. package/app/components/MarkdownEditor.tsx +95 -0
  43. package/app/components/MarkdownView.tsx +118 -0
  44. package/app/components/SearchModal.tsx +193 -0
  45. package/app/components/SettingsModal.tsx +237 -0
  46. package/app/components/Sidebar.tsx +136 -0
  47. package/app/components/SidebarLayout.tsx +36 -0
  48. package/app/components/TableOfContents.tsx +150 -0
  49. package/app/components/ThemeToggle.tsx +34 -0
  50. package/app/components/WysiwygEditor.tsx +75 -0
  51. package/app/components/ask/FileChip.tsx +30 -0
  52. package/app/components/ask/MentionPopover.tsx +52 -0
  53. package/app/components/ask/MessageList.tsx +126 -0
  54. package/app/components/ask/SessionHistory.tsx +49 -0
  55. package/app/components/renderers/AgentInspectorRenderer.tsx +277 -0
  56. package/app/components/renderers/BacklinksRenderer.tsx +147 -0
  57. package/app/components/renderers/ConfigRenderer.tsx +236 -0
  58. package/app/components/renderers/CsvRenderer.tsx +77 -0
  59. package/app/components/renderers/DiffRenderer.tsx +310 -0
  60. package/app/components/renderers/GraphRenderer.tsx +428 -0
  61. package/app/components/renderers/SummaryRenderer.tsx +251 -0
  62. package/app/components/renderers/TimelineRenderer.tsx +213 -0
  63. package/app/components/renderers/TodoRenderer.tsx +474 -0
  64. package/app/components/renderers/WorkflowRenderer.tsx +404 -0
  65. package/app/components/renderers/csv/BoardView.tsx +146 -0
  66. package/app/components/renderers/csv/ConfigPanel.tsx +117 -0
  67. package/app/components/renderers/csv/EditableCell.tsx +43 -0
  68. package/app/components/renderers/csv/GalleryView.tsx +40 -0
  69. package/app/components/renderers/csv/TableView.tsx +164 -0
  70. package/app/components/renderers/csv/types.ts +87 -0
  71. package/app/components/settings/AiTab.tsx +111 -0
  72. package/app/components/settings/AppearanceTab.tsx +101 -0
  73. package/app/components/settings/KnowledgeTab.tsx +157 -0
  74. package/app/components/settings/PluginsTab.tsx +82 -0
  75. package/app/components/settings/Primitives.tsx +60 -0
  76. package/app/components/settings/ShortcutsTab.tsx +22 -0
  77. package/app/components/settings/types.ts +41 -0
  78. package/app/components/ui/button.tsx +60 -0
  79. package/app/components/ui/dialog.tsx +157 -0
  80. package/app/components/ui/input.tsx +20 -0
  81. package/app/components/ui/scroll-area.tsx +55 -0
  82. package/app/components/ui/toggle.tsx +44 -0
  83. package/app/components/ui/tooltip.tsx +66 -0
  84. package/app/components.json +25 -0
  85. package/app/data/pages/home-dark.png +0 -0
  86. package/app/data/pages/home-mobile-crop.png +0 -0
  87. package/app/data/pages/home-mobile.png +0 -0
  88. package/app/data/pages/home.png +0 -0
  89. package/app/data/pages/view-dir.png +0 -0
  90. package/app/data/pages/view-file-bot.png +0 -0
  91. package/app/data/pages/view-file-dark-crop.png +0 -0
  92. package/app/data/pages/view-file-dark.png +0 -0
  93. package/app/data/pages/view-file-mobile.png +0 -0
  94. package/app/data/pages/view-file-sm.png +0 -0
  95. package/app/data/pages/view-file-top.png +0 -0
  96. package/app/data/pages/view-file.png +0 -0
  97. package/app/eslint.config.mjs +18 -0
  98. package/app/hooks/useAskSession.ts +181 -0
  99. package/app/hooks/useFileUpload.ts +126 -0
  100. package/app/hooks/useMention.ts +65 -0
  101. package/app/lib/LocaleContext.tsx +40 -0
  102. package/app/lib/actions.ts +40 -0
  103. package/app/lib/agent/index.ts +3 -0
  104. package/app/lib/agent/model.ts +18 -0
  105. package/app/lib/agent/prompt.ts +32 -0
  106. package/app/lib/agent/tools.ts +151 -0
  107. package/app/lib/api.ts +55 -0
  108. package/app/lib/core/backlinks.ts +40 -0
  109. package/app/lib/core/csv.ts +28 -0
  110. package/app/lib/core/fs-ops.ts +118 -0
  111. package/app/lib/core/git.ts +50 -0
  112. package/app/lib/core/index.ts +58 -0
  113. package/app/lib/core/lines.ts +89 -0
  114. package/app/lib/core/search.ts +79 -0
  115. package/app/lib/core/security.ts +43 -0
  116. package/app/lib/core/tree.ts +113 -0
  117. package/app/lib/core/types.ts +40 -0
  118. package/app/lib/fs.ts +467 -0
  119. package/app/lib/i18n.ts +300 -0
  120. package/app/lib/jwt.ts +58 -0
  121. package/app/lib/renderers/index.ts +79 -0
  122. package/app/lib/renderers/registry.ts +70 -0
  123. package/app/lib/settings.ts +150 -0
  124. package/app/lib/types.ts +32 -0
  125. package/app/lib/utils.ts +34 -0
  126. package/app/next-env.d.ts +6 -0
  127. package/app/next.config.ts +10 -0
  128. package/app/package-lock.json +15306 -0
  129. package/app/package.json +71 -0
  130. package/app/postcss.config.mjs +7 -0
  131. package/app/proxy.ts +64 -0
  132. package/app/public/file.svg +1 -0
  133. package/app/public/globe.svg +1 -0
  134. package/app/public/landing/index.html +353 -0
  135. package/app/public/landing/style.css +216 -0
  136. package/app/public/logo-square.svg +37 -0
  137. package/app/public/logo.svg +37 -0
  138. package/app/public/next.svg +1 -0
  139. package/app/public/vercel.svg +1 -0
  140. package/app/public/window.svg +1 -0
  141. package/app/scripts/extract-pdf.cjs +56 -0
  142. package/app/tsconfig.json +34 -0
  143. package/app/vitest.config.ts +14 -0
  144. package/assets/demo-flow-zh.html +622 -0
  145. package/assets/images/demo-flow-dark.png +0 -0
  146. package/assets/images/demo-flow-light.png +0 -0
  147. package/assets/images/demo-flow-zh-dark.png +0 -0
  148. package/assets/images/demo-flow-zh-light.png +0 -0
  149. package/assets/images/gui-sync-cv.png +0 -0
  150. package/assets/logo-square.svg +37 -0
  151. package/bin/cli.js +894 -0
  152. package/mcp/README.md +113 -0
  153. package/mcp/package-lock.json +1717 -0
  154. package/mcp/package.json +18 -0
  155. package/mcp/src/index.ts +494 -0
  156. package/mcp/tsconfig.json +13 -0
  157. package/package.json +49 -0
  158. package/scripts/setup.js +675 -0
  159. package/scripts/upgrade-prompt.md +147 -0
  160. package/skills/mindos/SKILL.md +319 -0
  161. package/skills/mindos-zh/SKILL.md +318 -0
  162. package/templates/README.md +31 -0
  163. package/templates/empty/CHANGELOG.md +9 -0
  164. package/templates/empty/CONFIG.json +197 -0
  165. package/templates/empty/CONFIG.md +73 -0
  166. package/templates/empty/INSTRUCTION.md +177 -0
  167. package/templates/empty/README.md +27 -0
  168. package/templates/en/CHANGELOG.md +9 -0
  169. package/templates/en/CONFIG.json +197 -0
  170. package/templates/en/CONFIG.md +73 -0
  171. package/templates/en/INSTRUCTION.md +177 -0
  172. package/templates/en/README.md +27 -0
  173. package/templates/en/TODO.md +13 -0
  174. package/templates/en//360/237/221/244 Profile/INSTRUCTION.md" +21 -0
  175. package/templates/en//360/237/221/244 Profile/README.md" +15 -0
  176. package/templates/en//360/237/221/244 Profile//342/232/231/357/270/217 Preferences.md" +21 -0
  177. package/templates/en//360/237/221/244 Profile//360/237/216/257 Focus.md" +31 -0
  178. package/templates/en//360/237/221/244 Profile//360/237/221/244 Identity.md" +22 -0
  179. package/templates/en//360/237/223/232 Resources/INSTRUCTION.md" +29 -0
  180. package/templates/en//360/237/223/232 Resources/README.md" +21 -0
  181. package/templates/en//360/237/223/232 Resources//360/237/247/276 AI Influencers.csv" +1 -0
  182. package/templates/en//360/237/223/232 Resources//360/237/247/276 AI Products.csv" +1 -0
  183. package/templates/en//360/237/223/232 Resources//360/237/247/276 AI Scholars.csv" +1 -0
  184. package/templates/en//360/237/223/232 Resources//360/237/247/276 AI Tools.csv" +1 -0
  185. package/templates/en//360/237/223/235 Notes/INSTRUCTION.md" +31 -0
  186. package/templates/en//360/237/223/235 Notes/Ideas/README.md" +8 -0
  187. package/templates/en//360/237/223/235 Notes/Ideas//360/237/247/252_example_product_idea.md" +16 -0
  188. package/templates/en//360/237/223/235 Notes/Inbox/README.md" +8 -0
  189. package/templates/en//360/237/223/235 Notes/Inbox//360/237/247/252_example_quick_capture.md" +14 -0
  190. package/templates/en//360/237/223/235 Notes/Meetings/README.md" +8 -0
  191. package/templates/en//360/237/223/235 Notes/Meetings//360/237/247/252_example_meeting_note.md" +17 -0
  192. package/templates/en//360/237/223/235 Notes/README.md" +24 -0
  193. package/templates/en//360/237/223/235 Notes/Waiting/README.md" +8 -0
  194. package/templates/en//360/237/223/235 Notes/Waiting//360/237/247/252_example_blocked_item.md" +16 -0
  195. package/templates/en//360/237/224/204 Workflows/Configurations/README.md" +3 -0
  196. package/templates/en//360/237/224/204 Workflows/Configurations//360/237/247/252_example_config_update_sop.md" +14 -0
  197. package/templates/en//360/237/224/204 Workflows/INSTRUCTION.md" +21 -0
  198. package/templates/en//360/237/224/204 Workflows/Information/README.md" +16 -0
  199. package/templates/en//360/237/224/204 Workflows/Information//360/237/247/252_example_info_capture_sop.md" +13 -0
  200. package/templates/en//360/237/224/204 Workflows/Media/README.md" +16 -0
  201. package/templates/en//360/237/224/204 Workflows/Media//360/237/247/252_example_content_publish_sop.md" +13 -0
  202. package/templates/en//360/237/224/204 Workflows/README.md" +22 -0
  203. package/templates/en//360/237/224/204 Workflows/Research/README.md" +16 -0
  204. package/templates/en//360/237/224/204 Workflows/Research//360/237/247/252_example_lit_review_sop.md" +16 -0
  205. package/templates/en//360/237/224/204 Workflows/Startup/README.md" +3 -0
  206. package/templates/en//360/237/224/204 Workflows/Startup//360/237/247/252_example_weekly_founder_ops.md" +22 -0
  207. package/templates/en//360/237/224/227 Connections/Classmates/README.md" +11 -0
  208. package/templates/en//360/237/224/227 Connections/Classmates//360/237/247/252_example_leo_chen.md" +16 -0
  209. package/templates/en//360/237/224/227 Connections/Colleagues/README.md" +11 -0
  210. package/templates/en//360/237/224/227 Connections/Colleagues//360/237/247/252_example_ethan_zhao.md" +16 -0
  211. package/templates/en//360/237/224/227 Connections/Connections Overview.csv" +5 -0
  212. package/templates/en//360/237/224/227 Connections/Family/README.md" +11 -0
  213. package/templates/en//360/237/224/227 Connections/Family//360/237/247/252_example_james_wang.md" +16 -0
  214. package/templates/en//360/237/224/227 Connections/Friends/README.md" +11 -0
  215. package/templates/en//360/237/224/227 Connections/Friends//360/237/247/252_example_lily_lin.md" +16 -0
  216. package/templates/en//360/237/224/227 Connections/INSTRUCTION.md" +56 -0
  217. package/templates/en//360/237/224/227 Connections/README.md" +20 -0
  218. package/templates/en//360/237/232/200 Projects/Archived/README.md" +9 -0
  219. package/templates/en//360/237/232/200 Projects/Archived//360/237/247/252_example_archived_project_note.md" +14 -0
  220. package/templates/en//360/237/232/200 Projects/INSTRUCTION.md" +29 -0
  221. package/templates/en//360/237/232/200 Projects/Products/README.md" +16 -0
  222. package/templates/en//360/237/232/200 Projects/Products//360/237/247/252_example_product_project_brief.md" +20 -0
  223. package/templates/en//360/237/232/200 Projects/README.md" +21 -0
  224. package/templates/en//360/237/232/200 Projects/Research/README.md" +16 -0
  225. package/templates/en//360/237/232/200 Projects/Research//360/237/247/252_example_research_project_brief.md" +16 -0
  226. package/templates/template-generation-skill.md +79 -0
  227. package/templates/zh/CHANGELOG.md +9 -0
  228. package/templates/zh/CONFIG.json +197 -0
  229. package/templates/zh/CONFIG.md +66 -0
  230. package/templates/zh/INSTRUCTION.md +177 -0
  231. package/templates/zh/README.md +27 -0
  232. package/templates/zh/TODO.md +13 -0
  233. package/templates/zh//360/237/221/244 /347/224/273/345/203/217/INSTRUCTION.md" +28 -0
  234. package/templates/zh//360/237/221/244 /347/224/273/345/203/217/README.md" +20 -0
  235. package/templates/zh//360/237/221/244 /347/224/273/345/203/217//342/232/231/357/270/217 Preferences.md" +21 -0
  236. package/templates/zh//360/237/221/244 /347/224/273/345/203/217//360/237/216/257 Focus.md" +31 -0
  237. package/templates/zh//360/237/221/244 /347/224/273/345/203/217//360/237/221/244 Identity.md" +22 -0
  238. package/templates/zh//360/237/223/232 /350/265/204/346/272/220/INSTRUCTION.md" +29 -0
  239. package/templates/zh//360/237/223/232 /350/265/204/346/272/220/README.md" +21 -0
  240. package/templates/zh//360/237/223/232 /350/265/204/346/272/220//360/237/247/276 AI Inferencers.csv" +1 -0
  241. package/templates/zh//360/237/223/232 /350/265/204/346/272/220//360/237/247/276 AI /344/272/247/345/223/201.csv" +1 -0
  242. package/templates/zh//360/237/223/232 /350/265/204/346/272/220//360/237/247/276 AI /345/255/246/350/200/205/346/270/205/345/215/225.csv" +1 -0
  243. package/templates/zh//360/237/223/232 /350/265/204/346/272/220//360/237/247/276 AI /345/267/245/345/205/267/346/270/205/345/215/225.csv" +1 -0
  244. package/templates/zh//360/237/223/235 /347/254/224/350/256/260/INSTRUCTION.md" +31 -0
  245. package/templates/zh//360/237/223/235 /347/254/224/350/256/260/README.md" +24 -0
  246. package/templates/zh//360/237/223/235 /347/254/224/350/256/260//344/274/232/350/256/256/README.md" +8 -0
  247. package/templates/zh//360/237/223/235 /347/254/224/350/256/260//344/274/232/350/256/256//360/237/247/252_example_/344/274/232/350/256/256/347/272/252/350/246/201.md" +17 -0
  248. package/templates/zh//360/237/223/235 /347/254/224/350/256/260//345/276/205/345/217/215/351/246/210/README.md" +8 -0
  249. package/templates/zh//360/237/223/235 /347/254/224/350/256/260//345/276/205/345/217/215/351/246/210//360/237/247/252_example_/345/276/205/345/217/215/351/246/210/344/272/213/351/241/271.md" +16 -0
  250. package/templates/zh//360/237/223/235 /347/254/224/350/256/260//346/203/263/346/263/225/README.md" +8 -0
  251. package/templates/zh//360/237/223/235 /347/254/224/350/256/260//346/203/263/346/263/225//360/237/247/252_example_/344/272/247/345/223/201/346/203/263/346/263/225.md" +16 -0
  252. package/templates/zh//360/237/223/235 /347/254/224/350/256/260//346/224/266/344/273/266/347/256/261/README.md" +8 -0
  253. package/templates/zh//360/237/223/235 /347/254/224/350/256/260//346/224/266/344/273/266/347/256/261//360/237/247/252_example_/344/270/264/346/227/266/351/200/237/350/256/260.md" +13 -0
  254. package/templates/zh//360/237/224/204 /346/265/201/347/250/213/INSTRUCTION.md" +29 -0
  255. package/templates/zh//360/237/224/204 /346/265/201/347/250/213/README.md" +21 -0
  256. package/templates/zh//360/237/224/204 /346/265/201/347/250/213//344/277/241/346/201/257/README.md" +16 -0
  257. package/templates/zh//360/237/224/204 /346/265/201/347/250/213//344/277/241/346/201/257//360/237/247/252_example_/344/277/241/346/201/257/351/207/207/351/233/206/346/265/201/347/250/213.md" +13 -0
  258. package/templates/zh//360/237/224/204 /346/265/201/347/250/213//345/252/222/344/275/223/README.md" +16 -0
  259. package/templates/zh//360/237/224/204 /346/265/201/347/250/213//345/252/222/344/275/223//360/237/247/252_example_/345/206/205/345/256/271/345/217/221/345/270/203/346/265/201/347/250/213.md" +13 -0
  260. package/templates/zh//360/237/224/204 /346/265/201/347/250/213//347/247/221/347/240/224/README.md" +16 -0
  261. package/templates/zh//360/237/224/204 /346/265/201/347/250/213//347/247/221/347/240/224//360/237/247/252_example_/346/226/207/347/214/256/347/273/274/350/277/260/346/265/201/347/250/213.md" +16 -0
  262. package/templates/zh//360/237/224/204 /346/265/201/347/250/213//351/205/215/347/275/256/README.md" +3 -0
  263. package/templates/zh//360/237/224/204 /346/265/201/347/250/213//351/205/215/347/275/256//360/237/247/252_example_/351/205/215/347/275/256/346/233/264/346/226/260/346/265/201/347/250/213.md" +26 -0
  264. package/templates/zh//360/237/224/227 /345/205/263/347/263/273/INSTRUCTION.md" +62 -0
  265. package/templates/zh//360/237/224/227 /345/205/263/347/263/273/README.md" +20 -0
  266. package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/205/263/347/263/273/346/200/273/350/247/210.csv" +5 -0
  267. package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/220/214/344/272/213/README.md" +11 -0
  268. package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/220/214/344/272/213//360/237/247/252_example_/345/220/214/344/272/213/350/265/265/344/270/200/350/276/260.md" +16 -0
  269. package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/220/214/345/255/246/README.md" +11 -0
  270. package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/220/214/345/255/246//360/237/247/252_example_/345/220/214/345/255/246/351/231/210/347/253/213/346/254/247.md" +16 -0
  271. package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/256/266/344/272/272/README.md" +11 -0
  272. package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/256/266/344/272/272//360/237/247/252_example_/345/256/266/344/272/272/347/216/213/345/273/272/345/233/275.md" +16 -0
  273. package/templates/zh//360/237/224/227 /345/205/263/347/263/273//346/234/213/345/217/213/README.md" +11 -0
  274. package/templates/zh//360/237/224/227 /345/205/263/347/263/273//346/234/213/345/217/213//360/237/247/252_example_/346/234/213/345/217/213/346/236/227/345/260/217/344/270/275.md" +16 -0
  275. package/templates/zh//360/237/232/200 /351/241/271/347/233/256/INSTRUCTION.md" +31 -0
  276. package/templates/zh//360/237/232/200 /351/241/271/347/233/256/README.md" +21 -0
  277. package/templates/zh//360/237/232/200 /351/241/271/347/233/256//344/272/247/345/223/201/README.md" +16 -0
  278. package/templates/zh//360/237/232/200 /351/241/271/347/233/256//344/272/247/345/223/201//360/237/247/252_example_/344/272/247/345/223/201/351/241/271/347/233/256/347/256/200/346/212/245.md" +20 -0
  279. package/templates/zh//360/237/232/200 /351/241/271/347/233/256//345/267/262/345/275/222/346/241/243/README.md" +9 -0
  280. package/templates/zh//360/237/232/200 /351/241/271/347/233/256//345/267/262/345/275/222/346/241/243//360/237/247/252_example_/345/275/222/346/241/243/351/241/271/347/233/256/350/256/260/345/275/225.md" +15 -0
  281. package/templates/zh//360/237/232/200 /351/241/271/347/233/256//347/247/221/347/240/224/README.md" +16 -0
  282. package/templates/zh//360/237/232/200 /351/241/271/347/233/256//347/247/221/347/240/224//360/237/247/252_example_/347/247/221/347/240/224/351/241/271/347/233/256/347/256/200/346/212/245.md" +16 -0
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+
3
+ import { Trash2 } from 'lucide-react';
4
+ import type { ChatSession } from '@/lib/types';
5
+ import { sessionTitle } from '@/hooks/useAskSession';
6
+
7
+ interface SessionHistoryProps {
8
+ sessions: ChatSession[];
9
+ activeSessionId: string | null;
10
+ onLoad: (id: string) => void;
11
+ onDelete: (id: string) => void;
12
+ }
13
+
14
+ export default function SessionHistory({ sessions, activeSessionId, onLoad, onDelete }: SessionHistoryProps) {
15
+ return (
16
+ <div className="border-b border-border px-4 py-2.5 max-h-[190px] overflow-y-auto">
17
+ <div className="text-[11px] text-muted-foreground mb-2">Session History</div>
18
+ <div className="flex flex-col gap-1.5">
19
+ {sessions.length === 0 && (
20
+ <div className="text-xs text-muted-foreground/70">No saved sessions.</div>
21
+ )}
22
+ {sessions.map((s) => (
23
+ <div key={s.id} className="flex items-center gap-1.5">
24
+ <button
25
+ type="button"
26
+ onClick={() => onLoad(s.id)}
27
+ className={`flex-1 text-left px-2 py-1.5 rounded text-xs transition-colors ${
28
+ activeSessionId === s.id
29
+ ? 'bg-accent text-foreground'
30
+ : 'text-muted-foreground hover:bg-muted hover:text-foreground'
31
+ }`}
32
+ >
33
+ <div className="truncate">{sessionTitle(s)}</div>
34
+ <div className="text-[10px] opacity-60">{new Date(s.updatedAt).toLocaleString()}</div>
35
+ </button>
36
+ <button
37
+ type="button"
38
+ onClick={() => onDelete(s.id)}
39
+ className="p-1 rounded text-muted-foreground hover:text-red-400 hover:bg-muted"
40
+ title="Delete session"
41
+ >
42
+ <Trash2 size={12} />
43
+ </button>
44
+ </div>
45
+ ))}
46
+ </div>
47
+ </div>
48
+ );
49
+ }
@@ -0,0 +1,277 @@
1
+ 'use client';
2
+
3
+ import { useMemo, useState } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { Terminal, FileEdit, FilePlus, Trash2, Search, Clock, ChevronDown, AlertCircle, CheckCircle2 } from 'lucide-react';
6
+ import type { RendererContext } from '@/lib/renderers/registry';
7
+
8
+ // ─── Log entry format ─────────────────────────────────────────────────────────
9
+ // Each entry is a fenced JSON block in the markdown:
10
+ //
11
+ // ```agent-op
12
+ // { "ts": "2025-01-15T10:30:00Z", "tool": "mindos_write_file",
13
+ // "params": { "path": "...", "content": "..." },
14
+ // "result": "ok" | "error", "message": "..." }
15
+ // ```
16
+
17
+ interface AgentOp {
18
+ ts: string;
19
+ tool: string;
20
+ params: Record<string, unknown>;
21
+ result: 'ok' | 'error';
22
+ message?: string;
23
+ }
24
+
25
+ // ─── Parser ───────────────────────────────────────────────────────────────────
26
+
27
+ function parseOps(content: string): AgentOp[] {
28
+ const ops: AgentOp[] = [];
29
+ const re = /```agent-op\n([\s\S]*?)```/g;
30
+ let m: RegExpExecArray | null;
31
+ while ((m = re.exec(content)) !== null) {
32
+ try {
33
+ const op = JSON.parse(m[1].trim()) as AgentOp;
34
+ if (op.tool && op.ts) ops.push(op);
35
+ } catch { /* skip malformed */ }
36
+ }
37
+ // newest first
38
+ return ops.sort((a, b) => new Date(b.ts).getTime() - new Date(a.ts).getTime());
39
+ }
40
+
41
+ // ─── Tool metadata ────────────────────────────────────────────────────────────
42
+
43
+ type OpKind = 'read' | 'write' | 'create' | 'delete' | 'search' | 'other';
44
+
45
+ function opKind(tool: string): OpKind {
46
+ if (/read|list|get|search/.test(tool)) {
47
+ if (/search/.test(tool)) return 'search';
48
+ return 'read';
49
+ }
50
+ if (/create/.test(tool)) return 'create';
51
+ if (/delete/.test(tool)) return 'delete';
52
+ if (/write|update|insert|append/.test(tool)) return 'write';
53
+ return 'other';
54
+ }
55
+
56
+ const KIND_STYLE: Record<OpKind, { bg: string; text: string; border: string }> = {
57
+ read: { bg: 'rgba(138,180,216,0.10)', text: '#8ab4d8', border: 'rgba(138,180,216,0.25)' },
58
+ write: { bg: 'rgba(200,135,58,0.10)', text: 'var(--amber)', border: 'rgba(200,135,58,0.25)' },
59
+ create: { bg: 'rgba(122,173,128,0.10)', text: '#7aad80', border: 'rgba(122,173,128,0.25)' },
60
+ delete: { bg: 'rgba(200,80,80,0.10)', text: '#c85050', border: 'rgba(200,80,80,0.25)' },
61
+ search: { bg: 'rgba(200,160,216,0.10)', text: '#c8a0d8', border: 'rgba(200,160,216,0.25)' },
62
+ other: { bg: 'var(--muted)', text: 'var(--muted-foreground)', border: 'var(--border)' },
63
+ };
64
+
65
+ function OpIcon({ kind, size = 13 }: { kind: OpKind; size?: number }) {
66
+ if (kind === 'read') return <Clock size={size} />;
67
+ if (kind === 'write') return <FileEdit size={size} />;
68
+ if (kind === 'create') return <FilePlus size={size} />;
69
+ if (kind === 'delete') return <Trash2 size={size} />;
70
+ if (kind === 'search') return <Search size={size} />;
71
+ return <Terminal size={size} />;
72
+ }
73
+
74
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
75
+
76
+ function formatTs(ts: string): string {
77
+ try {
78
+ const d = new Date(ts);
79
+ return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' });
80
+ } catch { return ts; }
81
+ }
82
+
83
+ function relativeTs(ts: string): string {
84
+ const diff = Date.now() - new Date(ts).getTime();
85
+ const m = Math.floor(diff / 60000);
86
+ const h = Math.floor(diff / 3600000);
87
+ const d = Math.floor(diff / 86400000);
88
+ if (m < 1) return 'just now';
89
+ if (m < 60) return `${m}m ago`;
90
+ if (h < 24) return `${h}h ago`;
91
+ return `${d}d ago`;
92
+ }
93
+
94
+ function getFilePath(params: Record<string, unknown>): string | null {
95
+ return typeof params.path === 'string' ? params.path : null;
96
+ }
97
+
98
+ function truncateContent(v: unknown, max = 120): string {
99
+ const s = typeof v === 'string' ? v : JSON.stringify(v);
100
+ return s.length > max ? s.slice(0, max) + '…' : s;
101
+ }
102
+
103
+ // ─── Single op card ───────────────────────────────────────────────────────────
104
+
105
+ function OpCard({ op }: { op: AgentOp }) {
106
+ const router = useRouter();
107
+ const [expanded, setExpanded] = useState(false);
108
+ const kind = opKind(op.tool);
109
+ const style = KIND_STYLE[kind];
110
+ const filePath = getFilePath(op.params);
111
+
112
+ const toolShort = op.tool.replace('mindos_', '');
113
+
114
+ return (
115
+ <div style={{
116
+ background: 'var(--card)',
117
+ border: `1px solid var(--border)`,
118
+ borderRadius: 10,
119
+ overflow: 'hidden',
120
+ marginBottom: 8,
121
+ }}>
122
+ {/* Header row */}
123
+ <div
124
+ style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 14px', cursor: 'pointer', userSelect: 'none' }}
125
+ onClick={() => setExpanded(v => !v)}
126
+ >
127
+ {/* kind badge */}
128
+ <span style={{
129
+ display: 'inline-flex', alignItems: 'center', gap: 4,
130
+ padding: '2px 8px', borderRadius: 999, fontSize: '0.68rem',
131
+ fontFamily: "'IBM Plex Mono',monospace", fontWeight: 600,
132
+ background: style.bg, color: style.text, border: `1px solid ${style.border}`,
133
+ flexShrink: 0,
134
+ }}>
135
+ <OpIcon kind={kind} size={10} />
136
+ {kind}
137
+ </span>
138
+
139
+ {/* tool name */}
140
+ <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.78rem', color: 'var(--foreground)', fontWeight: 600, flexShrink: 0 }}>
141
+ {toolShort}
142
+ </span>
143
+
144
+ {/* file path */}
145
+ {filePath && (
146
+ <span
147
+ style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.72rem', color: 'var(--amber)', flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', cursor: 'pointer' }}
148
+ onClick={e => { e.stopPropagation(); router.push('/view/' + filePath.split('/').map(encodeURIComponent).join('/')); }}
149
+ title={filePath}
150
+ >
151
+ {filePath}
152
+ </span>
153
+ )}
154
+
155
+ <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>
156
+ {/* result */}
157
+ {op.result === 'ok'
158
+ ? <CheckCircle2 size={13} style={{ color: '#7aad80' }} />
159
+ : <AlertCircle size={13} style={{ color: '#c85050' }} />
160
+ }
161
+ {/* timestamp */}
162
+ <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.68rem', color: 'var(--muted-foreground)', opacity: 0.6 }} title={formatTs(op.ts)}>
163
+ {relativeTs(op.ts)}
164
+ </span>
165
+ {/* chevron */}
166
+ <ChevronDown size={12} style={{ color: 'var(--muted-foreground)', transform: expanded ? 'rotate(180deg)' : 'none', transition: 'transform .15s' }} />
167
+ </div>
168
+ </div>
169
+
170
+ {/* Expanded params */}
171
+ {expanded && (
172
+ <div style={{ borderTop: '1px solid var(--border)', padding: '10px 14px', background: 'var(--background)' }}>
173
+ {/* params table */}
174
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 4, marginBottom: op.message ? 8 : 0 }}>
175
+ {Object.entries(op.params).map(([k, v]) => (
176
+ <div key={k} style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
177
+ <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.68rem', color: 'var(--muted-foreground)', opacity: 0.7, flexShrink: 0, minWidth: 80 }}>
178
+ {k}
179
+ </span>
180
+ <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.72rem', color: 'var(--foreground)', wordBreak: 'break-all', lineHeight: 1.5 }}>
181
+ {truncateContent(v)}
182
+ </span>
183
+ </div>
184
+ ))}
185
+ </div>
186
+ {/* result message */}
187
+ {op.message && (
188
+ <div style={{ marginTop: 6, padding: '5px 9px', borderRadius: 5, fontSize: '0.72rem', fontFamily: "'IBM Plex Mono',monospace",
189
+ background: op.result === 'error' ? 'rgba(200,80,80,0.08)' : 'rgba(122,173,128,0.08)',
190
+ color: op.result === 'error' ? '#c85050' : '#7aad80',
191
+ border: `1px solid ${op.result === 'error' ? 'rgba(200,80,80,0.2)' : 'rgba(122,173,128,0.2)'}`,
192
+ }}>
193
+ {op.message}
194
+ </div>
195
+ )}
196
+ {/* absolute timestamp */}
197
+ <div style={{ marginTop: 6, fontSize: '0.65rem', fontFamily: "'IBM Plex Mono',monospace", color: 'var(--muted-foreground)', opacity: 0.5 }}>
198
+ {formatTs(op.ts)}
199
+ </div>
200
+ </div>
201
+ )}
202
+ </div>
203
+ );
204
+ }
205
+
206
+ // ─── Filter bar ───────────────────────────────────────────────────────────────
207
+
208
+ const KINDS: Array<OpKind | 'all'> = ['all', 'write', 'create', 'delete', 'read', 'search'];
209
+
210
+ // ─── Main renderer ────────────────────────────────────────────────────────────
211
+
212
+ export function AgentInspectorRenderer({ content }: RendererContext) {
213
+ const [filter, setFilter] = useState<OpKind | 'all'>('all');
214
+ const ops = useMemo(() => parseOps(content), [content]);
215
+
216
+ const filtered = useMemo(() =>
217
+ filter === 'all' ? ops : ops.filter(op => opKind(op.tool) === filter),
218
+ [ops, filter]);
219
+
220
+ const counts = useMemo(() => {
221
+ const c: Record<string, number> = { all: ops.length };
222
+ for (const op of ops) {
223
+ const k = opKind(op.tool);
224
+ c[k] = (c[k] ?? 0) + 1;
225
+ }
226
+ return c;
227
+ }, [ops]);
228
+
229
+ if (ops.length === 0) {
230
+ return (
231
+ <div style={{ padding: '3rem 1rem', textAlign: 'center', color: 'var(--muted-foreground)', fontFamily: "'IBM Plex Mono',monospace", fontSize: 12 }}>
232
+ <Terminal size={28} style={{ margin: '0 auto 10px', opacity: 0.3 }} />
233
+ <p>No agent operations logged yet.</p>
234
+ <p style={{ marginTop: 6, opacity: 0.6, fontSize: 11 }}>
235
+ Agent writes appear here as <code style={{ background: 'var(--muted)', padding: '1px 5px', borderRadius: 4 }}>```agent-op</code> blocks.
236
+ </p>
237
+ </div>
238
+ );
239
+ }
240
+
241
+ return (
242
+ <div style={{ maxWidth: 760, margin: '0 auto', padding: '1.5rem 0' }}>
243
+ {/* filter bar */}
244
+ <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: '1.2rem', flexWrap: 'wrap' }}>
245
+ {KINDS.map(k => {
246
+ const cnt = counts[k] ?? 0;
247
+ if (k !== 'all' && !cnt) return null;
248
+ const style = k !== 'all' ? KIND_STYLE[k] : undefined;
249
+ const active = filter === k;
250
+ return (
251
+ <button
252
+ key={k}
253
+ onClick={() => setFilter(k)}
254
+ style={{
255
+ display: 'inline-flex', alignItems: 'center', gap: 4,
256
+ padding: '3px 10px', borderRadius: 999, fontSize: '0.7rem',
257
+ fontFamily: "'IBM Plex Mono',monospace", cursor: 'pointer', border: 'none',
258
+ background: active ? (style?.bg ?? 'var(--accent)') : 'var(--muted)',
259
+ color: active ? (style?.text ?? 'var(--foreground)') : 'var(--muted-foreground)',
260
+ outline: active ? `1px solid ${style?.border ?? 'var(--border)'}` : 'none',
261
+ transition: 'all .1s',
262
+ }}
263
+ >
264
+ {k !== 'all' && <OpIcon kind={k} size={10} />}
265
+ {k} <span style={{ opacity: 0.6 }}>({cnt})</span>
266
+ </button>
267
+ );
268
+ })}
269
+ </div>
270
+
271
+ {/* ops list */}
272
+ <div>
273
+ {filtered.map((op, i) => <OpCard key={i} op={op} />)}
274
+ </div>
275
+ </div>
276
+ );
277
+ }
@@ -0,0 +1,147 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { FileText, ExternalLink } from 'lucide-react';
6
+ import { encodePath } from '@/lib/utils';
7
+ import type { RendererContext } from '@/lib/renderers/registry';
8
+ import { apiFetch } from '@/lib/api';
9
+
10
+ interface BacklinkItem {
11
+ filePath: string;
12
+ snippets: string[];
13
+ }
14
+
15
+ function basename(p: string) {
16
+ return p.split('/').pop()?.replace(/\.md$/, '') ?? p;
17
+ }
18
+
19
+ function dirname(p: string) {
20
+ const parts = p.split('/');
21
+ return parts.length > 1 ? parts.slice(0, -1).join('/') : '';
22
+ }
23
+
24
+ // Highlight [[...]] and [text](url) references in snippet
25
+ function SnippetLine({ text }: { text: string }) {
26
+ // Replace wikilinks and md links with styled spans
27
+ const parts = text.split(/(\[\[[^\]]+\]\]|\[[^\]]+\]\([^)]+\))/g);
28
+ return (
29
+ <span>
30
+ {parts.map((part, i) => {
31
+ if (/^\[\[/.test(part) || /^\[/.test(part)) {
32
+ return <span key={i} style={{ color: 'var(--amber)', fontWeight: 500 }}>{part}</span>;
33
+ }
34
+ return <span key={i}>{part}</span>;
35
+ })}
36
+ </span>
37
+ );
38
+ }
39
+
40
+ export function BacklinksRenderer({ filePath }: RendererContext) {
41
+ const router = useRouter();
42
+ const [backlinks, setBacklinks] = useState<BacklinkItem[] | null>(null);
43
+ const [loading, setLoading] = useState(true);
44
+
45
+ useEffect(() => {
46
+ setLoading(true);
47
+ apiFetch<BacklinkItem[]>(`/api/backlinks?path=${encodeURIComponent(filePath)}`)
48
+ .then((data) => { setBacklinks(data); setLoading(false); })
49
+ .catch(() => setLoading(false));
50
+ }, [filePath]);
51
+
52
+ if (loading) {
53
+ return (
54
+ <div style={{ padding: '3rem 1rem', textAlign: 'center', fontFamily: "'IBM Plex Mono',monospace", fontSize: 12, color: 'var(--muted-foreground)' }}>
55
+ Scanning backlinks…
56
+ </div>
57
+ );
58
+ }
59
+
60
+ const items = backlinks ?? [];
61
+
62
+ return (
63
+ <div style={{ maxWidth: 720, margin: '0 auto', padding: '1.5rem 0' }}>
64
+ {/* header */}
65
+ <div style={{ marginBottom: '1.5rem', display: 'flex', alignItems: 'center', gap: 8 }}>
66
+ <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: 11, color: 'var(--muted-foreground)' }}>
67
+ {items.length === 0 ? 'No backlinks found' : `${items.length} file${items.length === 1 ? '' : 's'} link here`}
68
+ </span>
69
+ </div>
70
+
71
+ {items.length === 0 ? (
72
+ <div style={{
73
+ border: '1px dashed var(--border)',
74
+ borderRadius: 10,
75
+ padding: '2.5rem 1.5rem',
76
+ textAlign: 'center',
77
+ color: 'var(--muted-foreground)',
78
+ fontSize: 13,
79
+ }}>
80
+ <FileText size={28} style={{ margin: '0 auto 10px', opacity: 0.3 }} />
81
+ <p style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: 12 }}>
82
+ No other files link to <strong style={{ color: 'var(--foreground)' }}>{basename(filePath)}</strong> yet.
83
+ </p>
84
+ </div>
85
+ ) : (
86
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
87
+ {items.map(({ filePath: src, snippets }) => {
88
+ const name = basename(src);
89
+ const dir = dirname(src);
90
+ return (
91
+ <div
92
+ key={src}
93
+ style={{
94
+ background: 'var(--card)',
95
+ border: '1px solid var(--border)',
96
+ borderRadius: 10,
97
+ overflow: 'hidden',
98
+ cursor: 'pointer',
99
+ transition: 'border-color .15s',
100
+ }}
101
+ onClick={() => router.push('/view/' + encodePath(src))}
102
+ onMouseEnter={e => (e.currentTarget.style.borderColor = 'rgba(200,135,58,0.4)')}
103
+ onMouseLeave={e => (e.currentTarget.style.borderColor = 'var(--border)')}
104
+ >
105
+ {/* file header */}
106
+ <div style={{
107
+ display: 'flex',
108
+ alignItems: 'center',
109
+ gap: 8,
110
+ padding: '10px 14px',
111
+ borderBottom: snippets.length > 0 ? '1px solid var(--border)' : 'none',
112
+ background: 'var(--muted)',
113
+ }}>
114
+ <FileText size={13} style={{ color: 'var(--muted-foreground)', flexShrink: 0 }} />
115
+ <span style={{ fontFamily: "'IBM Plex Sans',sans-serif", fontWeight: 600, fontSize: '0.85rem', color: 'var(--foreground)', flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
116
+ {name}
117
+ </span>
118
+ {dir && (
119
+ <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.68rem', color: 'var(--muted-foreground)', opacity: 0.6, flexShrink: 0 }}>
120
+ {dir}
121
+ </span>
122
+ )}
123
+ <ExternalLink size={11} style={{ color: 'var(--muted-foreground)', opacity: 0.5, flexShrink: 0 }} />
124
+ </div>
125
+
126
+ {/* snippets */}
127
+ {snippets.map((snippet: string, i: number) => (
128
+ <div key={i} style={{
129
+ padding: '8px 14px',
130
+ borderBottom: i < snippets.length - 1 ? '1px solid var(--border)' : 'none',
131
+ background: 'var(--background)',
132
+ }}>
133
+ {snippet.split('\n').map((line: string, j: number) => (
134
+ <div key={j} style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.72rem', color: 'var(--muted-foreground)', lineHeight: 1.6, whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
135
+ <SnippetLine text={line} />
136
+ </div>
137
+ ))}
138
+ </div>
139
+ ))}
140
+ </div>
141
+ );
142
+ })}
143
+ </div>
144
+ )}
145
+ </div>
146
+ );
147
+ }