9 files changed,
+362,
-238
+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
+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+}
+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 */
+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+};
+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+}
+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
+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+};
+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)
+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()