adds default view when opening new document
9 files changed,  +362, -238
M client/Makefile
+1, -0
1@@ -28,6 +28,7 @@ static:
2 js:
3 	@cp src/ffi/*.js lib/es6/src/ffi/
4 	@cp src/ffi/*.ts lib/es6/src/ffi/
5+	@cp src/ui/*.js lib/es6/src/ui/
6 	@echo "moved updated JS files"
7 .PHONY: js
8 
M client/lib/es6/src/index.css
+127, -114
  1@@ -2,200 +2,213 @@
  2 @plugin "@tailwindcss/typography";
  3 
  4 @theme {
  5-  --color-paper: #f7f6f2;
  6-  --color-black: #100f0f;
  7-  --color-red: #af3029;
  8-  --color-orange: #bc5215;
  9-  --color-yellow: #ad8301;
 10-  --color-green: #66800b;
 11-  --color-cyan: #24837b;
 12-  --color-blue: #205ea6;
 13-  --color-purple: #5e409d;
 14-  --color-magenta: #a02f6f;
 15-  --color-bg-2: #f2f0e5;
 16-  --color-ui: #b7b5ac;
 17-  --color-ui-2: #6f6e69;
 18+    --color-paper: #f7f6f2;
 19+    --color-black: #100f0f;
 20+    --color-red: #af3029;
 21+    --color-orange: #bc5215;
 22+    --color-yellow: #ad8301;
 23+    --color-green: #66800b;
 24+    --color-cyan: #24837b;
 25+    --color-blue: #205ea6;
 26+    --color-purple: #5e409d;
 27+    --color-magenta: #a02f6f;
 28+    --color-bg-2: #f2f0e5;
 29+    --color-ui: #b7b5ac;
 30+    --color-ui-2: #6f6e69;
 31 }
 32 
 33 :root {
 34-  --sheet-width: 90ch;
 35-  --sheet-position: 2rem;
 36-
 37-  --shadow-color: 0deg 0% 0%;
 38-  --shadow-elevation-low:
 39-    0.8px 0px 0.8px hsl(var(--shadow-color) / 0.17),
 40-    1.3px 0px 1.3px -1.7px hsl(var(--shadow-color) / 0.14),
 41-    3.5px 0px 3.5px -3.3px hsl(var(--shadow-color) / 0.11);
 42-  --shadow-elevation-medium:
 43-    0.8px 0px 0.8px hsl(var(--shadow-color) / 0.18),
 44-    2.3px 0px 2.3px -1.1px hsl(var(--shadow-color) / 0.16),
 45-    6.7px 0.1px 6.7px -2.2px hsl(var(--shadow-color) / 0.14),
 46-    17.7px 0.1px 17.7px -3.3px hsl(var(--shadow-color) / 0.11);
 47-  --shadow-elevation-high:
 48-    0.8px 0px 0.8px hsl(var(--shadow-color) / 0.15),
 49-    3.6px 0px 3.6px -0.4px hsl(var(--shadow-color) / 0.14),
 50-    6.8px 0.1px 6.8px -0.8px hsl(var(--shadow-color) / 0.13),
 51-    11.6px 0.1px 11.6px -1.3px hsl(var(--shadow-color) / 0.13),
 52-    19px 0.2px 19px -1.7px hsl(var(--shadow-color) / 0.12),
 53-    30.2px 0.2px 30.1px -2.1px hsl(var(--shadow-color) / 0.11),
 54-    46.4px 0.4px 46.3px -2.5px hsl(var(--shadow-color) / 0.1),
 55-    68.7px 0.6px 68.5px -2.9px hsl(var(--shadow-color) / 0.09),
 56-    98.2px 0.8px 98px -3.3px hsl(var(--shadow-color) / 0.08);
 57+    --sheet-width: 90ch;
 58+    --sheet-position: 2rem;
 59+
 60+    --shadow-color: 0deg 0% 0%;
 61+    --shadow-elevation-low:
 62+        0.8px 0px 0.8px hsl(var(--shadow-color) / 0.17),
 63+        1.3px 0px 1.3px -1.7px hsl(var(--shadow-color) / 0.14),
 64+        3.5px 0px 3.5px -3.3px hsl(var(--shadow-color) / 0.11);
 65+    --shadow-elevation-medium:
 66+        0.8px 0px 0.8px hsl(var(--shadow-color) / 0.18),
 67+        2.3px 0px 2.3px -1.1px hsl(var(--shadow-color) / 0.16),
 68+        6.7px 0.1px 6.7px -2.2px hsl(var(--shadow-color) / 0.14),
 69+        17.7px 0.1px 17.7px -3.3px hsl(var(--shadow-color) / 0.11);
 70+    --shadow-elevation-high:
 71+        0.8px 0px 0.8px hsl(var(--shadow-color) / 0.15),
 72+        3.6px 0px 3.6px -0.4px hsl(var(--shadow-color) / 0.14),
 73+        6.8px 0.1px 6.8px -0.8px hsl(var(--shadow-color) / 0.13),
 74+        11.6px 0.1px 11.6px -1.3px hsl(var(--shadow-color) / 0.13),
 75+        19px 0.2px 19px -1.7px hsl(var(--shadow-color) / 0.12),
 76+        30.2px 0.2px 30.1px -2.1px hsl(var(--shadow-color) / 0.11),
 77+        46.4px 0.4px 46.3px -2.5px hsl(var(--shadow-color) / 0.1),
 78+        68.7px 0.6px 68.5px -2.9px hsl(var(--shadow-color) / 0.09),
 79+        98.2px 0.8px 98px -3.3px hsl(var(--shadow-color) / 0.08);
 80 }
 81 
 82 /* base-ui styles */
 83 body {
 84-  position: relative;
 85+    position: relative;
 86 }
 87 
 88 .root {
 89-  isolation: isolate;
 90+    isolation: isolate;
 91 }
 92 
 93 .popup {
 94-  box-sizing: border-box;
 95-  padding-block: 0.25rem;
 96-  border-radius: 0.375rem;
 97-  background-color: var(--color-paper);
 98-  color: var(--color-black-custom);
 99-  transform-origin: var(--transform-origin);
100-  transition:
101-    transform 150ms,
102-    opacity 150ms;
103-  padding: 0.5rem;
104-
105-  &[data-starting-style],
106-  &[data-ending-style] {
107-    opacity: 0;
108-    transform: scale(0.9);
109-  }
110+    box-sizing: border-box;
111+    padding-block: 0.25rem;
112+    border-radius: 0.375rem;
113+    background-color: var(--color-paper);
114+    color: var(--color-black-custom);
115+    transform-origin: var(--transform-origin);
116+    transition:
117+        transform 150ms,
118+        opacity 150ms;
119+    padding: 0.5rem;
120+
121+    &[data-starting-style],
122+    &[data-ending-style] {
123+        opacity: 0;
124+        transform: scale(0.9);
125+    }
126 }
127 
128 /* root styles */
129 #root {
130-  position: relative;
131-  height: 100vh;
132-  width: 100vw;
133-  z-index: 0;
134+    position: relative;
135+    height: 100vh;
136+    width: 100vw;
137+    z-index: 0;
138 }
139 
140 [contenteditable="true"] {
141-  outline: none;
142+    outline: none;
143 }
144 
145 rtw-waypoint {
146-  font-weight: 700;
147+    font-weight: 700;
148 }
149 
150 rtw-waypoint:hover {
151-  cursor: pointer;
152+    cursor: pointer;
153 }
154 
155 rtw-waypoint.error {
156-  color: var(--color-red);
157+    color: var(--color-red);
158 }
159 
160 .react-modal-sheet-container {
161-  background: var(--color-paper) !important;
162-  color: var(--color-black-custom) !important;
163+    background: var(--color-paper) !important;
164+    color: var(--color-black-custom) !important;
165 }
166 
167 #summary {
168-  width: 100%;
169-  z-index: 1;
170-  height: 100vh;
171-  box-sizing: border-box;
172+    width: 100%;
173+    z-index: 1;
174+    height: 100vh;
175+    box-sizing: border-box;
176 }
177 
178-#summary>div {
179-  z-index: 99999;
180-  background: var(--paper);
181+#summary > div {
182+    z-index: 99999;
183+    background: var(--paper);
184 }
185 
186 @media (min-width: calc(2 * 90ch)) {
187-  .react-modal-sheet-container {
188-    max-width: var(--sheet-width);
189-    box-sizing: border-box;
190-    box-shadow: var(--shadow-elevation-high);
191-  }
192+    .react-modal-sheet-container {
193+        max-width: var(--sheet-width);
194+        box-sizing: border-box;
195+        box-shadow: var(--shadow-elevation-high);
196+    }
197 
198-  #summary {
199-    max-width: var(--sheet-width);
200-  }
201+    #summary {
202+        max-width: var(--sheet-width);
203+    }
204 }
205 
206 /* Tiptap overrides */
207 .tiptap h1 {
208-  font-size: 1.875rem;
209-  font-weight: 800;
210-  padding-bottom: 0.5rem;
211+    font-size: 1.875rem;
212+    font-weight: 800;
213+    padding-bottom: 0.5rem;
214 }
215 
216 .tiptap h2 {
217-  font-size: 1.5rem;
218-  font-weight: 800;
219-  padding-bottom: 0.25rem;
220+    font-size: 1.5rem;
221+    font-weight: 800;
222+    padding-top: 0.5rem;
223+    padding-bottom: 0.25rem;
224 }
225 
226 .tiptap h3 {
227-  font-size: 1.25rem;
228-  font-weight: 700;
229-  padding-bottom: 0.25rem;
230+    font-size: 1.25rem;
231+    font-weight: 700;
232+    padding-top: 0.5rem;
233+    padding-bottom: 0.25rem;
234 }
235 
236 .tiptap h4 {
237-  font-size: 1.125rem;
238-  font-weight: 700;
239-  padding-bottom: 0.25rem;
240+    font-size: 1.125rem;
241+    font-weight: 700;
242+    padding-top: 0.5rem;
243+    padding-bottom: 0.25rem;
244 }
245 
246 .tiptap p {
247-  padding-top: 0.5rem;
248-  padding-bottom: 0.5rem;
249+    padding-top: 0.5rem;
250+    padding-bottom: 0.5rem;
251 }
252 
253 .tiptap ul,
254 .tiptap ol {
255-  line-height: 1.25rem;
256-  padding-left: 1.25rem;
257+    line-height: 1.25rem;
258+    padding-left: 1.5rem;
259 }
260 
261 .tiptap blockquote {
262-  margin: 0.5rem 0.625rem 0.5rem 0.625rem;
263-  padding: 0.5rem 0.625rem 0.5rem 0.625rem;
264-  border-left: 3px solid var(--color-blue);
265+    margin: 0.5rem 0.625rem 0.5rem 0.625rem;
266+    padding: 0.5rem 0.625rem 0.5rem 0.625rem;
267+    border-left: 3px solid var(--color-blue);
268 }
269 
270 .tiptap blockquote:has(+ p) {
271-  margin-bottom: 0;
272-  padding-bottom: 0;
273+    margin-bottom: 0;
274+    padding-bottom: 0;
275 }
276 
277 .tiptap ul li {
278-  list-style-type: circle;
279-  list-style-position: outside;
280+    list-style-type: circle;
281+    list-style-position: outside;
282 }
283 
284 .tiptap ol li {
285-  list-style-type: decimal;
286-  list-style-position: outside;
287+    list-style-type: decimal;
288+    list-style-position: outside;
289 }
290 
291-.tiptap li>p {
292-  margin: 0;
293-  padding: 0;
294+.tiptap li > p {
295+    margin: 0;
296+    padding: 0;
297+}
298+
299+.tiptap ol li,
300+.tiptap ul li {
301+    margin-bottom: 0.25rem;
302+}
303+
304+.tiptap ol li:last-of-type,
305+.tiptap ul li:last-of-type {
306+    margin-bottom: 0.5rem;
307 }
308 
309 .tiptap a {
310-  color: var(--color-blue);
311-  text-decoration: underline dotted;
312-  cursor: pointer;
313+    color: var(--color-blue);
314+    text-decoration: underline dotted;
315+    cursor: pointer;
316 }
317 
318 .tiptap p:has(+ ul) {
319-  margin-bottom: 0;
320+    margin-bottom: 0;
321 }
322 
323-.tiptap p+ul {
324-  margin: 0;
325-}
326+.tiptap p + ul {
327+    margin: 0;
328+}
M client/lib/es6/src/ui/Editor.res.mjs
+11, -3
 1@@ -6,6 +6,7 @@ import * as NanoID from "../ffi/NanoID.res.mjs";
 2 import * as Preact from "../ffi/Preact.res.mjs";
 3 import * as Tiptap from "../ffi/Tiptap.res.mjs";
 4 import * as PreactDOM from "../ffi/PreactDOM.res.mjs";
 5+import * as ContentJs from "./content.js";
 6 import * as Core from "@tiptap/core";
 7 import * as Core__Option from "@rescript/core/lib/es6/src/Core__Option.res.mjs";
 8 import * as React from "@tiptap/react";
 9@@ -14,6 +15,8 @@ import * as Signals from "@preact/signals";
10 import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
11 import * as JsxRuntime from "preact/jsx-runtime";
12 
13+let content = ContentJs.content;
14+
15 let FloatingUI = {};
16 
17 let Instance = {};
18@@ -49,11 +52,15 @@ function Editor(props) {
19         }
20       },
21       extensions: Tiptap.extensions(ydoc, doc),
22+      content: content,
23       onUpdate: _event => {
24         let ed = editorRef.contents;
25-        if (ed !== undefined) {
26-          return State.WaypointUpdater.trigger(doc, Primitive_option.valFromOption(ed));
27+        if (ed === undefined) {
28+          return;
29         }
30+        let ed$1 = Primitive_option.valFromOption(ed);
31+        State.WaypointUpdater.trigger(doc, ed$1);
32+        console.log(ed$1.getJSON());
33       }
34     });
35     editorRef.contents = Primitive_option.some(instance$1);
36@@ -180,6 +187,7 @@ let FloatingMenu = {};
37 let make = Editor;
38 
39 export {
40+  content,
41   FloatingUI,
42   Instance,
43   onSearchSelectCache,
44@@ -188,4 +196,4 @@ export {
45   BubbleMenu,
46   FloatingMenu,
47 }
48-/* onSearchSelectCache Not a pure module */
49+/* content Not a pure module */
A client/lib/es6/src/ui/content.js
+19, -0
 1@@ -0,0 +1,19 @@
 2+export const content = {
 3+  type: "doc",
 4+  content: [
 5+    {
 6+      type: "heading",
 7+      attrs: { level: 1 },
 8+      content: [{ type: "text", text: "Start with a title..." }],
 9+    },
10+    {
11+      type: "paragraph",
12+      content: [
13+        {
14+          type: "text",
15+          text: "To add a waypoint type @ or click on the menu button at the start of an empty line...",
16+        },
17+      ],
18+    },
19+  ],
20+};
M client/src/index.css
+127, -114
  1@@ -2,200 +2,213 @@
  2 @plugin "@tailwindcss/typography";
  3 
  4 @theme {
  5-  --color-paper: #f7f6f2;
  6-  --color-black: #100f0f;
  7-  --color-red: #af3029;
  8-  --color-orange: #bc5215;
  9-  --color-yellow: #ad8301;
 10-  --color-green: #66800b;
 11-  --color-cyan: #24837b;
 12-  --color-blue: #205ea6;
 13-  --color-purple: #5e409d;
 14-  --color-magenta: #a02f6f;
 15-  --color-bg-2: #f2f0e5;
 16-  --color-ui: #b7b5ac;
 17-  --color-ui-2: #6f6e69;
 18+    --color-paper: #f7f6f2;
 19+    --color-black: #100f0f;
 20+    --color-red: #af3029;
 21+    --color-orange: #bc5215;
 22+    --color-yellow: #ad8301;
 23+    --color-green: #66800b;
 24+    --color-cyan: #24837b;
 25+    --color-blue: #205ea6;
 26+    --color-purple: #5e409d;
 27+    --color-magenta: #a02f6f;
 28+    --color-bg-2: #f2f0e5;
 29+    --color-ui: #b7b5ac;
 30+    --color-ui-2: #6f6e69;
 31 }
 32 
 33 :root {
 34-  --sheet-width: 90ch;
 35-  --sheet-position: 2rem;
 36-
 37-  --shadow-color: 0deg 0% 0%;
 38-  --shadow-elevation-low:
 39-    0.8px 0px 0.8px hsl(var(--shadow-color) / 0.17),
 40-    1.3px 0px 1.3px -1.7px hsl(var(--shadow-color) / 0.14),
 41-    3.5px 0px 3.5px -3.3px hsl(var(--shadow-color) / 0.11);
 42-  --shadow-elevation-medium:
 43-    0.8px 0px 0.8px hsl(var(--shadow-color) / 0.18),
 44-    2.3px 0px 2.3px -1.1px hsl(var(--shadow-color) / 0.16),
 45-    6.7px 0.1px 6.7px -2.2px hsl(var(--shadow-color) / 0.14),
 46-    17.7px 0.1px 17.7px -3.3px hsl(var(--shadow-color) / 0.11);
 47-  --shadow-elevation-high:
 48-    0.8px 0px 0.8px hsl(var(--shadow-color) / 0.15),
 49-    3.6px 0px 3.6px -0.4px hsl(var(--shadow-color) / 0.14),
 50-    6.8px 0.1px 6.8px -0.8px hsl(var(--shadow-color) / 0.13),
 51-    11.6px 0.1px 11.6px -1.3px hsl(var(--shadow-color) / 0.13),
 52-    19px 0.2px 19px -1.7px hsl(var(--shadow-color) / 0.12),
 53-    30.2px 0.2px 30.1px -2.1px hsl(var(--shadow-color) / 0.11),
 54-    46.4px 0.4px 46.3px -2.5px hsl(var(--shadow-color) / 0.1),
 55-    68.7px 0.6px 68.5px -2.9px hsl(var(--shadow-color) / 0.09),
 56-    98.2px 0.8px 98px -3.3px hsl(var(--shadow-color) / 0.08);
 57+    --sheet-width: 90ch;
 58+    --sheet-position: 2rem;
 59+
 60+    --shadow-color: 0deg 0% 0%;
 61+    --shadow-elevation-low:
 62+        0.8px 0px 0.8px hsl(var(--shadow-color) / 0.17),
 63+        1.3px 0px 1.3px -1.7px hsl(var(--shadow-color) / 0.14),
 64+        3.5px 0px 3.5px -3.3px hsl(var(--shadow-color) / 0.11);
 65+    --shadow-elevation-medium:
 66+        0.8px 0px 0.8px hsl(var(--shadow-color) / 0.18),
 67+        2.3px 0px 2.3px -1.1px hsl(var(--shadow-color) / 0.16),
 68+        6.7px 0.1px 6.7px -2.2px hsl(var(--shadow-color) / 0.14),
 69+        17.7px 0.1px 17.7px -3.3px hsl(var(--shadow-color) / 0.11);
 70+    --shadow-elevation-high:
 71+        0.8px 0px 0.8px hsl(var(--shadow-color) / 0.15),
 72+        3.6px 0px 3.6px -0.4px hsl(var(--shadow-color) / 0.14),
 73+        6.8px 0.1px 6.8px -0.8px hsl(var(--shadow-color) / 0.13),
 74+        11.6px 0.1px 11.6px -1.3px hsl(var(--shadow-color) / 0.13),
 75+        19px 0.2px 19px -1.7px hsl(var(--shadow-color) / 0.12),
 76+        30.2px 0.2px 30.1px -2.1px hsl(var(--shadow-color) / 0.11),
 77+        46.4px 0.4px 46.3px -2.5px hsl(var(--shadow-color) / 0.1),
 78+        68.7px 0.6px 68.5px -2.9px hsl(var(--shadow-color) / 0.09),
 79+        98.2px 0.8px 98px -3.3px hsl(var(--shadow-color) / 0.08);
 80 }
 81 
 82 /* base-ui styles */
 83 body {
 84-  position: relative;
 85+    position: relative;
 86 }
 87 
 88 .root {
 89-  isolation: isolate;
 90+    isolation: isolate;
 91 }
 92 
 93 .popup {
 94-  box-sizing: border-box;
 95-  padding-block: 0.25rem;
 96-  border-radius: 0.375rem;
 97-  background-color: var(--color-paper);
 98-  color: var(--color-black-custom);
 99-  transform-origin: var(--transform-origin);
100-  transition:
101-    transform 150ms,
102-    opacity 150ms;
103-  padding: 0.5rem;
104-
105-  &[data-starting-style],
106-  &[data-ending-style] {
107-    opacity: 0;
108-    transform: scale(0.9);
109-  }
110+    box-sizing: border-box;
111+    padding-block: 0.25rem;
112+    border-radius: 0.375rem;
113+    background-color: var(--color-paper);
114+    color: var(--color-black-custom);
115+    transform-origin: var(--transform-origin);
116+    transition:
117+        transform 150ms,
118+        opacity 150ms;
119+    padding: 0.5rem;
120+
121+    &[data-starting-style],
122+    &[data-ending-style] {
123+        opacity: 0;
124+        transform: scale(0.9);
125+    }
126 }
127 
128 /* root styles */
129 #root {
130-  position: relative;
131-  height: 100vh;
132-  width: 100vw;
133-  z-index: 0;
134+    position: relative;
135+    height: 100vh;
136+    width: 100vw;
137+    z-index: 0;
138 }
139 
140 [contenteditable="true"] {
141-  outline: none;
142+    outline: none;
143 }
144 
145 rtw-waypoint {
146-  font-weight: 700;
147+    font-weight: 700;
148 }
149 
150 rtw-waypoint:hover {
151-  cursor: pointer;
152+    cursor: pointer;
153 }
154 
155 rtw-waypoint.error {
156-  color: var(--color-red);
157+    color: var(--color-red);
158 }
159 
160 .react-modal-sheet-container {
161-  background: var(--color-paper) !important;
162-  color: var(--color-black-custom) !important;
163+    background: var(--color-paper) !important;
164+    color: var(--color-black-custom) !important;
165 }
166 
167 #summary {
168-  width: 100%;
169-  z-index: 1;
170-  height: 100vh;
171-  box-sizing: border-box;
172+    width: 100%;
173+    z-index: 1;
174+    height: 100vh;
175+    box-sizing: border-box;
176 }
177 
178-#summary>div {
179-  z-index: 99999;
180-  background: var(--paper);
181+#summary > div {
182+    z-index: 99999;
183+    background: var(--paper);
184 }
185 
186 @media (min-width: calc(2 * 90ch)) {
187-  .react-modal-sheet-container {
188-    max-width: var(--sheet-width);
189-    box-sizing: border-box;
190-    box-shadow: var(--shadow-elevation-high);
191-  }
192+    .react-modal-sheet-container {
193+        max-width: var(--sheet-width);
194+        box-sizing: border-box;
195+        box-shadow: var(--shadow-elevation-high);
196+    }
197 
198-  #summary {
199-    max-width: var(--sheet-width);
200-  }
201+    #summary {
202+        max-width: var(--sheet-width);
203+    }
204 }
205 
206 /* Tiptap overrides */
207 .tiptap h1 {
208-  font-size: 1.875rem;
209-  font-weight: 800;
210-  padding-bottom: 0.5rem;
211+    font-size: 1.875rem;
212+    font-weight: 800;
213+    padding-bottom: 0.5rem;
214 }
215 
216 .tiptap h2 {
217-  font-size: 1.5rem;
218-  font-weight: 800;
219-  padding-bottom: 0.25rem;
220+    font-size: 1.5rem;
221+    font-weight: 800;
222+    padding-top: 0.5rem;
223+    padding-bottom: 0.25rem;
224 }
225 
226 .tiptap h3 {
227-  font-size: 1.25rem;
228-  font-weight: 700;
229-  padding-bottom: 0.25rem;
230+    font-size: 1.25rem;
231+    font-weight: 700;
232+    padding-top: 0.5rem;
233+    padding-bottom: 0.25rem;
234 }
235 
236 .tiptap h4 {
237-  font-size: 1.125rem;
238-  font-weight: 700;
239-  padding-bottom: 0.25rem;
240+    font-size: 1.125rem;
241+    font-weight: 700;
242+    padding-top: 0.5rem;
243+    padding-bottom: 0.25rem;
244 }
245 
246 .tiptap p {
247-  padding-top: 0.5rem;
248-  padding-bottom: 0.5rem;
249+    padding-top: 0.5rem;
250+    padding-bottom: 0.5rem;
251 }
252 
253 .tiptap ul,
254 .tiptap ol {
255-  line-height: 1.25rem;
256-  padding-left: 1.25rem;
257+    line-height: 1.25rem;
258+    padding-left: 1.5rem;
259 }
260 
261 .tiptap blockquote {
262-  margin: 0.5rem 0.625rem 0.5rem 0.625rem;
263-  padding: 0.5rem 0.625rem 0.5rem 0.625rem;
264-  border-left: 3px solid var(--color-blue);
265+    margin: 0.5rem 0.625rem 0.5rem 0.625rem;
266+    padding: 0.5rem 0.625rem 0.5rem 0.625rem;
267+    border-left: 3px solid var(--color-blue);
268 }
269 
270 .tiptap blockquote:has(+ p) {
271-  margin-bottom: 0;
272-  padding-bottom: 0;
273+    margin-bottom: 0;
274+    padding-bottom: 0;
275 }
276 
277 .tiptap ul li {
278-  list-style-type: circle;
279-  list-style-position: outside;
280+    list-style-type: circle;
281+    list-style-position: outside;
282 }
283 
284 .tiptap ol li {
285-  list-style-type: decimal;
286-  list-style-position: outside;
287+    list-style-type: decimal;
288+    list-style-position: outside;
289 }
290 
291-.tiptap li>p {
292-  margin: 0;
293-  padding: 0;
294+.tiptap li > p {
295+    margin: 0;
296+    padding: 0;
297+}
298+
299+.tiptap ol li,
300+.tiptap ul li {
301+    margin-bottom: 0.25rem;
302+}
303+
304+.tiptap ol li:last-of-type,
305+.tiptap ul li:last-of-type {
306+    margin-bottom: 0.5rem;
307 }
308 
309 .tiptap a {
310-  color: var(--color-blue);
311-  text-decoration: underline dotted;
312-  cursor: pointer;
313+    color: var(--color-blue);
314+    text-decoration: underline dotted;
315+    cursor: pointer;
316 }
317 
318 .tiptap p:has(+ ul) {
319-  margin-bottom: 0;
320+    margin-bottom: 0;
321 }
322 
323-.tiptap p+ul {
324-  margin: 0;
325-}
326+.tiptap p + ul {
327+    margin: 0;
328+}
M client/src/ui/Editor.res
+6, -1
 1@@ -1,5 +1,6 @@
 2 // src/ui/Editor.res
 3 // Preact-compatible FFI bindings for @tiptap/react composable API
 4+@module("./content.js") @val external content: RescriptCore.JSON.t = "content"
 5 
 6 // Re-export editor type for idiomatic Rescript usage
 7 type t = Tiptap.t
 8@@ -116,10 +117,14 @@ let make = (
 9         onUpdate: _event => {
10           // Trigger debounced waypoint extraction
11           switch editorRef.contents {
12-          | Some(ed) => State.WaypointUpdater.trigger(doc, ed)
13+          | Some(ed) => {
14+              State.WaypointUpdater.trigger(doc, ed)
15+              Console.log(ed->Tiptap.getJSON)
16+            }
17           | None => ()
18           }
19         },
20+        content,
21       })
22 
23       // Store reference for onUpdate callback
A client/src/ui/content.js
+19, -0
 1@@ -0,0 +1,19 @@
 2+export const content = {
 3+  type: "doc",
 4+  content: [
 5+    {
 6+      type: "heading",
 7+      attrs: { level: 1 },
 8+      content: [{ type: "text", text: "Start with a title..." }],
 9+    },
10+    {
11+      type: "paragraph",
12+      content: [
13+        {
14+          type: "text",
15+          text: "To add a waypoint type @ or click on the menu button at the start of an empty line...",
16+        },
17+      ],
18+    },
19+  ],
20+};
M server/api/ws_auth.go
+12, -2
 1@@ -13,11 +13,14 @@ import (
 2 	"git.kilimanjaro.io/rtw/pkg/abac"
 3 	"git.kilimanjaro.io/rtw/pkg/id"
 4 	"git.kilimanjaro.io/rtw/pkg/log"
 5+	"git.kilimanjaro.io/rtw/pkg/session"
 6 	"git.kilimanjaro.io/rtw/user"
 7 )
 8 
 9 var privateKey atomic.Pointer[paseto.V4AsymmetricSecretKey]
10 
11+const defaultDoc = "__default__"
12+
13 func init() {
14 	private := paseto.NewV4AsymmetricSecretKey()
15 	privateKey.CompareAndSwap(nil, &private)
16@@ -68,8 +71,15 @@ func NewWebsocketAuthHandler(publicURL string, us *user.Service, ds *document.Se
17 		// for an existing document
18 		var authLevel AuthLevel
19 		if !exists {
20-			// first time, create new document, assign admin permissions to this user
21-			resourceID, err = ds.NewDocument(docID)
22+			l.Debug("doc doesnt exist", "registered", userInfo.Type == session.Registered, "docID", docID)
23+			// first time, create new document or fork from default, assign admin permissions to this user
24+			if userInfo.Type != session.Registered && docID != defaultDoc {
25+				l.Debug("forking default doc")
26+				// anonymous user, fork the default doc with instructions
27+				resourceID, err = ds.ForkDocument(defaultDoc, docID)
28+			} else {
29+				resourceID, err = ds.NewDocument(docID)
30+			}
31 			if err != nil {
32 				l.Error("websocket auth: error inserting new document", "error", err)
33 				http.Error(w, "error inserting new document", http.StatusInternalServerError)
M server/document/service.go
+40, -4
 1@@ -6,6 +6,7 @@ import (
 2 	"encoding/json"
 3 	"fmt"
 4 	"log/slog"
 5+	"net/http"
 6 	"os"
 7 	"path"
 8 	"sync"
 9@@ -32,9 +33,10 @@ type Document struct {
10 }
11 
12 type Service struct {
13-	db       *sql.DB
14-	basePath string
15-	mu       *sync.RWMutex
16+	db        *sql.DB
17+	basePath  string
18+	mu        *sync.RWMutex
19+	ySweetURL string
20 }
21 
22 type ServiceOption func(*Service) error
23@@ -48,9 +50,18 @@ func WithBasePath(basePath string) ServiceOption {
24 	}
25 }
26 
27+// WithYSweetURL sets the URL to the upstream y-sweet API. Defaults to http://127.0.0.1:9000.
28+func WithYSweetURL(url string) ServiceOption {
29+	return func(s *Service) error {
30+		s.ySweetURL = url
31+		return nil
32+	}
33+}
34+
35 func NewService(opts ...ServiceOption) (*Service, error) {
36 	s := &Service{
37-		mu: &sync.RWMutex{},
38+		mu:        &sync.RWMutex{},
39+		ySweetURL: "http://127.0.0.1:9000",
40 	}
41 	for _, opt := range opts {
42 		if err := opt(s); err != nil {
43@@ -127,6 +138,31 @@ func (s *Service) NewDocumentWithID(resourceID id.Key, docID string) error {
44 	return nil
45 }
46 
47+func (s *Service) ForkDocument(fromDocID string, toDocID string) (id.Key, error) {
48+	existingKey, exists, err := s.DocumentExists(toDocID)
49+	if exists {
50+		return existingKey, nil
51+	}
52+	if err != nil {
53+		return id.NotExist, fmt.Errorf("forking document: doc exists: %w", err)
54+	}
55+	key, err := s.NewDocument(toDocID)
56+	if err != nil {
57+		return id.NotExist, fmt.Errorf("forking document: %w", err)
58+	}
59+	gResp, err := http.Get(fmt.Sprintf("%s/d/%s/as-update", s.ySweetURL, fromDocID))
60+	if err != nil || gResp.StatusCode >= 300 {
61+		return id.NotExist, fmt.Errorf("forking document: failed to get forked doc: %w", err)
62+	}
63+	defer gResp.Body.Close()
64+	pResp, err := http.Post(fmt.Sprintf("%s/d/%s/update", s.ySweetURL, toDocID), gResp.Header.Get("Content-Type"), gResp.Body)
65+	if err != nil || pResp.StatusCode >= 300 {
66+		return id.NotExist, fmt.Errorf("forking document: failed to post update: %w", err)
67+	}
68+	defer pResp.Body.Close()
69+	return key, nil
70+}
71+
72 func (s *Service) SetContent(docID string, content []byte) (id.Key, error) {
73 	s.mu.Lock()
74 	defer s.mu.Unlock()