ocean-brain 0.8.0 → 0.8.2

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 (279) hide show
  1. package/README.md +113 -0
  2. package/dist/mcp.js +230 -291
  3. package/package.json +6 -5
  4. package/server/client/dist/assets/ArrowRight.es-g2YZTklZ.js +1 -0
  5. package/server/client/dist/assets/Calendar-BP6HTuSn.js +25 -0
  6. package/server/client/dist/assets/Callout-B_IPSF_5.js +1 -0
  7. package/server/client/dist/assets/Copy.es-CNxe4ujr.js +1 -0
  8. package/server/client/dist/assets/Graph-D9GeO71k.js +1 -0
  9. package/server/client/dist/assets/Graph-Xha3fwhp.js +46 -0
  10. package/server/client/dist/assets/Image-BLEjLUqd.js +1 -0
  11. package/server/client/dist/assets/Image.es-DnON8cv2.js +1 -0
  12. package/server/client/dist/assets/Note-DjrCYtat.js +24 -0
  13. package/server/client/dist/assets/NotePropertiesPanel-2DgXEM4B.js +2 -0
  14. package/server/client/dist/assets/Plus.es-CQ8wn2is.js +1 -0
  15. package/server/client/dist/assets/Reminders-B4l3KdlK.js +1 -0
  16. package/server/client/dist/assets/Search-BN5sf7bE.js +1 -0
  17. package/server/client/dist/assets/SurfaceCard-BZm73nyS.js +1 -0
  18. package/server/client/dist/assets/Tag-oSnmViVp.js +1 -0
  19. package/server/client/dist/assets/TagNotes-BDTCcJcL.js +1 -0
  20. package/server/client/dist/assets/Trash.es-0aPpA-3S.js +1 -0
  21. package/server/client/dist/assets/ViewNotes-CiZtGJNE.js +1 -0
  22. package/server/client/dist/assets/{ViewSectionDialog-CzskgmMF.js → ViewSectionDialog-Ctl_694Y.js} +17 -17
  23. package/server/client/dist/assets/Views-D-zlatYD.js +1 -0
  24. package/server/client/dist/assets/app-DmqIOMWF.css +1 -0
  25. package/server/client/dist/assets/app-cTRcKMGc.js +26 -0
  26. package/server/client/dist/assets/floating-ui.react-dom-wWLZ1CdM.js +1 -0
  27. package/server/client/dist/assets/{image.api-8dlMwzMd.js → image.api-CVuuvTBk.js} +1 -1
  28. package/server/client/dist/assets/index-DKJChPr4.js +1 -0
  29. package/server/client/dist/assets/manage-image-BZjaxp-f.js +1 -0
  30. package/server/client/dist/assets/manage-image-detail-DPv0FQ2m.js +1 -0
  31. package/server/client/dist/assets/{mcp-EDurbggR.js → mcp-CT_9IV2c.js} +2 -2
  32. package/server/client/dist/assets/mcp-admin-adapter-xAuyZc_M.js +1 -0
  33. package/server/client/dist/assets/note-core-Buwzyxi7.js +13 -0
  34. package/server/client/dist/assets/note-runtime-CPDVOFEo.js +168 -0
  35. package/server/client/dist/assets/note-ui-CHI-tzj-.js +2 -0
  36. package/server/client/dist/assets/{index-BEUqDuAZ.css → note-ui-DL8owV-U.css} +1 -1
  37. package/server/client/dist/assets/note.api-D_00ZzJC.js +356 -0
  38. package/server/client/dist/assets/{placeholder-DeKgSBnC.js → placeholder-DbdGiYGL.js} +4 -4
  39. package/server/client/dist/assets/preload-helper-BXl3LOEh.js +1 -0
  40. package/server/client/dist/assets/properties-D447wsWk.js +1 -0
  41. package/server/client/dist/assets/route-preload.js +2 -0
  42. package/server/client/dist/assets/{tag.api-Dl8DQd0m.js → tag.api-CX0SE06J.js} +1 -1
  43. package/server/client/dist/assets/time-B3pt8vt4.js +9 -0
  44. package/server/client/dist/assets/trash-8sH7EZgc.js +1 -0
  45. package/server/client/dist/assets/url-BSnWpmJx.js +1 -0
  46. package/server/client/dist/assets/{useReminderMutate-CoXXqJY2.js → useReminderMutate-CLK1tHvT.js} +4 -4
  47. package/server/client/dist/index.html +10 -5
  48. package/server/dist/app.js +1 -2
  49. package/server/dist/features/auth/http/api.js +0 -1
  50. package/server/dist/features/auth/http/login-page.js +272 -5
  51. package/server/dist/features/auth/http/pages.js +20 -3
  52. package/server/dist/features/auth/service.js +3 -2
  53. package/server/dist/features/cache/graphql/cache.field.resolver.js +0 -1
  54. package/server/dist/features/cache/graphql/cache.mutation.resolver.js +0 -1
  55. package/server/dist/features/cache/graphql/cache.query.resolver.js +0 -1
  56. package/server/dist/features/cache/graphql/cache.resolver.js +0 -1
  57. package/server/dist/features/cache/graphql/cache.type-defs.js +0 -1
  58. package/server/dist/features/cache/graphql/index.js +0 -1
  59. package/server/dist/features/image/graphql/image.field.resolver.js +0 -1
  60. package/server/dist/features/image/graphql/image.mutation.resolver.js +0 -1
  61. package/server/dist/features/image/graphql/image.query.resolver.js +0 -1
  62. package/server/dist/features/image/graphql/image.resolver.js +0 -1
  63. package/server/dist/features/image/graphql/image.type-defs.js +0 -1
  64. package/server/dist/features/image/graphql/index.js +0 -1
  65. package/server/dist/features/image/http/upload.js +0 -1
  66. package/server/dist/features/image/services/delete.js +0 -1
  67. package/server/dist/features/image/services/upload.js +0 -1
  68. package/server/dist/features/image/services/validation.js +0 -1
  69. package/server/dist/features/mcp-admin/http/handlers.js +0 -1
  70. package/server/dist/features/mcp-admin/service.js +0 -1
  71. package/server/dist/features/note/graphql/index.js +0 -1
  72. package/server/dist/features/note/graphql/note.field.resolver.js +0 -1
  73. package/server/dist/features/note/graphql/note.mutation.resolver.js +0 -1
  74. package/server/dist/features/note/graphql/note.query.resolver.js +0 -1
  75. package/server/dist/features/note/graphql/note.resolver.js +0 -1
  76. package/server/dist/features/note/graphql/note.type-defs.js +0 -1
  77. package/server/dist/features/note/http/mcp.js +0 -1
  78. package/server/dist/features/note/services/authoring.js +0 -1
  79. package/server/dist/features/note/services/cleanup.js +0 -1
  80. package/server/dist/features/note/services/content-blocks.js +0 -1
  81. package/server/dist/features/note/services/markdown-intent-write.js +0 -1
  82. package/server/dist/features/note/services/markdown-patch.js +0 -1
  83. package/server/dist/features/note/services/properties.js +0 -1
  84. package/server/dist/features/note/services/search-backfill.js +0 -1
  85. package/server/dist/features/note/services/search.js +0 -1
  86. package/server/dist/features/note/services/snapshot.js +0 -1
  87. package/server/dist/features/note/services/tag-filter.js +0 -1
  88. package/server/dist/features/note/services/trash.js +0 -1
  89. package/server/dist/features/note/services/write-conflict.js +0 -1
  90. package/server/dist/features/note/services/write.js +0 -1
  91. package/server/dist/features/placeholder/graphql/index.js +0 -1
  92. package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js +0 -1
  93. package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js +0 -1
  94. package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js +0 -1
  95. package/server/dist/features/placeholder/graphql/placeholder.resolver.js +0 -1
  96. package/server/dist/features/placeholder/graphql/placeholder.type-defs.js +0 -1
  97. package/server/dist/features/reminder/graphql/index.js +0 -1
  98. package/server/dist/features/reminder/graphql/reminder.field.resolver.js +0 -1
  99. package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js +0 -1
  100. package/server/dist/features/reminder/graphql/reminder.query.resolver.js +0 -1
  101. package/server/dist/features/reminder/graphql/reminder.resolver.js +0 -1
  102. package/server/dist/features/reminder/graphql/reminder.type-defs.js +0 -1
  103. package/server/dist/features/tag/graphql/index.js +0 -1
  104. package/server/dist/features/tag/graphql/tag.field.resolver.js +0 -1
  105. package/server/dist/features/tag/graphql/tag.mutation.resolver.js +0 -1
  106. package/server/dist/features/tag/graphql/tag.query.resolver.js +0 -1
  107. package/server/dist/features/tag/graphql/tag.resolver.js +0 -1
  108. package/server/dist/features/tag/graphql/tag.type-defs.js +0 -1
  109. package/server/dist/features/tag/http/mcp.js +0 -1
  110. package/server/dist/features/tag/services/organization.js +0 -1
  111. package/server/dist/features/view/graphql/index.js +0 -1
  112. package/server/dist/features/view/graphql/view.mutation.resolver.js +0 -1
  113. package/server/dist/features/view/graphql/view.query.resolver.js +0 -1
  114. package/server/dist/features/view/graphql/view.resolver.js +0 -1
  115. package/server/dist/features/view/graphql/view.type-defs.js +0 -1
  116. package/server/dist/features/view/services/workspace.js +0 -1
  117. package/server/dist/main.js +0 -1
  118. package/server/dist/models.js +0 -1
  119. package/server/dist/modules/app-version.js +0 -1
  120. package/server/dist/modules/auth-guard.js +0 -1
  121. package/server/dist/modules/auth-mode.js +0 -1
  122. package/server/dist/modules/auth-redirect.js +0 -1
  123. package/server/dist/modules/blocknote-markdown-editor.js +53 -0
  124. package/server/dist/modules/blocknote-tree.js +0 -1
  125. package/server/dist/modules/blocknote.js +272 -79
  126. package/server/dist/modules/data-maintenance.js +0 -1
  127. package/server/dist/modules/error-handler.js +8 -1
  128. package/server/dist/modules/graphql.js +0 -1
  129. package/server/dist/modules/logger.js +0 -1
  130. package/server/dist/modules/mcp-auth.js +0 -1
  131. package/server/dist/modules/mcp-token.js +0 -1
  132. package/server/dist/modules/prisma-runtime.js +0 -1
  133. package/server/dist/modules/rate-limit.js +0 -1
  134. package/server/dist/modules/recovery-retention.js +0 -1
  135. package/server/dist/modules/server-events-handler.js +0 -1
  136. package/server/dist/modules/server-events.js +0 -1
  137. package/server/dist/modules/session-store.js +0 -1
  138. package/server/dist/modules/use-async.js +0 -1
  139. package/server/dist/paths.js +0 -1
  140. package/server/dist/routes/api.js +0 -1
  141. package/server/dist/routes/auth-pages.js +0 -1
  142. package/server/dist/routes/client.js +0 -1
  143. package/server/dist/routes/graphql.js +0 -1
  144. package/server/dist/routes/index.js +0 -1
  145. package/server/dist/routes/mcp.js +0 -1
  146. package/server/dist/schema/index.js +0 -1
  147. package/server/dist/start.js +0 -1
  148. package/server/dist/types/index.js +0 -1
  149. package/server/dist/types/input.js +0 -1
  150. package/server/client/dist/assets/ArrowRight.es-C4zlc54Z.js +0 -1
  151. package/server/client/dist/assets/Calendar-COrN6Hjv.js +0 -25
  152. package/server/client/dist/assets/Callout-BK0K9E74.js +0 -1
  153. package/server/client/dist/assets/Copy.es-zm8f_XXX.js +0 -1
  154. package/server/client/dist/assets/Graph-BH1fzhue.js +0 -1
  155. package/server/client/dist/assets/Image-KXuHOG4b.js +0 -1
  156. package/server/client/dist/assets/Image.es-BKahRbIm.js +0 -1
  157. package/server/client/dist/assets/Note-C143cKKw.js +0 -12
  158. package/server/client/dist/assets/Plus.es-BqeC8T8h.js +0 -1
  159. package/server/client/dist/assets/Reminders-BYG5rz1M.js +0 -1
  160. package/server/client/dist/assets/Search-DBWsIMDP.js +0 -1
  161. package/server/client/dist/assets/SurfaceCard-CNSQfYfM.js +0 -1
  162. package/server/client/dist/assets/Tag-5aCfSO8Y.js +0 -1
  163. package/server/client/dist/assets/TagNotes-BPZjN-6l.js +0 -1
  164. package/server/client/dist/assets/Trash.es-CRQkTnXZ.js +0 -1
  165. package/server/client/dist/assets/ViewNotes-DGLNF96x.js +0 -1
  166. package/server/client/dist/assets/Views-DR5jT23G.js +0 -1
  167. package/server/client/dist/assets/graph-vendor-UTvF2NS_.js +0 -46
  168. package/server/client/dist/assets/index-DGIAh3ZY.js +0 -1
  169. package/server/client/dist/assets/index-VHp17GQX.js +0 -342
  170. package/server/client/dist/assets/manage-image-RqtWKas2.js +0 -1
  171. package/server/client/dist/assets/manage-image-detail-BTSIbLlQ.js +0 -1
  172. package/server/client/dist/assets/mcp-admin-adapter-DmGUo5G8.js +0 -1
  173. package/server/client/dist/assets/note-core-BklwyZHk.js +0 -180
  174. package/server/client/dist/assets/note-vendor-DkYKUmLo.js +0 -54
  175. package/server/client/dist/assets/properties-BjrZvOYn.js +0 -1
  176. package/server/client/dist/assets/trash-C5ntddLu.js +0 -1
  177. package/server/client/dist/fonts/Pretendard-Bold.woff +0 -0
  178. package/server/client/dist/fonts/Pretendard-Regular.woff +0 -0
  179. package/server/dist/app.js.map +0 -1
  180. package/server/dist/features/auth/http/api.js.map +0 -1
  181. package/server/dist/features/auth/http/login-page.js.map +0 -1
  182. package/server/dist/features/auth/http/pages.js.map +0 -1
  183. package/server/dist/features/auth/service.js.map +0 -1
  184. package/server/dist/features/cache/graphql/cache.field.resolver.js.map +0 -1
  185. package/server/dist/features/cache/graphql/cache.mutation.resolver.js.map +0 -1
  186. package/server/dist/features/cache/graphql/cache.query.resolver.js.map +0 -1
  187. package/server/dist/features/cache/graphql/cache.resolver.js.map +0 -1
  188. package/server/dist/features/cache/graphql/cache.type-defs.js.map +0 -1
  189. package/server/dist/features/cache/graphql/index.js.map +0 -1
  190. package/server/dist/features/image/graphql/image.field.resolver.js.map +0 -1
  191. package/server/dist/features/image/graphql/image.mutation.resolver.js.map +0 -1
  192. package/server/dist/features/image/graphql/image.query.resolver.js.map +0 -1
  193. package/server/dist/features/image/graphql/image.resolver.js.map +0 -1
  194. package/server/dist/features/image/graphql/image.type-defs.js.map +0 -1
  195. package/server/dist/features/image/graphql/index.js.map +0 -1
  196. package/server/dist/features/image/http/upload.js.map +0 -1
  197. package/server/dist/features/image/services/delete.js.map +0 -1
  198. package/server/dist/features/image/services/upload.js.map +0 -1
  199. package/server/dist/features/image/services/validation.js.map +0 -1
  200. package/server/dist/features/mcp-admin/http/handlers.js.map +0 -1
  201. package/server/dist/features/mcp-admin/service.js.map +0 -1
  202. package/server/dist/features/note/graphql/index.js.map +0 -1
  203. package/server/dist/features/note/graphql/note.field.resolver.js.map +0 -1
  204. package/server/dist/features/note/graphql/note.mutation.resolver.js.map +0 -1
  205. package/server/dist/features/note/graphql/note.query.resolver.js.map +0 -1
  206. package/server/dist/features/note/graphql/note.resolver.js.map +0 -1
  207. package/server/dist/features/note/graphql/note.type-defs.js.map +0 -1
  208. package/server/dist/features/note/http/mcp.js.map +0 -1
  209. package/server/dist/features/note/services/authoring.js.map +0 -1
  210. package/server/dist/features/note/services/cleanup.js.map +0 -1
  211. package/server/dist/features/note/services/content-blocks.js.map +0 -1
  212. package/server/dist/features/note/services/markdown-intent-write.js.map +0 -1
  213. package/server/dist/features/note/services/markdown-patch.js.map +0 -1
  214. package/server/dist/features/note/services/properties.js.map +0 -1
  215. package/server/dist/features/note/services/search-backfill.js.map +0 -1
  216. package/server/dist/features/note/services/search.js.map +0 -1
  217. package/server/dist/features/note/services/snapshot.js.map +0 -1
  218. package/server/dist/features/note/services/tag-filter.js.map +0 -1
  219. package/server/dist/features/note/services/trash.js.map +0 -1
  220. package/server/dist/features/note/services/write-conflict.js.map +0 -1
  221. package/server/dist/features/note/services/write.js.map +0 -1
  222. package/server/dist/features/placeholder/graphql/index.js.map +0 -1
  223. package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js.map +0 -1
  224. package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js.map +0 -1
  225. package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js.map +0 -1
  226. package/server/dist/features/placeholder/graphql/placeholder.resolver.js.map +0 -1
  227. package/server/dist/features/placeholder/graphql/placeholder.type-defs.js.map +0 -1
  228. package/server/dist/features/reminder/graphql/index.js.map +0 -1
  229. package/server/dist/features/reminder/graphql/reminder.field.resolver.js.map +0 -1
  230. package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js.map +0 -1
  231. package/server/dist/features/reminder/graphql/reminder.query.resolver.js.map +0 -1
  232. package/server/dist/features/reminder/graphql/reminder.resolver.js.map +0 -1
  233. package/server/dist/features/reminder/graphql/reminder.type-defs.js.map +0 -1
  234. package/server/dist/features/tag/graphql/index.js.map +0 -1
  235. package/server/dist/features/tag/graphql/tag.field.resolver.js.map +0 -1
  236. package/server/dist/features/tag/graphql/tag.mutation.resolver.js.map +0 -1
  237. package/server/dist/features/tag/graphql/tag.query.resolver.js.map +0 -1
  238. package/server/dist/features/tag/graphql/tag.resolver.js.map +0 -1
  239. package/server/dist/features/tag/graphql/tag.type-defs.js.map +0 -1
  240. package/server/dist/features/tag/http/mcp.js.map +0 -1
  241. package/server/dist/features/tag/services/organization.js.map +0 -1
  242. package/server/dist/features/view/graphql/index.js.map +0 -1
  243. package/server/dist/features/view/graphql/view.mutation.resolver.js.map +0 -1
  244. package/server/dist/features/view/graphql/view.query.resolver.js.map +0 -1
  245. package/server/dist/features/view/graphql/view.resolver.js.map +0 -1
  246. package/server/dist/features/view/graphql/view.type-defs.js.map +0 -1
  247. package/server/dist/features/view/services/workspace.js.map +0 -1
  248. package/server/dist/main.js.map +0 -1
  249. package/server/dist/models.js.map +0 -1
  250. package/server/dist/modules/app-version.js.map +0 -1
  251. package/server/dist/modules/auth-guard.js.map +0 -1
  252. package/server/dist/modules/auth-mode.js.map +0 -1
  253. package/server/dist/modules/auth-redirect.js.map +0 -1
  254. package/server/dist/modules/blocknote-tree.js.map +0 -1
  255. package/server/dist/modules/blocknote.js.map +0 -1
  256. package/server/dist/modules/data-maintenance.js.map +0 -1
  257. package/server/dist/modules/error-handler.js.map +0 -1
  258. package/server/dist/modules/graphql.js.map +0 -1
  259. package/server/dist/modules/logger.js.map +0 -1
  260. package/server/dist/modules/mcp-auth.js.map +0 -1
  261. package/server/dist/modules/mcp-token.js.map +0 -1
  262. package/server/dist/modules/prisma-runtime.js.map +0 -1
  263. package/server/dist/modules/rate-limit.js.map +0 -1
  264. package/server/dist/modules/recovery-retention.js.map +0 -1
  265. package/server/dist/modules/server-events-handler.js.map +0 -1
  266. package/server/dist/modules/server-events.js.map +0 -1
  267. package/server/dist/modules/session-store.js.map +0 -1
  268. package/server/dist/modules/use-async.js.map +0 -1
  269. package/server/dist/paths.js.map +0 -1
  270. package/server/dist/routes/api.js.map +0 -1
  271. package/server/dist/routes/auth-pages.js.map +0 -1
  272. package/server/dist/routes/client.js.map +0 -1
  273. package/server/dist/routes/graphql.js.map +0 -1
  274. package/server/dist/routes/index.js.map +0 -1
  275. package/server/dist/routes/mcp.js.map +0 -1
  276. package/server/dist/schema/index.js.map +0 -1
  277. package/server/dist/start.js.map +0 -1
  278. package/server/dist/types/index.js.map +0 -1
  279. package/server/dist/types/input.js.map +0 -1
@@ -0,0 +1 @@
1
+ import{j as e,r as E}from"./floating-ui.react-dom-wWLZ1CdM.js";import{u as Q,e as k,m as F,q as L,i as M,d as _}from"./time-B3pt8vt4.js";import{aa as I,ai as K,U as O,a9 as B,b1 as H,T as i,b2 as U,b3 as W,aj as w,al as Y,Z as d,au as b,ak as $,ac as l,a8 as z,aA as G,b4 as V,at as y}from"./note.api-D_00ZzJC.js";import{S as C}from"./SurfaceCard-BZm73nyS.js";import{e as Z,N as J}from"./url-BSnWpmJx.js";import"./NotePropertiesPanel-2DgXEM4B.js";import"./Plus.es-CQ8wn2is.js";const m=25,v=K(Z),S=30,R=`Deleted notes stay here for ${S} days before permanent removal`,g={title:"Permanently delete note?",description:"This note is referenced by the notes below. If you permanently delete it, those links will stay broken. Other notes will not be edited automatically.",confirmLabel:"Delete now",confirmVariant:"danger"},P=a=>M(a).format("YYYY-MM-DD HH:mm"),X=({note:a,onClose:o})=>{const s=a?.id??"",c=a?.title||"Untitled note",r=_({queryKey:i.notes.trashDetail(s),enabled:!!a,queryFn:async()=>{const h=await V(s);if(h.type==="error")throw h;if(!h.trashedNote)throw new Error("Trashed note not found");return h.trashedNote}}),f=r.data?.contentAsMarkdown.trim()??"";return e.jsxs(y,{isOpen:!!a,onClose:o,variant:"inspect",children:[e.jsx(y.Header,{title:c,onClose:o}),e.jsx(y.Body,{children:e.jsxs("div",{className:"flex flex-col gap-3",children:[a&&e.jsxs(d,{as:"p",variant:"meta",tone:"secondary",children:["Deleted ",P(a.deletedAt)]}),r.isLoading&&e.jsx(d,{as:"p",variant:"meta",tone:"secondary",children:"Loading deleted note content..."}),r.isError&&e.jsx(d,{as:"p",variant:"meta",tone:"error",children:"Failed to load deleted note content."}),!r.isLoading&&!r.isError&&f&&e.jsx("pre",{className:"max-h-[60vh] overflow-auto whitespace-pre-wrap break-words rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-4 py-3 text-sm leading-6 text-fg-secondary",children:f}),!r.isLoading&&!r.isError&&!f&&e.jsx(d,{as:"p",variant:"meta",tone:"secondary",className:"rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-4 py-3",children:"No readable content was found for this deleted note."})]})}),e.jsx(y.Footer,{children:e.jsx(b,{variant:"ghost",size:"sm",onClick:o,children:"Close"})})]})},ee=()=>e.jsx(w,{title:"Trash",variant:"default",heading:e.jsx(l,{width:118,height:24,className:"rounded-full"}),description:e.jsx(l,{width:352,height:16,className:"rounded-full"}),children:e.jsx("div",{className:"grid gap-3",children:Array.from({length:3},(a,o)=>e.jsx(C,{padding:"compact",children:e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",children:[e.jsxs("div",{className:"min-w-0 flex-1 space-y-2",children:[e.jsx(l,{width:"40%",height:18,className:"rounded-full"}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(l,{width:168,height:14,className:"rounded-full"}),e.jsx(l,{width:176,height:14,className:"rounded-full"})]}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(l,{width:56,height:24,className:"rounded-full"}),e.jsx(l,{width:72,height:24,className:"rounded-full"})]})]}),e.jsx(l,{width:92,height:32,className:"rounded-[12px]"})]})},o))})}),te=()=>{const{page:a}=v.useSearch(),o=v.useNavigate(),s=O(),c=z(),r=Q(),[f,h]=E.useState(null),[x,j]=E.useState(null),{data:u}=B({queryKey:i.notes.trash({limit:m,offset:(a-1)*m}),queryFn:async()=>{const t=await H({limit:m,offset:(a-1)*m});if(t.type==="error")throw t;return t.trashedNotes}}),N=k({mutationFn:U,onSuccess:async t=>{if(t.type==="error")throw t;await Promise.all([s.invalidateQueries({queryKey:i.notes.all(),exact:!1}),s.invalidateQueries({queryKey:i.tags.all(),exact:!1}),s.invalidateQueries({queryKey:i.reminders.all(),exact:!1}),s.invalidateQueries({queryKey:i.images.all(),exact:!1}),s.invalidateQueries({queryKey:i.calendar.all(),exact:!1})]),c("The note has been restored."),o({to:J,params:{id:t.restoreTrashedNote.id}})},onError:()=>{c("Failed to restore the note.")}}),p=k({mutationFn:W,onSuccess:async t=>{if(t.type==="error")throw t;await s.invalidateQueries({queryKey:i.notes.trashAll(),exact:!1}),c("The note has been permanently deleted.")},onError:()=>{c("Failed to permanently delete the note.")}}),D=()=>{if(!x)return;const{id:t}=x;j(null),p.mutate(t)},A=async t=>{const n=await G(t);if(n.type==="error"){c("Failed to check linked notes before permanently deleting this note.");return}if(s.setQueryData(i.notes.backReferences(t),n.backReferences),n.backReferences.length>0){j({id:t,backReferences:n.backReferences});return}await r("Permanently delete this note? This cannot be undone.")&&p.mutate(t)},T=u.totalCount>0?`Trash (${u.totalCount})`:void 0,q=u.notes.find(t=>t.id===f)??null;return u.notes.length===0?e.jsx(w,{title:"Trash",variant:"default",heading:T,description:R,children:e.jsx(Y,{title:"Trash is empty",description:`Delete a note and you can restore it here for ${S} days before permanent removal`})}):e.jsxs(w,{title:"Trash",variant:"default",heading:T,description:R,children:[e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx("div",{className:"grid gap-3",children:u.notes.map(t=>e.jsx(C,{padding:"compact",children:e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex min-w-0 items-start gap-1.5",children:[e.jsx(d,{as:"h2",variant:"body",weight:"medium",className:"min-w-0 flex-1 break-words leading-[1.25]",children:t.title||"Untitled note"}),t.pinned&&e.jsx("span",{title:"Pinned",className:"inline-flex h-5 w-5 shrink-0 items-center justify-center text-fg-tertiary",children:e.jsx(F,{className:"h-3.5 w-3.5"})})]}),e.jsx("div",{className:"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1",children:e.jsxs(d,{as:"div",variant:"meta",weight:"medium",tone:"secondary",children:["Deleted ",P(t.deletedAt)]})}),t.tagNames.length>0&&e.jsx("div",{className:"mt-2 flex flex-wrap gap-1.5",children:t.tagNames.map(n=>e.jsx(d,{as:"span",variant:"label",weight:"medium",tone:"tertiary",className:"rounded-full border border-border-subtle bg-hover-subtle px-2 py-0.5",children:n},`${t.id}-${n}`))}),t.contentPreview&&e.jsx("div",{className:"mt-3 rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-3 py-2",children:e.jsx(d,{as:"p",variant:"meta",tone:"secondary",className:"line-clamp-3 whitespace-pre-wrap break-words",children:t.contentPreview})})]}),e.jsxs("div",{className:"flex shrink-0 flex-wrap gap-2 sm:justify-end",children:[e.jsx(b,{variant:"subtle",size:"sm",onClick:()=>h(t.id),children:"View content"}),e.jsx(b,{variant:"subtle",size:"sm",isLoading:N.isPending&&N.variables===t.id,onClick:()=>N.mutate(t.id),children:"Restore"}),e.jsx(b,{variant:"soft-danger",size:"sm",isLoading:p.isPending&&p.variables===t.id,onClick:()=>A(t.id),children:"Delete now"})]})]})},t.id))}),u.totalCount>m&&e.jsx($,{page:a,last:Math.ceil(u.totalCount/m),onChange:t=>{o({search:n=>({...n,page:t})})}})]}),e.jsx(X,{note:q,onClose:()=>h(null)}),e.jsx(L,{isOpen:!!x,title:g.title,description:g.description,references:x?.backReferences??[],confirmLabel:g.confirmLabel,confirmVariant:g.confirmVariant,onClose:()=>j(null),onConfirm:D})]})},de=()=>{const{page:a}=v.useSearch();return e.jsx(I,{fallback:e.jsx(ee,{}),errorTitle:"Failed to load trash",errorDescription:"Retry loading deleted notes",resetKeys:[a],children:e.jsx(te,{})})};export{de as default};
@@ -0,0 +1 @@
1
+ const E={HOME_ROUTE:"/",VIEWS_ROUTE:"/views",VIEW_NOTES_ROUTE:"/views/notes",CALENDAR_ROUTE:"/calendar",REMINDERS_ROUTE:"/reminders",GRAPH_ROUTE:"/graph",SEARCH_ROUTE:"/search",TAG_ROUTE:"/tag",NOTE_ROUTE:"/$id",TAG_NOTES_ROUTE:"/tag/$id",SETTINGS_ROUTE:"/setting",SETTINGS_MCP_ROUTE:"/setting/mcp",SETTINGS_TRASH_ROUTE:"/setting/trash",SETTINGS_MANAGE_IMAGE_ROUTE:"/setting/manage-image",SETTINGS_MANAGE_IMAGE_DETAIL_ROUTE:"/setting/manage-image/$id",SETTINGS_PLACEHOLDER_ROUTE:"/setting/placeholder",SETTINGS_PROPERTIES_ROUTE:"/setting/properties"},{HOME_ROUTE:T,VIEWS_ROUTE:_,VIEW_NOTES_ROUTE:R,CALENDAR_ROUTE:O,REMINDERS_ROUTE:S,GRAPH_ROUTE:U,SEARCH_ROUTE:s,TAG_ROUTE:a,NOTE_ROUTE:A,TAG_NOTES_ROUTE:G,SETTINGS_ROUTE:N,SETTINGS_MCP_ROUTE:I,SETTINGS_TRASH_ROUTE:e,SETTINGS_MANAGE_IMAGE_ROUTE:t,SETTINGS_MANAGE_IMAGE_DETAIL_ROUTE:i,SETTINGS_PLACEHOLDER_ROUTE:g,SETTINGS_PROPERTIES_ROUTE:n}=E;export{E as A,O as C,U as G,T as H,A as N,S as R,s as S,a as T,_ as V,N as a,G as b,R as c,I as d,e,t as f,i as g,g as h,n as i};
@@ -1,4 +1,4 @@
1
- import{j as y}from"./note-vendor-DkYKUmLo.js";import{h as u,a5 as f,q as n,k as R,m as $}from"./index-VHp17GQX.js";import{a as l}from"./graph-vendor-UTvF2NS_.js";const g=(e={})=>({limit:e.limit??10,offset:e.offset??0}),D=async(e,t)=>u(`
1
+ import{j as y,r as l}from"./floating-ui.react-dom-wWLZ1CdM.js";import{a6 as u,a9 as f,T as n,U as R,a8 as $}from"./note.api-D_00ZzJC.js";const g=(e={})=>({limit:e.limit??10,offset:e.offset??0}),D=async(e,t)=>u(`
2
2
  query FetchNoteReminders($noteId: ID!, $pagination: PaginationInput) {
3
3
  noteReminders(noteId: $noteId, pagination: $pagination) {
4
4
  totalCount
@@ -34,7 +34,7 @@ import{j as y}from"./note-vendor-DkYKUmLo.js";import{h as u,a5 as f,q as n,k as
34
34
  }
35
35
  }
36
36
  }
37
- `,{pagination:g(e)}),q=async e=>{const{noteId:t,reminderDate:a,priority:o="medium",content:i}=e;return u(`
37
+ `,{pagination:g(e)}),I=async e=>{const{noteId:t,reminderDate:a,priority:o="medium",content:i}=e;return u(`
38
38
  mutation CreateReminder($noteId: ID!, $reminderDate: String!, $priority: ReminderPriority, $content: String) {
39
39
  createReminder(
40
40
  noteId: $noteId,
@@ -51,7 +51,7 @@ import{j as y}from"./note-vendor-DkYKUmLo.js";import{h as u,a5 as f,q as n,k as
51
51
  updatedAt
52
52
  }
53
53
  }
54
- `,{noteId:t,reminderDate:a.toISOString(),priority:o,content:i})},I=async({id:e,reminderDate:t,completed:a,priority:o,content:i})=>{const r={id:e};return t&&(r.reminderDate=t.toISOString()),a!==void 0&&(r.completed=a),o&&(r.priority=o),i!==void 0&&(r.content=i),u(`
54
+ `,{noteId:t,reminderDate:a.toISOString(),priority:o,content:i})},q=async({id:e,reminderDate:t,completed:a,priority:o,content:i})=>{const r={id:e};return t&&(r.reminderDate=t.toISOString()),a!==void 0&&(r.completed=a),o&&(r.priority=o),i!==void 0&&(r.content=i),u(`
55
55
  mutation UpdateReminder(
56
56
  $id: ID!,
57
57
  $reminderDate: String,
@@ -79,4 +79,4 @@ import{j as y}from"./note-vendor-DkYKUmLo.js";import{h as u,a5 as f,q as n,k as
79
79
  mutation DeleteReminder($id: ID!) {
80
80
  deleteReminder(id: $id)
81
81
  }
82
- `,{id:e});function P({noteId:e,searchParams:t,render:a}){const{data:o}=f({queryKey:e?n.reminders.note(e,t):n.reminders.upcoming(t),queryFn:async()=>{if(e){const r=await D(e,t);if(r.type==="error")throw r;return r.noteReminders}const i=await A(t);if(i.type==="error")throw i;return i.upcomingReminders}});return y.jsx(y.Fragment,{children:a(o)})}function w(){const e=$(),t=R(),a=l.useCallback(async(r,m,d="medium",s,c)=>{const p=await q({noteId:r,reminderDate:m,priority:d,content:c});if(p.type==="error"){e(p.errors[0].message);return}await Promise.all([t.invalidateQueries({queryKey:n.reminders.noteAllPages(r),exact:!1}),t.invalidateQueries({queryKey:n.reminders.upcomingAllPages(),exact:!1}),t.invalidateQueries({queryKey:n.reminders.inDateRangeAll(),exact:!1})]),s&&s()},[t,e]),o=l.useCallback(async(r,m,d,s)=>{const c=await I({id:r,...d});if(c.type==="error"){e(c.errors[0].message);return}await Promise.all([t.invalidateQueries({queryKey:n.reminders.noteAllPages(m),exact:!1}),t.invalidateQueries({queryKey:n.reminders.upcomingAllPages(),exact:!1}),t.invalidateQueries({queryKey:n.reminders.inDateRangeAll(),exact:!1})]),s&&s()},[t,e]),i=l.useCallback(async(r,m,d)=>{const s=await x(r);if(s.type==="error"){e(s.errors[0].message);return}await Promise.all([t.invalidateQueries({queryKey:n.reminders.noteAllPages(m),exact:!1}),t.invalidateQueries({queryKey:n.reminders.upcomingAllPages(),exact:!1}),t.invalidateQueries({queryKey:n.reminders.inDateRangeAll(),exact:!1})]),d&&d()},[t,e]);return{onCreate:a,onUpdate:o,onDelete:i}}export{P as R,w as u};
82
+ `,{id:e});function K({noteId:e,searchParams:t,render:a}){const{data:o}=f({queryKey:e?n.reminders.note(e,t):n.reminders.upcoming(t),queryFn:async()=>{if(e){const r=await D(e,t);if(r.type==="error")throw r;return r.noteReminders}const i=await A(t);if(i.type==="error")throw i;return i.upcomingReminders}});return y.jsx(y.Fragment,{children:a(o)})}function P(){const e=$(),t=R(),a=l.useCallback(async(r,m,d="medium",s,c)=>{const p=await I({noteId:r,reminderDate:m,priority:d,content:c});if(p.type==="error"){e(p.errors[0].message);return}await Promise.all([t.invalidateQueries({queryKey:n.reminders.noteAllPages(r),exact:!1}),t.invalidateQueries({queryKey:n.reminders.upcomingAllPages(),exact:!1}),t.invalidateQueries({queryKey:n.reminders.inDateRangeAll(),exact:!1})]),s&&s()},[t,e]),o=l.useCallback(async(r,m,d,s)=>{const c=await q({id:r,...d});if(c.type==="error"){e(c.errors[0].message);return}await Promise.all([t.invalidateQueries({queryKey:n.reminders.noteAllPages(m),exact:!1}),t.invalidateQueries({queryKey:n.reminders.upcomingAllPages(),exact:!1}),t.invalidateQueries({queryKey:n.reminders.inDateRangeAll(),exact:!1})]),s&&s()},[t,e]),i=l.useCallback(async(r,m,d)=>{const s=await x(r);if(s.type==="error"){e(s.errors[0].message);return}await Promise.all([t.invalidateQueries({queryKey:n.reminders.noteAllPages(m),exact:!1}),t.invalidateQueries({queryKey:n.reminders.upcomingAllPages(),exact:!1}),t.invalidateQueries({queryKey:n.reminders.inDateRangeAll(),exact:!1})]),d&&d()},[t,e]);return{onCreate:a,onUpdate:o,onDelete:i}}export{K as R,P as u};
@@ -2,15 +2,20 @@
2
2
  <html lang="en">
3
3
 
4
4
  <head>
5
+ <script type="module" src="/assets/route-preload.js"></script>
6
+
5
7
  <meta charset="UTF-8" />
6
8
  <link rel="icon" type="image/png" href="/icon.png" />
7
9
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
10
  <title>Ocean Brain</title>
9
- <script type="module" crossorigin src="/assets/index-VHp17GQX.js"></script>
10
- <link rel="modulepreload" crossorigin href="/assets/graph-vendor-UTvF2NS_.js">
11
- <link rel="modulepreload" crossorigin href="/assets/note-core-BklwyZHk.js">
12
- <link rel="modulepreload" crossorigin href="/assets/note-vendor-DkYKUmLo.js">
13
- <link rel="stylesheet" crossorigin href="/assets/index-BEUqDuAZ.css">
11
+ <script type="module" crossorigin src="/assets/app-cTRcKMGc.js"></script>
12
+ <link rel="modulepreload" crossorigin href="/assets/floating-ui.react-dom-wWLZ1CdM.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/url-BSnWpmJx.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/note.api-D_00ZzJC.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/time-B3pt8vt4.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/preload-helper-BXl3LOEh.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/Graph-D9GeO71k.js">
18
+ <link rel="stylesheet" crossorigin href="/assets/app-DmqIOMWF.css">
14
19
  </head>
15
20
 
16
21
  <body>
@@ -18,7 +18,7 @@ const createAppWithMcpAuth = (authConfig, mcpAdminService) => {
18
18
  process.stderr.write(`[recovery] Startup cleanup failed: ${message}
19
19
  `);
20
20
  });
21
- app.use(logger).use(createSessionMiddleware(authConfig)).use(express.urlencoded({ extended: false })).use(express.json({ limit: "50mb" })).use("/api", createApiRouter(authConfig, mcpAdminService)).use(createAuthPagesRouter(authConfig)).use("/graphql", createGraphqlRouter(authConfig, mcpAdminService)).use(createClientRouter(authConfig)).use(createErrorHandler());
21
+ app.use(logger).use(createSessionMiddleware(authConfig)).use(express.urlencoded({ extended: false, inflate: false })).use(express.json({ limit: "50mb", inflate: false })).use("/api", createApiRouter(authConfig, mcpAdminService)).use(createAuthPagesRouter(authConfig)).use("/graphql", createGraphqlRouter(authConfig, mcpAdminService)).use(createClientRouter(authConfig)).use(createErrorHandler());
22
22
  return app;
23
23
  };
24
24
  var app_default = createApp;
@@ -27,4 +27,3 @@ export {
27
27
  createAppWithMcpAuth,
28
28
  app_default as default
29
29
  };
30
- //# sourceMappingURL=app.js.map
@@ -41,4 +41,3 @@ export {
41
41
  createLogoutHandler,
42
42
  createSessionStatusHandler
43
43
  };
44
- //# sourceMappingURL=api.js.map
@@ -1,9 +1,276 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  const LOGIN_ERROR_TOKEN = "<!-- OCEAN_BRAIN_LOGIN_ERROR -->";
3
3
  const NEXT_PATH_TOKEN = "OCEAN_BRAIN_NEXT_PATH";
4
+ const SESSION_GENERATION_TOKEN = "OCEAN_BRAIN_SESSION_GENERATION";
4
5
  const readLoginPageTemplate = () => {
5
6
  if (true) {
6
- return '<!doctype html>\n<html lang="en">\n<head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width, initial-scale=1" />\n <title>Ocean Brain Sign In</title>\n <style>\n :root {\n color-scheme: light dark;\n --surface: #f7f8fa;\n --elevated: #fbfcfd;\n --fg-default: #202631;\n --fg-secondary: #626d7c;\n --fg-tertiary: #8993a2;\n --fg-placeholder: #98a2b0;\n --fg-error: #a24545;\n --fg-on-filled: #f6f8fb;\n --border-subtle: #e5e9ee;\n --border-focus: #757f8d;\n --border-error: #d47777;\n --accent-soft-danger: rgba(214, 141, 135, 0.18);\n --accent-soft-primary: rgba(93, 102, 114, 0.12);\n --cta: #23272d;\n --cta-hover: #171a1f;\n --page-bg: #f2f5f8;\n font-family: "Pretendard Variable", "Pretendard", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;\n background: var(--page-bg);\n color: var(--fg-default);\n }\n @media (prefers-color-scheme: dark) {\n :root {\n --surface: #16191e;\n --elevated: #1d2229;\n --fg-default: #e6ebf2;\n --fg-secondary: #a9b3c2;\n --fg-tertiary: #7f8998;\n --fg-placeholder: #717b88;\n --fg-error: #efaaaa;\n --fg-on-filled: #12161c;\n --border-subtle: #29303a;\n --border-focus: #b4c0cf;\n --border-error: #efaaaa;\n --accent-soft-danger: rgba(215, 146, 139, 0.14);\n --accent-soft-primary: rgba(184, 192, 202, 0.12);\n --cta: #edf1f5;\n --cta-hover: #f8fafc;\n --page-bg: #101318;\n }\n }\n * {\n box-sizing: border-box;\n }\n body {\n margin: 0;\n min-height: 100vh;\n display: grid;\n place-items: center;\n background: var(--page-bg);\n padding: 24px;\n }\n .shell {\n width: min(392px, 100%);\n }\n .brand {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 14px;\n color: var(--fg-tertiary);\n font-size: 12px;\n font-weight: 700;\n line-height: 1.4;\n }\n .brand img {\n width: 28px;\n height: 28px;\n border-radius: 8px;\n }\n .panel {\n padding: 24px;\n border: 1px solid var(--border-subtle);\n border-radius: 16px;\n background: var(--surface);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.1),\n 0 1px 4px -2px rgba(23, 29, 38, 0.04);\n }\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n label {\n display: block;\n margin-bottom: 8px;\n color: var(--fg-secondary);\n font-size: 0.75rem;\n line-height: 1.4;\n font-weight: 600;\n }\n form {\n display: grid;\n gap: 16px;\n }\n .field {\n display: grid;\n }\n input {\n width: 100%;\n box-sizing: border-box;\n padding: 14px 16px;\n border: 1px solid var(--border-subtle);\n border-radius: 16px;\n background: var(--elevated);\n font-size: 16px;\n color: var(--fg-default);\n transition: border-color 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;\n }\n input::placeholder {\n color: var(--fg-placeholder);\n }\n input:focus {\n outline: none;\n border-color: var(--border-focus);\n box-shadow: 0 0 0 4px var(--accent-soft-primary);\n }\n button {\n width: 100%;\n border: 1px solid transparent;\n border-radius: 14px;\n background: var(--cta);\n color: var(--fg-on-filled);\n padding: 14px 18px;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;\n }\n button:hover {\n background: var(--cta-hover);\n }\n button:active {\n transform: translateY(1px);\n }\n button:focus-visible {\n outline: none;\n box-shadow: 0 0 0 4px var(--accent-soft-primary);\n }\n .error {\n margin-bottom: 18px;\n padding: 13px 14px;\n border: 1px solid var(--border-error);\n border-radius: 14px;\n background: var(--accent-soft-danger);\n color: var(--fg-error);\n font-size: 14px;\n font-weight: 600;\n }\n @media (prefers-color-scheme: dark) {\n .panel {\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.03),\n 0 1px 4px -2px rgba(0, 0, 0, 0.18);\n }\n }\n @media (max-width: 640px) {\n body {\n place-items: stretch;\n padding: 18px;\n }\n .shell {\n width: 100%;\n margin: auto 0;\n }\n .panel {\n padding: 22px;\n }\n }\n </style>\n</head>\n<body>\n <main class="shell">\n <div class="brand"><img src="/icon.png" alt="" aria-hidden="true" />Ocean Brain</div>\n <section class="panel" aria-labelledby="login-title">\n <h1 id="login-title" class="sr-only">Sign in</h1>\n <!-- OCEAN_BRAIN_LOGIN_ERROR -->\n <form method="post" action="/login">\n <!-- OCEAN_BRAIN_CSRF_INPUT -->\n <input type="hidden" name="next" value="OCEAN_BRAIN_NEXT_PATH" />\n <div class="field">\n <label for="password">Password</label>\n <input id="password" name="password" type="password" autocomplete="current-password" required autofocus />\n </div>\n <button type="submit">Sign in</button>\n </form>\n </section>\n </main>\n</body>\n</html>\n';
7
+ return `<!doctype html>
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="utf-8" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <meta name="ocean-brain-session-generation" content="OCEAN_BRAIN_SESSION_GENERATION" />
13
+ <title>Ocean Brain Sign In</title>
14
+ <style>
15
+ :root {
16
+ color-scheme: light dark;
17
+ --surface: #f7f8fa;
18
+ --elevated: #fbfcfd;
19
+ --fg-default: #202631;
20
+ --fg-secondary: #626d7c;
21
+ --fg-tertiary: #8993a2;
22
+ --fg-placeholder: #98a2b0;
23
+ --fg-error: #a24545;
24
+ --fg-on-filled: #f6f8fb;
25
+ --border-subtle: #e5e9ee;
26
+ --border-focus: #757f8d;
27
+ --border-error: #d47777;
28
+ --accent-soft-danger: rgba(214, 141, 135, 0.18);
29
+ --accent-soft-primary: rgba(93, 102, 114, 0.12);
30
+ --cta: #23272d;
31
+ --cta-hover: #171a1f;
32
+ --page-bg: #f2f5f8;
33
+ font-family: "Pretendard Variable", "Pretendard", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
34
+ background: var(--page-bg);
35
+ color: var(--fg-default);
36
+ }
37
+ @media (prefers-color-scheme: dark) {
38
+ :root {
39
+ --surface: #16191e;
40
+ --elevated: #1d2229;
41
+ --fg-default: #e6ebf2;
42
+ --fg-secondary: #a9b3c2;
43
+ --fg-tertiary: #7f8998;
44
+ --fg-placeholder: #717b88;
45
+ --fg-error: #efaaaa;
46
+ --fg-on-filled: #12161c;
47
+ --border-subtle: #29303a;
48
+ --border-focus: #b4c0cf;
49
+ --border-error: #efaaaa;
50
+ --accent-soft-danger: rgba(215, 146, 139, 0.14);
51
+ --accent-soft-primary: rgba(184, 192, 202, 0.12);
52
+ --cta: #edf1f5;
53
+ --cta-hover: #f8fafc;
54
+ --page-bg: #101318;
55
+ }
56
+ }
57
+ * {
58
+ box-sizing: border-box;
59
+ }
60
+ body {
61
+ margin: 0;
62
+ min-height: 100vh;
63
+ display: grid;
64
+ place-items: center;
65
+ background: var(--page-bg);
66
+ padding: 24px;
67
+ }
68
+ .shell {
69
+ width: min(392px, 100%);
70
+ }
71
+ .brand {
72
+ display: flex;
73
+ align-items: center;
74
+ gap: 10px;
75
+ margin-bottom: 14px;
76
+ color: var(--fg-tertiary);
77
+ font-size: 12px;
78
+ font-weight: 700;
79
+ line-height: 1.4;
80
+ }
81
+ .brand img {
82
+ width: 28px;
83
+ height: 28px;
84
+ border-radius: 8px;
85
+ }
86
+ .panel {
87
+ padding: 24px;
88
+ border: 1px solid var(--border-subtle);
89
+ border-radius: 16px;
90
+ background: var(--surface);
91
+ box-shadow:
92
+ inset 0 1px 0 rgba(255, 255, 255, 0.1),
93
+ 0 1px 4px -2px rgba(23, 29, 38, 0.04);
94
+ }
95
+ .sr-only {
96
+ position: absolute;
97
+ width: 1px;
98
+ height: 1px;
99
+ padding: 0;
100
+ margin: -1px;
101
+ overflow: hidden;
102
+ clip: rect(0, 0, 0, 0);
103
+ white-space: nowrap;
104
+ border: 0;
105
+ }
106
+ label {
107
+ display: block;
108
+ margin-bottom: 8px;
109
+ color: var(--fg-secondary);
110
+ font-size: 0.75rem;
111
+ line-height: 1.4;
112
+ font-weight: 600;
113
+ }
114
+ form {
115
+ display: grid;
116
+ gap: 16px;
117
+ }
118
+ .field {
119
+ display: grid;
120
+ }
121
+ input {
122
+ width: 100%;
123
+ box-sizing: border-box;
124
+ padding: 14px 16px;
125
+ border: 1px solid var(--border-subtle);
126
+ border-radius: 16px;
127
+ background: var(--elevated);
128
+ font-size: 16px;
129
+ color: var(--fg-default);
130
+ transition: border-color 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;
131
+ }
132
+ input::placeholder {
133
+ color: var(--fg-placeholder);
134
+ }
135
+ input:focus {
136
+ outline: none;
137
+ border-color: var(--border-focus);
138
+ box-shadow: 0 0 0 4px var(--accent-soft-primary);
139
+ }
140
+ button {
141
+ width: 100%;
142
+ border: 1px solid transparent;
143
+ border-radius: 14px;
144
+ background: var(--cta);
145
+ color: var(--fg-on-filled);
146
+ padding: 14px 18px;
147
+ font-size: 16px;
148
+ font-weight: 600;
149
+ cursor: pointer;
150
+ transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;
151
+ }
152
+ button:hover {
153
+ background: var(--cta-hover);
154
+ }
155
+ button:active {
156
+ transform: translateY(1px);
157
+ }
158
+ button:focus-visible {
159
+ outline: none;
160
+ box-shadow: 0 0 0 4px var(--accent-soft-primary);
161
+ }
162
+ .error {
163
+ margin-bottom: 18px;
164
+ padding: 13px 14px;
165
+ border: 1px solid var(--border-error);
166
+ border-radius: 14px;
167
+ background: var(--accent-soft-danger);
168
+ color: var(--fg-error);
169
+ font-size: 14px;
170
+ font-weight: 600;
171
+ }
172
+ @media (prefers-color-scheme: dark) {
173
+ .panel {
174
+ box-shadow:
175
+ inset 0 1px 0 rgba(255, 255, 255, 0.03),
176
+ 0 1px 4px -2px rgba(0, 0, 0, 0.18);
177
+ }
178
+ }
179
+ @media (max-width: 640px) {
180
+ body {
181
+ place-items: stretch;
182
+ padding: 18px;
183
+ }
184
+ .shell {
185
+ width: 100%;
186
+ margin: auto 0;
187
+ }
188
+ .panel {
189
+ padding: 22px;
190
+ }
191
+ }
192
+ </style>
193
+ </head>
194
+ <body>
195
+ <main class="shell">
196
+ <div class="brand"><img src="/icon.png" alt="" aria-hidden="true" />Ocean Brain</div>
197
+ <section class="panel" aria-labelledby="login-title">
198
+ <h1 id="login-title" class="sr-only">Sign in</h1>
199
+ <!-- OCEAN_BRAIN_LOGIN_ERROR -->
200
+ <form method="post" action="/login">
201
+ <!-- OCEAN_BRAIN_CSRF_INPUT -->
202
+ <input type="hidden" name="next" value="OCEAN_BRAIN_NEXT_PATH" />
203
+ <div class="field">
204
+ <label for="password">Password</label>
205
+ <input id="password" name="password" type="password" autocomplete="current-password" required autofocus />
206
+ </div>
207
+ <button type="submit">Sign in</button>
208
+ </form>
209
+ </section>
210
+ </main>
211
+ <script>
212
+ (() => {
213
+ const generationMeta = document.querySelector('meta[name="ocean-brain-session-generation"]');
214
+ const pageGeneration = generationMeta?.getAttribute('content');
215
+ const generationHeader = 'X-Ocean-Brain-Session-Generation';
216
+ const sessionEndpoint = '/api/auth/session';
217
+ const form = document.querySelector('form');
218
+ let checkPromise;
219
+ let submitting = false;
220
+
221
+ const checkForStaleLoginPage = async () => {
222
+ if (!pageGeneration) {
223
+ return false;
224
+ }
225
+
226
+ checkPromise ??= fetch(sessionEndpoint, {
227
+ cache: 'no-store',
228
+ credentials: 'same-origin',
229
+ })
230
+ .then((response) => {
231
+ const currentGeneration = response.headers.get(generationHeader);
232
+ const isStale = Boolean(currentGeneration && currentGeneration !== pageGeneration);
233
+
234
+ if (isStale) {
235
+ window.location.reload();
236
+ }
237
+
238
+ return isStale;
239
+ })
240
+ .catch(() => false)
241
+ .finally(() => {
242
+ checkPromise = undefined;
243
+ });
244
+
245
+ return checkPromise;
246
+ };
247
+
248
+ void checkForStaleLoginPage();
249
+ window.addEventListener('pageshow', () => {
250
+ void checkForStaleLoginPage();
251
+ });
252
+
253
+ form?.addEventListener('submit', async (event) => {
254
+ event.preventDefault();
255
+
256
+ if (submitting) {
257
+ return;
258
+ }
259
+
260
+ submitting = true;
261
+
262
+ const isStale = await checkForStaleLoginPage();
263
+ if (isStale) {
264
+ return;
265
+ }
266
+
267
+ form.submit();
268
+ });
269
+ })();
270
+ </script>
271
+ </body>
272
+ </html>
273
+ `;
7
274
  }
8
275
  return readFileSync(new URL("./login-page.html", import.meta.url), "utf8");
9
276
  };
@@ -22,14 +289,14 @@ const renderCsrfInput = (csrfToken) => {
22
289
  return `<input type="hidden" name="_csrf" value="${escapeHtml(csrfToken)}" />`;
23
290
  };
24
291
  const renderLoginTemplate = (values) => {
25
- return LOGIN_PAGE_TEMPLATE.replace(LOGIN_ERROR_TOKEN, values.errorBlock).replace(NEXT_PATH_TOKEN, values.nextPath).replace("<!-- OCEAN_BRAIN_CSRF_INPUT -->", values.csrfInput);
292
+ return LOGIN_PAGE_TEMPLATE.replace(LOGIN_ERROR_TOKEN, values.errorBlock).replace(SESSION_GENERATION_TOKEN, values.sessionGeneration).replace(NEXT_PATH_TOKEN, values.nextPath).replace("<!-- OCEAN_BRAIN_CSRF_INPUT -->", values.csrfInput);
26
293
  };
27
- const renderLoginPage = ({ nextPath, errorMessage, csrfToken }) => renderLoginTemplate({
294
+ const renderLoginPage = ({ nextPath, errorMessage, csrfToken, sessionGeneration }) => renderLoginTemplate({
28
295
  errorBlock: renderLoginError(errorMessage),
29
296
  nextPath: escapeHtml(nextPath),
30
- csrfInput: renderCsrfInput(csrfToken)
297
+ csrfInput: renderCsrfInput(csrfToken),
298
+ sessionGeneration: escapeHtml(sessionGeneration ?? "")
31
299
  });
32
300
  export {
33
301
  renderLoginPage
34
302
  };
35
- //# sourceMappingURL=login-page.js.map
@@ -1,35 +1,50 @@
1
1
  import {
2
2
  compareSharedSecret,
3
3
  destroySession,
4
+ getSessionGeneration,
4
5
  refreshCsrfToken,
5
6
  regenerateSession,
6
7
  sanitizeRedirectPath
7
8
  } from "../service.js";
8
9
  import { renderLoginPage } from "./login-page.js";
10
+ const setLoginPageHeaders = (res) => {
11
+ res.set("Cache-Control", "no-store");
12
+ };
9
13
  const createLoginPageHandler = (authConfig) => {
10
14
  return async (req, res) => {
11
15
  if (authConfig.mode !== "password" || req.session?.authenticated) {
16
+ setLoginPageHeaders(res);
12
17
  res.redirect(303, sanitizeRedirectPath(req.query.next));
13
18
  return;
14
19
  }
15
20
  const nextPath = sanitizeRedirectPath(req.query.next);
16
- res.status(200).type("html").send(renderLoginPage({ nextPath, csrfToken: res.locals._csrf })).end();
21
+ setLoginPageHeaders(res);
22
+ res.status(200).type("html").send(
23
+ renderLoginPage({
24
+ nextPath,
25
+ csrfToken: res.locals._csrf,
26
+ sessionGeneration: getSessionGeneration()
27
+ })
28
+ ).end();
17
29
  };
18
30
  };
19
31
  const createLoginPageSubmitHandler = (authConfig) => {
20
32
  return async (req, res) => {
21
33
  const nextPath = sanitizeRedirectPath(req.body?.next);
22
34
  if (authConfig.mode !== "password" || !authConfig.password) {
35
+ setLoginPageHeaders(res);
23
36
  res.redirect(303, nextPath);
24
37
  return;
25
38
  }
26
39
  const password = typeof req.body?.password === "string" ? req.body.password : "";
27
40
  if (!password || !compareSharedSecret(authConfig.password, password)) {
41
+ setLoginPageHeaders(res);
28
42
  res.status(401).type("html").send(
29
43
  renderLoginPage({
30
44
  nextPath,
31
45
  errorMessage: "Invalid password",
32
- csrfToken: res.locals._csrf
46
+ csrfToken: res.locals._csrf,
47
+ sessionGeneration: getSessionGeneration()
33
48
  })
34
49
  ).end();
35
50
  return;
@@ -37,6 +52,7 @@ const createLoginPageSubmitHandler = (authConfig) => {
37
52
  await regenerateSession(req);
38
53
  req.session.authenticated = true;
39
54
  refreshCsrfToken(req);
55
+ setLoginPageHeaders(res);
40
56
  res.redirect(303, nextPath);
41
57
  };
42
58
  };
@@ -44,9 +60,11 @@ const createLogoutPageHandler = (authConfig) => {
44
60
  return async (req, res) => {
45
61
  if (authConfig.mode === "password") {
46
62
  await destroySession(req);
63
+ setLoginPageHeaders(res);
47
64
  res.redirect(303, "/login");
48
65
  return;
49
66
  }
67
+ setLoginPageHeaders(res);
50
68
  res.redirect(303, "/");
51
69
  };
52
70
  };
@@ -55,4 +73,3 @@ export {
55
73
  createLoginPageSubmitHandler,
56
74
  createLogoutPageHandler
57
75
  };
58
- //# sourceMappingURL=pages.js.map
@@ -5,6 +5,7 @@ import { sanitizeRedirectPath } from "../../modules/auth-redirect.js";
5
5
  import { createAppError } from "../../modules/error-handler.js";
6
6
  const AUTH_SESSION_GENERATION_HEADER = "X-Ocean-Brain-Session-Generation";
7
7
  const AUTH_SESSION_GENERATION = crypto.randomUUID();
8
+ const getSessionGeneration = () => AUTH_SESSION_GENERATION;
8
9
  const createPasswordHash = async (password) => {
9
10
  const salt = crypto.randomBytes(16).toString("hex");
10
11
  const hash = crypto.pbkdf2Sync(password, salt, 1e3, 64, "sha512").toString("hex");
@@ -18,7 +19,7 @@ const comparePassword = async (password, storedHash) => {
18
19
  const compareSharedSecret = compareCommonSharedSecret;
19
20
  const buildSessionResponse = (authConfig, req) => buildAuthSessionResponse(authConfig, Boolean(req.session?.authenticated));
20
21
  const setSessionStatusHeaders = (res) => {
21
- res.set(AUTH_SESSION_GENERATION_HEADER, AUTH_SESSION_GENERATION);
22
+ res.set(AUTH_SESSION_GENERATION_HEADER, getSessionGeneration());
22
23
  res.set("Cache-Control", "no-store");
23
24
  };
24
25
  const refreshCsrfToken = (req) => {
@@ -64,9 +65,9 @@ export {
64
65
  compareSharedSecret,
65
66
  createPasswordHash,
66
67
  destroySession,
68
+ getSessionGeneration,
67
69
  refreshCsrfToken,
68
70
  regenerateSession,
69
71
  sanitizeRedirectPath,
70
72
  setSessionStatusHeaders
71
73
  };
72
- //# sourceMappingURL=service.js.map
@@ -2,4 +2,3 @@ const cacheFieldResolvers = {};
2
2
  export {
3
3
  cacheFieldResolvers
4
4
  };
5
- //# sourceMappingURL=cache.field.resolver.js.map
@@ -46,4 +46,3 @@ export {
46
46
  createDeleteCacheMutationResolver,
47
47
  createSetCacheMutationResolver
48
48
  };
49
- //# sourceMappingURL=cache.mutation.resolver.js.map
@@ -18,4 +18,3 @@ export {
18
18
  createAllCachesQueryResolver,
19
19
  createCacheQueryResolver
20
20
  };
21
- //# sourceMappingURL=cache.query.resolver.js.map
@@ -9,4 +9,3 @@ const cacheResolvers = {
9
9
  export {
10
10
  cacheResolvers
11
11
  };
12
- //# sourceMappingURL=cache.resolver.js.map
@@ -29,4 +29,3 @@ export {
29
29
  cacheType,
30
30
  cacheTypeDefs
31
31
  };
32
- //# sourceMappingURL=cache.type-defs.js.map
@@ -18,4 +18,3 @@ export {
18
18
  createDeleteCacheMutationResolver,
19
19
  createSetCacheMutationResolver
20
20
  };
21
- //# sourceMappingURL=index.js.map
@@ -11,4 +11,3 @@ export {
11
11
  createImageReferenceCountFieldResolver,
12
12
  imageFieldResolvers
13
13
  };
14
- //# sourceMappingURL=image.field.resolver.js.map
@@ -18,4 +18,3 @@ export {
18
18
  createDeleteImageMutationResolver,
19
19
  imageMutationResolvers
20
20
  };
21
- //# sourceMappingURL=image.mutation.resolver.js.map
@@ -37,4 +37,3 @@ export {
37
37
  createImageQueryResolver,
38
38
  imageQueryResolvers
39
39
  };
40
- //# sourceMappingURL=image.query.resolver.js.map
@@ -9,4 +9,3 @@ const imageResolvers = {
9
9
  export {
10
10
  imageResolvers
11
11
  };
12
- //# sourceMappingURL=image.resolver.js.map
@@ -28,4 +28,3 @@ const imageTypeDefs = gql`
28
28
  export {
29
29
  imageTypeDefs
30
30
  };
31
- //# sourceMappingURL=image.type-defs.js.map
@@ -13,4 +13,3 @@ export {
13
13
  imageResolvers,
14
14
  imageTypeDefs
15
15
  };
16
- //# sourceMappingURL=index.js.map
@@ -56,4 +56,3 @@ const createUploadImageHandler = (persistImage = persistUploadedImage) => {
56
56
  export {
57
57
  createUploadImageHandler
58
58
  };
59
- //# sourceMappingURL=upload.js.map
@@ -49,4 +49,3 @@ export {
49
49
  deleteImageById,
50
50
  resolveStoredImagePath
51
51
  };
52
- //# sourceMappingURL=delete.js.map
@@ -107,4 +107,3 @@ export {
107
107
  hashImageBuffer,
108
108
  persistUploadedImage
109
109
  };
110
- //# sourceMappingURL=upload.js.map
@@ -108,4 +108,3 @@ export {
108
108
  normalizeImageContentType,
109
109
  validateImagePayload
110
110
  };
111
- //# sourceMappingURL=validation.js.map
@@ -47,4 +47,3 @@ export {
47
47
  createMcpAdminSetEnabledHandler,
48
48
  createMcpAdminStatusHandler
49
49
  };
50
- //# sourceMappingURL=handlers.js.map
@@ -85,4 +85,3 @@ const createMcpAdminService = (db = models) => {
85
85
  export {
86
86
  createMcpAdminService
87
87
  };
88
- //# sourceMappingURL=service.js.map
@@ -5,4 +5,3 @@ export {
5
5
  noteResolvers,
6
6
  noteTypeDefs
7
7
  };
8
- //# sourceMappingURL=index.js.map
@@ -24,4 +24,3 @@ export {
24
24
  noteFieldResolvers,
25
25
  noteSnapshotFieldResolvers
26
26
  };
27
- //# sourceMappingURL=note.field.resolver.js.map