@dxos/react-ui-editor 0.6.8-main.3be982f → 0.6.8-staging.63bcb81

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 (53) hide show
  1. package/dist/lib/browser/index.mjs +535 -521
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/TextEditor.stories.d.ts +5 -2
  5. package/dist/types/src/TextEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/defaults.d.ts +2 -2
  7. package/dist/types/src/defaults.d.ts.map +1 -1
  8. package/dist/types/src/extensions/doc.d.ts +3 -0
  9. package/dist/types/src/extensions/doc.d.ts.map +1 -1
  10. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  11. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  12. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  13. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  14. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
  15. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  16. package/dist/types/src/extensions/markdown/highlight.d.ts +2 -1
  17. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  18. package/dist/types/src/extensions/markdown/link-paste.d.ts +3 -0
  19. package/dist/types/src/extensions/markdown/link-paste.d.ts.map +1 -1
  20. package/dist/types/src/extensions/markdown/link.d.ts +3 -1
  21. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  22. package/dist/types/src/extensions/state.d.ts +14 -14
  23. package/dist/types/src/extensions/state.d.ts.map +1 -1
  24. package/dist/types/src/extensions/util/react.d.ts +1 -1
  25. package/dist/types/src/extensions/util/react.d.ts.map +1 -1
  26. package/dist/types/src/hooks/useTextEditor.d.ts +5 -3
  27. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  28. package/dist/types/src/index.d.ts +1 -0
  29. package/dist/types/src/index.d.ts.map +1 -1
  30. package/dist/types/src/styles/markdown.d.ts +7 -17
  31. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  32. package/dist/types/src/styles/theme.d.ts +3 -1
  33. package/dist/types/src/styles/theme.d.ts.map +1 -1
  34. package/dist/types/src/styles/tokens.d.ts +5 -7
  35. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  36. package/package.json +24 -24
  37. package/src/TextEditor.stories.tsx +40 -27
  38. package/src/defaults.ts +9 -2
  39. package/src/extensions/doc.ts +3 -0
  40. package/src/extensions/factories.ts +3 -2
  41. package/src/extensions/folding.tsx +5 -7
  42. package/src/extensions/markdown/bundle.ts +1 -3
  43. package/src/extensions/markdown/decorate.ts +31 -24
  44. package/src/extensions/markdown/formatting.ts +3 -1
  45. package/src/extensions/markdown/highlight.ts +33 -19
  46. package/src/extensions/markdown/link-paste.ts +3 -0
  47. package/src/extensions/state.ts +41 -35
  48. package/src/extensions/util/react.tsx +3 -4
  49. package/src/hooks/useTextEditor.ts +24 -29
  50. package/src/index.ts +2 -0
  51. package/src/styles/markdown.ts +17 -40
  52. package/src/styles/theme.ts +91 -86
  53. package/src/styles/tokens.ts +9 -7
@@ -37,6 +37,280 @@ import { keymap as keymap11 } from "@codemirror/view";
37
37
  import { tags as tags2 } from "@lezer/highlight";
38
38
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
39
39
 
40
+ // packages/ui/react-ui-editor/src/styles/markdown.ts
41
+ import { mx } from "@dxos/react-ui-theme";
42
+ var headings = {
43
+ 1: "text-4xl",
44
+ 2: "text-3xl",
45
+ 3: "text-2xl",
46
+ 4: "text-xl",
47
+ 5: "text-lg",
48
+ 6: "text-md"
49
+ };
50
+ var theme = {
51
+ mark: "opacity-50",
52
+ code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
53
+ codeMark: "font-mono text-primary-500",
54
+ // TODO(burdon): Replace with widget.
55
+ blockquote: "pl-1 mr-1 border-is-4 border-orange-500 dark:border-orange-500 dark:text-neutral-500",
56
+ heading: (level) => {
57
+ return mx(headings[level], "dark:text-primary-400");
58
+ }
59
+ };
60
+
61
+ // packages/ui/react-ui-editor/src/styles/tokens.ts
62
+ import get from "lodash.get";
63
+ import { tailwindConfig } from "@dxos/react-ui-theme";
64
+ var tokens = tailwindConfig({}).theme;
65
+ var getToken = (path, defaultValue) => {
66
+ const value = get(tokens, path, defaultValue);
67
+ return value?.toString() ?? "";
68
+ };
69
+
70
+ // packages/ui/react-ui-editor/src/styles/theme.ts
71
+ var defaultTheme = {
72
+ "&": {},
73
+ "&.cm-focused": {
74
+ outline: "none"
75
+ },
76
+ /**
77
+ * Scroller
78
+ */
79
+ ".cm-scroller": {
80
+ overflowY: "auto"
81
+ },
82
+ /**
83
+ * Content
84
+ * NOTE: Apply margins to content so that scrollbar is at the edge of the container.
85
+ */
86
+ ".cm-content": {
87
+ padding: "unset",
88
+ // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
89
+ fontSize: "16px",
90
+ fontFamily: getToken("fontFamily.body"),
91
+ lineHeight: 1.5
92
+ },
93
+ "&light .cm-content": {
94
+ color: getToken("extend.semanticColors.base.fg.light", "black")
95
+ },
96
+ "&dark .cm-content": {
97
+ color: getToken("extend.semanticColors.base.fg.dark", "white")
98
+ },
99
+ /**
100
+ * Gutters
101
+ * NOTE: Gutters should have the same top margin as the content.
102
+ */
103
+ ".cm-gutters": {
104
+ background: "transparent"
105
+ },
106
+ ".cm-gutter": {},
107
+ ".cm-gutterElement": {
108
+ lineHeight: 1.5
109
+ },
110
+ //
111
+ // Cursor
112
+ //
113
+ "&light .cm-cursor, &light .cm-dropCursor": {
114
+ borderLeft: "2px solid black"
115
+ },
116
+ "&dark .cm-cursor, &dark .cm-dropCursor": {
117
+ borderLeft: "2px solid white"
118
+ },
119
+ "&light .cm-placeholder": {
120
+ color: getToken("extend.semanticColors.description.light", "rgba(0,0,0,.2)")
121
+ },
122
+ "&dark .cm-placeholder": {
123
+ color: getToken("extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
124
+ },
125
+ //
126
+ // line
127
+ //
128
+ ".cm-line": {
129
+ paddingInline: 0
130
+ },
131
+ ".cm-activeLine": {
132
+ background: "transparent"
133
+ },
134
+ //
135
+ // gutter
136
+ //
137
+ ".cm-lineNumbers": {
138
+ minWidth: "36px"
139
+ },
140
+ //
141
+ // Selection
142
+ //
143
+ "&light .cm-selectionBackground": {
144
+ background: getToken("extend.colors.primary.100")
145
+ },
146
+ "&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
147
+ background: getToken("extend.colors.primary.200")
148
+ },
149
+ "&dark .cm-selectionBackground": {
150
+ background: getToken("extend.colors.primary.700")
151
+ },
152
+ "&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
153
+ background: getToken("extend.colors.primary.600")
154
+ },
155
+ //
156
+ // Search
157
+ //
158
+ "&light .cm-searchMatch": {
159
+ backgroundColor: getToken("extend.colors.yellow.100")
160
+ },
161
+ "&dark .cm-searchMatch": {
162
+ backgroundColor: getToken("extend.colors.yellow.700")
163
+ },
164
+ //
165
+ // link
166
+ //
167
+ ".cm-link": {
168
+ textDecorationLine: "underline",
169
+ textDecorationThickness: "1px",
170
+ textUnderlineOffset: "2px",
171
+ borderRadius: ".125rem",
172
+ fontFamily: getToken("fontFamily.body")
173
+ },
174
+ "&light .cm-link > span": {
175
+ color: getToken("extend.colors.primary.600")
176
+ },
177
+ "&dark .cm-link > span": {
178
+ color: getToken("extend.colors.primary.400")
179
+ },
180
+ //
181
+ // tooltip
182
+ //
183
+ ".cm-tooltip": {},
184
+ "&light .cm-tooltip": {
185
+ background: `${getToken("extend.colors.neutral.100")} !important`
186
+ },
187
+ "&dark .cm-tooltip": {
188
+ background: `${getToken("extend.colors.neutral.900")} !important`
189
+ },
190
+ ".cm-tooltip-below": {},
191
+ //
192
+ // autocomplete
193
+ // https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
194
+ //
195
+ ".cm-tooltip.cm-tooltip-autocomplete": {
196
+ marginTop: "4px",
197
+ marginLeft: "-3px"
198
+ },
199
+ ".cm-tooltip.cm-tooltip-autocomplete > ul": {
200
+ maxHeight: "20em !important"
201
+ },
202
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {},
203
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {},
204
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
205
+ paddingLeft: "4px !important",
206
+ borderBottom: "none !important",
207
+ color: getToken("extend.colors.primary.500")
208
+ },
209
+ ".cm-tooltip.cm-completionInfo": {
210
+ border: getToken("extend.colors.neutral.500"),
211
+ width: "360px !important",
212
+ margin: "-10px 1px 0 1px",
213
+ padding: "8px !important"
214
+ },
215
+ ".cm-completionIcon": {
216
+ display: "none"
217
+ },
218
+ ".cm-completionLabel": {
219
+ fontFamily: getToken("fontFamily.body")
220
+ },
221
+ ".cm-completionMatchedText": {
222
+ textDecoration: "none !important",
223
+ opacity: 0.5
224
+ },
225
+ // TODO(burdon): Override vars --cm-background.
226
+ // https://www.npmjs.com/package/codemirror-theme-vars
227
+ /**
228
+ * Panels
229
+ * TODO(burdon): Needs styling attention (esp. dark mode).
230
+ * https://github.com/codemirror/search/blob/main/src/search.ts#L745
231
+ *
232
+ * Find/replace panel.
233
+ * <div class="cm-announced">...</div>
234
+ * <div class="cm-scroller">...</div>
235
+ * <div class="cm-panels cm-panels-bottom">
236
+ * <div class="cm-search cm-panel">
237
+ * <input class="cm-textfield" />
238
+ * <button class="cm-button">...</button>
239
+ * <label><input type="checkbox" />...</label>
240
+ * </div>
241
+ * </div
242
+ */
243
+ ".cm-panels": {},
244
+ ".cm-panel": {
245
+ fontFamily: getToken("fontFamily.body")
246
+ },
247
+ ".cm-panel input[type=checkbox]": {
248
+ marginRight: "0.4rem !important"
249
+ },
250
+ "&light .cm-panel": {
251
+ background: getToken("extend.colors.neutral.50")
252
+ },
253
+ "&dark .cm-panel": {
254
+ background: getToken("extend.colors.neutral.850")
255
+ },
256
+ ".cm-button": {
257
+ margin: "4px",
258
+ fontFamily: getToken("fontFamily.body"),
259
+ backgroundImage: "none",
260
+ border: "none",
261
+ "&:active": {
262
+ backgroundImage: "none"
263
+ }
264
+ },
265
+ "&light .cm-button": {
266
+ background: getToken("extend.colors.neutral.100"),
267
+ "&:hover": {
268
+ background: getToken("extend.colors.neutral.200")
269
+ },
270
+ "&:active": {
271
+ background: getToken("extend.colors.neutral.300")
272
+ }
273
+ },
274
+ "&dark .cm-button": {
275
+ background: getToken("extend.colors.neutral.800"),
276
+ "&:hover": {
277
+ background: getToken("extend.colors.neutral.700")
278
+ },
279
+ "&:active": {
280
+ background: getToken("extend.colors.neutral.600")
281
+ }
282
+ },
283
+ // TODO(burdon): Factor out element specific logic.
284
+ //
285
+ // table
286
+ //
287
+ ".cm-table *": {
288
+ fontFamily: `${getToken("fontFamily.mono")} !important`,
289
+ textDecoration: "none !important"
290
+ },
291
+ ".cm-table-head": {
292
+ padding: "2px 16px 2px 0px",
293
+ textAlign: "left",
294
+ borderBottom: `1px solid ${getToken("extend.colors.primary.500")}`,
295
+ color: getToken("extend.colors.neutral.500")
296
+ },
297
+ ".cm-table-cell": {
298
+ padding: "2px 16px 2px 0px"
299
+ },
300
+ //
301
+ // image
302
+ //
303
+ ".cm-image": {
304
+ display: "block",
305
+ height: "0"
306
+ },
307
+ ".cm-image.cm-loaded-image": {
308
+ height: "auto",
309
+ borderTop: "0.5rem solid transparent",
310
+ borderBottom: "0.5rem solid transparent"
311
+ }
312
+ };
313
+
40
314
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
41
315
  import { ChatText, Code, CodeBlock, Image, Link, ListBullets, ListChecks, ListNumbers, Paragraph, Quotes, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic, CaretDown, Check, PencilSimpleSlash, MarkdownLogo, PencilSimple } from "@phosphor-icons/react";
42
316
  import { createContext } from "@radix-ui/react-context";
@@ -1365,309 +1639,33 @@ import { nonNullable } from "@dxos/util";
1365
1639
  import { log as log3 } from "@dxos/log";
1366
1640
  var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/util/error.ts";
1367
1641
  var wrapWithCatch = (fn) => {
1368
- return (...args) => {
1369
- try {
1370
- return fn(...args);
1371
- } catch (err) {
1372
- log3.catch(err, void 0, {
1373
- F: __dxlog_file5,
1374
- L: 12,
1375
- S: void 0,
1376
- C: (f, a) => f(...a)
1377
- });
1378
- }
1379
- };
1380
- };
1381
-
1382
- // packages/ui/react-ui-editor/src/extensions/util/overlap.ts
1383
- var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
1384
-
1385
- // packages/ui/react-ui-editor/src/extensions/util/react.tsx
1386
- import React from "react";
1387
- import { createRoot } from "react-dom/client";
1388
- import { ThemeProvider } from "@dxos/react-ui";
1389
- import { defaultTx } from "@dxos/react-ui-theme";
1390
- var renderRoot = (node) => {
1391
- const el = document.createElement("div");
1392
- createRoot(el).render(/* @__PURE__ */ React.createElement(ThemeProvider, {
1393
- tx: defaultTx
1394
- }, node));
1395
- return el;
1396
- };
1397
-
1398
- // packages/ui/react-ui-editor/src/styles/markdown.ts
1399
- import { mx } from "@dxos/react-ui-theme";
1400
- var headings = {
1401
- 1: "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
1402
- 2: "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
1403
- 3: "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
1404
- 4: "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
1405
- 5: "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
1406
- 6: "mbs-4 mbe-2 font-medium text-inherit no-underline"
1407
- };
1408
- var heading = (level) => {
1409
- return mx(headings[level], "dark:text-primary-400");
1410
- };
1411
- var light = "text-neutral-200 dark:text-neutral-800";
1412
- var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
1413
- var bold = "font-bold";
1414
- var italic = "italic";
1415
- var strikethrough = "line-through";
1416
- var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
1417
- var codeMark = "font-mono text-primary-500";
1418
- var inlineUrl = mx(code, "px-1");
1419
- var blockquote = mx("pl-1 mr-1 border-is-4 border-orange-500 dark:border-orange-500 text-transparent");
1420
-
1421
- // packages/ui/react-ui-editor/src/styles/theme.ts
1422
- import get2 from "lodash.get";
1423
-
1424
- // packages/ui/react-ui-editor/src/styles/tokens.ts
1425
- import get from "lodash.get";
1426
- import { tailwindConfig } from "@dxos/react-ui-theme";
1427
- var tokens = tailwindConfig({}).theme;
1428
- var getToken = (path, defaultValue = void 0) => get(tokens, path, defaultValue);
1429
-
1430
- // packages/ui/react-ui-editor/src/styles/theme.ts
1431
- var defaultTheme = {
1432
- "&": {},
1433
- "&.cm-focused": {
1434
- outline: "none"
1435
- },
1436
- // Scroller.
1437
- // NOTE: See https://codemirror.net/docs/guide (DOM Structure).
1438
- ".cm-scroller": {
1439
- overflowY: "auto",
1440
- fontFamily: get2(tokens, "fontFamily.body", []).join(","),
1441
- lineHeight: 1.5
1442
- },
1443
- // Content.
1444
- ".cm-content": {
1445
- padding: "unset",
1446
- // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
1447
- fontSize: "16px"
1448
- },
1449
- "&light .cm-content": {
1450
- color: get2(tokens, "extend.semanticColors.base.fg.light", "black")
1451
- },
1452
- "&dark .cm-content": {
1453
- color: get2(tokens, "extend.semanticColors.base.fg.dark", "red")
1454
- },
1455
- //
1456
- // Cursor
1457
- //
1458
- "&light .cm-cursor, &light .cm-dropCursor": {
1459
- borderLeft: "2px solid black"
1460
- },
1461
- "&dark .cm-cursor, &dark .cm-dropCursor": {
1462
- borderLeft: "2px solid white"
1463
- },
1464
- "&light .cm-placeholder": {
1465
- color: get2(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
1466
- },
1467
- "&dark .cm-placeholder": {
1468
- color: get2(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
1469
- },
1470
- //
1471
- // line
1472
- //
1473
- ".cm-line": {
1474
- paddingInline: 0
1475
- },
1476
- ".cm-activeLine": {
1477
- background: "transparent"
1478
- },
1479
- //
1480
- // gutter
1481
- //
1482
- ".cm-lineNumbers": {
1483
- minWidth: "36px"
1484
- },
1485
- //
1486
- // Selection
1487
- //
1488
- "&light .cm-selectionBackground": {
1489
- background: get2(tokens, "extend.colors.primary.100")
1490
- },
1491
- "&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
1492
- background: get2(tokens, "extend.colors.primary.200")
1493
- },
1494
- "&dark .cm-selectionBackground": {
1495
- background: get2(tokens, "extend.colors.primary.700")
1496
- },
1497
- "&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
1498
- background: get2(tokens, "extend.colors.primary.600")
1499
- },
1500
- //
1501
- // Search
1502
- //
1503
- "&light .cm-searchMatch": {
1504
- backgroundColor: get2(tokens, "extend.colors.yellow.100")
1505
- },
1506
- "&dark .cm-searchMatch": {
1507
- backgroundColor: get2(tokens, "extend.colors.yellow.700")
1508
- },
1509
- //
1510
- // link
1511
- //
1512
- ".cm-link": {
1513
- color: get2(tokens, "extend.colors.primary.500"),
1514
- textDecorationLine: "underline",
1515
- textDecorationThickness: "1px",
1516
- textUnderlineOffset: "2px",
1517
- borderRadius: ".125rem",
1518
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1519
- },
1520
- //
1521
- // tooltip
1522
- //
1523
- ".cm-tooltip": {},
1524
- "&light .cm-tooltip": {
1525
- background: `${get2(tokens, "extend.colors.neutral.100")} !important`
1526
- },
1527
- "&dark .cm-tooltip": {
1528
- background: `${get2(tokens, "extend.colors.neutral.900")} !important`
1529
- },
1530
- ".cm-tooltip-below": {},
1531
- //
1532
- // autocomplete
1533
- // https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
1534
- //
1535
- ".cm-tooltip.cm-tooltip-autocomplete": {
1536
- marginTop: "4px",
1537
- marginLeft: "-3px"
1538
- },
1539
- ".cm-tooltip.cm-tooltip-autocomplete > ul": {
1540
- maxHeight: "20em !important"
1541
- },
1542
- ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {},
1543
- ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {},
1544
- // TODO(burdon): Can we add a class prefix to avoid adding !important?
1545
- ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
1546
- paddingLeft: "4px !important",
1547
- borderBottom: "none !important",
1548
- color: get2(tokens, "extend.colors.primary.500")
1549
- },
1550
- ".cm-tooltip.cm-completionInfo": {
1551
- border: get2(tokens, "extend.colors.neutral.500"),
1552
- width: "360px !important",
1553
- margin: "-10px 1px 0 1px",
1554
- padding: "8px !important"
1555
- },
1556
- ".cm-completionIcon": {
1557
- display: "none"
1558
- },
1559
- ".cm-completionLabel": {
1560
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1561
- },
1562
- ".cm-completionMatchedText": {
1563
- textDecoration: "none !important",
1564
- opacity: 0.5
1565
- },
1566
- //
1567
- // table
1568
- //
1569
- ".cm-table *": {
1570
- fontFamily: `${get2(tokens, "fontFamily.mono", []).join(",")} !important`,
1571
- textDecoration: "none !important"
1572
- },
1573
- ".cm-table-head": {
1574
- padding: "2px 16px 2px 0px",
1575
- textAlign: "left",
1576
- borderBottom: `1px solid ${get2(tokens, "extend.colors.primary.500")}`,
1577
- color: get2(tokens, "extend.colors.neutral.500")
1578
- },
1579
- ".cm-table-cell": {
1580
- padding: "2px 16px 2px 0px"
1581
- },
1582
- //
1583
- // image
1584
- //
1585
- ".cm-image": {
1586
- display: "block",
1587
- height: "0"
1588
- },
1589
- ".cm-image.cm-loaded-image": {
1590
- height: "auto",
1591
- borderTop: "0.5rem solid transparent",
1592
- borderBottom: "0.5rem solid transparent"
1593
- },
1594
- //
1595
- // font size
1596
- // TODO(thure): This appears to be the best or only way to set selection caret heights,
1597
- // but it's far more verbose than it needs to be.
1598
- //
1599
- // ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
1600
- // const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
1601
- //
1602
- // acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
1603
- // acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
1604
- // acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
1605
- // return acc;
1606
- // }, {}),
1607
- // TODO(burdon): Override vars --cm-background.
1608
- // https://www.npmjs.com/package/codemirror-theme-vars
1609
- /**
1610
- * Gutters
1611
- */
1612
- ".cm-gutters": {
1613
- background: "transparent"
1614
- },
1615
- /**
1616
- * Panels
1617
- * TODO(burdon): Needs styling attention (esp. dark mode).
1618
- * https://github.com/codemirror/search/blob/main/src/search.ts#L745
1619
- *
1620
- * Find/replace panel.
1621
- * <div class="cm-announced">...</div>
1622
- * <div class="cm-scroller">...</div>
1623
- * <div class="cm-panels cm-panels-bottom">
1624
- * <div class="cm-search cm-panel">
1625
- * <input class="cm-textfield" />
1626
- * <button class="cm-button">...</button>
1627
- * <label><input type="checkbox" />...</label>
1628
- * </div>
1629
- * </div
1630
- */
1631
- ".cm-panels": {},
1632
- ".cm-panel": {
1633
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1634
- },
1635
- ".cm-panel input[type=checkbox]": {
1636
- marginRight: "0.4rem !important"
1637
- },
1638
- "&light .cm-panel": {
1639
- background: get2(tokens, "extend.colors.neutral.50")
1640
- },
1641
- "&dark .cm-panel": {
1642
- background: get2(tokens, "extend.colors.neutral.850")
1643
- },
1644
- ".cm-button": {
1645
- margin: "4px",
1646
- fontFamily: get2(tokens, "fontFamily.body", []).join(","),
1647
- backgroundImage: "none",
1648
- border: "none",
1649
- "&:active": {
1650
- backgroundImage: "none"
1651
- }
1652
- },
1653
- "&light .cm-button": {
1654
- background: get2(tokens, "extend.colors.neutral.100"),
1655
- "&:hover": {
1656
- background: get2(tokens, "extend.colors.neutral.200")
1657
- },
1658
- "&:active": {
1659
- background: get2(tokens, "extend.colors.neutral.300")
1660
- }
1661
- },
1662
- "&dark .cm-button": {
1663
- background: get2(tokens, "extend.colors.neutral.800"),
1664
- "&:hover": {
1665
- background: get2(tokens, "extend.colors.neutral.700")
1666
- },
1667
- "&:active": {
1668
- background: get2(tokens, "extend.colors.neutral.600")
1642
+ return (...args) => {
1643
+ try {
1644
+ return fn(...args);
1645
+ } catch (err) {
1646
+ log3.catch(err, void 0, {
1647
+ F: __dxlog_file5,
1648
+ L: 12,
1649
+ S: void 0,
1650
+ C: (f, a) => f(...a)
1651
+ });
1669
1652
  }
1670
- }
1653
+ };
1654
+ };
1655
+
1656
+ // packages/ui/react-ui-editor/src/extensions/util/overlap.ts
1657
+ var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
1658
+
1659
+ // packages/ui/react-ui-editor/src/extensions/util/react.tsx
1660
+ import React from "react";
1661
+ import { createRoot } from "react-dom/client";
1662
+ import { ThemeProvider } from "@dxos/react-ui";
1663
+ import { defaultTx } from "@dxos/react-ui-theme";
1664
+ var renderRoot = (root, node) => {
1665
+ createRoot(root).render(/* @__PURE__ */ React.createElement(ThemeProvider, {
1666
+ tx: defaultTx
1667
+ }, node));
1668
+ return root;
1671
1669
  };
1672
1670
 
1673
1671
  // packages/ui/react-ui-editor/src/util.ts
@@ -1828,8 +1826,8 @@ var commentsDecorations = EditorView7.decorations.compute([
1828
1826
  } else if (range.from === range.to) {
1829
1827
  return void 0;
1830
1828
  }
1831
- const mark2 = createCommentMark(comment.comment.id, comment.comment.id === current);
1832
- return mark2.range(range.from, range.to);
1829
+ const mark = createCommentMark(comment.comment.id, comment.comment.id === current);
1830
+ return mark.range(range.from, range.to);
1833
1831
  }).filter(nonNullable);
1834
1832
  return Decoration4.set(decorations);
1835
1833
  });
@@ -2248,7 +2246,7 @@ var documentId2 = Facet5.define({
2248
2246
  combine: (providers) => {
2249
2247
  invariant3(providers.length <= 1, void 0, {
2250
2248
  F: __dxlog_file8,
2251
- L: 11,
2249
+ L: 14,
2252
2250
  S: void 0,
2253
2251
  A: [
2254
2252
  "providers.length <= 1",
@@ -2297,7 +2295,7 @@ var dropFile = (options = {}) => {
2297
2295
 
2298
2296
  // packages/ui/react-ui-editor/src/extensions/factories.ts
2299
2297
  import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
2300
- import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
2298
+ import { defaultKeymap, history, historyKeymap, standardKeymap } from "@codemirror/commands";
2301
2299
  import { bracketMatching } from "@codemirror/language";
2302
2300
  import { searchKeymap } from "@codemirror/search";
2303
2301
  import { EditorState } from "@codemirror/state";
@@ -2357,10 +2355,9 @@ var createBasicExtensions = (_props) => {
2357
2355
  // https://codemirror.net/docs/ref/#view.KeyBinding
2358
2356
  keymap5.of([
2359
2357
  ...(props.keymap && keymaps[props.keymap]) ?? [],
2358
+ // NOTE: Tab configured by markdown extension.
2360
2359
  // https://codemirror.net/docs/ref/#commands.indentWithTab
2361
- ...props.indentWithTab ? [
2362
- indentWithTab
2363
- ] : [],
2360
+ // ...(props.indentWithTab ? [indentWithTab] : []),
2364
2361
  // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
2365
2362
  ...props.closeBrackets ? closeBracketsKeymap : [],
2366
2363
  // https://codemirror.net/docs/ref/#commands.historyKeymap
@@ -2375,12 +2372,12 @@ var defaultThemeSlots = {
2375
2372
  className: "w-full bs-full"
2376
2373
  }
2377
2374
  };
2378
- var createThemeExtensions = ({ theme, themeMode, slots: _slots } = {}) => {
2375
+ var createThemeExtensions = ({ theme: theme2, themeMode, slots: _slots } = {}) => {
2379
2376
  const slots = defaultsDeep2({}, _slots, defaultThemeSlots);
2380
2377
  return [
2381
2378
  EditorView9.baseTheme(defaultTheme),
2382
2379
  EditorView9.darkTheme.of(themeMode === "dark"),
2383
- theme && EditorView9.theme(theme),
2380
+ theme2 && EditorView9.theme(theme2),
2384
2381
  slots.editor?.className && EditorView9.editorAttributes.of({
2385
2382
  class: slots.editor.className
2386
2383
  }),
@@ -2414,21 +2411,18 @@ var createDataExtensions = ({ id, text, space, identity }) => {
2414
2411
  // packages/ui/react-ui-editor/src/extensions/folding.tsx
2415
2412
  import { codeFolding, foldGutter } from "@codemirror/language";
2416
2413
  import React2 from "react";
2417
- import { ThemeProvider as ThemeProvider2 } from "@dxos/react-ui";
2418
- import { defaultTx as defaultTx2, getSize, mx as mx2 } from "@dxos/react-ui-theme";
2414
+ import { getSize, mx as mx2 } from "@dxos/react-ui-theme";
2419
2415
  var folding = (_props = {}) => [
2420
2416
  codeFolding({
2421
2417
  placeholderDOM: () => document.createElement("div")
2422
2418
  }),
2423
2419
  foldGutter({
2424
2420
  markerDOM: (open) => {
2425
- return renderRoot(/* @__PURE__ */ React2.createElement(ThemeProvider2, {
2426
- tx: defaultTx2
2427
- }, /* @__PURE__ */ React2.createElement("svg", {
2421
+ return renderRoot(document.createElement("div"), /* @__PURE__ */ React2.createElement("svg", {
2428
2422
  className: mx2(getSize(3), "m-3 cursor-pointer", open && "rotate-90")
2429
2423
  }, /* @__PURE__ */ React2.createElement("use", {
2430
2424
  href: "/icons.svg#ph--caret-right--regular"
2431
- }))));
2425
+ })));
2432
2426
  }
2433
2427
  })
2434
2428
  ];
@@ -2449,6 +2443,7 @@ var listener = ({ onFocus, onChange }) => {
2449
2443
 
2450
2444
  // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2451
2445
  import { snippet } from "@codemirror/autocomplete";
2446
+ import { indentWithTab } from "@codemirror/commands";
2452
2447
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2453
2448
  import { EditorSelection } from "@codemirror/state";
2454
2449
  import { EditorView as EditorView11, keymap as keymap6 } from "@codemirror/view";
@@ -2870,28 +2865,28 @@ var addLink = ({ url, image: image2 } = {}) => {
2870
2865
  let cursorOffset = 1;
2871
2866
  for (const style of cutStyles) {
2872
2867
  const type = InlineMarker[style.name];
2873
- const mark2 = inlineMarkerText(type);
2868
+ const mark = inlineMarkerText(type);
2874
2869
  if (style.from < from) {
2875
2870
  changes2.push({
2876
2871
  from: skipSpaces(from, state2.doc, -1),
2877
- insert: mark2
2872
+ insert: mark
2878
2873
  });
2879
2874
  changesAfter.push({
2880
2875
  from: skipSpaces(from, state2.doc, 1, to),
2881
- insert: mark2
2876
+ insert: mark
2882
2877
  });
2883
2878
  } else {
2884
2879
  changes2.push({
2885
2880
  from: skipSpaces(to, state2.doc, -1, from),
2886
- insert: mark2
2881
+ insert: mark
2887
2882
  });
2888
2883
  const after = skipSpaces(to, state2.doc, 1);
2889
2884
  if (after === to) {
2890
- cursorOffset += mark2.length;
2885
+ cursorOffset += mark.length;
2891
2886
  }
2892
2887
  changesAfter.push({
2893
2888
  from: after,
2894
- insert: mark2
2889
+ insert: mark
2895
2890
  });
2896
2891
  }
2897
2892
  }
@@ -2942,10 +2937,10 @@ var addList = (type) => {
2942
2937
  const itemText = itemLine.text.slice(item.from - itemLine.from);
2943
2938
  parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
2944
2939
  if (type === 0) {
2945
- const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
2946
- if (mark2) {
2947
- parentColumn += mark2[1].length;
2948
- counter = +mark2[1] + 1;
2940
+ const mark = /^\s*(\d+)[.)]/.exec(itemText);
2941
+ if (mark) {
2942
+ parentColumn += mark[1].length;
2943
+ counter = +mark[1] + 1;
2949
2944
  }
2950
2945
  }
2951
2946
  }
@@ -3006,7 +3001,7 @@ var addList = (type) => {
3006
3001
  if (parentColumn2 !== null && parentColumn2 > column) {
3007
3002
  padding = Math.max(padding, parentColumn2 - column);
3008
3003
  }
3009
- let mark2;
3004
+ let mark;
3010
3005
  if (type === 0) {
3011
3006
  let max = counter2;
3012
3007
  for (let j = i + 1; j < blocks.length; j++) {
@@ -3017,20 +3012,20 @@ var addList = (type) => {
3017
3012
  }
3018
3013
  const num = String(counter2);
3019
3014
  padding = Math.max(String(max).length, padding);
3020
- mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
3015
+ mark = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
3021
3016
  } else {
3022
- mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
3017
+ mark = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
3023
3018
  }
3024
3019
  changes.push({
3025
3020
  from: nodeFrom,
3026
- insert: mark2
3021
+ insert: mark
3027
3022
  });
3028
3023
  while (line.to < node.to) {
3029
3024
  line = state2.doc.lineAt(line.to + 1);
3030
3025
  const open = /^[\s>]*/.exec(line.text)[0].length;
3031
3026
  changes.push({
3032
3027
  from: line.from + Math.min(open, column),
3033
- insert: " ".repeat(mark2.length)
3028
+ insert: " ".repeat(mark.length)
3034
3029
  });
3035
3030
  }
3036
3031
  }
@@ -3079,14 +3074,14 @@ var removeList = (type) => {
3079
3074
  } else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
3080
3075
  lastBlock = node.from;
3081
3076
  let line = state2.doc.lineAt(node.from);
3082
- const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
3083
- if (!mark2) {
3077
+ const mark = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
3078
+ if (!mark) {
3084
3079
  return false;
3085
3080
  }
3086
3081
  const column = node.from - line.from;
3087
3082
  changes.push({
3088
3083
  from: node.from,
3089
- to: node.from + mark2[0].length
3084
+ to: node.from + mark[0].length
3090
3085
  });
3091
3086
  while (line.to < node.to) {
3092
3087
  line = state2.doc.lineAt(line.to + 1);
@@ -3094,7 +3089,7 @@ var removeList = (type) => {
3094
3089
  if (open > column) {
3095
3090
  changes.push({
3096
3091
  from: line.from + column,
3097
- to: line.from + Math.min(column + mark2[0].length, open)
3092
+ to: line.from + Math.min(column + mark[0].length, open)
3098
3093
  });
3099
3094
  }
3100
3095
  }
@@ -3337,7 +3332,7 @@ var removeCodeblock = ({ state: state2, dispatch }) => {
3337
3332
  var toggleCodeblock = (target) => {
3338
3333
  return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3339
3334
  };
3340
- var formattingKeymap = (options = {}) => {
3335
+ var formattingKeymap = (_options = {}) => {
3341
3336
  return [
3342
3337
  keymap6.of([
3343
3338
  {
@@ -3441,10 +3436,10 @@ var getFormatting = (state2) => {
3441
3436
  }
3442
3437
  }
3443
3438
  for (let i = 0; i < inline.length; i++) {
3444
- const mark2 = inlineMarkerText(i);
3445
- const found = contextAfter.indexOf(mark2);
3439
+ const mark = inlineMarkerText(i);
3440
+ const found = contextAfter.indexOf(mark);
3446
3441
  if (found > -1) {
3447
- contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
3442
+ contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark.length);
3448
3443
  if (inline[i] === null) {
3449
3444
  inline[i] = true;
3450
3445
  }
@@ -3653,7 +3648,7 @@ var markdownTagsExtensions = [
3653
3648
  ]
3654
3649
  }
3655
3650
  ];
3656
- var markdownHighlightStyle = (readonly) => {
3651
+ var markdownHighlightStyle = (_options = {}) => {
3657
3652
  return HighlightStyle.define([
3658
3653
  {
3659
3654
  tag: [
@@ -3691,6 +3686,7 @@ var markdownHighlightStyle = (readonly) => {
3691
3686
  tags.inserted,
3692
3687
  tags.invalid
3693
3688
  ],
3689
+ // TODO(burdon): Explain.
3694
3690
  color: "inherit !important"
3695
3691
  },
3696
3692
  // Markdown marks.
@@ -3702,26 +3698,29 @@ var markdownHighlightStyle = (readonly) => {
3702
3698
  markdownTags.LinkReference,
3703
3699
  markdownTags.ListMark
3704
3700
  ],
3705
- class: mark
3701
+ class: theme.mark
3706
3702
  },
3707
3703
  // Markdown marks.
3708
3704
  {
3709
3705
  tag: [
3706
+ //
3710
3707
  markdownTags.CodeMark,
3711
3708
  markdownTags.HeaderMark,
3712
3709
  markdownTags.QuoteMark,
3713
3710
  markdownTags.EmphasisMark
3714
3711
  ],
3715
- class: mark
3712
+ class: theme.mark
3716
3713
  },
3717
3714
  // E.g., code block language (after ```).
3718
3715
  {
3719
3716
  tag: [
3717
+ //
3720
3718
  tags.function(tags.variableName),
3721
3719
  tags.labelName
3722
3720
  ],
3723
- class: codeMark
3721
+ class: theme.codeMark
3724
3722
  },
3723
+ // Fonts.
3725
3724
  {
3726
3725
  tag: [
3727
3726
  tags.monospace
@@ -3731,40 +3730,40 @@ var markdownHighlightStyle = (readonly) => {
3731
3730
  // Headings.
3732
3731
  {
3733
3732
  tag: tags.heading1,
3734
- class: heading(1)
3733
+ class: theme.heading(1)
3735
3734
  },
3736
3735
  {
3737
3736
  tag: tags.heading2,
3738
- class: heading(2)
3737
+ class: theme.heading(2)
3739
3738
  },
3740
3739
  {
3741
3740
  tag: tags.heading3,
3742
- class: heading(3)
3741
+ class: theme.heading(3)
3743
3742
  },
3744
3743
  {
3745
3744
  tag: tags.heading4,
3746
- class: heading(4)
3745
+ class: theme.heading(4)
3747
3746
  },
3748
3747
  {
3749
3748
  tag: tags.heading5,
3750
- class: heading(5)
3749
+ class: theme.heading(5)
3751
3750
  },
3752
3751
  {
3753
3752
  tag: tags.heading6,
3754
- class: heading(6)
3753
+ class: theme.heading(6)
3755
3754
  },
3756
3755
  // Emphasis.
3757
3756
  {
3758
3757
  tag: tags.emphasis,
3759
- class: italic
3758
+ class: "italic"
3760
3759
  },
3761
3760
  {
3762
3761
  tag: tags.strong,
3763
- class: bold
3762
+ class: "font-bold"
3764
3763
  },
3765
3764
  {
3766
3765
  tag: tags.strikethrough,
3767
- class: strikethrough
3766
+ class: "line-through"
3768
3767
  },
3769
3768
  // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
3770
3769
  // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
@@ -3776,13 +3775,13 @@ var markdownHighlightStyle = (readonly) => {
3776
3775
  markdownTags.CodeText,
3777
3776
  markdownTags.InlineCode
3778
3777
  ],
3779
- class: code
3778
+ class: theme.code
3780
3779
  },
3781
3780
  {
3782
3781
  tag: [
3783
3782
  markdownTags.QuoteMark
3784
3783
  ],
3785
- class: blockquote
3784
+ class: theme.blockquote
3786
3785
  },
3787
3786
  {
3788
3787
  tag: [
@@ -3793,108 +3792,11 @@ var markdownHighlightStyle = (readonly) => {
3793
3792
  ], {
3794
3793
  scope: markdownLanguage2,
3795
3794
  all: {
3796
- fontFamily: getToken("fontFamily.body", []).join(",")
3795
+ fontFamily: getToken("fontFamily.body")
3797
3796
  }
3798
3797
  });
3799
3798
  };
3800
3799
 
3801
- // packages/ui/react-ui-editor/src/extensions/markdown/link-paste.ts
3802
- import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3803
- import { Transaction } from "@codemirror/state";
3804
- import { ViewPlugin as ViewPlugin5 } from "@codemirror/view";
3805
- var linkPastePlugin = ViewPlugin5.fromClass(class {
3806
- update(update2) {
3807
- for (const tr of update2.transactions) {
3808
- const event = tr.annotation(Transaction.userEvent);
3809
- if (event === "input.paste") {
3810
- const changes = tr.changes;
3811
- if (changes.empty) {
3812
- return;
3813
- }
3814
- changes.iterChangedRanges((fromA, toA, fromB, toB) => {
3815
- const insertedUrl = getValidUrl(update2.view.state.sliceDoc(fromB, toB));
3816
- if (insertedUrl && isValidPosition(update2.view.state, fromB)) {
3817
- const replacedText = tr.startState.sliceDoc(fromA, toA);
3818
- setTimeout(() => {
3819
- update2.view.dispatch(update2.view.state.update({
3820
- changes: {
3821
- from: fromA,
3822
- to: toB,
3823
- insert: createLink(insertedUrl, replacedText)
3824
- }
3825
- }));
3826
- });
3827
- }
3828
- });
3829
- }
3830
- }
3831
- }
3832
- });
3833
- var createLink = (url, label) => {
3834
- const { host, pathname } = url;
3835
- const [, extension] = pathname.split(".");
3836
- const imageExtensions = [
3837
- "jpg",
3838
- "jpeg",
3839
- "png",
3840
- "gif",
3841
- "svg",
3842
- "webp"
3843
- ];
3844
- if (imageExtensions.includes(extension)) {
3845
- return `![${label || host}](${url})`;
3846
- }
3847
- if (!label) {
3848
- label = createLinkLabel(url);
3849
- }
3850
- return `[${label}](${url})`;
3851
- };
3852
- var createLinkLabel = (url) => {
3853
- let { protocol, host, pathname } = url;
3854
- if (protocol === "http:" || protocol === "https:") {
3855
- protocol = "";
3856
- }
3857
- host = host.replace(/^www\./, "");
3858
- return [
3859
- protocol,
3860
- host
3861
- ].filter(Boolean).join("//") + (pathname !== "/" ? pathname : "");
3862
- };
3863
- var getValidUrl = (str) => {
3864
- const validProtocols = [
3865
- "http:",
3866
- "https:",
3867
- "mailto:",
3868
- "tel:"
3869
- ];
3870
- try {
3871
- const url = new URL(str);
3872
- if (!validProtocols.includes(url.protocol)) {
3873
- return void 0;
3874
- }
3875
- return url;
3876
- } catch (_err) {
3877
- return void 0;
3878
- }
3879
- };
3880
- var isValidPosition = (state2, pos) => {
3881
- const invalidPositions = /* @__PURE__ */ new Set([
3882
- "Link",
3883
- "LinkMark",
3884
- "Code",
3885
- "FencedCode"
3886
- ]);
3887
- const tree = syntaxTree3(state2);
3888
- let node = tree.resolveInner(pos, -1);
3889
- while (node) {
3890
- if (invalidPositions.has(node.name)) {
3891
- return false;
3892
- }
3893
- node = node.parent;
3894
- }
3895
- return true;
3896
- };
3897
-
3898
3800
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
3899
3801
  var createMarkdownExtensions = ({ themeMode } = {}) => {
3900
3802
  return [
@@ -3920,8 +3822,8 @@ var createMarkdownExtensions = ({ themeMode } = {}) => {
3920
3822
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
3921
3823
  // Custom styles.
3922
3824
  syntaxHighlighting(markdownHighlightStyle()),
3923
- linkPastePlugin,
3924
3825
  keymap7.of([
3826
+ // TODO(burdon): Indent by 4 if in task list.
3925
3827
  // https://codemirror.net/docs/ref/#commands.indentWithTab
3926
3828
  indentWithTab2,
3927
3829
  // https://codemirror.net/docs/ref/#commands.defaultKeymap
@@ -3940,7 +3842,7 @@ import { invariant as invariant4 } from "@dxos/invariant";
3940
3842
  import { mx as mx3 } from "@dxos/react-ui-theme";
3941
3843
 
3942
3844
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3943
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3845
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3944
3846
  import { StateField as StateField6 } from "@codemirror/state";
3945
3847
  import { Decoration as Decoration5, EditorView as EditorView12, WidgetType as WidgetType3 } from "@codemirror/view";
3946
3848
  var image = (_options = {}) => {
@@ -3983,7 +3885,7 @@ var preloadImage = (url) => {
3983
3885
  var buildDecorations = (from, to, state2) => {
3984
3886
  const decorations = [];
3985
3887
  const cursor = state2.selection.main.head;
3986
- syntaxTree4(state2).iterate({
3888
+ syntaxTree3(state2).iterate({
3987
3889
  enter: (node) => {
3988
3890
  if (node.name === "Image") {
3989
3891
  const urlNode = node.node.getChild("URL");
@@ -4022,6 +3924,103 @@ var ImageWidget = class extends WidgetType3 {
4022
3924
  var imageUpload = (options = {}) => {
4023
3925
  };
4024
3926
 
3927
+ // packages/ui/react-ui-editor/src/extensions/markdown/link-paste.ts
3928
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3929
+ import { Transaction } from "@codemirror/state";
3930
+ import { ViewPlugin as ViewPlugin5 } from "@codemirror/view";
3931
+ var linkPastePlugin = ViewPlugin5.fromClass(class {
3932
+ update(update2) {
3933
+ for (const tr of update2.transactions) {
3934
+ const event = tr.annotation(Transaction.userEvent);
3935
+ if (event === "input.paste") {
3936
+ const changes = tr.changes;
3937
+ if (changes.empty) {
3938
+ return;
3939
+ }
3940
+ changes.iterChangedRanges((fromA, toA, fromB, toB) => {
3941
+ const insertedUrl = getValidUrl(update2.view.state.sliceDoc(fromB, toB));
3942
+ if (insertedUrl && isValidPosition(update2.view.state, fromB)) {
3943
+ const replacedText = tr.startState.sliceDoc(fromA, toA);
3944
+ setTimeout(() => {
3945
+ update2.view.dispatch(update2.view.state.update({
3946
+ changes: {
3947
+ from: fromA,
3948
+ to: toB,
3949
+ insert: createLink(insertedUrl, replacedText)
3950
+ }
3951
+ }));
3952
+ });
3953
+ }
3954
+ });
3955
+ }
3956
+ }
3957
+ }
3958
+ });
3959
+ var createLink = (url, label) => {
3960
+ const { host, pathname } = url;
3961
+ const [, extension] = pathname.split(".");
3962
+ const imageExtensions = [
3963
+ "jpg",
3964
+ "jpeg",
3965
+ "png",
3966
+ "gif",
3967
+ "svg",
3968
+ "webp"
3969
+ ];
3970
+ if (imageExtensions.includes(extension)) {
3971
+ return `![${label || host}](${url})`;
3972
+ }
3973
+ if (!label) {
3974
+ label = createLinkLabel(url);
3975
+ }
3976
+ return `[${label}](${url})`;
3977
+ };
3978
+ var createLinkLabel = (url) => {
3979
+ let { protocol, host, pathname } = url;
3980
+ if (protocol === "http:" || protocol === "https:") {
3981
+ protocol = "";
3982
+ }
3983
+ host = host.replace(/^www\./, "");
3984
+ return [
3985
+ protocol,
3986
+ host
3987
+ ].filter(Boolean).join("//") + (pathname !== "/" ? pathname : "");
3988
+ };
3989
+ var getValidUrl = (str) => {
3990
+ const validProtocols = [
3991
+ "http:",
3992
+ "https:",
3993
+ "mailto:",
3994
+ "tel:"
3995
+ ];
3996
+ try {
3997
+ const url = new URL(str);
3998
+ if (!validProtocols.includes(url.protocol)) {
3999
+ return void 0;
4000
+ }
4001
+ return url;
4002
+ } catch (_err) {
4003
+ return void 0;
4004
+ }
4005
+ };
4006
+ var isValidPosition = (state2, pos) => {
4007
+ const invalidPositions = /* @__PURE__ */ new Set([
4008
+ "Link",
4009
+ "LinkMark",
4010
+ "Code",
4011
+ "FencedCode"
4012
+ ]);
4013
+ const tree = syntaxTree4(state2);
4014
+ let node = tree.resolveInner(pos, -1);
4015
+ while (node) {
4016
+ if (invalidPositions.has(node.name)) {
4017
+ return false;
4018
+ }
4019
+ node = node.parent;
4020
+ }
4021
+ return true;
4022
+ };
4023
+
4025
4024
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
4026
4025
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4027
4026
  import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField7 } from "@codemirror/state";
@@ -4155,8 +4154,9 @@ var CheckboxWidget = class extends WidgetType5 {
4155
4154
  }
4156
4155
  toDOM(view) {
4157
4156
  const input = document.createElement("input");
4158
- input.className = "cm-task-checkbox ch-checkbox ch-focus-ring";
4157
+ input.className = "cm-task-checkbox ch-checkbox";
4159
4158
  input.type = "checkbox";
4159
+ input.tabIndex = -1;
4160
4160
  input.checked = this._checked;
4161
4161
  if (view.state.readOnly) {
4162
4162
  input.setAttribute("disabled", "true");
@@ -4244,7 +4244,7 @@ var buildDecorations2 = (view, options, focus) => {
4244
4244
  const getHeaderLevels = (node, level) => {
4245
4245
  invariant4(level > 0, void 0, {
4246
4246
  F: __dxlog_file10,
4247
- L: 157,
4247
+ L: 159,
4248
4248
  S: void 0,
4249
4249
  A: [
4250
4250
  "level > 0",
@@ -4283,7 +4283,7 @@ var buildDecorations2 = (view, options, focus) => {
4283
4283
  const getCurrentList = () => {
4284
4284
  invariant4(listLevels.length, void 0, {
4285
4285
  F: __dxlog_file10,
4286
- L: 179,
4286
+ L: 181,
4287
4287
  S: void 0,
4288
4288
  A: [
4289
4289
  "listLevels.length",
@@ -4306,20 +4306,23 @@ var buildDecorations2 = (view, options, focus) => {
4306
4306
  headers[level - 1].number++;
4307
4307
  }
4308
4308
  const editing = editingRange(state2, node, focus);
4309
- if (!editing) {
4310
- const mark2 = node.node.firstChild;
4311
- if (mark2?.name === "HeaderMark") {
4312
- let text = view.state.sliceDoc(mark2.to, node.to).trim();
4313
- const { from, to } = options.numberedHeadings ?? {};
4314
- if (from && (!to || level <= to)) {
4315
- const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".");
4316
- if (num.length) {
4317
- text = `${num} ${text}`;
4318
- }
4309
+ if (editing) {
4310
+ break;
4311
+ }
4312
+ const mark = node.node.firstChild;
4313
+ if (mark?.name === "HeaderMark") {
4314
+ const { from, to = 6 } = options.numberedHeadings ?? {};
4315
+ const text = view.state.sliceDoc(node.from, node.to);
4316
+ const len = text.match(/[#\s]+/)[0].length;
4317
+ if (!from || level < from || level > to) {
4318
+ atomicDeco.add(mark.from, mark.from + len, hide);
4319
+ } else {
4320
+ const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
4321
+ if (num.length) {
4322
+ atomicDeco.add(mark.from, mark.from + len, Decoration7.replace({
4323
+ widget: new TextWidget(num, theme.heading(level))
4324
+ }));
4319
4325
  }
4320
- deco.add(node.from, node.to, Decoration7.replace({
4321
- widget: new TextWidget(text, heading(level))
4322
- }));
4323
4326
  }
4324
4327
  }
4325
4328
  return false;
@@ -4519,13 +4522,14 @@ var decorateMarkdown = (options = {}) => {
4519
4522
  ]
4520
4523
  }),
4521
4524
  formattingStyles,
4525
+ linkPastePlugin,
4522
4526
  image(),
4523
4527
  table()
4524
4528
  ];
4525
4529
  };
4526
4530
  var formattingStyles = EditorView14.baseTheme({
4527
4531
  "& .cm-code": {
4528
- fontFamily: getToken("fontFamily.mono", []).join(",")
4532
+ fontFamily: getToken("fontFamily.mono")
4529
4533
  },
4530
4534
  "& .cm-codeblock-line": {
4531
4535
  paddingInline: "1rem !important"
@@ -4578,7 +4582,6 @@ var formattingStyles = EditorView14.baseTheme({
4578
4582
  "& .cm-list-mark": {
4579
4583
  display: "inline-block",
4580
4584
  textAlign: "right",
4581
- color: getToken("extend.colors.neutral.500"),
4582
4585
  fontVariant: "tabular-nums"
4583
4586
  },
4584
4587
  "& .cm-list-mark-bullet": {
@@ -4717,57 +4720,69 @@ import { debounce as debounce2 } from "@dxos/async";
4717
4720
  import { invariant as invariant5 } from "@dxos/invariant";
4718
4721
  import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
4719
4722
  var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/state.ts";
4720
- var scrollAnnotation = "dxos.org/cm/scrolling";
4723
+ var stateRestoreAnnotation = "dxos.org/cm/state-restore";
4721
4724
  var keyPrefix = "dxos.org/react-ui-editor/state";
4722
4725
  var localStorageStateStoreAdapter = {
4723
- setState: (id, state2) => {
4726
+ getState: (id) => {
4724
4727
  invariant5(id, void 0, {
4725
4728
  F: __dxlog_file12,
4726
- L: 35,
4729
+ L: 34,
4727
4730
  S: void 0,
4728
4731
  A: [
4729
4732
  "id",
4730
4733
  ""
4731
4734
  ]
4732
4735
  });
4733
- localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state2));
4736
+ const state2 = localStorage.getItem(`${keyPrefix}/${id}`);
4737
+ return state2 ? JSON.parse(state2) : void 0;
4734
4738
  },
4735
- getState: (id) => {
4739
+ setState: (id, state2) => {
4736
4740
  invariant5(id, void 0, {
4737
4741
  F: __dxlog_file12,
4738
- L: 39,
4742
+ L: 40,
4739
4743
  S: void 0,
4740
4744
  A: [
4741
4745
  "id",
4742
4746
  ""
4743
4747
  ]
4744
4748
  });
4745
- const state2 = localStorage.getItem(`${keyPrefix}/${id}`);
4746
- return state2 ? JSON.parse(state2) : void 0;
4749
+ localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state2));
4747
4750
  }
4748
4751
  };
4752
+ var createEditorStateTransaction = ({ scrollTo, selection }) => {
4753
+ return {
4754
+ selection,
4755
+ scrollIntoView: !scrollTo,
4756
+ effects: scrollTo ? EditorView15.scrollIntoView(scrollTo, {
4757
+ yMargin: 96
4758
+ }) : void 0,
4759
+ annotations: Transaction2.userEvent.of(stateRestoreAnnotation)
4760
+ };
4761
+ };
4749
4762
  var state = ({ getState, setState } = {}) => {
4750
4763
  const setStateDebounced = debounce2(setState, 1e3);
4751
4764
  return [
4752
4765
  // TODO(burdon): Track scrolling (currently only updates when cursor moves).
4753
- EditorView15.updateListener.of(({ view, changes, transactions }) => {
4766
+ // EditorView.domEventHandlers({
4767
+ // scroll: (event) => {
4768
+ // setStateDebounced(id, {});
4769
+ // },
4770
+ // }),
4771
+ EditorView15.updateListener.of(({ view, transactions }) => {
4754
4772
  const id = view.state.facet(documentId2);
4755
- if (!id || transactions.some((tr) => tr.isUserEvent(scrollAnnotation))) {
4773
+ if (!id || transactions.some((tr) => tr.isUserEvent(stateRestoreAnnotation))) {
4756
4774
  return;
4757
4775
  }
4758
4776
  if (setState) {
4759
- const { top } = view.dom.getBoundingClientRect();
4777
+ const { scrollTop } = view.scrollDOM;
4760
4778
  const pos = view.posAtCoords({
4761
4779
  x: 0,
4762
- y: top
4780
+ y: scrollTop
4763
4781
  });
4764
4782
  if (pos !== null) {
4765
4783
  const { anchor, head } = view.state.selection.main;
4766
4784
  setStateDebounced(id, {
4767
- scrollTo: {
4768
- from: pos,
4769
- yMargin: 0
4770
- },
4785
+ scrollTo: pos,
4771
4786
  selection: {
4772
4787
  anchor,
4773
4788
  head
@@ -4782,13 +4797,7 @@ var state = ({ getState, setState } = {}) => {
4782
4797
  run: (view) => {
4783
4798
  const state2 = getState(view.state.facet(documentId2));
4784
4799
  if (state2) {
4785
- view.dispatch({
4786
- effects: EditorView15.scrollIntoView(state2.scrollTo.from, {
4787
- yMargin: 0
4788
- }),
4789
- selection: state2.selection,
4790
- annotations: Transaction2.userEvent.of(scrollAnnotation)
4791
- });
4800
+ view.dispatch(createEditorStateTransaction(state2));
4792
4801
  }
4793
4802
  return true;
4794
4803
  }
@@ -5234,18 +5243,24 @@ var Toolbar = {
5234
5243
 
5235
5244
  // packages/ui/react-ui-editor/src/defaults.ts
5236
5245
  import { EditorView as EditorView16 } from "@codemirror/view";
5237
- var editorContent = "!mt-[16px] !mli-auto w-full max-w-[min(50rem,100%-4rem)]";
5246
+ var editorContent = "!mt-[16px] !mb-[32px] !mli-auto w-full max-w-[min(50rem,100%-8rem)]";
5238
5247
  var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
5239
5248
  var editorGutter = EditorView16.baseTheme({
5240
5249
  ".cm-gutters": {
5241
5250
  // Match margin from content.
5242
5251
  marginTop: "16px",
5252
+ marginBottom: "16px",
5243
5253
  // Inside within content margin.
5244
5254
  marginRight: "-32px",
5245
5255
  width: "32px",
5246
5256
  backgroundColor: "transparent !important"
5247
5257
  }
5248
5258
  });
5259
+ var editorMonospace = EditorView16.baseTheme({
5260
+ ".cm-content": {
5261
+ fontFamily: `${getToken("fontFamily.mono")} !important`
5262
+ }
5263
+ });
5249
5264
 
5250
5265
  // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
5251
5266
  var useActionHandler = (view) => {
@@ -5258,18 +5273,14 @@ import { EditorView as EditorView17 } from "@codemirror/view";
5258
5273
  import { useFocusableGroup } from "@fluentui/react-tabster";
5259
5274
  import { useCallback, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState4 } from "react";
5260
5275
  import { log as log8 } from "@dxos/log";
5261
- import { useDefaultValue } from "@dxos/react-ui";
5262
5276
  import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
5263
5277
  var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
5264
5278
  var instanceCount = 0;
5265
5279
  var useTextEditor = (props = {}, deps = []) => {
5266
- const { id, initialValue, selection, extensions, autoFocus, scrollTo: _scrollTo, moveToEndOfLine, debug } = useMemo3(() => {
5280
+ const { id, initialValue, extensions, autoFocus, scrollTo, selection, moveToEndOfLine, debug } = useMemo3(() => {
5267
5281
  return typeof props === "function" ? props() : props;
5268
5282
  }, deps ?? []);
5269
5283
  const [instanceId] = useState4(() => `text-editor-${++instanceCount}`);
5270
- const scrollTo = useDefaultValue(_scrollTo, EditorView17.scrollIntoView(0, {
5271
- yMargin: 0
5272
- }));
5273
5284
  const onUpdate = useRef2();
5274
5285
  const [view, setView] = useState4();
5275
5286
  const parentRef = useRef2(null);
@@ -5282,12 +5293,16 @@ var useTextEditor = (props = {}, deps = []) => {
5282
5293
  doc: initialValue?.length ?? 0
5283
5294
  }, {
5284
5295
  F: __dxlog_file13,
5285
- L: 86,
5296
+ L: 78,
5286
5297
  S: void 0,
5287
5298
  C: (f, a) => f(...a)
5288
5299
  });
5289
- let initialSelection = selection;
5290
- if (moveToEndOfLine && selection === void 0) {
5300
+ let initialSelection;
5301
+ if (selection?.anchor && initialValue?.length) {
5302
+ if (selection.anchor <= initialValue.length && (selection?.head ?? 0) <= initialValue.length) {
5303
+ initialSelection = selection;
5304
+ }
5305
+ } else if (moveToEndOfLine && selection === void 0) {
5291
5306
  const index = initialValue?.indexOf("\n");
5292
5307
  const anchor = !index || index === -1 ? 0 : index;
5293
5308
  initialSelection = {
@@ -5303,20 +5318,21 @@ var useTextEditor = (props = {}, deps = []) => {
5303
5318
  EditorView17.exceptionSink.of((err) => {
5304
5319
  log8.catch(err, void 0, {
5305
5320
  F: __dxlog_file13,
5306
- L: 104,
5321
+ L: 100,
5307
5322
  S: void 0,
5308
5323
  C: (f, a) => f(...a)
5309
5324
  });
5310
5325
  }),
5311
5326
  extensions,
5312
5327
  EditorView17.updateListener.of(() => {
5313
- onUpdate.current?.();
5328
+ setTimeout(() => {
5329
+ onUpdate.current?.();
5330
+ });
5314
5331
  })
5315
5332
  ].filter(isNotFalsy4)
5316
5333
  });
5317
5334
  view2 = new EditorView17({
5318
5335
  parent: parentRef.current,
5319
- scrollTo,
5320
5336
  selection: initialSelection,
5321
5337
  state: state2,
5322
5338
  // NOTE: Uncomment to debug/monitor all transactions.
@@ -5343,7 +5359,7 @@ var useTextEditor = (props = {}, deps = []) => {
5343
5359
  id
5344
5360
  }, {
5345
5361
  F: __dxlog_file13,
5346
- L: 139,
5362
+ L: 136,
5347
5363
  S: void 0,
5348
5364
  C: (f, a) => f(...a)
5349
5365
  });
@@ -5352,17 +5368,13 @@ var useTextEditor = (props = {}, deps = []) => {
5352
5368
  }, deps);
5353
5369
  useEffect3(() => {
5354
5370
  if (view) {
5355
- if (scrollTo) {
5356
- onUpdate.current = () => {
5357
- onUpdate.current = void 0;
5358
- view.dispatch({
5359
- effects: scrollTo && [
5360
- scrollTo
5361
- ],
5362
- scrollIntoView: !scrollTo
5363
- });
5364
- };
5365
- }
5371
+ onUpdate.current = () => {
5372
+ onUpdate.current = void 0;
5373
+ view.dispatch(createEditorStateTransaction({
5374
+ scrollTo,
5375
+ selection
5376
+ }));
5377
+ };
5366
5378
  if (view.state.facet(editorInputMode).noTabster) {
5367
5379
  parentRef.current?.removeAttribute("data-tabster");
5368
5380
  }
@@ -5436,6 +5448,7 @@ export {
5436
5448
  createBasicExtensions,
5437
5449
  createComment,
5438
5450
  createDataExtensions,
5451
+ createEditorStateTransaction,
5439
5452
  createExternalCommentSync,
5440
5453
  createMarkdownExtensions,
5441
5454
  createThemeExtensions,
@@ -5447,6 +5460,7 @@ export {
5447
5460
  editorContent,
5448
5461
  editorGutter,
5449
5462
  editorInputMode,
5463
+ editorMonospace,
5450
5464
  editorWithToolbarLayout,
5451
5465
  focusEvent,
5452
5466
  folding,