@damiro/stylo 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/dist/InPlaceView-Ba7JCZ2Y.js +2346 -0
  4. package/dist/InPlaceView-Ba7JCZ2Y.js.map +1 -0
  5. package/dist/Preview-Dxb9zPVv.js +93 -0
  6. package/dist/Preview-Dxb9zPVv.js.map +1 -0
  7. package/dist/SplitView.d.ts +23 -0
  8. package/dist/SplitView.d.ts.map +1 -0
  9. package/dist/Stylo.d.ts +15 -0
  10. package/dist/Stylo.d.ts.map +1 -0
  11. package/dist/callout-oLE630ZP.js +37 -0
  12. package/dist/callout-oLE630ZP.js.map +1 -0
  13. package/dist/callout.d.ts +18 -0
  14. package/dist/callout.d.ts.map +1 -0
  15. package/dist/config-DTBxVhtH.js +101 -0
  16. package/dist/config-DTBxVhtH.js.map +1 -0
  17. package/dist/editor/SourceView.d.ts +17 -0
  18. package/dist/editor/SourceView.d.ts.map +1 -0
  19. package/dist/editor/extensions.d.ts +20 -0
  20. package/dist/editor/extensions.d.ts.map +1 -0
  21. package/dist/editor/highlight.d.ts +19 -0
  22. package/dist/editor/highlight.d.ts.map +1 -0
  23. package/dist/editor/save.d.ts +12 -0
  24. package/dist/editor/save.d.ts.map +1 -0
  25. package/dist/editor/theme.d.ts +3 -0
  26. package/dist/editor/theme.d.ts.map +1 -0
  27. package/dist/editor/useCodeMirror.d.ts +26 -0
  28. package/dist/editor/useCodeMirror.d.ts.map +1 -0
  29. package/dist/frontmatter.d.ts +21 -0
  30. package/dist/frontmatter.d.ts.map +1 -0
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/inplace/InPlaceView.d.ts +30 -0
  34. package/dist/inplace/InPlaceView.d.ts.map +1 -0
  35. package/dist/inplace/autoformat.d.ts +23 -0
  36. package/dist/inplace/autoformat.d.ts.map +1 -0
  37. package/dist/inplace/config.d.ts +57 -0
  38. package/dist/inplace/config.d.ts.map +1 -0
  39. package/dist/inplace/context-menu-actions.d.ts +53 -0
  40. package/dist/inplace/context-menu-actions.d.ts.map +1 -0
  41. package/dist/inplace/context-menu.d.ts +50 -0
  42. package/dist/inplace/context-menu.d.ts.map +1 -0
  43. package/dist/inplace/decorate.d.ts +17 -0
  44. package/dist/inplace/decorate.d.ts.map +1 -0
  45. package/dist/inplace/edit-boundaries.d.ts +59 -0
  46. package/dist/inplace/edit-boundaries.d.ts.map +1 -0
  47. package/dist/inplace/edit-insert-assoc.d.ts +17 -0
  48. package/dist/inplace/edit-insert-assoc.d.ts.map +1 -0
  49. package/dist/inplace/edit-line-prefix.d.ts +23 -0
  50. package/dist/inplace/edit-line-prefix.d.ts.map +1 -0
  51. package/dist/inplace/extension.d.ts +21 -0
  52. package/dist/inplace/extension.d.ts.map +1 -0
  53. package/dist/inplace/frontmatter.d.ts +13 -0
  54. package/dist/inplace/frontmatter.d.ts.map +1 -0
  55. package/dist/inplace/inline-md.d.ts +13 -0
  56. package/dist/inplace/inline-md.d.ts.map +1 -0
  57. package/dist/inplace/lazyInPlace.d.ts +7 -0
  58. package/dist/inplace/lazyInPlace.d.ts.map +1 -0
  59. package/dist/inplace/link-click.d.ts +24 -0
  60. package/dist/inplace/link-click.d.ts.map +1 -0
  61. package/dist/inplace/link-hover.d.ts +11 -0
  62. package/dist/inplace/link-hover.d.ts.map +1 -0
  63. package/dist/inplace/list-guides.d.ts +16 -0
  64. package/dist/inplace/list-guides.d.ts.map +1 -0
  65. package/dist/inplace/long-press.d.ts +39 -0
  66. package/dist/inplace/long-press.d.ts.map +1 -0
  67. package/dist/inplace/math.d.ts +16 -0
  68. package/dist/inplace/math.d.ts.map +1 -0
  69. package/dist/inplace/menu-plugin.d.ts +30 -0
  70. package/dist/inplace/menu-plugin.d.ts.map +1 -0
  71. package/dist/inplace/nodes.d.ts +24 -0
  72. package/dist/inplace/nodes.d.ts.map +1 -0
  73. package/dist/inplace/plugin.d.ts +21 -0
  74. package/dist/inplace/plugin.d.ts.map +1 -0
  75. package/dist/inplace/reveal.d.ts +8 -0
  76. package/dist/inplace/reveal.d.ts.map +1 -0
  77. package/dist/inplace/scan.d.ts +11 -0
  78. package/dist/inplace/scan.d.ts.map +1 -0
  79. package/dist/inplace/selection-bar.d.ts +38 -0
  80. package/dist/inplace/selection-bar.d.ts.map +1 -0
  81. package/dist/inplace/table-cell-dom.d.ts +30 -0
  82. package/dist/inplace/table-cell-dom.d.ts.map +1 -0
  83. package/dist/inplace/table-enter.d.ts +22 -0
  84. package/dist/inplace/table-enter.d.ts.map +1 -0
  85. package/dist/inplace/table-gizmos.d.ts +55 -0
  86. package/dist/inplace/table-gizmos.d.ts.map +1 -0
  87. package/dist/inplace/table-structure.d.ts +23 -0
  88. package/dist/inplace/table-structure.d.ts.map +1 -0
  89. package/dist/inplace/table-widget.d.ts +81 -0
  90. package/dist/inplace/table-widget.d.ts.map +1 -0
  91. package/dist/inplace/tables.d.ts +8 -0
  92. package/dist/inplace/tables.d.ts.map +1 -0
  93. package/dist/inplace/theme.d.ts +14 -0
  94. package/dist/inplace/theme.d.ts.map +1 -0
  95. package/dist/inplace/widgets.d.ts +25 -0
  96. package/dist/inplace/widgets.d.ts.map +1 -0
  97. package/dist/inplace/wikilinks.d.ts +10 -0
  98. package/dist/inplace/wikilinks.d.ts.map +1 -0
  99. package/dist/katex-BfiLlqfN.js +21979 -0
  100. package/dist/katex-BfiLlqfN.js.map +1 -0
  101. package/dist/katex.css +3 -0
  102. package/dist/markdown-N3lRmU2n.js +8524 -0
  103. package/dist/markdown-N3lRmU2n.js.map +1 -0
  104. package/dist/render/Preview.d.ts +10 -0
  105. package/dist/render/Preview.d.ts.map +1 -0
  106. package/dist/render/lazyPreview.d.ts +8 -0
  107. package/dist/render/lazyPreview.d.ts.map +1 -0
  108. package/dist/render/remark-callout.d.ts +10 -0
  109. package/dist/render/remark-callout.d.ts.map +1 -0
  110. package/dist/render/remark-wikilink.d.ts +8 -0
  111. package/dist/render/remark-wikilink.d.ts.map +1 -0
  112. package/dist/styles.css +2 -0
  113. package/dist/stylo.js +293 -0
  114. package/dist/stylo.js.map +1 -0
  115. package/dist/toolbar/Toolbar.d.ts +28 -0
  116. package/dist/toolbar/Toolbar.d.ts.map +1 -0
  117. package/dist/toolbar/block.d.ts +56 -0
  118. package/dist/toolbar/block.d.ts.map +1 -0
  119. package/dist/toolbar/cell-inline.d.ts +16 -0
  120. package/dist/toolbar/cell-inline.d.ts.map +1 -0
  121. package/dist/toolbar/commands.d.ts +25 -0
  122. package/dist/toolbar/commands.d.ts.map +1 -0
  123. package/dist/toolbar/config.d.ts +21 -0
  124. package/dist/toolbar/config.d.ts.map +1 -0
  125. package/dist/toolbar/fence.d.ts +28 -0
  126. package/dist/toolbar/fence.d.ts.map +1 -0
  127. package/dist/toolbar/floating-watchdog.d.ts +18 -0
  128. package/dist/toolbar/floating-watchdog.d.ts.map +1 -0
  129. package/dist/toolbar/icon-paths.d.ts +10 -0
  130. package/dist/toolbar/icon-paths.d.ts.map +1 -0
  131. package/dist/toolbar/icons.d.ts +10 -0
  132. package/dist/toolbar/icons.d.ts.map +1 -0
  133. package/dist/toolbar/inline-ops.d.ts +101 -0
  134. package/dist/toolbar/inline-ops.d.ts.map +1 -0
  135. package/dist/toolbar/inline.d.ts +38 -0
  136. package/dist/toolbar/inline.d.ts.map +1 -0
  137. package/dist/toolbar/keyboard-inset.d.ts +10 -0
  138. package/dist/toolbar/keyboard-inset.d.ts.map +1 -0
  139. package/dist/toolbar/keymap.d.ts +8 -0
  140. package/dist/toolbar/keymap.d.ts.map +1 -0
  141. package/dist/toolbar/labels.d.ts +16 -0
  142. package/dist/toolbar/labels.d.ts.map +1 -0
  143. package/dist/toolbar/table-grid.d.ts +23 -0
  144. package/dist/toolbar/table-grid.d.ts.map +1 -0
  145. package/dist/toolbar/table.d.ts +35 -0
  146. package/dist/toolbar/table.d.ts.map +1 -0
  147. package/dist/toolbar-settings/SortableRow.d.ts +15 -0
  148. package/dist/toolbar-settings/SortableRow.d.ts.map +1 -0
  149. package/dist/toolbar-settings/StyloToolbarSettings.d.ts +29 -0
  150. package/dist/toolbar-settings/StyloToolbarSettings.d.ts.map +1 -0
  151. package/dist/toolbar-settings/index.d.ts +3 -0
  152. package/dist/toolbar-settings/index.d.ts.map +1 -0
  153. package/dist/toolbar-settings/items.d.ts +24 -0
  154. package/dist/toolbar-settings/items.d.ts.map +1 -0
  155. package/dist/toolbar-settings.js +269 -0
  156. package/dist/toolbar-settings.js.map +1 -0
  157. package/dist/types.d.ts +264 -0
  158. package/dist/types.d.ts.map +1 -0
  159. package/dist/useCodeMirror-YnIFkvKn.js +1361 -0
  160. package/dist/useCodeMirror-YnIFkvKn.js.map +1 -0
  161. package/dist/wikilink-BFpTehto.js +42 -0
  162. package/dist/wikilink-BFpTehto.js.map +1 -0
  163. package/dist/wikilink.d.ts +11 -0
  164. package/dist/wikilink.d.ts.map +1 -0
  165. package/package.json +122 -0
@@ -0,0 +1,10 @@
1
+ import type { FrontmatterDisplay } from "../types";
2
+ export interface PreviewProps {
3
+ value: string;
4
+ onWikiLinkClick?: (target: string) => void;
5
+ /** `"code"` renders the `---` block as a styled `<pre>`; `"hidden"` (default) drops it. */
6
+ frontmatter?: FrontmatterDisplay;
7
+ }
8
+ /** Rendered Markdown + KaTeX view. A pure function of the string. */
9
+ export declare function Preview({ value, onWikiLinkClick, frontmatter }: PreviewProps): import("react").JSX.Element;
10
+ //# sourceMappingURL=Preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Preview.d.ts","sourceRoot":"","sources":["../../src/render/Preview.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAOlD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,2FAA2F;IAC3F,WAAW,CAAC,EAAE,kBAAkB,CAAA;CACjC;AAED,qEAAqE;AACrE,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,WAAsB,EAAE,EAAE,YAAY,+BAuCvF"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Preview pulls in react-markdown + remark/rehype + KaTeX. It is loaded only
3
+ * when a rendered mode (`preview`, `split`) is actually used, so `mode="source"`
4
+ * consumers never pay for it. Both callers share this one wrapper, so the render
5
+ * pipeline stays a single lazy chunk.
6
+ */
7
+ export declare const LazyPreview: import("react").LazyExoticComponent<typeof import("./Preview").Preview>;
8
+ //# sourceMappingURL=lazyPreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazyPreview.d.ts","sourceRoot":"","sources":["../../src/render/lazyPreview.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,yEAErB,CAAA"}
@@ -0,0 +1,10 @@
1
+ import type { Root } from "mdast";
2
+ /**
3
+ * Turns an Obsidian callout blockquote (`> [!note] Title`) into a
4
+ * `<blockquote class="stylo-callout stylo-callout-<bucket>" data-callout="note">`.
5
+ * The `[!type]` token is stripped from the first line; a `::before` label keyed
6
+ * off `data-callout` stands in for it (matching the in-place canvas). The
7
+ * `-` / `+` fold marker is accepted and ignored — the box always renders open.
8
+ */
9
+ export declare function remarkCallout(): (tree: Root) => void;
10
+ //# sourceMappingURL=remark-callout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remark-callout.d.ts","sourceRoot":"","sources":["../../src/render/remark-callout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,IAAI,EAAE,MAAM,OAAO,CAAA;AAI7C;;;;;;GAMG;AACH,wBAAgB,aAAa,WACb,IAAI,UAkBnB"}
@@ -0,0 +1,8 @@
1
+ import type { Root } from "mdast";
2
+ /**
3
+ * Rewrites `[[target]]` and `[[target|label]]` inside text into link nodes that
4
+ * carry a `data-wikilink` attribute holding the target. The rendered href is
5
+ * inert (`#`); navigation is the host's job via `onWikiLinkClick`.
6
+ */
7
+ export declare function remarkWikilink(): (tree: Root) => void;
8
+ //# sourceMappingURL=remark-wikilink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remark-wikilink.d.ts","sourceRoot":"","sources":["../../src/render/remark-wikilink.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,IAAI,EAAQ,MAAM,OAAO,CAAA;AAI7C;;;;GAIG;AACH,wBAAgB,cAAc,WACd,IAAI,UA+BnB"}
@@ -0,0 +1,2 @@
1
+ ._root_18e48_1{min-height:0;color:var(--stylo-text);background:var(--stylo-bg);border:1px solid var(--stylo-border);border-radius:var(--stylo-radius);flex-direction:column;display:flex;overflow:hidden}._root_18e48_1 .cm-scroller{scrollbar-width:thin;scrollbar-color:color-mix(in srgb, var(--stylo-border) 90%, transparent) transparent}._root_18e48_1 .cm-scroller::-webkit-scrollbar{width:10px;height:10px}._root_18e48_1 .cm-scroller::-webkit-scrollbar-thumb{background:color-mix(in srgb, var(--stylo-border) 90%, transparent);background-clip:padding-box;border:3px solid #0000;border-radius:6px}._root_18e48_1 .cm-scroller::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb, var(--stylo-text-muted) 55%, transparent);background-clip:padding-box}._root_18e48_1 .cm-scroller::-webkit-scrollbar-corner{background:0 0}._source_18e48_42{flex:auto;min-height:0;overflow:auto}._source_18e48_42 .cm-editor{height:100%}._source_18e48_42 .cm-editor.cm-focused{outline:2px solid var(--stylo-ring);outline-offset:-2px}._inplace_18e48_57{flex:auto;min-height:0;overflow:auto}._toolbar_18e48_63{background:var(--stylo-bg);border-bottom:1px solid var(--stylo-border);flex-wrap:wrap;align-items:center;gap:2px;padding:4px 6px;display:flex}._toolbarButton_18e48_73{box-sizing:border-box;border-radius:calc(var(--stylo-radius) - 3px);min-width:28px;height:28px;color:var(--stylo-text-muted);font:inherit;cursor:pointer;background:0 0;border:0;justify-content:center;align-items:center;padding:0 5px;font-size:.75rem;font-weight:600;line-height:1;display:inline-flex}._toolbarButton_18e48_73:hover:not(:disabled){background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._toolbarButton_18e48_73[data-active]{background:color-mix(in srgb, var(--stylo-accent) 12%, transparent);color:var(--stylo-text)}._toolbarButton_18e48_73:disabled{opacity:.4;cursor:default}._toolbarButton_18e48_73:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-2px}._toolbarButton_18e48_73 svg{width:16px;height:16px;display:block}._toolbarButton_18e48_73 code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.8125rem;font-weight:500}._toolbarSep_18e48_125{background:var(--stylo-border);align-self:stretch;width:1px;margin:4px 3px}._toolbarSticky_18e48_135{z-index:50;-webkit-overflow-scrolling:touch;scrollbar-width:none;flex-wrap:nowrap;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;transition:opacity .15s;overflow-x:auto}._toolbarSticky_18e48_135::-webkit-scrollbar{display:none}._toolbarSticky_18e48_135 ._toolbarButton_18e48_73{flex:none;min-width:40px;height:40px}._toolbarStickyBottom_18e48_135{border-bottom:none;border-top:1px solid var(--stylo-border);box-shadow:0 -2px 8px color-mix(in srgb, var(--stylo-text) 10%, transparent);position:fixed;bottom:0;left:0;right:0}._toolbarStickyTop_18e48_135{box-shadow:0 2px 8px color-mix(in srgb, var(--stylo-text) 10%, transparent);position:fixed;top:0;left:0;right:0}._toolbarStickyHidden_18e48_194{opacity:0;pointer-events:none}._stickyToolbarRootBottom_18e48_205 ._source_18e48_42,._stickyToolbarRootBottom_18e48_205 ._inplace_18e48_57{padding-bottom:52px}._stickyToolbarRootTop_18e48_210 ._source_18e48_42,._stickyToolbarRootTop_18e48_210 ._inplace_18e48_57{padding-top:52px}._inplace_18e48_57 .cm-editor{height:100%}._inplace_18e48_57 .cm-editor.cm-focused{outline:2px solid var(--stylo-ring);outline-offset:-2px}._split_18e48_224{flex:1;grid-template-columns:1fr 1fr;min-height:0;display:grid}._splitPane_18e48_231{box-sizing:border-box;flex-direction:column;min-height:0;display:flex;overflow:auto}._splitPane_18e48_231+._splitPane_18e48_231{border-left:1px solid var(--stylo-border)}._splitPane_18e48_231>._source_18e48_42{flex:1;min-height:0}._preview_18e48_251{overflow-wrap:break-word;padding:.75rem 1rem;font-size:.9375rem;line-height:1.75}._preview_18e48_251>:first-child{margin-top:0}._preview_18e48_251>:last-child{margin-bottom:0}._preview_18e48_251 :is(h1,h2,h3,h4,h5,h6){font-weight:600}._preview_18e48_251 h1{margin:0 0 .888889em;font-size:2.25em;line-height:1.11111}._preview_18e48_251 h2{margin:2em 0 1em;font-size:1.5em;line-height:1.33333}._preview_18e48_251 h3{margin:1.6em 0 .6em;font-size:1.25em;line-height:1.6}._preview_18e48_251 :is(h4,h5,h6){margin:1.5em 0 .5em;line-height:1.5}._preview_18e48_251 p{margin:1.25em 0}._preview_18e48_251 :is(ul,ol){margin:1.25em 0;padding-left:1.625em}._preview_18e48_251 li{margin:.5em 0}._preview_18e48_251 li>:is(ul,ol){margin:.75em 0}._preview_18e48_251 a{color:var(--stylo-link);text-decoration:none}._preview_18e48_251 code{border-radius:calc(var(--stylo-radius) - 3px);background:color-mix(in srgb, var(--stylo-border) 45%, transparent);padding:.1em .35em;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.9em}._preview_18e48_251 pre{border-radius:var(--stylo-radius);background:color-mix(in srgb, var(--stylo-border) 35%, transparent);margin:1.71429em 0;padding:.75rem .9rem;overflow-x:auto}._preview_18e48_251 pre code{background:0 0;padding:0;font-size:.85em}.stylo-frontmatter{border-left:3px solid var(--stylo-border);border-radius:calc(var(--stylo-radius) - 3px);background:color-mix(in srgb, var(--stylo-border) 22%, transparent);color:var(--stylo-text-muted);white-space:pre-wrap;margin:0 0 1.2em;padding:.7rem .9rem;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.8125rem;line-height:1.5;overflow-x:auto}.stylo-frontmatter:before{content:"Frontmatter";letter-spacing:.04em;text-transform:uppercase;margin-bottom:.5em;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:.7rem;font-weight:600;display:block}._preview_18e48_251 blockquote{border-left:.25rem solid var(--stylo-border);color:var(--stylo-text-muted);margin:1.6em 0;padding-left:1em}._preview_18e48_251 .stylo-callout{border-left:.25rem solid var(--stylo-callout-accent,var(--stylo-border));border-radius:var(--stylo-radius);background:color-mix(in srgb, var(--stylo-callout-accent,var(--stylo-border)) 10%, transparent);color:var(--stylo-text);margin:1.6em 0;padding:.6em 1em}._preview_18e48_251 .stylo-callout:before{content:attr(data-callout);letter-spacing:.04em;text-transform:uppercase;color:var(--stylo-callout-accent,var(--stylo-text-muted));margin-bottom:.2em;font-size:.8em;font-weight:700;display:block}._preview_18e48_251 .stylo-callout-note{--stylo-callout-accent:var(--stylo-callout-note,#3b82f6)}._preview_18e48_251 .stylo-callout-tip{--stylo-callout-accent:var(--stylo-callout-tip,#22c55e)}._preview_18e48_251 .stylo-callout-warn{--stylo-callout-accent:var(--stylo-callout-warn,#f59e0b)}._preview_18e48_251 .stylo-callout-danger{--stylo-callout-accent:var(--stylo-callout-danger,#ef4444)}._preview_18e48_251 .stylo-callout-example{--stylo-callout-accent:var(--stylo-callout-example,#a855f7)}._preview_18e48_251 table{border-collapse:collapse;margin:2em 0;display:block;overflow-x:auto}._preview_18e48_251 :is(th,td){border:1px solid var(--stylo-table-border);text-align:left;padding:.35em .6em}._preview_18e48_251 thead th{background:var(--stylo-table-header-bg);font-weight:600}._preview_18e48_251 tbody tr:nth-child(2n) td{background:var(--stylo-table-stripe-bg)}._preview_18e48_251 hr{border:none;border-top:1px solid var(--stylo-border);margin:1em 0}._preview_18e48_251 .katex-display{padding:.25em 0;overflow:auto hidden}.stylo{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--stylo-bg:#fff;--stylo-surface-floating:#fff;--stylo-text:#09090b;--stylo-text-muted:#71717a;--stylo-border:#e4e4e7;--stylo-accent:#18181b;--stylo-link:#2563eb;--stylo-ring:#a1a1aa;--stylo-radius:.5rem;--stylo-table-border:var(--stylo-border);--stylo-table-header-bg:color-mix(in srgb, var(--stylo-border) 30%, transparent);--stylo-table-stripe-bg:transparent;--stylo-guide:var(--stylo-border);--stylo-syntax-keyword:#7c3aed;--stylo-syntax-string:#0a7c2f;--stylo-syntax-escape:#b45309;--stylo-syntax-comment:#8a8f98;--stylo-syntax-number:#b45309;--stylo-syntax-constant:#b45309;--stylo-syntax-function:#2563eb;--stylo-syntax-type:#a16207;--stylo-syntax-property:#0f766e;--stylo-syntax-tag:#cf222e;--stylo-syntax-invalid:#dc2626}:where(.dark,[data-theme=dark]) .stylo,.stylo:where(.dark,[data-theme=dark]){--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--stylo-bg:#09090b;--stylo-surface-floating:#09090b;--stylo-text:#fafafa;--stylo-text-muted:#a1a1aa;--stylo-border:#27272a;--stylo-accent:#fafafa;--stylo-link:#60a5fa;--stylo-ring:#52525b;--stylo-syntax-keyword:#c678dd;--stylo-syntax-string:#98c379;--stylo-syntax-escape:#d19a66;--stylo-syntax-comment:#7f848e;--stylo-syntax-number:#d19a66;--stylo-syntax-constant:#d19a66;--stylo-syntax-function:#61afef;--stylo-syntax-type:#e5c07b;--stylo-syntax-property:#56b6c2;--stylo-syntax-tag:#e06c75;--stylo-syntax-invalid:#e06c75}._root_18hcd_3{color:var(--stylo-text);font:inherit;grid-template-columns:1fr 1fr;gap:12px;display:grid}@media (width<=460px){._root_18hcd_3{grid-template-columns:1fr}}._col_18hcd_17{border:1px solid var(--stylo-border);border-radius:var(--stylo-radius);background:var(--stylo-bg);flex-direction:column;min-width:0;display:flex}._colHead_18hcd_26{border-bottom:1px solid var(--stylo-border);letter-spacing:.04em;text-transform:uppercase;color:var(--stylo-text-muted);margin:0;padding:8px 12px;font-size:.75rem;font-weight:600}._list_18hcd_37{flex-direction:column;flex:1;gap:2px;margin:0;padding:4px;list-style:none;display:flex}._row_18hcd_47{border-radius:calc(var(--stylo-radius) - 3px);background:var(--stylo-bg);align-items:center;gap:8px;padding:5px 6px;display:flex}._row_18hcd_47:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-2px}._row_18hcd_47:hover{background:color-mix(in srgb, var(--stylo-border) 30%, transparent)}._row_18hcd_47[data-dragging]{background:var(--stylo-surface-floating,#fff);opacity:.9;box-shadow:0 4px 16px #00000024}._handle_18hcd_73{border-radius:calc(var(--stylo-radius) - 4px);width:20px;height:22px;color:var(--stylo-text-muted);font:inherit;cursor:grab;touch-action:none;background:0 0;border:1px solid #0000;flex:none;justify-content:center;align-items:center;padding:0;line-height:1;display:inline-flex}._handle_18hcd_73:hover{background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._handle_18hcd_73:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-1px}._handle_18hcd_73:active{cursor:grabbing}._glyph_18hcd_105{width:18px;height:18px;color:var(--stylo-text-muted);flex:none;justify-content:center;align-items:center;display:inline-flex}._glyph_18hcd_105 svg{width:16px;height:16px;display:block}._label_18hcd_121{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-size:.8125rem;overflow:hidden}._rowActions_18hcd_130{flex:none;gap:2px;display:flex}._iconButton_18hcd_136,._textButton_18hcd_137{box-sizing:border-box;border-radius:calc(var(--stylo-radius) - 4px);color:var(--stylo-text-muted);font:inherit;cursor:pointer;background:0 0;border:1px solid #0000}._iconButton_18hcd_136{width:22px;height:22px;font-size:.8125rem;line-height:1}._textButton_18hcd_137{padding:3px 8px;font-size:.75rem}._iconButton_18hcd_136:hover:not(:disabled),._textButton_18hcd_137:hover:not(:disabled){background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._iconButton_18hcd_136:focus-visible,._textButton_18hcd_137:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-1px}._iconButton_18hcd_136:disabled{opacity:.35;cursor:default}._footer_18hcd_176{border-top:1px solid var(--stylo-border);flex-wrap:wrap;gap:6px;padding:6px;display:flex}._empty_18hcd_184{color:var(--stylo-text-muted);padding:8px 6px;font-size:.8125rem}._srOnly_18hcd_190{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}
2
+ /*$vite$:1*/
package/dist/stylo.js ADDED
@@ -0,0 +1,293 @@
1
+ import { i as e, r as t } from "./wikilink-BFpTehto.js";
2
+ import { n, t as r } from "./useCodeMirror-YnIFkvKn.js";
3
+ import { n as i, r as a } from "./config-DTBxVhtH.js";
4
+ import { Suspense as o, forwardRef as s, lazy as c, useEffect as l, useImperativeHandle as u, useReducer as d, useRef as f, useState as p } from "react";
5
+ import { EditorView as m } from "@codemirror/view";
6
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
7
+ //#region src/editor/SourceView.tsx
8
+ function _({ value: t, onChange: n, readOnly: i, placeholder: a, codeLanguages: o, onSave: s, onViewChange: c }) {
9
+ let l = r({
10
+ value: t,
11
+ onChange: n,
12
+ readOnly: i,
13
+ placeholder: a,
14
+ codeLanguages: o,
15
+ onSave: s,
16
+ onViewChange: c
17
+ });
18
+ return /* @__PURE__ */ h("div", {
19
+ className: e.source,
20
+ ref: l
21
+ });
22
+ }
23
+ //#endregion
24
+ //#region src/inplace/lazyInPlace.ts
25
+ var v = c(async () => ({ default: (await import("./InPlaceView-Ba7JCZ2Y.js")).InPlaceView })), y = c(async () => ({ default: (await import("./Preview-Dxb9zPVv.js")).Preview }));
26
+ //#endregion
27
+ //#region src/SplitView.tsx
28
+ function b({ value: t, onChange: n, onWikiLinkClick: r, readOnly: i, placeholder: a, codeLanguages: s, frontmatter: c, onSave: u, onViewChange: d }) {
29
+ let [m, v] = p(null), b = f(null), x = f(!1);
30
+ return l(() => {
31
+ let e = m?.scrollDOM, t = b.current;
32
+ if (!e || !t) return;
33
+ let n = (e, t) => {
34
+ if (x.current) return;
35
+ let n = e.scrollHeight - e.clientHeight;
36
+ n <= 0 || (x.current = !0, t.scrollTop = e.scrollTop / n * (t.scrollHeight - t.clientHeight), requestAnimationFrame(() => {
37
+ x.current = !1;
38
+ }));
39
+ }, r = () => n(e, t), i = () => n(t, e);
40
+ return e.addEventListener("scroll", r, { passive: !0 }), t.addEventListener("scroll", i, { passive: !0 }), () => {
41
+ e.removeEventListener("scroll", r), t.removeEventListener("scroll", i);
42
+ };
43
+ }, [m]), /* @__PURE__ */ g("div", {
44
+ className: e.split,
45
+ children: [/* @__PURE__ */ h("div", {
46
+ className: e.splitPane,
47
+ children: /* @__PURE__ */ h(_, {
48
+ value: t,
49
+ onChange: n,
50
+ readOnly: i,
51
+ placeholder: a,
52
+ codeLanguages: s,
53
+ onSave: u,
54
+ onViewChange: (e) => {
55
+ v(e), d?.(e);
56
+ }
57
+ })
58
+ }), /* @__PURE__ */ h("div", {
59
+ className: e.splitPane,
60
+ ref: b,
61
+ children: /* @__PURE__ */ h(o, {
62
+ fallback: /* @__PURE__ */ h("div", {
63
+ className: e.preview,
64
+ "aria-busy": "true"
65
+ }),
66
+ children: /* @__PURE__ */ h(y, {
67
+ value: t,
68
+ onWikiLinkClick: r
69
+ })
70
+ })
71
+ })]
72
+ });
73
+ }
74
+ //#endregion
75
+ //#region src/toolbar/floating-watchdog.ts
76
+ function x(e, t) {
77
+ l(() => {
78
+ if (!t) return;
79
+ let n = 0, r = !1, i = () => {
80
+ let t = e.current;
81
+ t && (r = !r, t.style.transform = r ? "translate3d(0, 0.01px, 0)" : "translate3d(0, 0, 0)"), n = requestAnimationFrame(i);
82
+ };
83
+ return n = requestAnimationFrame(i), () => cancelAnimationFrame(n);
84
+ }, [e, t]);
85
+ }
86
+ //#endregion
87
+ //#region src/toolbar/keyboard-inset.ts
88
+ function S(e) {
89
+ let [t, n] = p(0);
90
+ return l(() => {
91
+ let t = typeof window > "u" ? null : window.visualViewport;
92
+ if (!e || !t) {
93
+ n(0);
94
+ return;
95
+ }
96
+ let r = () => n(Math.max(0, window.innerHeight - t.height - t.offsetTop));
97
+ return r(), t.addEventListener("resize", r), t.addEventListener("scroll", r), () => {
98
+ t.removeEventListener("resize", r), t.removeEventListener("scroll", r);
99
+ };
100
+ }, [e]), t;
101
+ }
102
+ //#endregion
103
+ //#region src/toolbar/Toolbar.tsx
104
+ function C(e, t) {
105
+ if (typeof e != "string") {
106
+ let { id: t, icon: n, title: r, run: i, isActive: a, disabled: o } = e;
107
+ return {
108
+ key: t,
109
+ icon: n,
110
+ title: r,
111
+ run: i,
112
+ isActive: a,
113
+ disabled: o
114
+ };
115
+ }
116
+ let r = n[e];
117
+ return r ? {
118
+ key: e,
119
+ icon: t?.[e] ?? a[e],
120
+ title: r.title,
121
+ run: r.run,
122
+ isActive: r.isActive,
123
+ disabled: r.disabled
124
+ } : null;
125
+ }
126
+ function w({ view: t, items: n, icons: r, disabled: i, sticky: a, stickyVisibility: o }) {
127
+ let [, s] = d((e) => e + 1, 0), [c, u] = p(!1), m = S(a === "bottom"), g = f(null);
128
+ x(g, a === "top"), l(() => {
129
+ if (!t) return;
130
+ let e = t.contentDOM;
131
+ u(t.hasFocus);
132
+ let n = () => {
133
+ u(!0), s();
134
+ }, r = () => {
135
+ u(!1), s();
136
+ }, i = [
137
+ "keyup",
138
+ "mouseup",
139
+ "input"
140
+ ];
141
+ for (let t of i) e.addEventListener(t, s);
142
+ return e.addEventListener("focus", n), e.addEventListener("blur", r), () => {
143
+ for (let t of i) e.removeEventListener(t, s);
144
+ e.removeEventListener("focus", n), e.removeEventListener("blur", r);
145
+ };
146
+ }, [t]);
147
+ let _ = !!a && o === "dynamic" && !c, v = [
148
+ e.toolbar,
149
+ a && e.toolbarSticky,
150
+ a === "top" && e.toolbarStickyTop,
151
+ a === "bottom" && e.toolbarStickyBottom,
152
+ _ && e.toolbarStickyHidden
153
+ ].filter(Boolean).join(" "), y = a === "bottom" ? { transform: `translateY(-${m}px)` } : void 0;
154
+ return /* @__PURE__ */ h("div", {
155
+ ref: g,
156
+ className: v,
157
+ role: "toolbar",
158
+ "aria-label": "Formatting",
159
+ "aria-hidden": _ || void 0,
160
+ style: y,
161
+ children: n.map((n, a) => {
162
+ if (n === "|") return /* @__PURE__ */ h("span", {
163
+ className: e.toolbarSep,
164
+ "aria-hidden": "true"
165
+ }, `sep-${a}`);
166
+ let o = C(n, r);
167
+ if (!o) return null;
168
+ let c = i || !t || !!o.disabled?.(t.state), l = !!(!c && t && o.isActive?.(t.state));
169
+ return /* @__PURE__ */ h("button", {
170
+ type: "button",
171
+ className: e.toolbarButton,
172
+ "data-command": o.key,
173
+ title: o.title,
174
+ "aria-label": o.title,
175
+ "aria-pressed": o.isActive ? l : void 0,
176
+ "data-active": l ? "" : void 0,
177
+ disabled: c,
178
+ onMouseDown: (e) => e.preventDefault(),
179
+ onClick: () => {
180
+ t && (o.run(t), s());
181
+ },
182
+ children: o.icon
183
+ }, o.key);
184
+ })
185
+ });
186
+ }
187
+ //#endregion
188
+ //#region src/Stylo.tsx
189
+ var T = s(function({ value: n, onChange: r, mode: a = "in-place", onSave: s, onFrontmatter: c, onWikiLinkClick: d, onLinkClick: x, readOnly: S, placeholder: C, className: T, inPlace: E, codeLanguages: D, toolbar: O, icons: k, frontmatter: A }, j) {
190
+ let M = a === "preview" || a === "split" || a === "in-place" ? a : "source", [N, P] = p(null), F = M === "preview" ? null : i(O), I = O && typeof O == "object" ? O.render : void 0, L = O && typeof O == "object" ? O.sticky : void 0, R = L === "top" ? "top" : L ? "bottom" : !1, z = O && typeof O == "object" ? O.stickyVisibility : void 0, B = f(c);
191
+ B.current = c;
192
+ let V = f(void 0);
193
+ l(() => {
194
+ let e = t(n)?.frontmatter ?? null;
195
+ e !== V.current && (V.current = e, B.current?.(e));
196
+ }, [n]), u(j, () => ({
197
+ focus: () => N?.focus(),
198
+ getView: () => N,
199
+ insertAtCursor: (e) => {
200
+ N && (N.dispatch(N.state.replaceSelection(e)), N.focus());
201
+ },
202
+ scrollToHeading: (e) => {
203
+ if (!N) return !1;
204
+ let t = e.trim().toLowerCase(), { doc: n } = N.state;
205
+ for (let e = 1; e <= n.lines; e++) {
206
+ let r = n.line(e), i = /^ {0,3}#{1,6}[ \t]+(.*?)(?:[ \t]+#+)?[ \t]*$/.exec(r.text);
207
+ if (!(!i || i[1] === void 0) && i[1].trim().toLowerCase() === t) return N.dispatch({
208
+ selection: { anchor: r.from },
209
+ effects: m.scrollIntoView(r.from, { y: "start" })
210
+ }), N.focus(), !0;
211
+ }
212
+ return !1;
213
+ }
214
+ }), [N]);
215
+ let H = [
216
+ e.root,
217
+ R === "bottom" && e.stickyToolbarRootBottom,
218
+ R === "top" && e.stickyToolbarRootTop,
219
+ "stylo",
220
+ T
221
+ ].filter(Boolean).join(" ");
222
+ return /* @__PURE__ */ g("div", {
223
+ className: H,
224
+ "data-stylo-mode": M,
225
+ children: [
226
+ F && (() => {
227
+ let e = /* @__PURE__ */ h(w, {
228
+ view: N,
229
+ items: F,
230
+ icons: k,
231
+ disabled: S,
232
+ sticky: R,
233
+ stickyVisibility: z
234
+ });
235
+ return I ? I(e, { view: N }) : e;
236
+ })(),
237
+ M === "source" && /* @__PURE__ */ h(_, {
238
+ value: n,
239
+ onChange: r,
240
+ readOnly: S,
241
+ placeholder: C,
242
+ codeLanguages: D,
243
+ onSave: s,
244
+ onViewChange: P
245
+ }),
246
+ M === "preview" && /* @__PURE__ */ h(o, {
247
+ fallback: /* @__PURE__ */ h("div", {
248
+ className: e.preview,
249
+ "aria-busy": "true"
250
+ }),
251
+ children: /* @__PURE__ */ h(y, {
252
+ value: n,
253
+ onWikiLinkClick: d,
254
+ frontmatter: A
255
+ })
256
+ }),
257
+ M === "split" && /* @__PURE__ */ h(b, {
258
+ value: n,
259
+ onChange: r,
260
+ onWikiLinkClick: d,
261
+ readOnly: S,
262
+ placeholder: C,
263
+ codeLanguages: D,
264
+ frontmatter: A,
265
+ onSave: s,
266
+ onViewChange: P
267
+ }),
268
+ M === "in-place" && /* @__PURE__ */ h(o, {
269
+ fallback: /* @__PURE__ */ h("div", {
270
+ className: e.inplace,
271
+ "aria-busy": "true"
272
+ }),
273
+ children: /* @__PURE__ */ h(v, {
274
+ value: n,
275
+ onChange: r,
276
+ readOnly: S,
277
+ placeholder: C,
278
+ onWikiLinkClick: d,
279
+ onLinkClick: x,
280
+ inPlace: E,
281
+ codeLanguages: D,
282
+ onSave: s,
283
+ onViewChange: P
284
+ })
285
+ })
286
+ ]
287
+ });
288
+ });
289
+ T.displayName = "Stylo";
290
+ //#endregion
291
+ export { T as Stylo, t as splitFrontmatter };
292
+
293
+ //# sourceMappingURL=stylo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stylo.js","names":[],"sources":["../src/editor/SourceView.tsx","../src/inplace/lazyInPlace.ts","../src/render/lazyPreview.ts","../src/SplitView.tsx","../src/toolbar/floating-watchdog.ts","../src/toolbar/keyboard-inset.ts","../src/toolbar/Toolbar.tsx","../src/Stylo.tsx"],"sourcesContent":["import type { EditorView } from \"@codemirror/view\"\nimport styles from \"../styles/stylo.module.css\"\nimport type { CodeLanguages } from \"../types\"\nimport { useCodeMirror } from \"./useCodeMirror\"\n\nexport interface SourceViewProps {\n value: string\n onChange: (next: string) => void\n readOnly?: boolean\n placeholder?: string\n /** Fenced-code grammars, forwarded to the Markdown language. Read once. */\n codeLanguages?: CodeLanguages\n /** Called with the doc string on `Mod-s`. */\n onSave?: (value: string) => void\n /** Called with the `EditorView` once created, and with `null` on teardown. */\n onViewChange?: (view: EditorView | null) => void\n}\n\n/** Raw CodeMirror 6 Markdown surface bound to the canonical string. */\nexport function SourceView({\n value,\n onChange,\n readOnly,\n placeholder,\n codeLanguages,\n onSave,\n onViewChange,\n}: SourceViewProps) {\n const ref = useCodeMirror({\n value,\n onChange,\n readOnly,\n placeholder,\n codeLanguages,\n onSave,\n onViewChange,\n })\n return <div className={styles.source} ref={ref} />\n}\n","import { lazy } from \"react\"\n\n/**\n * The in-place canvas carries the decoration plugin (and, in later increments,\n * its KaTeX widgets). It loads only when `mode` resolves to `in-place`, so\n * `source` consumers never fetch it.\n */\nexport const LazyInPlaceView = lazy(async () => ({\n default: (await import(\"./InPlaceView\")).InPlaceView,\n}))\n","import { lazy } from \"react\"\n\n/**\n * Preview pulls in react-markdown + remark/rehype + KaTeX. It is loaded only\n * when a rendered mode (`preview`, `split`) is actually used, so `mode=\"source\"`\n * consumers never pay for it. Both callers share this one wrapper, so the render\n * pipeline stays a single lazy chunk.\n */\nexport const LazyPreview = lazy(async () => ({\n default: (await import(\"./Preview\")).Preview,\n}))\n","import { Suspense, useEffect, useRef, useState } from \"react\"\nimport type { EditorView } from \"@codemirror/view\"\nimport { SourceView } from \"./editor/SourceView\"\nimport { LazyPreview } from \"./render/lazyPreview\"\nimport styles from \"./styles/stylo.module.css\"\nimport type { CodeLanguages, FrontmatterDisplay } from \"./types\"\n\nexport interface SplitViewProps {\n value: string\n onChange: (next: string) => void\n onWikiLinkClick?: (target: string) => void\n readOnly?: boolean\n placeholder?: string\n codeLanguages?: CodeLanguages\n frontmatter?: FrontmatterDisplay\n /** Called with the doc string on `Mod-s`. */\n onSave?: (value: string) => void\n /** Forwarded the source pane's `EditorView` for the shared toolbar. */\n onViewChange?: (view: EditorView | null) => void\n}\n\n/**\n * Source and preview side by side. Both panes are a pure function of the same\n * string; the only link between them is a proportional scroll sync. Expects the\n * Stylo root to have a bounded height — otherwise both panes grow and the page\n * scrolls instead.\n */\nexport function SplitView({\n value,\n onChange,\n onWikiLinkClick,\n readOnly,\n placeholder,\n codeLanguages,\n frontmatter,\n onSave,\n onViewChange,\n}: SplitViewProps) {\n const [view, setView] = useState<EditorView | null>(null)\n const previewPane = useRef<HTMLDivElement | null>(null)\n const syncing = useRef(false)\n\n const handleView = (next: EditorView | null) => {\n setView(next)\n onViewChange?.(next)\n }\n\n useEffect(() => {\n const source = view?.scrollDOM\n const preview = previewPane.current\n if (!source || !preview) return\n\n const sync = (from: HTMLElement, to: HTMLElement) => {\n if (syncing.current) return\n const range = from.scrollHeight - from.clientHeight\n if (range <= 0) return\n syncing.current = true\n to.scrollTop = (from.scrollTop / range) * (to.scrollHeight - to.clientHeight)\n requestAnimationFrame(() => {\n syncing.current = false\n })\n }\n\n const fromSource = () => sync(source, preview)\n const fromPreview = () => sync(preview, source)\n source.addEventListener(\"scroll\", fromSource, { passive: true })\n preview.addEventListener(\"scroll\", fromPreview, { passive: true })\n return () => {\n source.removeEventListener(\"scroll\", fromSource)\n preview.removeEventListener(\"scroll\", fromPreview)\n }\n }, [view])\n\n return (\n <div className={styles.split}>\n <div className={styles.splitPane}>\n <SourceView\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n placeholder={placeholder}\n codeLanguages={codeLanguages}\n onSave={onSave}\n onViewChange={handleView}\n />\n </div>\n <div className={styles.splitPane} ref={previewPane}>\n <Suspense fallback={<div className={styles.preview} aria-busy=\"true\" />}>\n <LazyPreview value={value} onWikiLinkClick={onWikiLinkClick} />\n </Suspense>\n </div>\n </div>\n )\n}\n","import { useEffect, type RefObject } from \"react\"\n\n/**\n * Continuously re-asserts a `transform` on a `position: fixed` element,\n * alternating an imperceptible sub-pixel jitter every animation frame,\n * instead of trusting the browser to keep it correctly composited on its\n * own. Several rounds of on-device testing found iOS Safari losing track of\n * this exact kind of element — across its own address-bar animation, and in\n * at least one report tied to the keyboard opening — in ways a one-time\n * static fix (a plain `translateZ(0)`) did not reliably prevent. A value\n * that never changes gives the browser nothing to recompute; alternating it\n * every frame forces a fresh compositor pass regardless of what triggered\n * the drift, so it can't stay wrong for more than one frame (~16ms).\n *\n * Writes to the DOM directly rather than through React state, so this never\n * causes a re-render — the transform is the only thing touched.\n */\nexport function useFloatingWatchdog(ref: RefObject<HTMLElement | null>, enabled: boolean): void {\n useEffect(() => {\n if (!enabled) return\n let raf = 0\n let jitter = false\n const tick = () => {\n const el = ref.current\n if (el) {\n jitter = !jitter\n el.style.transform = jitter ? \"translate3d(0, 0.01px, 0)\" : \"translate3d(0, 0, 0)\"\n }\n raf = requestAnimationFrame(tick)\n }\n raf = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(raf)\n }, [ref, enabled])\n}\n","import { useEffect, useState } from \"react\"\n\n/**\n * How far the on-screen keyboard eats into the window, tracked through the\n * `visualViewport` API. 0 when `enabled` is false or the browser has no\n * `visualViewport` (older Safari, SSR, most desktop browsers). A mobile\n * keyboard shrinks the *visual* viewport, not the layout viewport a plain\n * `position: fixed` bottom offset is computed against — this is what lets the\n * sticky toolbar ride up above the keyboard instead of hiding behind it.\n */\nexport function useKeyboardInset(enabled: boolean): number {\n const [inset, setInset] = useState(0)\n\n useEffect(() => {\n const vv = typeof window === \"undefined\" ? null : window.visualViewport\n if (!enabled || !vv) {\n setInset(0)\n return\n }\n const update = () => setInset(Math.max(0, window.innerHeight - vv.height - vv.offsetTop))\n update()\n vv.addEventListener(\"resize\", update)\n vv.addEventListener(\"scroll\", update)\n return () => {\n vv.removeEventListener(\"resize\", update)\n vv.removeEventListener(\"scroll\", update)\n }\n }, [enabled])\n\n return inset\n}\n","import { useEffect, useReducer, useRef, useState } from \"react\"\nimport type { ReactNode } from \"react\"\nimport type { EditorState } from \"@codemirror/state\"\nimport type { EditorView } from \"@codemirror/view\"\nimport styles from \"../styles/stylo.module.css\"\nimport type { ToolbarCommandId } from \"../types\"\nimport { BUILTIN_BY_ID } from \"./commands\"\nimport type { ToolbarItem } from \"./config\"\nimport { useFloatingWatchdog } from \"./floating-watchdog\"\nimport { DEFAULT_ICONS } from \"./icons\"\nimport { useKeyboardInset } from \"./keyboard-inset\"\n\nexport interface ToolbarProps {\n /** The surface the commands act on. `null` while a lazy view is mounting. */\n view: EditorView | null\n /** Ordered items to render; `\"|\"` is a separator. */\n items: ToolbarItem[]\n /** Per-id glyph overrides; any id left out keeps its built-in icon. */\n icons?: Partial<Record<ToolbarCommandId, ReactNode>>\n /** Render every button inert (e.g. a read-only surface). */\n disabled?: boolean\n /** Fix the bar to a window edge (normalised from `ToolbarConfig.sticky`; the\n * caller resolves `true` to `\"bottom\"`). */\n sticky?: \"top\" | \"bottom\" | false\n /** Fade the bar out while the editing surface is unfocused (`ToolbarConfig.stickyVisibility`). */\n stickyVisibility?: \"consistent\" | \"dynamic\"\n}\n\n/** A button to render, normalised from a built-in id or a custom item. */\ninterface Btn {\n key: string\n icon: ReactNode\n title: string\n run: (view: EditorView) => unknown\n isActive?: (state: EditorState) => boolean\n disabled?: (state: EditorState) => boolean\n}\n\n/** Resolve one non-separator item to a renderable button, or `null` to skip it. */\nfunction toButton(item: Exclude<ToolbarItem, \"|\">, icons: ToolbarProps[\"icons\"]): Btn | null {\n if (typeof item !== \"string\") {\n const { id, icon, title, run, isActive, disabled } = item\n return { key: id, icon, title, run, isActive, disabled }\n }\n const cmd = BUILTIN_BY_ID[item]\n if (!cmd) return null\n return {\n key: item,\n icon: icons?.[item] ?? DEFAULT_ICONS[item],\n title: cmd.title,\n run: cmd.run,\n isActive: cmd.isActive,\n disabled: cmd.disabled,\n }\n}\n\n/**\n * Formatting bar above the editing surface. It holds no document state — each\n * button runs a command against the live `EditorView`. Pressed states are read\n * back from the view whenever the selection, keys, or pointer move. Built-in\n * ids and consumer-supplied {@link ToolbarCustomItem}s render through the same\n * button path.\n */\nexport function Toolbar({ view, items, icons, disabled, sticky, stickyVisibility }: ToolbarProps) {\n const [, refresh] = useReducer((n: number) => n + 1, 0)\n const [focused, setFocused] = useState(false)\n // Only \"bottom\" needs keyboard tracking — nothing eats into the top of the\n // screen the way a keyboard eats the bottom.\n const keyboardInset = useKeyboardInset(sticky === \"bottom\")\n const barRef = useRef<HTMLDivElement>(null)\n useFloatingWatchdog(barRef, sticky === \"top\")\n\n useEffect(() => {\n if (!view) return\n const el = view.contentDOM\n setFocused(view.hasFocus)\n const onFocus = () => {\n setFocused(true)\n refresh()\n }\n const onBlur = () => {\n setFocused(false)\n refresh()\n }\n const events = [\"keyup\", \"mouseup\", \"input\"] as const\n for (const ev of events) el.addEventListener(ev, refresh)\n el.addEventListener(\"focus\", onFocus)\n el.addEventListener(\"blur\", onBlur)\n return () => {\n for (const ev of events) el.removeEventListener(ev, refresh)\n el.removeEventListener(\"focus\", onFocus)\n el.removeEventListener(\"blur\", onBlur)\n }\n }, [view])\n\n // \"dynamic\" fades the bar out while nothing is focused, so it doesn't sit\n // over the content while the caret is elsewhere (e.g. scrolling to read).\n const dynamicHidden = Boolean(sticky) && stickyVisibility === \"dynamic\" && !focused\n\n const className = [\n styles.toolbar,\n sticky && styles.toolbarSticky,\n sticky === \"top\" && styles.toolbarStickyTop,\n sticky === \"bottom\" && styles.toolbarStickyBottom,\n dynamicHidden && styles.toolbarStickyHidden,\n ]\n .filter(Boolean)\n .join(\" \")\n // `transform`, not `bottom` — a `position: fixed` element repositioned via\n // `bottom` doesn't reliably repaint in step with the keyboard animation on\n // iOS Safari; `translateY` forces a compositor update on every\n // `visualViewport` event instead. The bar's resting position (keyboard\n // closed) is `bottom: 0` in CSS; this only nudges it up. `\"top\"` needs no\n // offset at all.\n const stickyStyle =\n sticky === \"bottom\" ? { transform: `translateY(-${keyboardInset}px)` } : undefined\n\n return (\n <div\n ref={barRef}\n className={className}\n role=\"toolbar\"\n aria-label=\"Formatting\"\n aria-hidden={dynamicHidden || undefined}\n style={stickyStyle}\n >\n {items.map((item, i) => {\n if (item === \"|\") {\n return <span key={`sep-${i}`} className={styles.toolbarSep} aria-hidden=\"true\" />\n }\n const btn = toButton(item, icons)\n if (!btn) return null\n const off = disabled || !view || Boolean(btn.disabled?.(view.state))\n const active = Boolean(!off && view && btn.isActive?.(view.state))\n return (\n <button\n key={btn.key}\n type=\"button\"\n className={styles.toolbarButton}\n data-command={btn.key}\n title={btn.title}\n aria-label={btn.title}\n aria-pressed={btn.isActive ? active : undefined}\n data-active={active ? \"\" : undefined}\n disabled={off}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n if (!view) return\n btn.run(view)\n refresh()\n }}\n >\n {btn.icon}\n </button>\n )\n })}\n </div>\n )\n}\n","import { forwardRef, Suspense, useEffect, useImperativeHandle, useRef, useState } from \"react\"\nimport { EditorView } from \"@codemirror/view\"\nimport { SourceView } from \"./editor/SourceView\"\nimport { LazyInPlaceView } from \"./inplace/lazyInPlace\"\nimport { LazyPreview } from \"./render/lazyPreview\"\nimport { SplitView } from \"./SplitView\"\nimport styles from \"./styles/stylo.module.css\"\nimport { Toolbar } from \"./toolbar/Toolbar\"\nimport { resolveToolbarItems } from \"./toolbar/config\"\nimport { splitFrontmatter } from \"./frontmatter\"\nimport \"./styles/tokens.css\"\nimport type { StyloHandle, StyloProps } from \"./types\"\n\n/**\n * Plain-text-first Markdown editor. `value` is the canonical Markdown string;\n * every view is a pure function of it.\n *\n * The default `mode` is `in-place`, the live decoration canvas (ADR-002 §1,\n * ADR-004). Pass `mode=\"source\"` for the plain surface with no lazy render\n * chunk. `preview` and `split` are also available.\n *\n * A formatting `toolbar` sits above every editing surface (all modes but\n * `preview`); pass `toolbar={false}` to drop it or a `ToolbarConfig` to trim it.\n */\nexport const Stylo = forwardRef<StyloHandle, StyloProps>(function Stylo(\n {\n value,\n onChange,\n mode = \"in-place\",\n onSave,\n onFrontmatter,\n onWikiLinkClick,\n onLinkClick,\n readOnly,\n placeholder,\n className,\n inPlace,\n codeLanguages,\n toolbar,\n icons,\n frontmatter,\n },\n ref,\n) {\n const resolved = mode === \"preview\" || mode === \"split\" || mode === \"in-place\" ? mode : \"source\"\n\n const [view, setView] = useState<EditorView | null>(null)\n const toolbarItems = resolved === \"preview\" ? null : resolveToolbarItems(toolbar)\n const toolbarRender = toolbar && typeof toolbar === \"object\" ? toolbar.render : undefined\n const stickyConfig = toolbar && typeof toolbar === \"object\" ? toolbar.sticky : undefined\n const stickyToolbar: \"top\" | \"bottom\" | false =\n stickyConfig === \"top\" ? \"top\" : stickyConfig ? \"bottom\" : false\n const stickyVisibility =\n toolbar && typeof toolbar === \"object\" ? toolbar.stickyVisibility : undefined\n\n // Report the raw frontmatter block on mount and whenever it changes. Stylo\n // does not parse it — the host passes `raw` to its own YAML parser.\n const onFrontmatterRef = useRef(onFrontmatter)\n onFrontmatterRef.current = onFrontmatter\n const lastFrontmatter = useRef<string | null | undefined>(undefined)\n useEffect(() => {\n const raw = splitFrontmatter(value)?.frontmatter ?? null\n if (raw === lastFrontmatter.current) return\n lastFrontmatter.current = raw\n onFrontmatterRef.current?.(raw)\n }, [value])\n\n useImperativeHandle(\n ref,\n () => ({\n focus: () => view?.focus(),\n getView: () => view,\n insertAtCursor: (md) => {\n if (!view) return\n view.dispatch(view.state.replaceSelection(md))\n view.focus()\n },\n scrollToHeading: (text) => {\n if (!view) return false\n const target = text.trim().toLowerCase()\n const { doc } = view.state\n for (let n = 1; n <= doc.lines; n++) {\n const line = doc.line(n)\n const m = /^ {0,3}#{1,6}[ \\t]+(.*?)(?:[ \\t]+#+)?[ \\t]*$/.exec(line.text)\n if (!m || m[1] === undefined) continue\n if (m[1].trim().toLowerCase() !== target) continue\n view.dispatch({\n selection: { anchor: line.from },\n effects: EditorView.scrollIntoView(line.from, { y: \"start\" }),\n })\n view.focus()\n return true\n }\n return false\n },\n }),\n [view],\n )\n\n const rootClass = [\n styles.root,\n stickyToolbar === \"bottom\" && styles.stickyToolbarRootBottom,\n stickyToolbar === \"top\" && styles.stickyToolbarRootTop,\n \"stylo\",\n className,\n ]\n .filter(Boolean)\n .join(\" \")\n\n return (\n <div className={rootClass} data-stylo-mode={resolved}>\n {toolbarItems &&\n (() => {\n const bar = (\n <Toolbar\n view={view}\n items={toolbarItems}\n icons={icons}\n disabled={readOnly}\n sticky={stickyToolbar}\n stickyVisibility={stickyVisibility}\n />\n )\n return toolbarRender ? toolbarRender(bar, { view }) : bar\n })()}\n\n {resolved === \"source\" && (\n <SourceView\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n placeholder={placeholder}\n codeLanguages={codeLanguages}\n onSave={onSave}\n onViewChange={setView}\n />\n )}\n\n {resolved === \"preview\" && (\n <Suspense fallback={<div className={styles.preview} aria-busy=\"true\" />}>\n <LazyPreview value={value} onWikiLinkClick={onWikiLinkClick} frontmatter={frontmatter} />\n </Suspense>\n )}\n\n {resolved === \"split\" && (\n <SplitView\n value={value}\n onChange={onChange}\n onWikiLinkClick={onWikiLinkClick}\n readOnly={readOnly}\n placeholder={placeholder}\n codeLanguages={codeLanguages}\n frontmatter={frontmatter}\n onSave={onSave}\n onViewChange={setView}\n />\n )}\n\n {resolved === \"in-place\" && (\n <Suspense fallback={<div className={styles.inplace} aria-busy=\"true\" />}>\n <LazyInPlaceView\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n placeholder={placeholder}\n onWikiLinkClick={onWikiLinkClick}\n onLinkClick={onLinkClick}\n inPlace={inPlace}\n codeLanguages={codeLanguages}\n onSave={onSave}\n onViewChange={setView}\n />\n </Suspense>\n )}\n </div>\n )\n})\n\nStylo.displayName = \"Stylo\"\n"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { ReactNode } from "react";
2
+ import type { EditorView } from "@codemirror/view";
3
+ import type { ToolbarCommandId } from "../types";
4
+ import type { ToolbarItem } from "./config";
5
+ export interface ToolbarProps {
6
+ /** The surface the commands act on. `null` while a lazy view is mounting. */
7
+ view: EditorView | null;
8
+ /** Ordered items to render; `"|"` is a separator. */
9
+ items: ToolbarItem[];
10
+ /** Per-id glyph overrides; any id left out keeps its built-in icon. */
11
+ icons?: Partial<Record<ToolbarCommandId, ReactNode>>;
12
+ /** Render every button inert (e.g. a read-only surface). */
13
+ disabled?: boolean;
14
+ /** Fix the bar to a window edge (normalised from `ToolbarConfig.sticky`; the
15
+ * caller resolves `true` to `"bottom"`). */
16
+ sticky?: "top" | "bottom" | false;
17
+ /** Fade the bar out while the editing surface is unfocused (`ToolbarConfig.stickyVisibility`). */
18
+ stickyVisibility?: "consistent" | "dynamic";
19
+ }
20
+ /**
21
+ * Formatting bar above the editing surface. It holds no document state — each
22
+ * button runs a command against the live `EditorView`. Pressed states are read
23
+ * back from the view whenever the selection, keys, or pointer move. Built-in
24
+ * ids and consumer-supplied {@link ToolbarCustomItem}s render through the same
25
+ * button path.
26
+ */
27
+ export declare function Toolbar({ view, items, icons, disabled, sticky, stickyVisibility }: ToolbarProps): import("react").JSX.Element;
28
+ //# sourceMappingURL=Toolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/toolbar/Toolbar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAK3C,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;IACvB,qDAAqD;IACrD,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAA;IACpD,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;iDAC6C;IAC7C,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,KAAK,CAAA;IACjC,kGAAkG;IAClG,gBAAgB,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CAC5C;AA8BD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE,YAAY,+BA+F/F"}
@@ -0,0 +1,56 @@
1
+ import type { EditorState } from "@codemirror/state";
2
+ import type { EditorView } from "@codemirror/view";
3
+ /** The 1-based line numbers the primary selection touches. */
4
+ export declare function selectedLines(state: EditorState): [number, number];
5
+ /**
6
+ * Set, swap, or clear an ATX heading prefix on every line the selection touches.
7
+ * A line already at `level` is cleared; any other level (or none) is set to it.
8
+ */
9
+ export declare function toggleHeading(view: EditorView, level: number): boolean;
10
+ /** Strip any ATX heading prefix from every line the selection touches — the
11
+ * explicit "back to body text" that toggling a heading level does obliquely. */
12
+ export declare function clearHeading(view: EditorView): boolean;
13
+ export interface LinePrefixSpec {
14
+ /** Detects this exact prefix; `match[0]` (from line start) is what gets stripped. */
15
+ match: RegExp;
16
+ /**
17
+ * Inserted after any indent when the prefix is absent. A function receives the
18
+ * 0-based position among the lines being prefixed — used to number an ordered
19
+ * list `1.`, `2.`, `3.` rather than stamping `1.` on every line.
20
+ */
21
+ insert: string | ((n: number) => string);
22
+ /**
23
+ * When set, an existing *sibling* prefix matched here (another list marker) is
24
+ * overwritten rather than stacked, so the list buttons stay mutually
25
+ * exclusive. `siblings[1]` is the indent, `siblings[0]` the marker to replace.
26
+ */
27
+ siblings?: RegExp;
28
+ }
29
+ /**
30
+ * Toggle a Markdown line prefix (`> `, `- `, `1. `, `- [ ] `) over the selected
31
+ * lines. If every non-blank line already carries it the block is stripped;
32
+ * otherwise it is added to the lines that lack it — replacing a sibling list
33
+ * marker in place when `spec.siblings` says to.
34
+ */
35
+ export declare function toggleLinePrefix(view: EditorView, spec: LinePrefixSpec): boolean;
36
+ /** True when the primary caret's line carries the prefix. */
37
+ export declare function linePrefixActive(state: EditorState, match: RegExp): boolean;
38
+ /**
39
+ * Insert a `---` thematic break, or remove the one the caret is on. A blank line
40
+ * is inserted before the rule when the current line has content — otherwise
41
+ * CommonMark reads `text` + `---` as a setext H2, not a divider.
42
+ */
43
+ export declare function toggleHorizontalRule(view: EditorView): boolean;
44
+ /** True when the primary caret's line is a thematic break. */
45
+ export declare function horizontalRuleActive(state: EditorState): boolean;
46
+ /**
47
+ * Toggle the leading `---` YAML block. With none present, the top of the
48
+ * document — line 1 through the last selected line — is wrapped in `---` fences
49
+ * (so the "type it, select it, click" flow works). With one present, only the
50
+ * two fence lines are removed; the YAML text stays in the document. Keeping it
51
+ * out of rendered output is the preview pipeline's job, not this toggle's.
52
+ */
53
+ export declare function toggleFrontmatter(view: EditorView): boolean;
54
+ /** True when the document has a leading `---` YAML block. */
55
+ export declare function frontmatterActive(state: EditorState): boolean;
56
+ //# sourceMappingURL=block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block.d.ts","sourceRoot":"","sources":["../../src/toolbar/block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAGlD,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAmBtE;AAED;iFACiF;AACjF,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAatD;AAED,MAAM,WAAW,cAAc;IAC7B,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;IACxC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAsChF;AAED,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE3E;AAKD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAyB9D;AAED,8DAA8D;AAC9D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAEhE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CA2B3D;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAE7D"}
@@ -0,0 +1,16 @@
1
+ import type { EditorView } from "@codemirror/view";
2
+ import { type InlineStr } from "./inline-ops";
3
+ /** Rewrites a cell's text + selection given the current text and selection span. */
4
+ type Build = (text: string, from: number, to: number) => InlineStr;
5
+ /** The focused in-place table cell within `view`'s DOM, or `null`. */
6
+ export declare function activeTableCell(view: EditorView): HTMLElement | null;
7
+ /** If the caret is in a table cell, apply `build` there and report it handled. */
8
+ export declare function runInlineInCell(view: EditorView, build: Build): boolean;
9
+ /**
10
+ * Handle a formatting shortcut (`Mod-b` / `Mod-i` / `Mod-u` / `Mod-k` /
11
+ * `Mod-Shift-k`) typed inside `cell`. The widget calls this because
12
+ * `ignoreEvent()` keeps such keydowns from ever reaching CodeMirror's keymap.
13
+ */
14
+ export declare function handleCellShortcut(event: KeyboardEvent, cell: HTMLElement): boolean;
15
+ export {};
16
+ //# sourceMappingURL=cell-inline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cell-inline.d.ts","sourceRoot":"","sources":["../../src/toolbar/cell-inline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,OAAO,EACL,KAAK,SAAS,EAKf,MAAM,cAAc,CAAA;AAErB,oFAAoF;AACpF,KAAK,KAAK,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,SAAS,CAAA;AAElE,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CAIpE;AAgBD,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAKvE;AAWD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAOnF"}
@@ -0,0 +1,25 @@
1
+ import type { EditorState } from "@codemirror/state";
2
+ import type { EditorView } from "@codemirror/view";
3
+ import type { ToolbarCommandId } from "../types";
4
+ export interface ToolbarCommand {
5
+ id: ToolbarCommandId;
6
+ /** Tooltip and accessible label. */
7
+ title: string;
8
+ /** Mutate the document against the live view; returns true when handled. */
9
+ run: (view: EditorView) => boolean;
10
+ /** Reflected as the button's pressed state. */
11
+ isActive?: (state: EditorState) => boolean;
12
+ /**
13
+ * True when the command can't produce valid Markdown at the current selection
14
+ * — the button is rendered `disabled` and the shortcut is a no-op. Driven by
15
+ * the context predicates above (a table cell, a heading line, a frontmatter /
16
+ * fenced-code / `$$` block).
17
+ */
18
+ disabled?: (state: EditorState) => boolean;
19
+ /** Default key bindings in CodeMirror `key` syntax. */
20
+ keys?: string[];
21
+ }
22
+ /** Every built-in command, in a stable order. The toolbar picks from these by id. */
23
+ export declare const BUILTIN_COMMANDS: ToolbarCommand[];
24
+ export declare const BUILTIN_BY_ID: Record<string, ToolbarCommand>;
25
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/toolbar/commands.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAoEhD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,gBAAgB,CAAA;IACpB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,GAAG,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAA;IAClC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAyED,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAgI5C,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAExD,CAAA"}
@@ -0,0 +1,21 @@
1
+ import type { ToolbarCommandId, ToolbarConfig, ToolbarItem } from "../types";
2
+ /**
3
+ * An entry in the rendered bar: a built-in command id, `"|"` for a separator,
4
+ * or a consumer-supplied {@link ToolbarCustomItem}. Defined in `types.ts`;
5
+ * re-exported here because the toolbar internals are the main consumers.
6
+ */
7
+ export type { ToolbarItem };
8
+ /**
9
+ * The full built-in bar, in display order. Grouped by kind: history, headings,
10
+ * inline text (+ link), the three list markers, block structure, then code and
11
+ * math together. Built-in ids only — a consumer adds custom items through the
12
+ * `toolbar` prop.
13
+ */
14
+ export declare const DEFAULT_TOOLBAR_ITEMS: (ToolbarCommandId | "|")[];
15
+ /**
16
+ * Resolve the `toolbar` prop to the list to render:
17
+ * `undefined` / `true` → the default set, `false` → no bar (`null`), an object →
18
+ * its `items` or the default.
19
+ */
20
+ export declare function resolveToolbarItems(config: boolean | ToolbarConfig | undefined): ToolbarItem[] | null;
21
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/toolbar/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE5E;;;;GAIG;AACH,YAAY,EAAE,WAAW,EAAE,CAAA;AAE3B;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,CAAC,gBAAgB,GAAG,GAAG,CAAC,EA2B3D,CAAA;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,GAC1C,WAAW,EAAE,GAAG,IAAI,CAItB"}
@@ -0,0 +1,28 @@
1
+ import type { EditorState } from "@codemirror/state";
2
+ import type { EditorView } from "@codemirror/view";
3
+ /**
4
+ * Wrap the selected lines in a ```` ``` ```` fence, or unwrap the block when the
5
+ * caret already sits inside one.
6
+ */
7
+ export declare function toggleFencedCode(view: EditorView): boolean;
8
+ /** True when the primary caret is inside a fenced code block. */
9
+ export declare function fencedCodeActive(state: EditorState): boolean;
10
+ /**
11
+ * The info-string (language) span on the opening fence of the block at the
12
+ * caret, plus its current text. `null` when the caret is not in a fence. The
13
+ * opening line may be hidden in the seamless canvas, but its text is still in
14
+ * the document, so this stays editable through the menu.
15
+ */
16
+ export declare function fenceInfoAt(state: EditorState): {
17
+ from: number;
18
+ to: number;
19
+ lang: string;
20
+ } | null;
21
+ /**
22
+ * Wrap the selected lines in a `$$` … `$$` block, or unwrap the block the caret
23
+ * already sits in.
24
+ */
25
+ export declare function toggleMathBlock(view: EditorView): boolean;
26
+ /** True when the primary caret is inside a `$$` math block. */
27
+ export declare function mathBlockActive(state: EditorState): boolean;
28
+ //# sourceMappingURL=fence.d.ts.map