@dxos/react-ui-editor 0.6.8-main.046e6cf → 0.6.8-staging.77f93a3

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 (50) hide show
  1. package/dist/lib/browser/index.mjs +290 -293
  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 +1 -1
  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/styles/markdown.d.ts +7 -17
  29. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  30. package/dist/types/src/styles/theme.d.ts +2 -1
  31. package/dist/types/src/styles/theme.d.ts.map +1 -1
  32. package/dist/types/src/styles/tokens.d.ts +5 -7
  33. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  34. package/package.json +24 -24
  35. package/src/TextEditor.stories.tsx +40 -27
  36. package/src/defaults.ts +2 -1
  37. package/src/extensions/doc.ts +3 -0
  38. package/src/extensions/factories.ts +3 -2
  39. package/src/extensions/folding.tsx +5 -7
  40. package/src/extensions/markdown/bundle.ts +1 -3
  41. package/src/extensions/markdown/decorate.ts +29 -23
  42. package/src/extensions/markdown/formatting.ts +3 -1
  43. package/src/extensions/markdown/highlight.ts +33 -19
  44. package/src/extensions/markdown/link-paste.ts +3 -0
  45. package/src/extensions/state.ts +41 -35
  46. package/src/extensions/util/react.tsx +3 -4
  47. package/src/hooks/useTextEditor.ts +24 -29
  48. package/src/styles/markdown.ts +17 -40
  49. package/src/styles/theme.ts +42 -49
  50. package/src/styles/tokens.ts +9 -7
@@ -1387,45 +1387,42 @@ import React from "react";
1387
1387
  import { createRoot } from "react-dom/client";
1388
1388
  import { ThemeProvider } from "@dxos/react-ui";
1389
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, {
1390
+ var renderRoot = (root, node) => {
1391
+ createRoot(root).render(/* @__PURE__ */ React.createElement(ThemeProvider, {
1393
1392
  tx: defaultTx
1394
1393
  }, node));
1395
- return el;
1394
+ return root;
1396
1395
  };
1397
1396
 
1398
1397
  // packages/ui/react-ui-editor/src/styles/markdown.ts
1399
1398
  import { mx } from "@dxos/react-ui-theme";
1400
1399
  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";
1400
+ 1: "text-4xl",
1401
+ 2: "text-3xl",
1402
+ 3: "text-2xl",
1403
+ 4: "text-xl",
1404
+ 5: "text-lg",
1405
+ 6: "text-md"
1406
+ };
1407
+ var theme = {
1408
+ mark: "opacity-50",
1409
+ code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
1410
+ codeMark: "font-mono text-primary-500",
1411
+ // TODO(burdon): Replace with widget.
1412
+ blockquote: "pl-1 mr-1 border-is-4 border-orange-500 dark:border-orange-500 dark:text-neutral-500",
1413
+ heading: (level) => {
1414
+ return mx(headings[level], "dark:text-primary-400");
1415
+ }
1416
+ };
1423
1417
 
1424
1418
  // packages/ui/react-ui-editor/src/styles/tokens.ts
1425
1419
  import get from "lodash.get";
1426
1420
  import { tailwindConfig } from "@dxos/react-ui-theme";
1427
1421
  var tokens = tailwindConfig({}).theme;
1428
- var getToken = (path, defaultValue = void 0) => get(tokens, path, defaultValue);
1422
+ var getToken = (path, defaultValue) => {
1423
+ const value = get(tokens, path, defaultValue);
1424
+ return value?.toString() ?? "";
1425
+ };
1429
1426
 
1430
1427
  // packages/ui/react-ui-editor/src/styles/theme.ts
1431
1428
  var defaultTheme = {
@@ -1436,21 +1433,21 @@ var defaultTheme = {
1436
1433
  // Scroller.
1437
1434
  // NOTE: See https://codemirror.net/docs/guide (DOM Structure).
1438
1435
  ".cm-scroller": {
1439
- overflowY: "auto",
1440
- fontFamily: get2(tokens, "fontFamily.body", []).join(","),
1441
- lineHeight: 1.5
1436
+ overflowY: "auto"
1442
1437
  },
1443
1438
  // Content.
1444
1439
  ".cm-content": {
1445
1440
  padding: "unset",
1446
1441
  // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
1447
- fontSize: "16px"
1442
+ fontSize: "16px",
1443
+ fontFamily: getToken("fontFamily.body"),
1444
+ lineHeight: 1.5
1448
1445
  },
1449
1446
  "&light .cm-content": {
1450
- color: get2(tokens, "extend.semanticColors.base.fg.light", "black")
1447
+ color: getToken("extend.semanticColors.base.fg.light", "black")
1451
1448
  },
1452
1449
  "&dark .cm-content": {
1453
- color: get2(tokens, "extend.semanticColors.base.fg.dark", "red")
1450
+ color: getToken("extend.semanticColors.base.fg.dark", "white")
1454
1451
  },
1455
1452
  //
1456
1453
  // Cursor
@@ -1462,10 +1459,10 @@ var defaultTheme = {
1462
1459
  borderLeft: "2px solid white"
1463
1460
  },
1464
1461
  "&light .cm-placeholder": {
1465
- color: get2(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
1462
+ color: getToken("extend.semanticColors.description.light", "rgba(0,0,0,.2)")
1466
1463
  },
1467
1464
  "&dark .cm-placeholder": {
1468
- color: get2(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
1465
+ color: getToken("extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
1469
1466
  },
1470
1467
  //
1471
1468
  // line
@@ -1486,46 +1483,51 @@ var defaultTheme = {
1486
1483
  // Selection
1487
1484
  //
1488
1485
  "&light .cm-selectionBackground": {
1489
- background: get2(tokens, "extend.colors.primary.100")
1486
+ background: getToken("extend.colors.primary.100")
1490
1487
  },
1491
1488
  "&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
1492
- background: get2(tokens, "extend.colors.primary.200")
1489
+ background: getToken("extend.colors.primary.200")
1493
1490
  },
1494
1491
  "&dark .cm-selectionBackground": {
1495
- background: get2(tokens, "extend.colors.primary.700")
1492
+ background: getToken("extend.colors.primary.700")
1496
1493
  },
1497
1494
  "&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
1498
- background: get2(tokens, "extend.colors.primary.600")
1495
+ background: getToken("extend.colors.primary.600")
1499
1496
  },
1500
1497
  //
1501
1498
  // Search
1502
1499
  //
1503
1500
  "&light .cm-searchMatch": {
1504
- backgroundColor: get2(tokens, "extend.colors.yellow.100")
1501
+ backgroundColor: getToken("extend.colors.yellow.100")
1505
1502
  },
1506
1503
  "&dark .cm-searchMatch": {
1507
- backgroundColor: get2(tokens, "extend.colors.yellow.700")
1504
+ backgroundColor: getToken("extend.colors.yellow.700")
1508
1505
  },
1509
1506
  //
1510
1507
  // link
1511
1508
  //
1512
1509
  ".cm-link": {
1513
- color: get2(tokens, "extend.colors.primary.500"),
1514
1510
  textDecorationLine: "underline",
1515
1511
  textDecorationThickness: "1px",
1516
1512
  textUnderlineOffset: "2px",
1517
1513
  borderRadius: ".125rem",
1518
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1514
+ fontFamily: getToken("fontFamily.body")
1515
+ },
1516
+ "&light .cm-link > span": {
1517
+ color: getToken("extend.colors.primary.600")
1518
+ },
1519
+ "&dark .cm-link > span": {
1520
+ color: getToken("extend.colors.primary.400")
1519
1521
  },
1520
1522
  //
1521
1523
  // tooltip
1522
1524
  //
1523
1525
  ".cm-tooltip": {},
1524
1526
  "&light .cm-tooltip": {
1525
- background: `${get2(tokens, "extend.colors.neutral.100")} !important`
1527
+ background: `${getToken("extend.colors.neutral.100")} !important`
1526
1528
  },
1527
1529
  "&dark .cm-tooltip": {
1528
- background: `${get2(tokens, "extend.colors.neutral.900")} !important`
1530
+ background: `${getToken("extend.colors.neutral.900")} !important`
1529
1531
  },
1530
1532
  ".cm-tooltip-below": {},
1531
1533
  //
@@ -1541,14 +1543,13 @@ var defaultTheme = {
1541
1543
  },
1542
1544
  ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {},
1543
1545
  ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {},
1544
- // TODO(burdon): Can we add a class prefix to avoid adding !important?
1545
1546
  ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
1546
1547
  paddingLeft: "4px !important",
1547
1548
  borderBottom: "none !important",
1548
- color: get2(tokens, "extend.colors.primary.500")
1549
+ color: getToken("extend.colors.primary.500")
1549
1550
  },
1550
1551
  ".cm-tooltip.cm-completionInfo": {
1551
- border: get2(tokens, "extend.colors.neutral.500"),
1552
+ border: getToken("extend.colors.neutral.500"),
1552
1553
  width: "360px !important",
1553
1554
  margin: "-10px 1px 0 1px",
1554
1555
  padding: "8px !important"
@@ -1557,7 +1558,7 @@ var defaultTheme = {
1557
1558
  display: "none"
1558
1559
  },
1559
1560
  ".cm-completionLabel": {
1560
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1561
+ fontFamily: getToken("fontFamily.body")
1561
1562
  },
1562
1563
  ".cm-completionMatchedText": {
1563
1564
  textDecoration: "none !important",
@@ -1567,14 +1568,14 @@ var defaultTheme = {
1567
1568
  // table
1568
1569
  //
1569
1570
  ".cm-table *": {
1570
- fontFamily: `${get2(tokens, "fontFamily.mono", []).join(",")} !important`,
1571
+ fontFamily: `${getToken("fontFamily.mono")} !important`,
1571
1572
  textDecoration: "none !important"
1572
1573
  },
1573
1574
  ".cm-table-head": {
1574
1575
  padding: "2px 16px 2px 0px",
1575
1576
  textAlign: "left",
1576
- borderBottom: `1px solid ${get2(tokens, "extend.colors.primary.500")}`,
1577
- color: get2(tokens, "extend.colors.neutral.500")
1577
+ borderBottom: `1px solid ${getToken("extend.colors.primary.500")}`,
1578
+ color: getToken("extend.colors.neutral.500")
1578
1579
  },
1579
1580
  ".cm-table-cell": {
1580
1581
  padding: "2px 16px 2px 0px"
@@ -1591,19 +1592,6 @@ var defaultTheme = {
1591
1592
  borderTop: "0.5rem solid transparent",
1592
1593
  borderBottom: "0.5rem solid transparent"
1593
1594
  },
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
1595
  // TODO(burdon): Override vars --cm-background.
1608
1596
  // https://www.npmjs.com/package/codemirror-theme-vars
1609
1597
  /**
@@ -1630,20 +1618,20 @@ var defaultTheme = {
1630
1618
  */
1631
1619
  ".cm-panels": {},
1632
1620
  ".cm-panel": {
1633
- fontFamily: get2(tokens, "fontFamily.body", []).join(",")
1621
+ fontFamily: getToken("fontFamily.body")
1634
1622
  },
1635
1623
  ".cm-panel input[type=checkbox]": {
1636
1624
  marginRight: "0.4rem !important"
1637
1625
  },
1638
1626
  "&light .cm-panel": {
1639
- background: get2(tokens, "extend.colors.neutral.50")
1627
+ background: getToken("extend.colors.neutral.50")
1640
1628
  },
1641
1629
  "&dark .cm-panel": {
1642
- background: get2(tokens, "extend.colors.neutral.850")
1630
+ background: getToken("extend.colors.neutral.850")
1643
1631
  },
1644
1632
  ".cm-button": {
1645
1633
  margin: "4px",
1646
- fontFamily: get2(tokens, "fontFamily.body", []).join(","),
1634
+ fontFamily: getToken("fontFamily.body"),
1647
1635
  backgroundImage: "none",
1648
1636
  border: "none",
1649
1637
  "&:active": {
@@ -1651,21 +1639,21 @@ var defaultTheme = {
1651
1639
  }
1652
1640
  },
1653
1641
  "&light .cm-button": {
1654
- background: get2(tokens, "extend.colors.neutral.100"),
1642
+ background: getToken("extend.colors.neutral.100"),
1655
1643
  "&:hover": {
1656
- background: get2(tokens, "extend.colors.neutral.200")
1644
+ background: getToken("extend.colors.neutral.200")
1657
1645
  },
1658
1646
  "&:active": {
1659
- background: get2(tokens, "extend.colors.neutral.300")
1647
+ background: getToken("extend.colors.neutral.300")
1660
1648
  }
1661
1649
  },
1662
1650
  "&dark .cm-button": {
1663
- background: get2(tokens, "extend.colors.neutral.800"),
1651
+ background: getToken("extend.colors.neutral.800"),
1664
1652
  "&:hover": {
1665
- background: get2(tokens, "extend.colors.neutral.700")
1653
+ background: getToken("extend.colors.neutral.700")
1666
1654
  },
1667
1655
  "&:active": {
1668
- background: get2(tokens, "extend.colors.neutral.600")
1656
+ background: getToken("extend.colors.neutral.600")
1669
1657
  }
1670
1658
  }
1671
1659
  };
@@ -1828,8 +1816,8 @@ var commentsDecorations = EditorView7.decorations.compute([
1828
1816
  } else if (range.from === range.to) {
1829
1817
  return void 0;
1830
1818
  }
1831
- const mark2 = createCommentMark(comment.comment.id, comment.comment.id === current);
1832
- return mark2.range(range.from, range.to);
1819
+ const mark = createCommentMark(comment.comment.id, comment.comment.id === current);
1820
+ return mark.range(range.from, range.to);
1833
1821
  }).filter(nonNullable);
1834
1822
  return Decoration4.set(decorations);
1835
1823
  });
@@ -2248,7 +2236,7 @@ var documentId2 = Facet5.define({
2248
2236
  combine: (providers) => {
2249
2237
  invariant3(providers.length <= 1, void 0, {
2250
2238
  F: __dxlog_file8,
2251
- L: 11,
2239
+ L: 14,
2252
2240
  S: void 0,
2253
2241
  A: [
2254
2242
  "providers.length <= 1",
@@ -2297,7 +2285,7 @@ var dropFile = (options = {}) => {
2297
2285
 
2298
2286
  // packages/ui/react-ui-editor/src/extensions/factories.ts
2299
2287
  import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
2300
- import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
2288
+ import { defaultKeymap, history, historyKeymap, standardKeymap } from "@codemirror/commands";
2301
2289
  import { bracketMatching } from "@codemirror/language";
2302
2290
  import { searchKeymap } from "@codemirror/search";
2303
2291
  import { EditorState } from "@codemirror/state";
@@ -2357,10 +2345,9 @@ var createBasicExtensions = (_props) => {
2357
2345
  // https://codemirror.net/docs/ref/#view.KeyBinding
2358
2346
  keymap5.of([
2359
2347
  ...(props.keymap && keymaps[props.keymap]) ?? [],
2348
+ // NOTE: Tab configured by markdown extension.
2360
2349
  // https://codemirror.net/docs/ref/#commands.indentWithTab
2361
- ...props.indentWithTab ? [
2362
- indentWithTab
2363
- ] : [],
2350
+ // ...(props.indentWithTab ? [indentWithTab] : []),
2364
2351
  // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
2365
2352
  ...props.closeBrackets ? closeBracketsKeymap : [],
2366
2353
  // https://codemirror.net/docs/ref/#commands.historyKeymap
@@ -2375,12 +2362,12 @@ var defaultThemeSlots = {
2375
2362
  className: "w-full bs-full"
2376
2363
  }
2377
2364
  };
2378
- var createThemeExtensions = ({ theme, themeMode, slots: _slots } = {}) => {
2365
+ var createThemeExtensions = ({ theme: theme2, themeMode, slots: _slots } = {}) => {
2379
2366
  const slots = defaultsDeep2({}, _slots, defaultThemeSlots);
2380
2367
  return [
2381
2368
  EditorView9.baseTheme(defaultTheme),
2382
2369
  EditorView9.darkTheme.of(themeMode === "dark"),
2383
- theme && EditorView9.theme(theme),
2370
+ theme2 && EditorView9.theme(theme2),
2384
2371
  slots.editor?.className && EditorView9.editorAttributes.of({
2385
2372
  class: slots.editor.className
2386
2373
  }),
@@ -2414,21 +2401,18 @@ var createDataExtensions = ({ id, text, space, identity }) => {
2414
2401
  // packages/ui/react-ui-editor/src/extensions/folding.tsx
2415
2402
  import { codeFolding, foldGutter } from "@codemirror/language";
2416
2403
  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";
2404
+ import { getSize, mx as mx2 } from "@dxos/react-ui-theme";
2419
2405
  var folding = (_props = {}) => [
2420
2406
  codeFolding({
2421
2407
  placeholderDOM: () => document.createElement("div")
2422
2408
  }),
2423
2409
  foldGutter({
2424
2410
  markerDOM: (open) => {
2425
- return renderRoot(/* @__PURE__ */ React2.createElement(ThemeProvider2, {
2426
- tx: defaultTx2
2427
- }, /* @__PURE__ */ React2.createElement("svg", {
2411
+ return renderRoot(document.createElement("div"), /* @__PURE__ */ React2.createElement("svg", {
2428
2412
  className: mx2(getSize(3), "m-3 cursor-pointer", open && "rotate-90")
2429
2413
  }, /* @__PURE__ */ React2.createElement("use", {
2430
2414
  href: "/icons.svg#ph--caret-right--regular"
2431
- }))));
2415
+ })));
2432
2416
  }
2433
2417
  })
2434
2418
  ];
@@ -2449,6 +2433,7 @@ var listener = ({ onFocus, onChange }) => {
2449
2433
 
2450
2434
  // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2451
2435
  import { snippet } from "@codemirror/autocomplete";
2436
+ import { indentWithTab } from "@codemirror/commands";
2452
2437
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2453
2438
  import { EditorSelection } from "@codemirror/state";
2454
2439
  import { EditorView as EditorView11, keymap as keymap6 } from "@codemirror/view";
@@ -2870,28 +2855,28 @@ var addLink = ({ url, image: image2 } = {}) => {
2870
2855
  let cursorOffset = 1;
2871
2856
  for (const style of cutStyles) {
2872
2857
  const type = InlineMarker[style.name];
2873
- const mark2 = inlineMarkerText(type);
2858
+ const mark = inlineMarkerText(type);
2874
2859
  if (style.from < from) {
2875
2860
  changes2.push({
2876
2861
  from: skipSpaces(from, state2.doc, -1),
2877
- insert: mark2
2862
+ insert: mark
2878
2863
  });
2879
2864
  changesAfter.push({
2880
2865
  from: skipSpaces(from, state2.doc, 1, to),
2881
- insert: mark2
2866
+ insert: mark
2882
2867
  });
2883
2868
  } else {
2884
2869
  changes2.push({
2885
2870
  from: skipSpaces(to, state2.doc, -1, from),
2886
- insert: mark2
2871
+ insert: mark
2887
2872
  });
2888
2873
  const after = skipSpaces(to, state2.doc, 1);
2889
2874
  if (after === to) {
2890
- cursorOffset += mark2.length;
2875
+ cursorOffset += mark.length;
2891
2876
  }
2892
2877
  changesAfter.push({
2893
2878
  from: after,
2894
- insert: mark2
2879
+ insert: mark
2895
2880
  });
2896
2881
  }
2897
2882
  }
@@ -2942,10 +2927,10 @@ var addList = (type) => {
2942
2927
  const itemText = itemLine.text.slice(item.from - itemLine.from);
2943
2928
  parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
2944
2929
  if (type === 0) {
2945
- const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
2946
- if (mark2) {
2947
- parentColumn += mark2[1].length;
2948
- counter = +mark2[1] + 1;
2930
+ const mark = /^\s*(\d+)[.)]/.exec(itemText);
2931
+ if (mark) {
2932
+ parentColumn += mark[1].length;
2933
+ counter = +mark[1] + 1;
2949
2934
  }
2950
2935
  }
2951
2936
  }
@@ -3006,7 +2991,7 @@ var addList = (type) => {
3006
2991
  if (parentColumn2 !== null && parentColumn2 > column) {
3007
2992
  padding = Math.max(padding, parentColumn2 - column);
3008
2993
  }
3009
- let mark2;
2994
+ let mark;
3010
2995
  if (type === 0) {
3011
2996
  let max = counter2;
3012
2997
  for (let j = i + 1; j < blocks.length; j++) {
@@ -3017,20 +3002,20 @@ var addList = (type) => {
3017
3002
  }
3018
3003
  const num = String(counter2);
3019
3004
  padding = Math.max(String(max).length, padding);
3020
- mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
3005
+ mark = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
3021
3006
  } else {
3022
- mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
3007
+ mark = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
3023
3008
  }
3024
3009
  changes.push({
3025
3010
  from: nodeFrom,
3026
- insert: mark2
3011
+ insert: mark
3027
3012
  });
3028
3013
  while (line.to < node.to) {
3029
3014
  line = state2.doc.lineAt(line.to + 1);
3030
3015
  const open = /^[\s>]*/.exec(line.text)[0].length;
3031
3016
  changes.push({
3032
3017
  from: line.from + Math.min(open, column),
3033
- insert: " ".repeat(mark2.length)
3018
+ insert: " ".repeat(mark.length)
3034
3019
  });
3035
3020
  }
3036
3021
  }
@@ -3079,14 +3064,14 @@ var removeList = (type) => {
3079
3064
  } else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
3080
3065
  lastBlock = node.from;
3081
3066
  let line = state2.doc.lineAt(node.from);
3082
- const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
3083
- if (!mark2) {
3067
+ const mark = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
3068
+ if (!mark) {
3084
3069
  return false;
3085
3070
  }
3086
3071
  const column = node.from - line.from;
3087
3072
  changes.push({
3088
3073
  from: node.from,
3089
- to: node.from + mark2[0].length
3074
+ to: node.from + mark[0].length
3090
3075
  });
3091
3076
  while (line.to < node.to) {
3092
3077
  line = state2.doc.lineAt(line.to + 1);
@@ -3094,7 +3079,7 @@ var removeList = (type) => {
3094
3079
  if (open > column) {
3095
3080
  changes.push({
3096
3081
  from: line.from + column,
3097
- to: line.from + Math.min(column + mark2[0].length, open)
3082
+ to: line.from + Math.min(column + mark[0].length, open)
3098
3083
  });
3099
3084
  }
3100
3085
  }
@@ -3337,7 +3322,7 @@ var removeCodeblock = ({ state: state2, dispatch }) => {
3337
3322
  var toggleCodeblock = (target) => {
3338
3323
  return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3339
3324
  };
3340
- var formattingKeymap = (options = {}) => {
3325
+ var formattingKeymap = (_options = {}) => {
3341
3326
  return [
3342
3327
  keymap6.of([
3343
3328
  {
@@ -3441,10 +3426,10 @@ var getFormatting = (state2) => {
3441
3426
  }
3442
3427
  }
3443
3428
  for (let i = 0; i < inline.length; i++) {
3444
- const mark2 = inlineMarkerText(i);
3445
- const found = contextAfter.indexOf(mark2);
3429
+ const mark = inlineMarkerText(i);
3430
+ const found = contextAfter.indexOf(mark);
3446
3431
  if (found > -1) {
3447
- contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
3432
+ contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark.length);
3448
3433
  if (inline[i] === null) {
3449
3434
  inline[i] = true;
3450
3435
  }
@@ -3653,7 +3638,7 @@ var markdownTagsExtensions = [
3653
3638
  ]
3654
3639
  }
3655
3640
  ];
3656
- var markdownHighlightStyle = (readonly) => {
3641
+ var markdownHighlightStyle = (_options = {}) => {
3657
3642
  return HighlightStyle.define([
3658
3643
  {
3659
3644
  tag: [
@@ -3691,6 +3676,7 @@ var markdownHighlightStyle = (readonly) => {
3691
3676
  tags.inserted,
3692
3677
  tags.invalid
3693
3678
  ],
3679
+ // TODO(burdon): Explain.
3694
3680
  color: "inherit !important"
3695
3681
  },
3696
3682
  // Markdown marks.
@@ -3702,26 +3688,29 @@ var markdownHighlightStyle = (readonly) => {
3702
3688
  markdownTags.LinkReference,
3703
3689
  markdownTags.ListMark
3704
3690
  ],
3705
- class: mark
3691
+ class: theme.mark
3706
3692
  },
3707
3693
  // Markdown marks.
3708
3694
  {
3709
3695
  tag: [
3696
+ //
3710
3697
  markdownTags.CodeMark,
3711
3698
  markdownTags.HeaderMark,
3712
3699
  markdownTags.QuoteMark,
3713
3700
  markdownTags.EmphasisMark
3714
3701
  ],
3715
- class: mark
3702
+ class: theme.mark
3716
3703
  },
3717
3704
  // E.g., code block language (after ```).
3718
3705
  {
3719
3706
  tag: [
3707
+ //
3720
3708
  tags.function(tags.variableName),
3721
3709
  tags.labelName
3722
3710
  ],
3723
- class: codeMark
3711
+ class: theme.codeMark
3724
3712
  },
3713
+ // Fonts.
3725
3714
  {
3726
3715
  tag: [
3727
3716
  tags.monospace
@@ -3731,40 +3720,40 @@ var markdownHighlightStyle = (readonly) => {
3731
3720
  // Headings.
3732
3721
  {
3733
3722
  tag: tags.heading1,
3734
- class: heading(1)
3723
+ class: theme.heading(1)
3735
3724
  },
3736
3725
  {
3737
3726
  tag: tags.heading2,
3738
- class: heading(2)
3727
+ class: theme.heading(2)
3739
3728
  },
3740
3729
  {
3741
3730
  tag: tags.heading3,
3742
- class: heading(3)
3731
+ class: theme.heading(3)
3743
3732
  },
3744
3733
  {
3745
3734
  tag: tags.heading4,
3746
- class: heading(4)
3735
+ class: theme.heading(4)
3747
3736
  },
3748
3737
  {
3749
3738
  tag: tags.heading5,
3750
- class: heading(5)
3739
+ class: theme.heading(5)
3751
3740
  },
3752
3741
  {
3753
3742
  tag: tags.heading6,
3754
- class: heading(6)
3743
+ class: theme.heading(6)
3755
3744
  },
3756
3745
  // Emphasis.
3757
3746
  {
3758
3747
  tag: tags.emphasis,
3759
- class: italic
3748
+ class: "italic"
3760
3749
  },
3761
3750
  {
3762
3751
  tag: tags.strong,
3763
- class: bold
3752
+ class: "font-bold"
3764
3753
  },
3765
3754
  {
3766
3755
  tag: tags.strikethrough,
3767
- class: strikethrough
3756
+ class: "line-through"
3768
3757
  },
3769
3758
  // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
3770
3759
  // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
@@ -3776,13 +3765,13 @@ var markdownHighlightStyle = (readonly) => {
3776
3765
  markdownTags.CodeText,
3777
3766
  markdownTags.InlineCode
3778
3767
  ],
3779
- class: code
3768
+ class: theme.code
3780
3769
  },
3781
3770
  {
3782
3771
  tag: [
3783
3772
  markdownTags.QuoteMark
3784
3773
  ],
3785
- class: blockquote
3774
+ class: theme.blockquote
3786
3775
  },
3787
3776
  {
3788
3777
  tag: [
@@ -3793,108 +3782,11 @@ var markdownHighlightStyle = (readonly) => {
3793
3782
  ], {
3794
3783
  scope: markdownLanguage2,
3795
3784
  all: {
3796
- fontFamily: getToken("fontFamily.body", []).join(",")
3785
+ fontFamily: getToken("fontFamily.body")
3797
3786
  }
3798
3787
  });
3799
3788
  };
3800
3789
 
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
3790
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
3899
3791
  var createMarkdownExtensions = ({ themeMode } = {}) => {
3900
3792
  return [
@@ -3920,8 +3812,8 @@ var createMarkdownExtensions = ({ themeMode } = {}) => {
3920
3812
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
3921
3813
  // Custom styles.
3922
3814
  syntaxHighlighting(markdownHighlightStyle()),
3923
- linkPastePlugin,
3924
3815
  keymap7.of([
3816
+ // TODO(burdon): Indent by 4 if in task list.
3925
3817
  // https://codemirror.net/docs/ref/#commands.indentWithTab
3926
3818
  indentWithTab2,
3927
3819
  // https://codemirror.net/docs/ref/#commands.defaultKeymap
@@ -3940,7 +3832,7 @@ import { invariant as invariant4 } from "@dxos/invariant";
3940
3832
  import { mx as mx3 } from "@dxos/react-ui-theme";
3941
3833
 
3942
3834
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3943
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3835
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3944
3836
  import { StateField as StateField6 } from "@codemirror/state";
3945
3837
  import { Decoration as Decoration5, EditorView as EditorView12, WidgetType as WidgetType3 } from "@codemirror/view";
3946
3838
  var image = (_options = {}) => {
@@ -3983,7 +3875,7 @@ var preloadImage = (url) => {
3983
3875
  var buildDecorations = (from, to, state2) => {
3984
3876
  const decorations = [];
3985
3877
  const cursor = state2.selection.main.head;
3986
- syntaxTree4(state2).iterate({
3878
+ syntaxTree3(state2).iterate({
3987
3879
  enter: (node) => {
3988
3880
  if (node.name === "Image") {
3989
3881
  const urlNode = node.node.getChild("URL");
@@ -4022,6 +3914,103 @@ var ImageWidget = class extends WidgetType3 {
4022
3914
  var imageUpload = (options = {}) => {
4023
3915
  };
4024
3916
 
3917
+ // packages/ui/react-ui-editor/src/extensions/markdown/link-paste.ts
3918
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3919
+ import { Transaction } from "@codemirror/state";
3920
+ import { ViewPlugin as ViewPlugin5 } from "@codemirror/view";
3921
+ var linkPastePlugin = ViewPlugin5.fromClass(class {
3922
+ update(update2) {
3923
+ for (const tr of update2.transactions) {
3924
+ const event = tr.annotation(Transaction.userEvent);
3925
+ if (event === "input.paste") {
3926
+ const changes = tr.changes;
3927
+ if (changes.empty) {
3928
+ return;
3929
+ }
3930
+ changes.iterChangedRanges((fromA, toA, fromB, toB) => {
3931
+ const insertedUrl = getValidUrl(update2.view.state.sliceDoc(fromB, toB));
3932
+ if (insertedUrl && isValidPosition(update2.view.state, fromB)) {
3933
+ const replacedText = tr.startState.sliceDoc(fromA, toA);
3934
+ setTimeout(() => {
3935
+ update2.view.dispatch(update2.view.state.update({
3936
+ changes: {
3937
+ from: fromA,
3938
+ to: toB,
3939
+ insert: createLink(insertedUrl, replacedText)
3940
+ }
3941
+ }));
3942
+ });
3943
+ }
3944
+ });
3945
+ }
3946
+ }
3947
+ }
3948
+ });
3949
+ var createLink = (url, label) => {
3950
+ const { host, pathname } = url;
3951
+ const [, extension] = pathname.split(".");
3952
+ const imageExtensions = [
3953
+ "jpg",
3954
+ "jpeg",
3955
+ "png",
3956
+ "gif",
3957
+ "svg",
3958
+ "webp"
3959
+ ];
3960
+ if (imageExtensions.includes(extension)) {
3961
+ return `![${label || host}](${url})`;
3962
+ }
3963
+ if (!label) {
3964
+ label = createLinkLabel(url);
3965
+ }
3966
+ return `[${label}](${url})`;
3967
+ };
3968
+ var createLinkLabel = (url) => {
3969
+ let { protocol, host, pathname } = url;
3970
+ if (protocol === "http:" || protocol === "https:") {
3971
+ protocol = "";
3972
+ }
3973
+ host = host.replace(/^www\./, "");
3974
+ return [
3975
+ protocol,
3976
+ host
3977
+ ].filter(Boolean).join("//") + (pathname !== "/" ? pathname : "");
3978
+ };
3979
+ var getValidUrl = (str) => {
3980
+ const validProtocols = [
3981
+ "http:",
3982
+ "https:",
3983
+ "mailto:",
3984
+ "tel:"
3985
+ ];
3986
+ try {
3987
+ const url = new URL(str);
3988
+ if (!validProtocols.includes(url.protocol)) {
3989
+ return void 0;
3990
+ }
3991
+ return url;
3992
+ } catch (_err) {
3993
+ return void 0;
3994
+ }
3995
+ };
3996
+ var isValidPosition = (state2, pos) => {
3997
+ const invalidPositions = /* @__PURE__ */ new Set([
3998
+ "Link",
3999
+ "LinkMark",
4000
+ "Code",
4001
+ "FencedCode"
4002
+ ]);
4003
+ const tree = syntaxTree4(state2);
4004
+ let node = tree.resolveInner(pos, -1);
4005
+ while (node) {
4006
+ if (invalidPositions.has(node.name)) {
4007
+ return false;
4008
+ }
4009
+ node = node.parent;
4010
+ }
4011
+ return true;
4012
+ };
4013
+
4025
4014
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
4026
4015
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4027
4016
  import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField7 } from "@codemirror/state";
@@ -4244,7 +4233,7 @@ var buildDecorations2 = (view, options, focus) => {
4244
4233
  const getHeaderLevels = (node, level) => {
4245
4234
  invariant4(level > 0, void 0, {
4246
4235
  F: __dxlog_file10,
4247
- L: 157,
4236
+ L: 158,
4248
4237
  S: void 0,
4249
4238
  A: [
4250
4239
  "level > 0",
@@ -4283,7 +4272,7 @@ var buildDecorations2 = (view, options, focus) => {
4283
4272
  const getCurrentList = () => {
4284
4273
  invariant4(listLevels.length, void 0, {
4285
4274
  F: __dxlog_file10,
4286
- L: 179,
4275
+ L: 180,
4287
4276
  S: void 0,
4288
4277
  A: [
4289
4278
  "listLevels.length",
@@ -4306,20 +4295,23 @@ var buildDecorations2 = (view, options, focus) => {
4306
4295
  headers[level - 1].number++;
4307
4296
  }
4308
4297
  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
- }
4298
+ if (editing) {
4299
+ break;
4300
+ }
4301
+ const mark = node.node.firstChild;
4302
+ if (mark?.name === "HeaderMark") {
4303
+ const { from, to = 6 } = options.numberedHeadings ?? {};
4304
+ const text = view.state.sliceDoc(node.from, node.to);
4305
+ const len = text.match(/[#\s]+/)[0].length;
4306
+ if (!from || level < from || level > to) {
4307
+ atomicDeco.add(mark.from, mark.from + len, hide);
4308
+ } else {
4309
+ const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
4310
+ if (num.length) {
4311
+ atomicDeco.add(mark.from, mark.from + len, Decoration7.replace({
4312
+ widget: new TextWidget(num, theme.heading(level))
4313
+ }));
4319
4314
  }
4320
- deco.add(node.from, node.to, Decoration7.replace({
4321
- widget: new TextWidget(text, heading(level))
4322
- }));
4323
4315
  }
4324
4316
  }
4325
4317
  return false;
@@ -4519,13 +4511,14 @@ var decorateMarkdown = (options = {}) => {
4519
4511
  ]
4520
4512
  }),
4521
4513
  formattingStyles,
4514
+ linkPastePlugin,
4522
4515
  image(),
4523
4516
  table()
4524
4517
  ];
4525
4518
  };
4526
4519
  var formattingStyles = EditorView14.baseTheme({
4527
4520
  "& .cm-code": {
4528
- fontFamily: getToken("fontFamily.mono", []).join(",")
4521
+ fontFamily: getToken("fontFamily.mono")
4529
4522
  },
4530
4523
  "& .cm-codeblock-line": {
4531
4524
  paddingInline: "1rem !important"
@@ -4578,7 +4571,6 @@ var formattingStyles = EditorView14.baseTheme({
4578
4571
  "& .cm-list-mark": {
4579
4572
  display: "inline-block",
4580
4573
  textAlign: "right",
4581
- color: getToken("extend.colors.neutral.500"),
4582
4574
  fontVariant: "tabular-nums"
4583
4575
  },
4584
4576
  "& .cm-list-mark-bullet": {
@@ -4717,57 +4709,69 @@ import { debounce as debounce2 } from "@dxos/async";
4717
4709
  import { invariant as invariant5 } from "@dxos/invariant";
4718
4710
  import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
4719
4711
  var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/state.ts";
4720
- var scrollAnnotation = "dxos.org/cm/scrolling";
4712
+ var stateRestoreAnnotation = "dxos.org/cm/state-restore";
4721
4713
  var keyPrefix = "dxos.org/react-ui-editor/state";
4722
4714
  var localStorageStateStoreAdapter = {
4723
- setState: (id, state2) => {
4715
+ getState: (id) => {
4724
4716
  invariant5(id, void 0, {
4725
4717
  F: __dxlog_file12,
4726
- L: 35,
4718
+ L: 34,
4727
4719
  S: void 0,
4728
4720
  A: [
4729
4721
  "id",
4730
4722
  ""
4731
4723
  ]
4732
4724
  });
4733
- localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state2));
4725
+ const state2 = localStorage.getItem(`${keyPrefix}/${id}`);
4726
+ return state2 ? JSON.parse(state2) : void 0;
4734
4727
  },
4735
- getState: (id) => {
4728
+ setState: (id, state2) => {
4736
4729
  invariant5(id, void 0, {
4737
4730
  F: __dxlog_file12,
4738
- L: 39,
4731
+ L: 40,
4739
4732
  S: void 0,
4740
4733
  A: [
4741
4734
  "id",
4742
4735
  ""
4743
4736
  ]
4744
4737
  });
4745
- const state2 = localStorage.getItem(`${keyPrefix}/${id}`);
4746
- return state2 ? JSON.parse(state2) : void 0;
4738
+ localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state2));
4747
4739
  }
4748
4740
  };
4741
+ var createEditorStateTransaction = ({ scrollTo, selection }) => {
4742
+ return {
4743
+ selection,
4744
+ scrollIntoView: !scrollTo,
4745
+ effects: scrollTo ? EditorView15.scrollIntoView(scrollTo, {
4746
+ yMargin: 96
4747
+ }) : void 0,
4748
+ annotations: Transaction2.userEvent.of(stateRestoreAnnotation)
4749
+ };
4750
+ };
4749
4751
  var state = ({ getState, setState } = {}) => {
4750
4752
  const setStateDebounced = debounce2(setState, 1e3);
4751
4753
  return [
4752
4754
  // TODO(burdon): Track scrolling (currently only updates when cursor moves).
4753
- EditorView15.updateListener.of(({ view, changes, transactions }) => {
4755
+ // EditorView.domEventHandlers({
4756
+ // scroll: (event) => {
4757
+ // setStateDebounced(id, {});
4758
+ // },
4759
+ // }),
4760
+ EditorView15.updateListener.of(({ view, transactions }) => {
4754
4761
  const id = view.state.facet(documentId2);
4755
- if (!id || transactions.some((tr) => tr.isUserEvent(scrollAnnotation))) {
4762
+ if (!id || transactions.some((tr) => tr.isUserEvent(stateRestoreAnnotation))) {
4756
4763
  return;
4757
4764
  }
4758
4765
  if (setState) {
4759
- const { top } = view.dom.getBoundingClientRect();
4766
+ const { scrollTop } = view.scrollDOM;
4760
4767
  const pos = view.posAtCoords({
4761
4768
  x: 0,
4762
- y: top
4769
+ y: scrollTop
4763
4770
  });
4764
4771
  if (pos !== null) {
4765
4772
  const { anchor, head } = view.state.selection.main;
4766
4773
  setStateDebounced(id, {
4767
- scrollTo: {
4768
- from: pos,
4769
- yMargin: 0
4770
- },
4774
+ scrollTo: pos,
4771
4775
  selection: {
4772
4776
  anchor,
4773
4777
  head
@@ -4782,13 +4786,7 @@ var state = ({ getState, setState } = {}) => {
4782
4786
  run: (view) => {
4783
4787
  const state2 = getState(view.state.facet(documentId2));
4784
4788
  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
- });
4789
+ view.dispatch(createEditorStateTransaction(state2));
4792
4790
  }
4793
4791
  return true;
4794
4792
  }
@@ -5234,12 +5232,13 @@ var Toolbar = {
5234
5232
 
5235
5233
  // packages/ui/react-ui-editor/src/defaults.ts
5236
5234
  import { EditorView as EditorView16 } from "@codemirror/view";
5237
- var editorContent = "!mt-[16px] !mli-auto w-full max-w-[min(50rem,100%-4rem)]";
5235
+ var editorContent = "!mt-[16px] !mb-[32px] !mli-auto w-full max-w-[min(50rem,100%-4rem)]";
5238
5236
  var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
5239
5237
  var editorGutter = EditorView16.baseTheme({
5240
5238
  ".cm-gutters": {
5241
5239
  // Match margin from content.
5242
5240
  marginTop: "16px",
5241
+ marginBottom: "16px",
5243
5242
  // Inside within content margin.
5244
5243
  marginRight: "-32px",
5245
5244
  width: "32px",
@@ -5258,18 +5257,14 @@ import { EditorView as EditorView17 } from "@codemirror/view";
5258
5257
  import { useFocusableGroup } from "@fluentui/react-tabster";
5259
5258
  import { useCallback, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState4 } from "react";
5260
5259
  import { log as log8 } from "@dxos/log";
5261
- import { useDefaultValue } from "@dxos/react-ui";
5262
5260
  import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
5263
5261
  var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
5264
5262
  var instanceCount = 0;
5265
5263
  var useTextEditor = (props = {}, deps = []) => {
5266
- const { id, initialValue, selection, extensions, autoFocus, scrollTo: _scrollTo, moveToEndOfLine, debug } = useMemo3(() => {
5264
+ const { id, initialValue, extensions, autoFocus, scrollTo, selection, moveToEndOfLine, debug } = useMemo3(() => {
5267
5265
  return typeof props === "function" ? props() : props;
5268
5266
  }, deps ?? []);
5269
5267
  const [instanceId] = useState4(() => `text-editor-${++instanceCount}`);
5270
- const scrollTo = useDefaultValue(_scrollTo, EditorView17.scrollIntoView(0, {
5271
- yMargin: 0
5272
- }));
5273
5268
  const onUpdate = useRef2();
5274
5269
  const [view, setView] = useState4();
5275
5270
  const parentRef = useRef2(null);
@@ -5282,12 +5277,16 @@ var useTextEditor = (props = {}, deps = []) => {
5282
5277
  doc: initialValue?.length ?? 0
5283
5278
  }, {
5284
5279
  F: __dxlog_file13,
5285
- L: 86,
5280
+ L: 78,
5286
5281
  S: void 0,
5287
5282
  C: (f, a) => f(...a)
5288
5283
  });
5289
- let initialSelection = selection;
5290
- if (moveToEndOfLine && selection === void 0) {
5284
+ let initialSelection;
5285
+ if (selection?.anchor && initialValue?.length) {
5286
+ if (selection.anchor <= initialValue.length && (selection?.head ?? 0) <= initialValue.length) {
5287
+ initialSelection = selection;
5288
+ }
5289
+ } else if (moveToEndOfLine && selection === void 0) {
5291
5290
  const index = initialValue?.indexOf("\n");
5292
5291
  const anchor = !index || index === -1 ? 0 : index;
5293
5292
  initialSelection = {
@@ -5303,20 +5302,21 @@ var useTextEditor = (props = {}, deps = []) => {
5303
5302
  EditorView17.exceptionSink.of((err) => {
5304
5303
  log8.catch(err, void 0, {
5305
5304
  F: __dxlog_file13,
5306
- L: 104,
5305
+ L: 100,
5307
5306
  S: void 0,
5308
5307
  C: (f, a) => f(...a)
5309
5308
  });
5310
5309
  }),
5311
5310
  extensions,
5312
5311
  EditorView17.updateListener.of(() => {
5313
- onUpdate.current?.();
5312
+ setTimeout(() => {
5313
+ onUpdate.current?.();
5314
+ });
5314
5315
  })
5315
5316
  ].filter(isNotFalsy4)
5316
5317
  });
5317
5318
  view2 = new EditorView17({
5318
5319
  parent: parentRef.current,
5319
- scrollTo,
5320
5320
  selection: initialSelection,
5321
5321
  state: state2,
5322
5322
  // NOTE: Uncomment to debug/monitor all transactions.
@@ -5343,7 +5343,7 @@ var useTextEditor = (props = {}, deps = []) => {
5343
5343
  id
5344
5344
  }, {
5345
5345
  F: __dxlog_file13,
5346
- L: 139,
5346
+ L: 136,
5347
5347
  S: void 0,
5348
5348
  C: (f, a) => f(...a)
5349
5349
  });
@@ -5352,17 +5352,13 @@ var useTextEditor = (props = {}, deps = []) => {
5352
5352
  }, deps);
5353
5353
  useEffect3(() => {
5354
5354
  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
- }
5355
+ onUpdate.current = () => {
5356
+ onUpdate.current = void 0;
5357
+ view.dispatch(createEditorStateTransaction({
5358
+ scrollTo,
5359
+ selection
5360
+ }));
5361
+ };
5366
5362
  if (view.state.facet(editorInputMode).noTabster) {
5367
5363
  parentRef.current?.removeAttribute("data-tabster");
5368
5364
  }
@@ -5436,6 +5432,7 @@ export {
5436
5432
  createBasicExtensions,
5437
5433
  createComment,
5438
5434
  createDataExtensions,
5435
+ createEditorStateTransaction,
5439
5436
  createExternalCommentSync,
5440
5437
  createMarkdownExtensions,
5441
5438
  createThemeExtensions,