ocean-brain 0.4.2 → 0.5.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 (64) hide show
  1. package/package.json +1 -1
  2. package/server/client/dist/assets/{Calendar-DI0PhQTq.js → Calendar-BU9XgX2N.js} +1 -1
  3. package/server/client/dist/assets/Callout-C6tVCxOT.js +1 -0
  4. package/server/client/dist/assets/{Graph-Dhuog4tc.js → Graph-Dond2hGK.js} +1 -1
  5. package/server/client/dist/assets/{Image-DlJ4MDo3.js → Image-DxWhlIDG.js} +1 -1
  6. package/server/client/dist/assets/{Image.es-COwqkQuh.js → Image.es-BjGK7y6Y.js} +1 -1
  7. package/server/client/dist/assets/Note-Cr6GtXUM.js +21 -0
  8. package/server/client/dist/assets/{Plus.es-KeV96R_e.js → Plus.es-EcmtS_uw.js} +1 -1
  9. package/server/client/dist/assets/{Reminders-D7MCTNxb.js → Reminders-BlLLT_x0.js} +1 -1
  10. package/server/client/dist/assets/{Search-B-fwTr5y.js → Search-CrZ0ap8x.js} +1 -1
  11. package/server/client/dist/assets/{SurfaceCard-CHjcCuqZ.js → SurfaceCard-CO0oVxeL.js} +1 -1
  12. package/server/client/dist/assets/{Tag-BbV2z-Db.js → Tag-vws5AfNW.js} +1 -1
  13. package/server/client/dist/assets/{TagNotes-2HKF09Jm.js → TagNotes-Fmf1Kgim.js} +1 -1
  14. package/server/client/dist/assets/{Trash.es-BqRTZPrI.js → Trash.es-DmwfSy3l.js} +1 -1
  15. package/server/client/dist/assets/{ViewNotes-TfM3Uzbp.js → ViewNotes-BALN7IfH.js} +1 -1
  16. package/server/client/dist/assets/{Views-CUwtnfa8.js → Views-Ccc_Ml5U.js} +1 -1
  17. package/server/client/dist/assets/{image.api-CMdQbCyN.js → image.api-DeF9ck7g.js} +1 -1
  18. package/server/client/dist/assets/{index-zhxZjg7V.js → index-BSVE2cGd.js} +1 -1
  19. package/server/client/dist/assets/{index-D2gRB1ig.css → index-BZUGWdOU.css} +1 -1
  20. package/server/client/dist/assets/{index-B4l2UvuC.js → index-BvKBTjQq.js} +51 -37
  21. package/server/client/dist/assets/{manage-image-DdXb5Bkj.js → manage-image-IoTXaMBP.js} +1 -1
  22. package/server/client/dist/assets/{manage-image-detail-CrrXCQIX.js → manage-image-detail-BRTXwOsh.js} +1 -1
  23. package/server/client/dist/assets/{mcp-DqGLZv90.js → mcp-Bh743HBz.js} +1 -1
  24. package/server/client/dist/assets/note-core-Z6kqulGB.js +180 -0
  25. package/server/client/dist/assets/note-vendor-i1M2FSI2.js +54 -0
  26. package/server/client/dist/assets/{placeholder-BXLtvdtN.js → placeholder-XQqo1Fvr.js} +3 -3
  27. package/server/client/dist/assets/{trash-3eUAjFRh.js → trash-hiJIg6-c.js} +1 -1
  28. package/server/client/dist/assets/{useReminderMutate-v1EXqM90.js → useReminderMutate-D21PurIS.js} +1 -1
  29. package/server/client/dist/assets/{view-dashboard-T6gmohr-.js → view-dashboard-BuaZ4MvI.js} +1 -1
  30. package/server/client/dist/index.html +4 -4
  31. package/server/dist/features/note/graphql/note.field.resolver.js +14 -1
  32. package/server/dist/features/note/graphql/note.field.resolver.js.map +1 -1
  33. package/server/dist/features/note/graphql/note.mutation.resolver.js +73 -36
  34. package/server/dist/features/note/graphql/note.mutation.resolver.js.map +1 -1
  35. package/server/dist/features/note/graphql/note.query.resolver.js +74 -76
  36. package/server/dist/features/note/graphql/note.query.resolver.js.map +1 -1
  37. package/server/dist/features/note/graphql/note.resolver.js +3 -2
  38. package/server/dist/features/note/graphql/note.resolver.js.map +1 -1
  39. package/server/dist/features/note/graphql/note.type-defs.js +4 -1
  40. package/server/dist/features/note/graphql/note.type-defs.js.map +1 -1
  41. package/server/dist/features/note/services/cleanup.js +4 -3
  42. package/server/dist/features/note/services/cleanup.js.map +1 -1
  43. package/server/dist/features/note/services/content-blocks.js +113 -0
  44. package/server/dist/features/note/services/content-blocks.js.map +1 -0
  45. package/server/dist/features/note/services/search.js +10 -29
  46. package/server/dist/features/note/services/search.js.map +1 -1
  47. package/server/dist/features/note/services/snapshot.js +172 -12
  48. package/server/dist/features/note/services/snapshot.js.map +1 -1
  49. package/server/dist/features/note/services/write-conflict.js +87 -0
  50. package/server/dist/features/note/services/write-conflict.js.map +1 -0
  51. package/server/dist/features/note/services/write.js +107 -0
  52. package/server/dist/features/note/services/write.js.map +1 -0
  53. package/server/dist/modules/blocknote-tree.js +41 -0
  54. package/server/dist/modules/blocknote-tree.js.map +1 -0
  55. package/server/dist/modules/error-handler.js +7 -4
  56. package/server/dist/modules/error-handler.js.map +1 -1
  57. package/server/dist/modules/recovery-retention.js +2 -2
  58. package/server/dist/modules/recovery-retention.js.map +1 -1
  59. package/server/client/dist/assets/Callout-BAqbXI0L.js +0 -1
  60. package/server/client/dist/assets/Note-ChUWU3Su.js +0 -21
  61. package/server/client/dist/assets/note-core-GgFcksAF.js +0 -178
  62. package/server/client/dist/assets/note-vendor-B0t5F04o.js +0 -54
  63. package/server/dist/features/note/graphql/note.graphql.shared.js +0 -23
  64. package/server/dist/features/note/graphql/note.graphql.shared.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import{j as e}from"./note-vendor-B0t5F04o.js";import{p as Y,h as E,w as J,q as v,aM as V,u as ee,af as M,P as ae,T as s,ai as te,aN as j,aO as N,B as C,a7 as se,M as m,l as w,I as b,r as le,ab as ne,a0 as re,i as $,j as ce,ay as ie,W as oe}from"./index-B4l2UvuC.js";import{a}from"./graph-vendor-CUxe67Lr.js";import{S as D}from"./SurfaceCard-CHjcCuqZ.js";import{n as de}from"./Plus.es-KeV96R_e.js";import"./note-core-GgFcksAF.js";const me=new Map([["bold",a.createElement(a.Fragment,null,a.createElement("path",{d:"M216.49,168.49a12,12,0,0,1-17,0L128,97,56.49,168.49a12,12,0,0,1-17-17l80-80a12,12,0,0,1,17,0l80,80A12,12,0,0,1,216.49,168.49Z"}))],["duotone",a.createElement(a.Fragment,null,a.createElement("path",{d:"M208,160H48l80-80Z",opacity:"0.2"}),a.createElement("path",{d:"M213.66,154.34l-80-80a8,8,0,0,0-11.32,0l-80,80A8,8,0,0,0,48,168H208a8,8,0,0,0,5.66-13.66ZM67.31,152,128,91.31,188.69,152Z"}))],["fill",a.createElement(a.Fragment,null,a.createElement("path",{d:"M215.39,163.06A8,8,0,0,1,208,168H48a8,8,0,0,1-5.66-13.66l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,215.39,163.06Z"}))],["light",a.createElement(a.Fragment,null,a.createElement("path",{d:"M212.24,164.24a6,6,0,0,1-8.48,0L128,88.49,52.24,164.24a6,6,0,0,1-8.48-8.48l80-80a6,6,0,0,1,8.48,0l80,80A6,6,0,0,1,212.24,164.24Z"}))],["regular",a.createElement(a.Fragment,null,a.createElement("path",{d:"M213.66,165.66a8,8,0,0,1-11.32,0L128,91.31,53.66,165.66a8,8,0,0,1-11.32-11.32l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,213.66,165.66Z"}))],["thin",a.createElement(a.Fragment,null,a.createElement("path",{d:"M210.83,162.83a4,4,0,0,1-5.66,0L128,85.66,50.83,162.83a4,4,0,0,1-5.66-5.66l80-80a4,4,0,0,1,5.66,0l80,80A4,4,0,0,1,210.83,162.83Z"}))]]),O=a.forwardRef((l,c)=>a.createElement(Y,{ref:c,...l,weights:me}));O.displayName="CaretUpIcon";const he=O,k=["id","name","template","replacement","createdAt","updatedAt"],ue=new Set(k),pe=`query FetchPlaceholders(
1
+ import{j as e}from"./note-vendor-i1M2FSI2.js";import{p as Y,h as E,w as J,q as v,aS as V,u as ee,ag as M,P as ae,T as s,ak as te,aT as j,aU as N,B as C,a7 as se,M as m,l as w,I as b,r as le,ab as ne,a0 as re,i as $,j as ce,aE as ie,W as oe}from"./index-BvKBTjQq.js";import{a}from"./graph-vendor-CUxe67Lr.js";import{S as D}from"./SurfaceCard-CO0oVxeL.js";import{n as de}from"./Plus.es-EcmtS_uw.js";import"./note-core-Z6kqulGB.js";const me=new Map([["bold",a.createElement(a.Fragment,null,a.createElement("path",{d:"M216.49,168.49a12,12,0,0,1-17,0L128,97,56.49,168.49a12,12,0,0,1-17-17l80-80a12,12,0,0,1,17,0l80,80A12,12,0,0,1,216.49,168.49Z"}))],["duotone",a.createElement(a.Fragment,null,a.createElement("path",{d:"M208,160H48l80-80Z",opacity:"0.2"}),a.createElement("path",{d:"M213.66,154.34l-80-80a8,8,0,0,0-11.32,0l-80,80A8,8,0,0,0,48,168H208a8,8,0,0,0,5.66-13.66ZM67.31,152,128,91.31,188.69,152Z"}))],["fill",a.createElement(a.Fragment,null,a.createElement("path",{d:"M215.39,163.06A8,8,0,0,1,208,168H48a8,8,0,0,1-5.66-13.66l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,215.39,163.06Z"}))],["light",a.createElement(a.Fragment,null,a.createElement("path",{d:"M212.24,164.24a6,6,0,0,1-8.48,0L128,88.49,52.24,164.24a6,6,0,0,1-8.48-8.48l80-80a6,6,0,0,1,8.48,0l80,80A6,6,0,0,1,212.24,164.24Z"}))],["regular",a.createElement(a.Fragment,null,a.createElement("path",{d:"M213.66,165.66a8,8,0,0,1-11.32,0L128,91.31,53.66,165.66a8,8,0,0,1-11.32-11.32l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,213.66,165.66Z"}))],["thin",a.createElement(a.Fragment,null,a.createElement("path",{d:"M210.83,162.83a4,4,0,0,1-5.66,0L128,85.66,50.83,162.83a4,4,0,0,1-5.66-5.66l80-80a4,4,0,0,1,5.66,0l80,80A4,4,0,0,1,210.83,162.83Z"}))]]),k=a.forwardRef((l,c)=>a.createElement(Y,{ref:c,...l,weights:me}));k.displayName="CaretUpIcon";const he=k,O=["id","name","template","replacement","createdAt","updatedAt"],ue=new Set(O),pe=`query FetchPlaceholders(
2
2
  $searchFilter: SearchFilterInput,
3
3
  $pagination: PaginationInput
4
4
  ) {
@@ -10,7 +10,7 @@ import{j as e}from"./note-vendor-B0t5F04o.js";import{p as Y,h as E,w as J,q as v
10
10
  placeholders {
11
11
  __PLACEHOLDER_FIELDS__ }
12
12
  }
13
- }`,xe=l=>{const c=l&&l.length>0?l.filter(r=>ue.has(r)):k;return Array.from(new Set(c)).map(r=>" "+r).join(`
13
+ }`,xe=l=>{const c=l&&l.length>0?l.filter(r=>ue.has(r)):O;return Array.from(new Set(c)).map(r=>" "+r).join(`
14
14
  `)+`
15
15
  `},ge=async({limit:l=25,offset:c=0,query:h="",fields:r}={})=>{const g=xe(r),i=pe.replace("__PLACEHOLDER_FIELDS__",g);return E(i,{searchFilter:{query:h},pagination:{limit:l,offset:c}})},ye=l=>E(`mutation CreatePlaceholder(
16
16
  $name: String!,
@@ -31,4 +31,4 @@ __PLACEHOLDER_FIELDS__ }
31
31
  }
32
32
  }`,l),fe=l=>E(`mutation DeletePlaceholder($id: ID!) {
33
33
  deletePlaceholder(id: $id)
34
- }`,{id:l}),I=ce(ie),Pe=()=>{const l=oe(),c=J(),h=I.useNavigate(),{page:r}=I.useSearch(),[g,i]=a.useState(!1),[y,H]=a.useState(!0),[n,u]=a.useState({name:"",template:"",replacement:""}),o=25,T=v.placeholders.list({limit:o,offset:(r-1)*o}),[P]=a.useState(V),{data:d,isLoading:p}=ee({queryKey:T,queryFn:async()=>{const t=await ge({offset:(r-1)*o,limit:o});if(t.type==="error")throw t;return t.allPlaceholders}}),F=M({mutationFn:ye,onSuccess:async()=>{l("Placeholder added successfully"),i(!1),u({name:"",template:"",replacement:""}),await c.invalidateQueries({queryKey:v.placeholders.listAll(),exact:!1})}}),q=M({mutationFn:fe,onSuccess:async()=>{l("Placeholder deleted successfully"),await c.invalidateQueries({queryKey:v.placeholders.listAll(),exact:!1})}}),B=P.length,x=d?.totalCount??0,Z=d?.placeholders??[],f="font-medium text-fg-tertiary",L="leading-[1.2]",A="break-all leading-[1.2]",S="leading-[1.2]",_="flex flex-col gap-px",R="space-y-px",Q="focus-ring-soft inline-flex items-center gap-2 rounded-[12px] border border-transparent px-2.5 py-1.5 text-fg-tertiary outline-none transition-colors hover:bg-hover-subtle hover:text-fg-default",K="relative",U="absolute right-2 top-2",G="focus-ring-soft surface-base group flex flex-col items-center justify-center gap-0.5 px-4 py-4 text-center text-fg-secondary outline-none transition-colors hover:bg-hover-subtle hover:text-fg-default",X="inline-flex h-[34px] w-[34px] items-center justify-center rounded-full border border-border-subtle bg-surface text-current transition-colors group-hover:border-border-secondary/70",z=x>0?`Placeholders (${x})`:void 0;return e.jsxs(ae,{title:"Placeholders",variant:"default",heading:p?e.jsx($,{width:244,height:24,className:"rounded-full"}):z,description:p?e.jsx($,{width:260,height:16,className:"rounded-full"}):"Create tokens that get replaced with values when you clone a note",children:[e.jsxs("div",{className:"flex flex-col gap-5",children:[e.jsxs("section",{className:"space-y-3 border-b border-border-subtle/80 pb-5",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(s,{as:"p",variant:"body",weight:"medium",children:"System placeholders"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"Built-in tokens for date and time that are always available when cloning notes"})]}),e.jsxs("button",{type:"button",onClick:()=>H(!y),className:Q,children:[e.jsxs(s,{as:"span",variant:"meta",weight:"medium",tone:"secondary",children:[B," items"]}),y?e.jsx(he,{className:"h-4 w-4"}):e.jsx(te,{className:"h-4 w-4"})]})]}),y&&e.jsx("div",{className:"grid-auto-cards grid gap-5",children:P.map(t=>e.jsx(D,{children:e.jsxs("div",{className:_,children:[e.jsx(s,{as:"div",variant:"body",weight:"medium",className:L,children:t.name}),e.jsxs("div",{className:R,children:[e.jsxs(s,{as:"div",variant:"meta",weight:"medium",tone:"secondary",className:A,children:[j,t.template,N]}),e.jsxs(s,{as:"div",variant:"label",weight:"medium",tone:"tertiary",className:S,children:["Current value: ",t.replacement]})]})]})},t.name))})]}),e.jsxs("section",{className:"space-y-3",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(s,{as:"p",variant:"body",weight:"medium",children:"Custom placeholders"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"Custom tokens you define once and reuse whenever you clone a note"})]}),!p&&e.jsx(s,{as:"span",variant:"meta",weight:"medium",tone:"secondary",children:x===1?"1 item":`${x} items`})]}),!p&&e.jsxs("div",{className:"grid-auto-cards grid gap-5",children:[Z.map(t=>e.jsxs(D,{className:K,children:[e.jsx(C,{variant:"ghost",size:"icon-sm",className:U,onClick:()=>q.mutate(t.id.toString()),children:e.jsx(se,{className:"h-4 w-4"})}),e.jsxs("div",{className:_,children:[e.jsx(s,{as:"div",variant:"body",weight:"medium",className:`min-w-0 pr-8 ${L}`,children:t.name}),e.jsxs("div",{className:R,children:[e.jsxs(s,{as:"div",variant:"meta",weight:"medium",tone:"secondary",className:A,children:[j,t.template,N]}),e.jsxs(s,{as:"div",variant:"label",weight:"medium",tone:"tertiary",className:S,children:["Replaces with: ",t.replacement]})]})]})]},t.id)),e.jsxs("button",{type:"button",onClick:()=>i(!0),className:G,children:[e.jsx("span",{className:X,children:e.jsx(de,{className:"h-4.5 w-4.5"})}),e.jsx(s,{as:"div",variant:"meta",weight:"medium",tone:"secondary",className:"text-current",children:"New custom"})]})]})]})]}),e.jsxs(m,{isOpen:g,onClose:()=>i(!1),variant:"form",children:[e.jsx(m.Header,{title:"Add Placeholder",onClose:()=>i(!1)}),e.jsxs(m.Body,{children:[e.jsx(m.Description,{className:"mb-4",children:"Create a token that swaps in a value when you clone a note"}),e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(w,{htmlFor:"name",className:f,children:"Name"}),e.jsx(b,{id:"name",type:"text",value:n.name,onChange:t=>u({...n,name:t.target.value}),placeholder:"Description of this placeholder"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"A readable label so you can find this placeholder later"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(w,{htmlFor:"template",className:f,children:"Placeholder"}),e.jsx(b,{id:"template",type:"text",value:n.template,onChange:t=>u({...n,template:t.target.value}),placeholder:"note_app"}),e.jsxs(s,{as:"p",variant:"meta",tone:"secondary",children:["Use it in a note as ",j,"your_placeholder",N]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(w,{htmlFor:"replacement",className:f,children:"Replacement"}),e.jsx(b,{id:"replacement",type:"text",value:n.replacement,onChange:t=>u({...n,replacement:t.target.value}),placeholder:"Ocean Brain"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"This value is inserted when the placeholder is replaced during cloning"})]})]})]}),e.jsx(m.Footer,{children:e.jsxs(le,{children:[e.jsx(C,{variant:"ghost",onClick:()=>i(!1),children:"Cancel"}),e.jsx(C,{variant:"primary",isLoading:F.isPending,onClick:()=>{if(!n.name||!n.template||!n.replacement){l("All fields are required");return}F.mutate({name:n.name,template:n.template,replacement:n.replacement})},children:"Add"})]})})]}),e.jsx(ne,{fallback:null,children:d?.totalCount&&o<d.totalCount&&e.jsx(re,{page:r,last:Math.ceil(d.totalCount/o),onChange:t=>{h({search:W=>({...W,page:t})})}})})]})};export{Pe as default};
34
+ }`,{id:l}),I=ce(ie),Pe=()=>{const l=oe(),c=J(),h=I.useNavigate(),{page:r}=I.useSearch(),[g,i]=a.useState(!1),[y,H]=a.useState(!0),[n,u]=a.useState({name:"",template:"",replacement:""}),o=25,T=v.placeholders.list({limit:o,offset:(r-1)*o}),[P]=a.useState(V),{data:d,isLoading:p}=ee({queryKey:T,queryFn:async()=>{const t=await ge({offset:(r-1)*o,limit:o});if(t.type==="error")throw t;return t.allPlaceholders}}),F=M({mutationFn:ye,onSuccess:async()=>{l("Placeholder added successfully"),i(!1),u({name:"",template:"",replacement:""}),await c.invalidateQueries({queryKey:v.placeholders.listAll(),exact:!1})}}),q=M({mutationFn:fe,onSuccess:async()=>{l("Placeholder deleted successfully"),await c.invalidateQueries({queryKey:v.placeholders.listAll(),exact:!1})}}),B=P.length,x=d?.totalCount??0,Z=d?.placeholders??[],f="font-medium text-fg-tertiary",L="leading-[1.2]",S="break-all leading-[1.2]",A="leading-[1.2]",_="flex flex-col gap-px",R="space-y-px",Q="focus-ring-soft inline-flex items-center gap-2 rounded-[12px] border border-transparent px-2.5 py-1.5 text-fg-tertiary outline-none transition-colors hover:bg-hover-subtle hover:text-fg-default",U="relative",K="absolute right-2 top-2",G="focus-ring-soft surface-base group flex flex-col items-center justify-center gap-0.5 px-4 py-4 text-center text-fg-secondary outline-none transition-colors hover:bg-hover-subtle hover:text-fg-default",X="inline-flex h-[34px] w-[34px] items-center justify-center rounded-full border border-border-subtle bg-surface text-current transition-colors group-hover:border-border-secondary/70",z=x>0?`Placeholders (${x})`:void 0;return e.jsxs(ae,{title:"Placeholders",variant:"default",heading:p?e.jsx($,{width:244,height:24,className:"rounded-full"}):z,description:p?e.jsx($,{width:260,height:16,className:"rounded-full"}):"Create tokens that get replaced with values when you clone a note",children:[e.jsxs("div",{className:"flex flex-col gap-5",children:[e.jsxs("section",{className:"space-y-3 border-b border-border-subtle/80 pb-5",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(s,{as:"p",variant:"body",weight:"medium",children:"System placeholders"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"Built-in tokens for date and time that are always available when cloning notes"})]}),e.jsxs("button",{type:"button",onClick:()=>H(!y),className:Q,children:[e.jsxs(s,{as:"span",variant:"meta",weight:"medium",tone:"secondary",children:[B," items"]}),y?e.jsx(he,{className:"h-4 w-4"}):e.jsx(te,{className:"h-4 w-4"})]})]}),y&&e.jsx("div",{className:"grid-auto-cards grid gap-5",children:P.map(t=>e.jsx(D,{children:e.jsxs("div",{className:_,children:[e.jsx(s,{as:"div",variant:"body",weight:"medium",className:L,children:t.name}),e.jsxs("div",{className:R,children:[e.jsxs(s,{as:"div",variant:"meta",weight:"medium",tone:"secondary",className:S,children:[j,t.template,N]}),e.jsxs(s,{as:"div",variant:"label",weight:"medium",tone:"tertiary",className:A,children:["Current value: ",t.replacement]})]})]})},t.name))})]}),e.jsxs("section",{className:"space-y-3",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(s,{as:"p",variant:"body",weight:"medium",children:"Custom placeholders"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"Custom tokens you define once and reuse whenever you clone a note"})]}),!p&&e.jsx(s,{as:"span",variant:"meta",weight:"medium",tone:"secondary",children:x===1?"1 item":`${x} items`})]}),!p&&e.jsxs("div",{className:"grid-auto-cards grid gap-5",children:[Z.map(t=>e.jsxs(D,{className:U,children:[e.jsx(C,{variant:"ghost",size:"icon-sm",className:K,onClick:()=>q.mutate(t.id.toString()),children:e.jsx(se,{className:"h-4 w-4"})}),e.jsxs("div",{className:_,children:[e.jsx(s,{as:"div",variant:"body",weight:"medium",className:`min-w-0 pr-8 ${L}`,children:t.name}),e.jsxs("div",{className:R,children:[e.jsxs(s,{as:"div",variant:"meta",weight:"medium",tone:"secondary",className:S,children:[j,t.template,N]}),e.jsxs(s,{as:"div",variant:"label",weight:"medium",tone:"tertiary",className:A,children:["Replaces with: ",t.replacement]})]})]})]},t.id)),e.jsxs("button",{type:"button",onClick:()=>i(!0),className:G,children:[e.jsx("span",{className:X,children:e.jsx(de,{className:"h-4.5 w-4.5"})}),e.jsx(s,{as:"div",variant:"meta",weight:"medium",tone:"secondary",className:"text-current",children:"New custom"})]})]})]})]}),e.jsxs(m,{isOpen:g,onClose:()=>i(!1),variant:"form",children:[e.jsx(m.Header,{title:"Add Placeholder",onClose:()=>i(!1)}),e.jsxs(m.Body,{children:[e.jsx(m.Description,{className:"mb-4",children:"Create a token that swaps in a value when you clone a note"}),e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(w,{htmlFor:"name",className:f,children:"Name"}),e.jsx(b,{id:"name",type:"text",value:n.name,onChange:t=>u({...n,name:t.target.value}),placeholder:"Description of this placeholder"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"A readable label so you can find this placeholder later"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(w,{htmlFor:"template",className:f,children:"Placeholder"}),e.jsx(b,{id:"template",type:"text",value:n.template,onChange:t=>u({...n,template:t.target.value}),placeholder:"note_app"}),e.jsxs(s,{as:"p",variant:"meta",tone:"secondary",children:["Use it in a note as ",j,"your_placeholder",N]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(w,{htmlFor:"replacement",className:f,children:"Replacement"}),e.jsx(b,{id:"replacement",type:"text",value:n.replacement,onChange:t=>u({...n,replacement:t.target.value}),placeholder:"Ocean Brain"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"This value is inserted when the placeholder is replaced during cloning"})]})]})]}),e.jsx(m.Footer,{children:e.jsxs(le,{children:[e.jsx(C,{variant:"ghost",onClick:()=>i(!1),children:"Cancel"}),e.jsx(C,{variant:"primary",isLoading:F.isPending,onClick:()=>{if(!n.name||!n.template||!n.replacement){l("All fields are required");return}F.mutate({name:n.name,template:n.template,replacement:n.replacement})},children:"Add"})]})})]}),e.jsx(ne,{fallback:null,children:d?.totalCount&&o<d.totalCount&&e.jsx(re,{page:r,last:Math.ceil(d.totalCount/o),onChange:t=>{h({search:W=>({...W,page:t})})}})})]})};export{Pe as default};
@@ -1 +1 @@
1
- import{j as e}from"./note-vendor-B0t5F04o.js";import{$ as F,j as M,ar as Q,w as L,v as _,a5 as I,aC as K,q as i,af as E,aD as B,N as O,aE as W,P as N,F as $,T as d,as as G,B as j,a0 as H,aF as Y,i as l,W as z,d as U,ah as V,u as Z,aG as J,M as y}from"./index-B4l2UvuC.js";import{a as k}from"./graph-vendor-CUxe67Lr.js";import{S as C}from"./SurfaceCard-CHjcCuqZ.js";import"./note-core-GgFcksAF.js";const m=25,v=M(Q),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=>U(a).format("YYYY-MM-DD HH:mm"),X=({note:a,onClose:o})=>{const s=a?.id??"",c=a?.title||"Untitled note",r=Z({queryKey:i.notes.trashDetail(s),enabled:!!a,queryFn:async()=>{const h=await J(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(j,{variant:"ghost",size:"sm",onClick:o,children:"Close"})})]})},ee=()=>e.jsx(N,{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=L(),c=z(),r=_(),[f,h]=k.useState(null),[x,b]=k.useState(null),{data:u}=I({queryKey:i.notes.trash({limit:m,offset:(a-1)*m}),queryFn:async()=>{const t=await K({limit:m,offset:(a-1)*m});if(t.type==="error")throw t;return t.trashedNotes}}),w=E({mutationFn:B,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:O,params:{id:t.restoreTrashedNote.id}})},onError:()=>{c("Failed to restore the note.")}}),p=E({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;b(null),p.mutate(t)},q=async t=>{const n=await V(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){b({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,A=u.notes.find(t=>t.id===f)??null;return u.notes.length===0?e.jsx(N,{title:"Trash",variant:"default",heading:T,description:R,children:e.jsx($,{title:"Trash is empty",description:`Delete a note and you can restore it here for ${S} days before permanent removal`})}):e.jsxs(N,{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(G,{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(j,{variant:"subtle",size:"sm",onClick:()=>h(t.id),children:"View content"}),e.jsx(j,{variant:"subtle",size:"sm",isLoading:w.isPending&&w.variables===t.id,onClick:()=>w.mutate(t.id),children:"Restore"}),e.jsx(j,{variant:"soft-danger",size:"sm",isLoading:p.isPending&&p.variables===t.id,onClick:()=>q(t.id),children:"Delete now"})]})]})},t.id))}),u.totalCount>m&&e.jsx(H,{page:a,last:Math.ceil(u.totalCount/m),onChange:t=>{o({search:n=>({...n,page:t})})}})]}),e.jsx(X,{note:A,onClose:()=>h(null)}),e.jsx(Y,{isOpen:!!x,title:g.title,description:g.description,references:x?.backReferences??[],confirmLabel:g.confirmLabel,confirmVariant:g.confirmVariant,onClose:()=>b(null),onConfirm:D})]})},oe=()=>{const{page:a}=v.useSearch();return e.jsx(F,{fallback:e.jsx(ee,{}),errorTitle:"Failed to load trash",errorDescription:"Retry loading deleted notes",resetKeys:[a],children:e.jsx(te,{})})};export{oe as default};
1
+ import{j as e}from"./note-vendor-i1M2FSI2.js";import{$ as M,j as F,av as L,w as Q,v as I,a5 as K,aI as _,q as i,ag as E,aJ as B,N as O,aK as W,P as N,F as $,T as d,aw as H,B as j,a0 as Y,aL as z,i as l,W as G,d as U,aj as V,u as J,aM as Z,M as y}from"./index-BvKBTjQq.js";import{a as k}from"./graph-vendor-CUxe67Lr.js";import{S as C}from"./SurfaceCard-CO0oVxeL.js";import"./note-core-Z6kqulGB.js";const m=25,v=F(L),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=>U(a).format("YYYY-MM-DD HH:mm"),X=({note:a,onClose:o})=>{const s=a?.id??"",c=a?.title||"Untitled note",r=J({queryKey:i.notes.trashDetail(s),enabled:!!a,queryFn:async()=>{const h=await Z(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(j,{variant:"ghost",size:"sm",onClick:o,children:"Close"})})]})},ee=()=>e.jsx(N,{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=Q(),c=G(),r=I(),[f,h]=k.useState(null),[x,b]=k.useState(null),{data:u}=K({queryKey:i.notes.trash({limit:m,offset:(a-1)*m}),queryFn:async()=>{const t=await _({limit:m,offset:(a-1)*m});if(t.type==="error")throw t;return t.trashedNotes}}),w=E({mutationFn:B,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:O,params:{id:t.restoreTrashedNote.id}})},onError:()=>{c("Failed to restore the note.")}}),p=E({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;b(null),p.mutate(t)},q=async t=>{const n=await V(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){b({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,A=u.notes.find(t=>t.id===f)??null;return u.notes.length===0?e.jsx(N,{title:"Trash",variant:"default",heading:T,description:R,children:e.jsx($,{title:"Trash is empty",description:`Delete a note and you can restore it here for ${S} days before permanent removal`})}):e.jsxs(N,{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(H,{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(j,{variant:"subtle",size:"sm",onClick:()=>h(t.id),children:"View content"}),e.jsx(j,{variant:"subtle",size:"sm",isLoading:w.isPending&&w.variables===t.id,onClick:()=>w.mutate(t.id),children:"Restore"}),e.jsx(j,{variant:"soft-danger",size:"sm",isLoading:p.isPending&&p.variables===t.id,onClick:()=>q(t.id),children:"Delete now"})]})]})},t.id))}),u.totalCount>m&&e.jsx(Y,{page:a,last:Math.ceil(u.totalCount/m),onChange:t=>{o({search:n=>({...n,page:t})})}})]}),e.jsx(X,{note:A,onClose:()=>h(null)}),e.jsx(z,{isOpen:!!x,title:g.title,description:g.description,references:x?.backReferences??[],confirmLabel:g.confirmLabel,confirmVariant:g.confirmVariant,onClose:()=>b(null),onConfirm:D})]})},oe=()=>{const{page:a}=v.useSearch();return e.jsx(M,{fallback:e.jsx(ee,{}),errorTitle:"Failed to load trash",errorDescription:"Retry loading deleted notes",resetKeys:[a],children:e.jsx(te,{})})};export{oe as default};
@@ -1,4 +1,4 @@
1
- import{j as y}from"./note-vendor-B0t5F04o.js";import{h as u,a5 as f,q as n,w as R,W as $}from"./index-B4l2UvuC.js";import{a as l}from"./graph-vendor-CUxe67Lr.js";const g=(e={})=>({limit:e.limit??10,offset:e.offset??0}),D=async(e,t)=>u(`
1
+ import{j as y}from"./note-vendor-i1M2FSI2.js";import{h as u,a5 as f,q as n,w as R,W as $}from"./index-BvKBTjQq.js";import{a as l}from"./graph-vendor-CUxe67Lr.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
@@ -1,4 +1,4 @@
1
- import{h as n}from"./index-B4l2UvuC.js";function b(){return n(`query FetchViewWorkspace {
1
+ import{h as n}from"./index-BvKBTjQq.js";function b(){return n(`query FetchViewWorkspace {
2
2
  viewWorkspace {
3
3
  activeTabId
4
4
  tabs {
@@ -6,11 +6,11 @@
6
6
  <link rel="icon" type="image/png" href="/icon.png" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
8
  <title>Ocean Brain</title>
9
- <script type="module" crossorigin src="/assets/index-B4l2UvuC.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-BvKBTjQq.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/graph-vendor-CUxe67Lr.js">
11
- <link rel="modulepreload" crossorigin href="/assets/note-core-GgFcksAF.js">
12
- <link rel="modulepreload" crossorigin href="/assets/note-vendor-B0t5F04o.js">
13
- <link rel="stylesheet" crossorigin href="/assets/index-D2gRB1ig.css">
11
+ <link rel="modulepreload" crossorigin href="/assets/note-core-Z6kqulGB.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/note-vendor-i1M2FSI2.js">
13
+ <link rel="stylesheet" crossorigin href="/assets/index-BZUGWdOU.css">
14
14
  </head>
15
15
 
16
16
  <body>
@@ -1,3 +1,4 @@
1
+ import { renderNoteSnapshotContentAsMarkdown } from "../../../features/note/services/snapshot.js";
1
2
  import models from "../../../models.js";
2
3
  const noteFieldResolvers = {
3
4
  tags: async (note) => models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),
@@ -6,7 +7,19 @@ const noteFieldResolvers = {
6
7
  return blocksToMarkdown(note.content);
7
8
  }
8
9
  };
10
+ const noteSnapshotFieldResolvers = {
11
+ contentAsMarkdown: async (snapshot) => {
12
+ if (typeof snapshot.contentAsMarkdown === "string") {
13
+ return snapshot.contentAsMarkdown;
14
+ }
15
+ if (typeof snapshot.payload === "string") {
16
+ return renderNoteSnapshotContentAsMarkdown(snapshot.payload);
17
+ }
18
+ return "";
19
+ }
20
+ };
9
21
  export {
10
- noteFieldResolvers
22
+ noteFieldResolvers,
23
+ noteSnapshotFieldResolvers
11
24
  };
12
25
  //# sourceMappingURL=note.field.resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/features/note/graphql/note.field.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport type { Note } from '~/models.js';\nimport models from '~/models.js';\n\ntype NoteFieldResolvers = NonNullable<IResolvers['Note']>;\n\nexport const noteFieldResolvers: NoteFieldResolvers = {\n tags: async (note: Note) => models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),\n contentAsMarkdown: async (note: Note) => {\n const { blocksToMarkdown } = await import('~/modules/blocknote.js');\n return blocksToMarkdown(note.content);\n },\n};\n"],"mappings":"AAEA,OAAO,YAAY;AAIZ,MAAM,qBAAyC;AAAA,EAClD,MAAM,OAAO,SAAe,OAAO,IAAI,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,KAAK,GAAG,EAAE,EAAE,EAAE,CAAC;AAAA,EAC/F,mBAAmB,OAAO,SAAe;AACrC,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,wBAAwB;AAClE,WAAO,iBAAiB,KAAK,OAAO;AAAA,EACxC;AACJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/features/note/graphql/note.field.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { renderNoteSnapshotContentAsMarkdown } from '~/features/note/services/snapshot.js';\nimport type { Note } from '~/models.js';\nimport models from '~/models.js';\n\ntype NoteFieldResolvers = NonNullable<IResolvers['Note']>;\ntype NoteSnapshotFieldResolvers = NonNullable<IResolvers['NoteSnapshot']>;\n\ninterface NoteSnapshotSource {\n contentAsMarkdown?: string;\n payload?: string;\n}\n\nexport const noteFieldResolvers: NoteFieldResolvers = {\n tags: async (note: Note) => models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),\n contentAsMarkdown: async (note: Note) => {\n const { blocksToMarkdown } = await import('~/modules/blocknote.js');\n return blocksToMarkdown(note.content);\n },\n};\n\nexport const noteSnapshotFieldResolvers: NoteSnapshotFieldResolvers = {\n contentAsMarkdown: async (snapshot: NoteSnapshotSource) => {\n if (typeof snapshot.contentAsMarkdown === 'string') {\n return snapshot.contentAsMarkdown;\n }\n\n if (typeof snapshot.payload === 'string') {\n return renderNoteSnapshotContentAsMarkdown(snapshot.payload);\n }\n\n return '';\n },\n};\n"],"mappings":"AACA,SAAS,2CAA2C;AAEpD,OAAO,YAAY;AAUZ,MAAM,qBAAyC;AAAA,EAClD,MAAM,OAAO,SAAe,OAAO,IAAI,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,KAAK,GAAG,EAAE,EAAE,EAAE,CAAC;AAAA,EAC/F,mBAAmB,OAAO,SAAe;AACrC,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,wBAAwB;AAClE,WAAO,iBAAiB,KAAK,OAAO;AAAA,EACxC;AACJ;AAEO,MAAM,6BAAyD;AAAA,EAClE,mBAAmB,OAAO,aAAiC;AACvD,QAAI,OAAO,SAAS,sBAAsB,UAAU;AAChD,aAAO,SAAS;AAAA,IACpB;AAEA,QAAI,OAAO,SAAS,YAAY,UAAU;AACtC,aAAO,oCAAoC,SAAS,OAAO;AAAA,IAC/D;AAEA,WAAO;AAAA,EACX;AACJ;","names":[]}
@@ -1,12 +1,15 @@
1
+ import { GraphQLError } from "graphql";
2
+ import { extractBlocksByType, parseNoteContent } from "../../../features/note/services/content-blocks.js";
1
3
  import { buildNoteSearchProjection } from "../../../features/note/services/search.js";
2
- import {
3
- captureNoteBaseline,
4
- createSnapshotMetaFromUserAgent,
5
- restoreNoteSnapshot
6
- } from "../../../features/note/services/snapshot.js";
4
+ import { createSnapshotMetaFromUserAgent, restoreNoteSnapshot } from "../../../features/note/services/snapshot.js";
7
5
  import { purgeTrashedNoteById, restoreTrashedNoteById, trashNoteById } from "../../../features/note/services/trash.js";
6
+ import { updateNoteWithVersionGuard } from "../../../features/note/services/write.js";
7
+ import {
8
+ isInvalidNoteVersionError,
9
+ isMissingNoteVersionError,
10
+ isNoteVersionConflictError
11
+ } from "../../../features/note/services/write-conflict.js";
8
12
  import models from "../../../models.js";
9
- import { extractBlocksByType } from "./note.graphql.shared.js";
10
13
  const PLACEHOLDER_PREFIX = "{%";
11
14
  const PLACEHOLDER_SUFFIX = "%}";
12
15
  const replacePlaceholders = async (content) => {
@@ -31,10 +34,26 @@ const getRequestUserAgent = (req) => {
31
34
  const userAgentHeader = req?.headers["user-agent"];
32
35
  return Array.isArray(userAgentHeader) ? userAgentHeader[0] : userAgentHeader;
33
36
  };
37
+ const toTagConnections = (blocks) => {
38
+ return blocks.map((block) => block.props?.id).filter((id) => id !== void 0 && id !== null && String(id).trim() !== "").map((id) => Number(id)).filter(Number.isFinite).map((id) => ({ id }));
39
+ };
40
+ const parseRequiredNoteContent = (content) => {
41
+ const parsedContent = parseNoteContent(content);
42
+ if (!parsedContent) {
43
+ throw new GraphQLError("Note content must be valid BlockNote JSON.", {
44
+ extensions: {
45
+ code: "INVALID_NOTE_CONTENT"
46
+ }
47
+ });
48
+ }
49
+ return parsedContent;
50
+ };
34
51
  const noteMutationResolvers = {
35
52
  createNote: async (_, { note }) => {
36
53
  const replacedTitle = await replacePlaceholders(note.title);
37
54
  const replacedContent = await replacePlaceholders(note.content);
55
+ const parsedContent = replacedContent ? parseRequiredNoteContent(replacedContent) : null;
56
+ const blocks = parsedContent ? extractBlocksByType("tag", parsedContent) : [];
38
57
  const createdNote = await models.note.create({
39
58
  data: {
40
59
  title: replacedTitle,
@@ -49,46 +68,64 @@ const noteMutationResolvers = {
49
68
  if (!replacedContent) {
50
69
  return createdNote;
51
70
  }
52
- const blocks = extractBlocksByType("tag", JSON.parse(replacedContent));
53
71
  return models.note.update({
54
72
  where: { id: createdNote.id },
55
- data: { tags: { set: blocks.map((block) => ({ id: Number(block.props.id) })) } }
73
+ data: { tags: { set: toTagConnections(blocks) } }
56
74
  });
57
75
  },
58
76
  updateNote: async (_, {
59
77
  id,
60
78
  note,
61
- editSessionId
79
+ editSessionId,
80
+ expectedUpdatedAt,
81
+ force
62
82
  }, context) => {
63
- const existingNote = await models.note.findUnique({
64
- where: { id: Number(id) },
65
- select: {
66
- title: true,
67
- content: true
83
+ const parsedContent = note.content ? parseRequiredNoteContent(note.content) : null;
84
+ const blocks = parsedContent ? extractBlocksByType("tag", parsedContent) : [];
85
+ try {
86
+ const updatedNote = await updateNoteWithVersionGuard({
87
+ id: Number(id),
88
+ data: {
89
+ ...note.title !== void 0 ? { title: note.title } : {},
90
+ ...note.content !== void 0 ? { content: note.content } : {},
91
+ ...note.layout !== void 0 ? { layout: note.layout } : {},
92
+ ...note.content !== void 0 ? { tagIds: toTagConnections(blocks).map((tag) => tag.id) } : {}
93
+ },
94
+ ...editSessionId ? { editSessionId } : {},
95
+ ...expectedUpdatedAt ? { expectedUpdatedAt } : {},
96
+ ...force ? { force: true } : {},
97
+ snapshotMeta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req))
98
+ });
99
+ if (!updatedNote) {
100
+ throw "NOT FOUND";
68
101
  }
69
- });
70
- if (!existingNote) {
71
- throw "NOT FOUND";
72
- }
73
- const blocks = note.content ? extractBlocksByType("tag", JSON.parse(note.content)) : [];
74
- await captureNoteBaseline({
75
- noteId: Number(id),
76
- ...editSessionId ? { editSessionId } : {},
77
- meta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req))
78
- });
79
- const nextTitle = note.title ?? existingNote.title;
80
- const nextContent = note.content ?? existingNote.content;
81
- return models.note.update({
82
- where: { id: Number(id) },
83
- data: {
84
- ...note,
85
- ...buildNoteSearchProjection({
86
- title: nextTitle,
87
- content: nextContent
88
- }),
89
- ...note.content ? { tags: { set: blocks.map((block) => ({ id: Number(block.props.id) })) } } : {}
102
+ return updatedNote;
103
+ } catch (error) {
104
+ if (isNoteVersionConflictError(error)) {
105
+ throw new GraphQLError(error.message, {
106
+ extensions: {
107
+ code: error.code,
108
+ currentUpdatedAt: error.currentUpdatedAt,
109
+ expectedUpdatedAt: error.expectedUpdatedAt
110
+ }
111
+ });
90
112
  }
91
- });
113
+ if (isInvalidNoteVersionError(error)) {
114
+ throw new GraphQLError(error.message, {
115
+ extensions: {
116
+ code: error.code
117
+ }
118
+ });
119
+ }
120
+ if (isMissingNoteVersionError(error)) {
121
+ throw new GraphQLError(error.message, {
122
+ extensions: {
123
+ code: error.code
124
+ }
125
+ });
126
+ }
127
+ throw error;
128
+ }
92
129
  },
93
130
  deleteNote: async (_, { id }) => {
94
131
  const trashedNote = await trashNoteById(Number(id));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/features/note/graphql/note.mutation.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport type { Request } from 'express';\nimport { buildNoteSearchProjection } from '~/features/note/services/search.js';\nimport {\n captureNoteBaseline,\n createSnapshotMetaFromUserAgent,\n restoreNoteSnapshot,\n} from '~/features/note/services/snapshot.js';\nimport { purgeTrashedNoteById, restoreTrashedNoteById, trashNoteById } from '~/features/note/services/trash.js';\nimport models from '~/models.js';\nimport type { NoteInput } from '~/types/index.js';\nimport { extractBlocksByType } from './note.graphql.shared.js';\n\nconst PLACEHOLDER_PREFIX = '{%';\nconst PLACEHOLDER_SUFFIX = '%}';\n\nconst replacePlaceholders = async (content: string) => {\n const placeholders = content.matchAll(new RegExp(`${PLACEHOLDER_PREFIX}([^}]+)${PLACEHOLDER_SUFFIX}`, 'g'));\n const resolvedPlaceholders = await models.placeholder.findMany({\n select: {\n template: true,\n replacement: true,\n },\n where: { template: { in: Array.from(new Set(Array.from(placeholders, (match) => match[1]))) } },\n });\n\n let replacedContent = content;\n\n for (const placeholder of resolvedPlaceholders) {\n replacedContent = replacedContent.replace(\n new RegExp(`${PLACEHOLDER_PREFIX}${placeholder.template}${PLACEHOLDER_SUFFIX}`, 'g'),\n placeholder.replacement,\n );\n }\n\n return replacedContent;\n};\n\nconst getRequestUserAgent = (req?: Request) => {\n const userAgentHeader = req?.headers['user-agent'];\n return Array.isArray(userAgentHeader) ? userAgentHeader[0] : userAgentHeader;\n};\n\ntype NoteMutationResolvers = NonNullable<IResolvers['Mutation']>;\n\nexport const noteMutationResolvers: NoteMutationResolvers = {\n createNote: async (_, { note }: { note: NoteInput }) => {\n const replacedTitle = await replacePlaceholders(note.title);\n const replacedContent = await replacePlaceholders(note.content);\n const createdNote = await models.note.create({\n data: {\n title: replacedTitle,\n content: replacedContent,\n ...buildNoteSearchProjection({\n title: replacedTitle,\n content: replacedContent,\n }),\n ...(note.layout && { layout: note.layout }),\n },\n });\n\n if (!replacedContent) {\n return createdNote;\n }\n\n const blocks = extractBlocksByType<{ id: string }>('tag', JSON.parse(replacedContent));\n\n return models.note.update({\n where: { id: createdNote.id },\n data: { tags: { set: blocks.map((block) => ({ id: Number(block.props.id) })) } },\n });\n },\n updateNote: async (\n _,\n {\n id,\n note,\n editSessionId,\n }: {\n id: number;\n note: NoteInput;\n editSessionId?: string;\n },\n context: {\n req?: Request;\n },\n ) => {\n const existingNote = await models.note.findUnique({\n where: { id: Number(id) },\n select: {\n title: true,\n content: true,\n },\n });\n\n if (!existingNote) {\n throw 'NOT FOUND';\n }\n\n const blocks = note.content ? extractBlocksByType<{ id: string }>('tag', JSON.parse(note.content)) : [];\n\n await captureNoteBaseline({\n noteId: Number(id),\n ...(editSessionId ? { editSessionId } : {}),\n meta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req)),\n });\n\n const nextTitle = note.title ?? existingNote.title;\n const nextContent = note.content ?? existingNote.content;\n\n return models.note.update({\n where: { id: Number(id) },\n data: {\n ...note,\n ...buildNoteSearchProjection({\n title: nextTitle,\n content: nextContent,\n }),\n ...(note.content ? { tags: { set: blocks.map((block) => ({ id: Number(block.props.id) })) } } : {}),\n },\n });\n },\n deleteNote: async (_, { id }: { id: string }) => {\n const trashedNote = await trashNoteById(Number(id));\n\n if (!trashedNote) {\n throw 'NOT FOUND';\n }\n\n return true;\n },\n restoreNoteSnapshot: async (\n _,\n { id }: { id: string },\n context: {\n req?: Request;\n },\n ) => {\n const note = await restoreNoteSnapshot(Number(id), {\n meta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req)),\n });\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n return note;\n },\n restoreTrashedNote: async (_, { id }: { id: string }) => {\n const note = await restoreTrashedNoteById(Number(id));\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n return note;\n },\n purgeTrashedNote: async (_, { id }: { id: string }) => {\n const purgedNote = await purgeTrashedNoteById(Number(id));\n\n if (!purgedNote) {\n throw 'NOT FOUND';\n }\n\n return true;\n },\n pinNote: (_, { id, pinned }: { id: string; pinned: boolean }) =>\n models.note.update({\n where: { id: Number(id) },\n data: { pinned: Boolean(pinned) },\n }),\n reorderNotes: async (_, { notes }: { notes: Array<{ id: string; order: number }> }) => {\n const updatePromises = notes.map(({ id, order }) =>\n models.note.update({\n where: { id: Number(id) },\n data: { order },\n }),\n );\n\n return Promise.all(updatePromises);\n },\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/features/note/graphql/note.mutation.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport type { Request } from 'express';\nimport { GraphQLError } from 'graphql';\nimport { extractBlocksByType, parseNoteContent } from '~/features/note/services/content-blocks.js';\nimport { buildNoteSearchProjection } from '~/features/note/services/search.js';\nimport { createSnapshotMetaFromUserAgent, restoreNoteSnapshot } from '~/features/note/services/snapshot.js';\nimport { purgeTrashedNoteById, restoreTrashedNoteById, trashNoteById } from '~/features/note/services/trash.js';\nimport { updateNoteWithVersionGuard } from '~/features/note/services/write.js';\nimport {\n isInvalidNoteVersionError,\n isMissingNoteVersionError,\n isNoteVersionConflictError,\n} from '~/features/note/services/write-conflict.js';\nimport models from '~/models.js';\nimport type { NoteInput } from '~/types/index.js';\n\nconst PLACEHOLDER_PREFIX = '{%';\nconst PLACEHOLDER_SUFFIX = '%}';\n\nconst replacePlaceholders = async (content: string) => {\n const placeholders = content.matchAll(new RegExp(`${PLACEHOLDER_PREFIX}([^}]+)${PLACEHOLDER_SUFFIX}`, 'g'));\n const resolvedPlaceholders = await models.placeholder.findMany({\n select: {\n template: true,\n replacement: true,\n },\n where: { template: { in: Array.from(new Set(Array.from(placeholders, (match) => match[1]))) } },\n });\n\n let replacedContent = content;\n\n for (const placeholder of resolvedPlaceholders) {\n replacedContent = replacedContent.replace(\n new RegExp(`${PLACEHOLDER_PREFIX}${placeholder.template}${PLACEHOLDER_SUFFIX}`, 'g'),\n placeholder.replacement,\n );\n }\n\n return replacedContent;\n};\n\nconst getRequestUserAgent = (req?: Request) => {\n const userAgentHeader = req?.headers['user-agent'];\n return Array.isArray(userAgentHeader) ? userAgentHeader[0] : userAgentHeader;\n};\n\nconst toTagConnections = (blocks: Array<{ props?: { id?: string | number } }>) => {\n return blocks\n .map((block) => block.props?.id)\n .filter((id) => id !== undefined && id !== null && String(id).trim() !== '')\n .map((id) => Number(id))\n .filter(Number.isFinite)\n .map((id) => ({ id }));\n};\n\nconst parseRequiredNoteContent = (content: string) => {\n const parsedContent = parseNoteContent(content);\n\n if (!parsedContent) {\n throw new GraphQLError('Note content must be valid BlockNote JSON.', {\n extensions: {\n code: 'INVALID_NOTE_CONTENT',\n },\n });\n }\n\n return parsedContent;\n};\n\ntype NoteMutationResolvers = NonNullable<IResolvers['Mutation']>;\ntype CreateNoteInput = Required<Pick<NoteInput, 'title' | 'content'>> & Pick<NoteInput, 'layout'>;\n\nexport const noteMutationResolvers: NoteMutationResolvers = {\n createNote: async (_, { note }: { note: CreateNoteInput }) => {\n const replacedTitle = await replacePlaceholders(note.title);\n const replacedContent = await replacePlaceholders(note.content);\n const parsedContent = replacedContent ? parseRequiredNoteContent(replacedContent) : null;\n const blocks = parsedContent ? extractBlocksByType<{ id: string }>('tag', parsedContent) : [];\n const createdNote = await models.note.create({\n data: {\n title: replacedTitle,\n content: replacedContent,\n ...buildNoteSearchProjection({\n title: replacedTitle,\n content: replacedContent,\n }),\n ...(note.layout && { layout: note.layout }),\n },\n });\n\n if (!replacedContent) {\n return createdNote;\n }\n\n return models.note.update({\n where: { id: createdNote.id },\n data: { tags: { set: toTagConnections(blocks) } },\n });\n },\n updateNote: async (\n _,\n {\n id,\n note,\n editSessionId,\n expectedUpdatedAt,\n force,\n }: {\n id: number;\n note: NoteInput;\n editSessionId?: string;\n expectedUpdatedAt?: string;\n force?: boolean;\n },\n context: {\n req?: Request;\n },\n ) => {\n const parsedContent = note.content ? parseRequiredNoteContent(note.content) : null;\n const blocks = parsedContent ? extractBlocksByType<{ id: string }>('tag', parsedContent) : [];\n try {\n const updatedNote = await updateNoteWithVersionGuard({\n id: Number(id),\n data: {\n ...(note.title !== undefined ? { title: note.title } : {}),\n ...(note.content !== undefined ? { content: note.content } : {}),\n ...(note.layout !== undefined ? { layout: note.layout } : {}),\n ...(note.content !== undefined ? { tagIds: toTagConnections(blocks).map((tag) => tag.id) } : {}),\n },\n ...(editSessionId ? { editSessionId } : {}),\n ...(expectedUpdatedAt ? { expectedUpdatedAt } : {}),\n ...(force ? { force: true } : {}),\n snapshotMeta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req)),\n });\n\n if (!updatedNote) {\n throw 'NOT FOUND';\n }\n\n return updatedNote;\n } catch (error) {\n if (isNoteVersionConflictError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n currentUpdatedAt: error.currentUpdatedAt,\n expectedUpdatedAt: error.expectedUpdatedAt,\n },\n });\n }\n\n if (isInvalidNoteVersionError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n },\n });\n }\n\n if (isMissingNoteVersionError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n },\n });\n }\n\n throw error;\n }\n },\n deleteNote: async (_, { id }: { id: string }) => {\n const trashedNote = await trashNoteById(Number(id));\n\n if (!trashedNote) {\n throw 'NOT FOUND';\n }\n\n return true;\n },\n restoreNoteSnapshot: async (\n _,\n { id }: { id: string },\n context: {\n req?: Request;\n },\n ) => {\n const note = await restoreNoteSnapshot(Number(id), {\n meta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req)),\n });\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n return note;\n },\n restoreTrashedNote: async (_, { id }: { id: string }) => {\n const note = await restoreTrashedNoteById(Number(id));\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n return note;\n },\n purgeTrashedNote: async (_, { id }: { id: string }) => {\n const purgedNote = await purgeTrashedNoteById(Number(id));\n\n if (!purgedNote) {\n throw 'NOT FOUND';\n }\n\n return true;\n },\n pinNote: (_, { id, pinned }: { id: string; pinned: boolean }) =>\n models.note.update({\n where: { id: Number(id) },\n data: { pinned: Boolean(pinned) },\n }),\n reorderNotes: async (_, { notes }: { notes: Array<{ id: string; order: number }> }) => {\n const updatePromises = notes.map(({ id, order }) =>\n models.note.update({\n where: { id: Number(id) },\n data: { order },\n }),\n );\n\n return Promise.all(updatePromises);\n },\n};\n"],"mappings":"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","names":[]}
@@ -1,10 +1,18 @@
1
1
  import { getNoteCleanupPreview, listNoteCleanupCandidates } from "../../../features/note/services/cleanup.js";
2
2
  import {
3
+ buildNoteGraph,
4
+ contentReferencesNote,
5
+ extractReferenceBlocksFromContent,
6
+ normalizeReferenceId,
7
+ syncReferenceTitlesInContent
8
+ } from "../../../features/note/services/content-blocks.js";
9
+ import {
10
+ buildNoteSearchProjection,
3
11
  filterNotesBySearchQuery,
4
12
  NOTE_SEARCH_TEXT_SCHEMA_VERSION,
5
13
  parseNoteSearchQuery
6
14
  } from "../../../features/note/services/search.js";
7
- import { listNoteSnapshots } from "../../../features/note/services/snapshot.js";
15
+ import { getNoteSnapshot, listNoteSnapshots } from "../../../features/note/services/snapshot.js";
8
16
  import {
9
17
  buildNoteTagNamesWhere,
10
18
  normalizeNoteTagNames
@@ -12,7 +20,6 @@ import {
12
20
  import { getTrashedNoteById, listTrashedNotes } from "../../../features/note/services/trash.js";
13
21
  import models from "../../../models.js";
14
22
  import { runDataMaintenanceInBackground } from "../../../modules/data-maintenance.js";
15
- import { extractBlocksByType } from "./note.graphql.shared.js";
16
23
  const buildAllNotesOrderBy = (searchFilter) => {
17
24
  const sortBy = searchFilter.sortBy || "updatedAt";
18
25
  const sortOrder = searchFilter.sortOrder || "desc";
@@ -88,6 +95,9 @@ const mergeSortedNotesForSearch = (left, right, searchFilter) => {
88
95
  }
89
96
  return merged;
90
97
  };
98
+ const isRecordNotFoundError = (error) => {
99
+ return typeof error === "object" && error !== null && "code" in error && error.code === "P2025";
100
+ };
91
101
  const createAllNotesQueryResolver = (deps = {
92
102
  countNotes: ({ where }) => models.note.count({ where }),
93
103
  findNotes: ({ orderBy, where, take, skip }) => models.note.findMany({
@@ -174,6 +184,33 @@ const createAllNotesQueryResolver = (deps = {
174
184
  };
175
185
  };
176
186
  };
187
+ const createBackReferencesQueryResolver = (deps = {
188
+ findCandidateNotes: (noteId) => models.note.findMany({
189
+ orderBy: [{ pinned: "desc" }, { updatedAt: "desc" }],
190
+ where: {
191
+ NOT: { id: noteId }
192
+ }
193
+ })
194
+ }) => {
195
+ return async (_, { id }) => {
196
+ const notes = await deps.findCandidateNotes(Number(id));
197
+ return notes.filter((note) => contentReferencesNote(note.content, id));
198
+ };
199
+ };
200
+ const createNoteGraphQueryResolver = (deps = {
201
+ findNotes: () => models.note.findMany({
202
+ select: {
203
+ id: true,
204
+ title: true,
205
+ content: true
206
+ }
207
+ })
208
+ }) => {
209
+ return async () => {
210
+ const notes = await deps.findNotes();
211
+ return buildNoteGraph(notes);
212
+ };
213
+ };
177
214
  const noteQueryResolvers = {
178
215
  allNotes: createAllNotesQueryResolver(),
179
216
  notesInDateRange: async (_, {
@@ -248,12 +285,7 @@ const noteQueryResolvers = {
248
285
  orderBy: { updatedAt: "desc" },
249
286
  where: { content: { contains: src } }
250
287
  }),
251
- backReferences: async (_, { id }) => {
252
- return models.note.findMany({
253
- orderBy: [{ pinned: "desc" }, { updatedAt: "desc" }],
254
- where: { content: { contains: `reference","props":{"id":"${id}"` } }
255
- });
256
- },
288
+ backReferences: createBackReferencesQueryResolver(),
257
289
  note: async (_, { id }) => {
258
290
  const note = await models.note.findUnique({ where: { id: Number(id) } });
259
291
  if (!note) {
@@ -262,33 +294,42 @@ const noteQueryResolvers = {
262
294
  if (!note.content) {
263
295
  return note;
264
296
  }
265
- const blocks = extractBlocksByType("reference", JSON.parse(note.content));
297
+ const blocks = extractReferenceBlocksFromContent(note.content);
266
298
  if (blocks.length === 0) {
267
299
  return note;
268
300
  }
269
- const referenceIds = blocks.map((block) => Number(block.props.id));
301
+ const referenceIds = Array.from(
302
+ new Set(
303
+ blocks.map((block) => normalizeReferenceId(block.props?.id)).filter((referenceId) => referenceId !== null).map(Number).filter(Number.isFinite)
304
+ )
305
+ );
270
306
  const references = await models.note.findMany({ where: { id: { in: referenceIds } } });
271
- const newContent = references.reduce((content, referenceNote) => {
272
- const reference = blocks.find((block) => Number(block.props.id) === referenceNote.id);
273
- if (reference && reference.props.title !== referenceNote.title) {
274
- return content.replace(
275
- `reference","props":{"id":"${reference.props.id}","title":"${reference.props.title}"`,
276
- `reference","props":{"id":"${referenceNote.id}","title":"${referenceNote.title}"`
277
- );
278
- }
279
- return content;
280
- }, note.content);
281
- if (newContent === note.content) {
307
+ const titlesById = new Map(
308
+ references.map((referenceNote) => [String(referenceNote.id), referenceNote.title])
309
+ );
310
+ const newContent = syncReferenceTitlesInContent(note.content, titlesById);
311
+ if (!newContent || newContent === note.content) {
282
312
  return note;
283
313
  }
284
314
  try {
285
- JSON.parse(newContent);
286
315
  return await models.note.update({
287
- where: { id: note.id },
288
- data: { content: newContent }
316
+ where: {
317
+ id: note.id,
318
+ updatedAt: note.updatedAt
319
+ },
320
+ data: {
321
+ content: newContent,
322
+ ...buildNoteSearchProjection({
323
+ title: note.title,
324
+ content: newContent
325
+ })
326
+ }
289
327
  });
290
- } catch {
291
- return note;
328
+ } catch (error) {
329
+ if (isRecordNotFoundError(error)) {
330
+ return note;
331
+ }
332
+ throw error;
292
333
  }
293
334
  },
294
335
  noteCleanupCandidates: async (_, {
@@ -310,10 +351,13 @@ const noteQueryResolvers = {
310
351
  },
311
352
  noteSnapshots: async (_, {
312
353
  id,
313
- limit = 5
354
+ limit = 10
314
355
  }) => {
315
356
  return listNoteSnapshots(Number(id), Number(limit));
316
357
  },
358
+ noteSnapshot: async (_, { id }) => {
359
+ return getNoteSnapshot(Number(id));
360
+ },
317
361
  trashedNote: async (_, { id }) => {
318
362
  return getTrashedNoteById(Number(id));
319
363
  },
@@ -328,58 +372,12 @@ const noteQueryResolvers = {
328
372
  offset: Number(pagination.offset)
329
373
  });
330
374
  },
331
- noteGraph: async () => {
332
- const notes = await models.note.findMany({
333
- select: {
334
- id: true,
335
- title: true,
336
- content: true
337
- }
338
- });
339
- const nodes = [];
340
- const links = [];
341
- const connectionCount = {};
342
- const linkSet = /* @__PURE__ */ new Set();
343
- for (const note of notes) {
344
- if (!note.content) {
345
- continue;
346
- }
347
- try {
348
- const blocks = extractBlocksByType("reference", JSON.parse(note.content));
349
- for (const block of blocks) {
350
- const targetId = block.props.id;
351
- if (targetId && String(note.id) !== targetId) {
352
- const linkKey = `${note.id}-${targetId}`;
353
- const reverseLinkKey = `${targetId}-${note.id}`;
354
- if (!linkSet.has(linkKey) && !linkSet.has(reverseLinkKey)) {
355
- linkSet.add(linkKey);
356
- links.push({
357
- source: String(note.id),
358
- target: targetId
359
- });
360
- connectionCount[String(note.id)] = (connectionCount[String(note.id)] || 0) + 1;
361
- connectionCount[targetId] = (connectionCount[targetId] || 0) + 1;
362
- }
363
- }
364
- }
365
- } catch {
366
- }
367
- }
368
- for (const note of notes) {
369
- nodes.push({
370
- id: String(note.id),
371
- title: note.title || "Untitled",
372
- connections: connectionCount[String(note.id)] || 0
373
- });
374
- }
375
- return {
376
- nodes,
377
- links
378
- };
379
- }
375
+ noteGraph: createNoteGraphQueryResolver()
380
376
  };
381
377
  export {
382
378
  createAllNotesQueryResolver,
379
+ createBackReferencesQueryResolver,
380
+ createNoteGraphQueryResolver,
383
381
  noteQueryResolvers
384
382
  };
385
383
  //# sourceMappingURL=note.query.resolver.js.map