@damiro/stylo 0.7.0 → 0.9.1
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.
- package/README.md +41 -14
- package/dist/{InPlaceView-CS1qy-Ba.js → InPlaceView-Bu91_3Ig.js} +1199 -855
- package/dist/InPlaceView-Bu91_3Ig.js.map +1 -0
- package/dist/Preview-AaGGwLQP.js +170 -0
- package/dist/Preview-AaGGwLQP.js.map +1 -0
- package/dist/SplitView.d.ts +4 -2
- package/dist/SplitView.d.ts.map +1 -1
- package/dist/Stylo.d.ts.map +1 -1
- package/dist/editor/SourceView.d.ts +4 -2
- package/dist/editor/SourceView.d.ts.map +1 -1
- package/dist/editor/extensions.d.ts +2 -2
- package/dist/editor/extensions.d.ts.map +1 -1
- package/dist/editor/useCodeMirror.d.ts +4 -2
- package/dist/editor/useCodeMirror.d.ts.map +1 -1
- package/dist/editor/wikilink-complete.d.ts +5 -3
- package/dist/editor/wikilink-complete.d.ts.map +1 -1
- package/dist/embed-B2Dfkpd_.js +124 -0
- package/dist/embed-B2Dfkpd_.js.map +1 -0
- package/dist/embed.d.ts +14 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/frontmatter-DGUkb9z-.js +39 -0
- package/dist/frontmatter-DGUkb9z-.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/inplace/InPlaceView.d.ts +11 -2
- package/dist/inplace/InPlaceView.d.ts.map +1 -1
- package/dist/inplace/config.d.ts +8 -0
- package/dist/inplace/config.d.ts.map +1 -1
- package/dist/inplace/context-menu-actions.d.ts +5 -14
- package/dist/inplace/context-menu-actions.d.ts.map +1 -1
- package/dist/inplace/context-menu-helpers.d.ts +30 -0
- package/dist/inplace/context-menu-helpers.d.ts.map +1 -0
- package/dist/inplace/context-menu-shell.d.ts +23 -0
- package/dist/inplace/context-menu-shell.d.ts.map +1 -0
- package/dist/inplace/context-menu-types.d.ts +46 -0
- package/dist/inplace/context-menu-types.d.ts.map +1 -0
- package/dist/inplace/context-menu.d.ts +2 -42
- package/dist/inplace/context-menu.d.ts.map +1 -1
- package/dist/inplace/decorate.d.ts.map +1 -1
- package/dist/inplace/edit-boundaries.d.ts +1 -25
- package/dist/inplace/edit-boundaries.d.ts.map +1 -1
- package/dist/inplace/edit-divider.d.ts +27 -0
- package/dist/inplace/edit-divider.d.ts.map +1 -0
- package/dist/inplace/embed-registry.d.ts +39 -0
- package/dist/inplace/embed-registry.d.ts.map +1 -0
- package/dist/inplace/embed.d.ts +8 -0
- package/dist/inplace/embed.d.ts.map +1 -0
- package/dist/inplace/extension.d.ts +6 -0
- package/dist/inplace/extension.d.ts.map +1 -1
- package/dist/inplace/inline-md.d.ts +6 -1
- package/dist/inplace/inline-md.d.ts.map +1 -1
- package/dist/inplace/link-row.d.ts +21 -0
- package/dist/inplace/link-row.d.ts.map +1 -0
- package/dist/inplace/math-edit.d.ts +36 -0
- package/dist/inplace/math-edit.d.ts.map +1 -0
- package/dist/inplace/math.d.ts +12 -0
- package/dist/inplace/math.d.ts.map +1 -1
- package/dist/inplace/menu-plugin.d.ts.map +1 -1
- package/dist/inplace/nodes-blocks.d.ts +14 -0
- package/dist/inplace/nodes-blocks.d.ts.map +1 -0
- package/dist/inplace/nodes-inline.d.ts +16 -0
- package/dist/inplace/nodes-inline.d.ts.map +1 -0
- package/dist/inplace/nodes.d.ts +3 -2
- package/dist/inplace/nodes.d.ts.map +1 -1
- package/dist/inplace/scan.d.ts +6 -0
- package/dist/inplace/scan.d.ts.map +1 -1
- package/dist/inplace/selection-bar-position.d.ts +31 -0
- package/dist/inplace/selection-bar-position.d.ts.map +1 -0
- package/dist/inplace/selection-bar.d.ts +0 -2
- package/dist/inplace/selection-bar.d.ts.map +1 -1
- package/dist/inplace/table-widget-caret.d.ts +11 -0
- package/dist/inplace/table-widget-caret.d.ts.map +1 -0
- package/dist/inplace/table-widget-render.d.ts +18 -0
- package/dist/inplace/table-widget-render.d.ts.map +1 -0
- package/dist/inplace/table-widget.d.ts +5 -2
- package/dist/inplace/table-widget.d.ts.map +1 -1
- package/dist/inplace/tables.d.ts.map +1 -1
- package/dist/inplace/theme-callout.d.ts +50 -0
- package/dist/inplace/theme-callout.d.ts.map +1 -0
- package/dist/inplace/theme-canvas.d.ts +166 -0
- package/dist/inplace/theme-canvas.d.ts.map +1 -0
- package/dist/inplace/theme-fonts.d.ts +4 -0
- package/dist/inplace/theme-fonts.d.ts.map +1 -0
- package/dist/inplace/theme-menu.d.ts +87 -0
- package/dist/inplace/theme-menu.d.ts.map +1 -0
- package/dist/inplace/theme-popups.d.ts +94 -0
- package/dist/inplace/theme-popups.d.ts.map +1 -0
- package/dist/inplace/theme-table-gizmos.d.ts +57 -0
- package/dist/inplace/theme-table-gizmos.d.ts.map +1 -0
- package/dist/inplace/theme-table.d.ts +30 -0
- package/dist/inplace/theme-table.d.ts.map +1 -0
- package/dist/inplace/theme.d.ts +5 -0
- package/dist/inplace/theme.d.ts.map +1 -1
- package/dist/inplace/wikilinks.d.ts.map +1 -1
- package/dist/inplace/wrap-at.d.ts +32 -0
- package/dist/inplace/wrap-at.d.ts.map +1 -0
- package/dist/{markdown-N3lRmU2n.js → markdown-BYXs9Tj8.js} +2 -2
- package/dist/{markdown-N3lRmU2n.js.map → markdown-BYXs9Tj8.js.map} +1 -1
- package/dist/render/Embed.d.ts +24 -0
- package/dist/render/Embed.d.ts.map +1 -0
- package/dist/render/Preview.d.ts +6 -2
- package/dist/render/Preview.d.ts.map +1 -1
- package/dist/render/embed-cache.d.ts +10 -0
- package/dist/render/embed-cache.d.ts.map +1 -0
- package/dist/render/remark-embed.d.ts +19 -0
- package/dist/render/remark-embed.d.ts.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/stylo.js +70 -54
- package/dist/stylo.js.map +1 -1
- package/dist/toolbar/block.d.ts +0 -26
- package/dist/toolbar/block.d.ts.map +1 -1
- package/dist/toolbar/command-helpers.d.ts +57 -0
- package/dist/toolbar/command-helpers.d.ts.map +1 -0
- package/dist/toolbar/commands.d.ts +1 -21
- package/dist/toolbar/commands.d.ts.map +1 -1
- package/dist/toolbar/frontmatter-toggle.d.ts +13 -0
- package/dist/toolbar/frontmatter-toggle.d.ts.map +1 -0
- package/dist/toolbar/heading.d.ts +10 -0
- package/dist/toolbar/heading.d.ts.map +1 -0
- package/dist/toolbar/inline-ops.d.ts +3 -53
- package/dist/toolbar/inline-ops.d.ts.map +1 -1
- package/dist/toolbar/link-ops.d.ts +22 -0
- package/dist/toolbar/link-ops.d.ts.map +1 -0
- package/dist/toolbar/rule.d.ts +11 -0
- package/dist/toolbar/rule.d.ts.map +1 -0
- package/dist/toolbar/table-position.d.ts +32 -0
- package/dist/toolbar/table-position.d.ts.map +1 -0
- package/dist/toolbar/table.d.ts +1 -16
- package/dist/toolbar/table.d.ts.map +1 -1
- package/dist/toolbar/underline-ops.d.ts +15 -0
- package/dist/toolbar/underline-ops.d.ts.map +1 -0
- package/dist/toolbar/wikilink-ops.d.ts +22 -0
- package/dist/toolbar/wikilink-ops.d.ts.map +1 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/{useCodeMirror-GPSkaYW6.js → useCodeMirror-DtwupFgc.js} +559 -531
- package/dist/useCodeMirror-DtwupFgc.js.map +1 -0
- package/package.json +2 -1
- package/dist/InPlaceView-CS1qy-Ba.js.map +0 -1
- package/dist/Preview-Bw0k3Mev.js +0 -93
- package/dist/Preview-Bw0k3Mev.js.map +0 -1
- package/dist/callout-oLE630ZP.js +0 -37
- package/dist/callout-oLE630ZP.js.map +0 -1
- package/dist/useCodeMirror-GPSkaYW6.js.map +0 -1
- package/dist/wikilink-CTR8IFKc.js +0 -42
- package/dist/wikilink-CTR8IFKc.js.map +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { EmbedSource, ResolveErrorInfo } from "../types";
|
|
2
|
+
interface EmbedProps {
|
|
3
|
+
/** The raw `![[ref]]` reference, trimmed. */
|
|
4
|
+
reference: string;
|
|
5
|
+
source: EmbedSource;
|
|
6
|
+
/** Notified if `source` rejects; the literal fallback still renders. */
|
|
7
|
+
onError?: (error: unknown, info: ResolveErrorInfo) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Render as phrasing content in a `<span>` (a `![[…]]` mid-sentence) rather
|
|
10
|
+
* than the default block `<div>`. The host node should be inline in this mode.
|
|
11
|
+
*/
|
|
12
|
+
inline?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Renders one `![[ref]]` embed. Resolves the reference through `embedSource` —
|
|
16
|
+
* via a shared cache (`embed-cache.ts`), so an embed re-mounted by a scroll or a
|
|
17
|
+
* `preview` re-render is not re-fetched — and drops the result into
|
|
18
|
+
* `<div class="stylo-embed-content">`, or a `<span>` when `inline`. While it
|
|
19
|
+
* resolves — and if it rejects, or resolves to `null` — the literal `![[ref]]`
|
|
20
|
+
* text stands in, so a reference is never silently lost.
|
|
21
|
+
*/
|
|
22
|
+
export declare function Embed({ reference, source, onError, inline }: EmbedProps): import("react").JSX.Element;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=Embed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Embed.d.ts","sourceRoot":"","sources":["../../src/render/Embed.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAG7D,UAAU,UAAU;IAClB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,WAAW,CAAA;IACnB,wEAAwE;IACxE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAC1D;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAID;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,UAAU,+BAoCvE"}
|
package/dist/render/Preview.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import type { FrontmatterDisplay } from "../types";
|
|
1
|
+
import type { EmbedSource, FrontmatterDisplay, ResolveErrorInfo } from "../types";
|
|
2
2
|
export interface PreviewProps {
|
|
3
3
|
value: string;
|
|
4
4
|
onWikiLinkClick?: (target: string) => void;
|
|
5
|
+
/** Resolves `![[ref]]` embeds. Omit and `![[…]]` stays literal. */
|
|
6
|
+
embedSource?: EmbedSource;
|
|
7
|
+
/** Notified when `embedSource` rejects; the literal fallback still renders. */
|
|
8
|
+
onResolveError?: (error: unknown, info: ResolveErrorInfo) => void;
|
|
5
9
|
/** `"code"` renders the `---` block as a styled `<pre>`; `"hidden"` (default) drops it. */
|
|
6
10
|
frontmatter?: FrontmatterDisplay;
|
|
7
11
|
}
|
|
8
12
|
/** Rendered Markdown + KaTeX view. A pure function of the string. */
|
|
9
|
-
export declare function Preview({ value, onWikiLinkClick, frontmatter }: PreviewProps): import("react").JSX.Element;
|
|
13
|
+
export declare function Preview({ value, onWikiLinkClick, embedSource, onResolveError, frontmatter, }: PreviewProps): import("react").JSX.Element;
|
|
10
14
|
//# sourceMappingURL=Preview.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Preview.d.ts","sourceRoot":"","sources":["../../src/render/Preview.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Preview.d.ts","sourceRoot":"","sources":["../../src/render/Preview.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAQjF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,mEAAmE;IACnE,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAA;IACjE,2FAA2F;IAC3F,WAAW,CAAC,EAAE,kBAAkB,CAAA;CACjC;AAED,qEAAqE;AACrE,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,eAAe,EACf,WAAW,EACX,cAAc,EACd,WAAsB,GACvB,EAAE,YAAY,+BA6Ed"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { EmbedSource } from "../types";
|
|
3
|
+
/** A settled result for `ref`, or `undefined` while it is unresolved or absent. */
|
|
4
|
+
export declare function peekEmbed(source: EmbedSource, ref: string): {
|
|
5
|
+
node: ReactNode;
|
|
6
|
+
} | undefined;
|
|
7
|
+
/** Resolve `ref` through the cache — reusing an in-flight or settled entry, or
|
|
8
|
+
* calling `source` on a miss. */
|
|
9
|
+
export declare function resolveEmbed(source: EmbedSource, ref: string): Promise<ReactNode>;
|
|
10
|
+
//# sourceMappingURL=embed-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed-cache.d.ts","sourceRoot":"","sources":["../../src/render/embed-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAwC3C,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG,SAAS,CAG3F;AAED;kCACkC;AAClC,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA2BjF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Root } from "mdast";
|
|
2
|
+
/**
|
|
3
|
+
* Turns `![[ref]]` transclusion syntax into empty carrier elements the `Embed`
|
|
4
|
+
* component (wired in `Preview`) fills from the host's `embedSource`.
|
|
5
|
+
*
|
|
6
|
+
* - A **lone** `![[ref]]` — the whole paragraph, trimmed — becomes a block
|
|
7
|
+
* `<div class="stylo-embed" data-stylo-embed="ref">`.
|
|
8
|
+
* - A `![[ref]]` **inside other text** becomes an inline
|
|
9
|
+
* `<span class="stylo-embed" data-stylo-embed-inline="ref">`, and the
|
|
10
|
+
* surrounding text is preserved around it. The host node should be phrasing
|
|
11
|
+
* content in this case.
|
|
12
|
+
*
|
|
13
|
+
* `![[…]]` inside inline or fenced code is left literal (it is not a `text`
|
|
14
|
+
* node). Must run before `remarkWikilink`, or the inner `[[ref]]` is rewritten
|
|
15
|
+
* to a link first. Off unless the consumer passes `embedSource` — `Preview` only
|
|
16
|
+
* adds this plugin then.
|
|
17
|
+
*/
|
|
18
|
+
export declare function remarkEmbed(): (tree: Root) => void;
|
|
19
|
+
//# sourceMappingURL=remark-embed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remark-embed.d.ts","sourceRoot":"","sources":["../../src/render/remark-embed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8B,IAAI,EAAQ,MAAM,OAAO,CAAA;AAInE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,WACX,IAAI,UA+CnB"}
|
package/dist/styles.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
._root_12qra_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_12qra_1 .cm-scroller{scrollbar-width:thin;scrollbar-color:color-mix(in srgb, var(--stylo-border) 90%, transparent) transparent}._root_12qra_1 .cm-scroller::-webkit-scrollbar{width:10px;height:10px}._root_12qra_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_12qra_1 .cm-scroller::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb, var(--stylo-text-muted) 55%, transparent);background-clip:padding-box}._root_12qra_1 .cm-scroller::-webkit-scrollbar-corner{background:0 0}._source_12qra_42{flex:auto;min-height:0;overflow:auto}._source_12qra_42 .cm-editor{height:100%}._source_12qra_42 .cm-editor.cm-focused{outline:2px solid var(--stylo-ring);outline-offset:-2px}._inplace_12qra_57{flex:auto;min-height:0;overflow:auto}._toolbar_12qra_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_12qra_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_12qra_73:hover:not(:disabled){background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._toolbarButton_12qra_73[data-active]{background:color-mix(in srgb, var(--stylo-accent) 12%, transparent);color:var(--stylo-text)}._toolbarButton_12qra_73:disabled{opacity:.4;cursor:default}._toolbarButton_12qra_73:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-2px}._toolbarButton_12qra_73 svg{width:16px;height:16px;display:block}._toolbarButton_12qra_73 code{font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);font-size:.8125rem;font-weight:500}._toolbarSep_12qra_132{background:var(--stylo-border);align-self:stretch;width:1px;margin:4px 3px}._toolbarSticky_12qra_142{z-index:50;-webkit-overflow-scrolling:touch;scrollbar-width:none;font-family:var(--stylo-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);flex-wrap:nowrap;transition:opacity .15s;overflow-x:auto}._toolbarSticky_12qra_142::-webkit-scrollbar{display:none}._toolbarSticky_12qra_142 ._toolbarButton_12qra_73{flex:none;min-width:40px;height:40px}._toolbarStickyBottom_12qra_142{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_12qra_142{box-shadow:0 2px 8px color-mix(in srgb, var(--stylo-text) 10%, transparent);position:fixed;top:0;left:0;right:0}._toolbarStickyHidden_12qra_211{opacity:0;pointer-events:none}._stickyToolbarRootBottom_12qra_222 ._source_12qra_42,._stickyToolbarRootBottom_12qra_222 ._inplace_12qra_57{padding-bottom:52px}._stickyToolbarRootTop_12qra_227 ._source_12qra_42,._stickyToolbarRootTop_12qra_227 ._inplace_12qra_57{padding-top:52px}._inplace_12qra_57 .cm-editor{height:100%}._inplace_12qra_57 .cm-editor.cm-focused{outline:2px solid var(--stylo-ring);outline-offset:-2px}._split_12qra_241{flex:1;grid-template-columns:1fr 1fr;min-height:0;display:grid}._splitPane_12qra_248{box-sizing:border-box;flex-direction:column;min-height:0;display:flex;overflow:auto}._splitPane_12qra_248+._splitPane_12qra_248{border-left:1px solid var(--stylo-border)}._splitPane_12qra_248>._source_12qra_42{flex:1;min-height:0}._preview_12qra_268{overflow-wrap:break-word;padding:.75rem 1rem;font-size:.9375rem;line-height:1.75}._preview_12qra_268>:first-child{margin-top:0}._preview_12qra_268>:last-child{margin-bottom:0}._preview_12qra_268 :is(h1,h2,h3,h4,h5,h6){font-weight:600}._preview_12qra_268 h1{margin:0 0 .888889em;font-size:2.25em;line-height:1.11111}._preview_12qra_268 h2{margin:2em 0 1em;font-size:1.5em;line-height:1.33333}._preview_12qra_268 h3{margin:1.6em 0 .6em;font-size:1.25em;line-height:1.6}._preview_12qra_268 :is(h4,h5,h6){margin:1.5em 0 .5em;line-height:1.5}._preview_12qra_268 p{margin:1.25em 0}._preview_12qra_268 :is(ul,ol){margin:1.25em 0;padding-left:1.625em}._preview_12qra_268 li{margin:.5em 0}._preview_12qra_268 li>:is(ul,ol){margin:.75em 0}._preview_12qra_268 a{color:var(--stylo-link);text-decoration:none}._preview_12qra_268 code{font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);border-radius:calc(var(--stylo-radius) - 3px);background:color-mix(in srgb, var(--stylo-border) 45%, transparent);padding:.1em .35em;font-size:.9em}._preview_12qra_268 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_12qra_268 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);font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);white-space:pre-wrap;margin:0 0 1.2em;padding:.7rem .9rem;font-size:.8125rem;line-height:1.5;overflow-x:auto}.stylo-frontmatter:before{content:"Frontmatter";font-family:var(--stylo-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);letter-spacing:.04em;text-transform:uppercase;margin-bottom:.5em;font-size:.7rem;font-weight:600;display:block}._preview_12qra_268 blockquote{border-left:.25rem solid var(--stylo-border);color:var(--stylo-text-muted);margin:1.6em 0;padding-left:1em}._preview_12qra_268 .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_12qra_268 .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_12qra_268 .stylo-callout-note{--stylo-callout-accent:var(--stylo-callout-note,#3b82f6)}._preview_12qra_268 .stylo-callout-tip{--stylo-callout-accent:var(--stylo-callout-tip,#22c55e)}._preview_12qra_268 .stylo-callout-warn{--stylo-callout-accent:var(--stylo-callout-warn,#f59e0b)}._preview_12qra_268 .stylo-callout-danger{--stylo-callout-accent:var(--stylo-callout-danger,#ef4444)}._preview_12qra_268 .stylo-callout-example{--stylo-callout-accent:var(--stylo-callout-example,#a855f7)}._preview_12qra_268 table{border-collapse:collapse;margin:2em 0;display:block;overflow-x:auto}._preview_12qra_268 :is(th,td){border:1px solid var(--stylo-table-border);text-align:left;padding:.35em .6em}._preview_12qra_268 thead th{background:var(--stylo-table-header-bg);font-weight:600}._preview_12qra_268 tbody tr:nth-child(2n) td{background:var(--stylo-table-stripe-bg)}._preview_12qra_268 hr{border:none;border-top:1px solid var(--stylo-border);margin:1em 0}._preview_12qra_268 .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-font-size:.9375rem;--stylo-font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;--stylo-font-family-mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;--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}
|
|
1
|
+
._root_1y1lh_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_1y1lh_1 .cm-scroller{scrollbar-width:thin;scrollbar-color:color-mix(in srgb, var(--stylo-border) 90%, transparent) transparent}._root_1y1lh_1 .cm-scroller::-webkit-scrollbar{width:10px;height:10px}._root_1y1lh_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_1y1lh_1 .cm-scroller::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb, var(--stylo-text-muted) 55%, transparent);background-clip:padding-box}._root_1y1lh_1 .cm-scroller::-webkit-scrollbar-corner{background:0 0}._source_1y1lh_42{flex:auto;min-height:0;overflow:auto}._source_1y1lh_42 .cm-editor{height:100%}._source_1y1lh_42 .cm-editor.cm-focused{outline:2px solid var(--stylo-ring);outline-offset:-2px}._inplace_1y1lh_57{flex:auto;min-height:0;overflow:auto}._toolbar_1y1lh_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_1y1lh_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_1y1lh_73:hover:not(:disabled){background:color-mix(in srgb, var(--stylo-border) 45%, transparent);color:var(--stylo-text)}._toolbarButton_1y1lh_73[data-active]{background:color-mix(in srgb, var(--stylo-accent) 12%, transparent);color:var(--stylo-text)}._toolbarButton_1y1lh_73:disabled{opacity:.4;cursor:default}._toolbarButton_1y1lh_73:focus-visible{outline:2px solid var(--stylo-ring);outline-offset:-2px}._toolbarButton_1y1lh_73 svg{width:16px;height:16px;display:block}._toolbarButton_1y1lh_73 code{font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);font-size:.8125rem;font-weight:500}._toolbarSep_1y1lh_132{background:var(--stylo-border);align-self:stretch;width:1px;margin:4px 3px}._toolbarSticky_1y1lh_142{z-index:50;-webkit-overflow-scrolling:touch;scrollbar-width:none;font-family:var(--stylo-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);flex-wrap:nowrap;transition:opacity .15s;overflow-x:auto}._toolbarSticky_1y1lh_142::-webkit-scrollbar{display:none}._toolbarSticky_1y1lh_142 ._toolbarButton_1y1lh_73{flex:none;min-width:40px;height:40px}._toolbarStickyBottom_1y1lh_142{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_1y1lh_142{box-shadow:0 2px 8px color-mix(in srgb, var(--stylo-text) 10%, transparent);position:fixed;top:0;left:0;right:0}._toolbarStickyHidden_1y1lh_211{opacity:0;pointer-events:none}._stickyToolbarRootBottom_1y1lh_222 ._source_1y1lh_42,._stickyToolbarRootBottom_1y1lh_222 ._inplace_1y1lh_57{padding-bottom:52px}._stickyToolbarRootTop_1y1lh_227 ._source_1y1lh_42,._stickyToolbarRootTop_1y1lh_227 ._inplace_1y1lh_57{padding-top:52px}._inplace_1y1lh_57 .cm-editor{height:100%}._inplace_1y1lh_57 .cm-editor.cm-focused{outline:2px solid var(--stylo-ring);outline-offset:-2px}._split_1y1lh_241{flex:1;grid-template-columns:1fr 1fr;min-height:0;display:grid}._splitPane_1y1lh_248{box-sizing:border-box;flex-direction:column;min-height:0;display:flex;overflow:auto}._splitPane_1y1lh_248+._splitPane_1y1lh_248{border-left:1px solid var(--stylo-border)}._splitPane_1y1lh_248>._source_1y1lh_42{flex:1;min-height:0}._preview_1y1lh_268{overflow-wrap:break-word;padding:.75rem 1rem;font-size:.9375rem;line-height:1.75}._preview_1y1lh_268>:first-child{margin-top:0}._preview_1y1lh_268>:last-child{margin-bottom:0}._preview_1y1lh_268 :is(h1,h2,h3,h4,h5,h6){font-weight:600}._preview_1y1lh_268 h1{margin:0 0 .888889em;font-size:2.25em;line-height:1.11111}._preview_1y1lh_268 h2{margin:2em 0 1em;font-size:1.5em;line-height:1.33333}._preview_1y1lh_268 h3{margin:1.6em 0 .6em;font-size:1.25em;line-height:1.6}._preview_1y1lh_268 :is(h4,h5,h6){margin:1.5em 0 .5em;line-height:1.5}._preview_1y1lh_268 p{margin:1.25em 0}._preview_1y1lh_268 :is(ul,ol){margin:1.25em 0;padding-left:1.625em}._preview_1y1lh_268 li{margin:.5em 0}._preview_1y1lh_268 li>:is(ul,ol){margin:.75em 0}._preview_1y1lh_268 a{color:var(--stylo-link);text-decoration:none}._preview_1y1lh_268 code{font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);border-radius:calc(var(--stylo-radius) - 3px);background:color-mix(in srgb, var(--stylo-border) 45%, transparent);padding:.1em .35em;font-size:.9em}._preview_1y1lh_268 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_1y1lh_268 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);font-family:var(--stylo-font-family-mono,ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);white-space:pre-wrap;margin:0 0 1.2em;padding:.7rem .9rem;font-size:.8125rem;line-height:1.5;overflow-x:auto}.stylo-frontmatter:before{content:"Frontmatter";font-family:var(--stylo-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);letter-spacing:.04em;text-transform:uppercase;margin-bottom:.5em;font-size:.7rem;font-weight:600;display:block}._preview_1y1lh_268 blockquote{border-left:.25rem solid var(--stylo-border);color:var(--stylo-text-muted);margin:1.6em 0;padding-left:1em}._preview_1y1lh_268 .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_1y1lh_268 .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_1y1lh_268 .stylo-callout-note{--stylo-callout-accent:var(--stylo-callout-note,#3b82f6)}._preview_1y1lh_268 .stylo-callout-tip{--stylo-callout-accent:var(--stylo-callout-tip,#22c55e)}._preview_1y1lh_268 .stylo-callout-warn{--stylo-callout-accent:var(--stylo-callout-warn,#f59e0b)}._preview_1y1lh_268 .stylo-callout-danger{--stylo-callout-accent:var(--stylo-callout-danger,#ef4444)}._preview_1y1lh_268 .stylo-callout-example{--stylo-callout-accent:var(--stylo-callout-example,#a855f7)}._preview_1y1lh_268 .stylo-embed{border-left:.25rem solid var(--stylo-embed-accent,var(--stylo-border));margin:1.6em 0;padding-left:1em}._preview_1y1lh_268 .stylo-embed-content>:first-child{margin-top:0}._preview_1y1lh_268 .stylo-embed-content>:last-child{margin-bottom:0}._preview_1y1lh_268 table{border-collapse:collapse;margin:2em 0;display:block;overflow-x:auto}._preview_1y1lh_268 :is(th,td){border:1px solid var(--stylo-table-border);text-align:left;padding:.35em .6em}._preview_1y1lh_268 thead th{background:var(--stylo-table-header-bg);font-weight:600}._preview_1y1lh_268 tbody tr:nth-child(2n) td{background:var(--stylo-table-stripe-bg)}._preview_1y1lh_268 hr{border:none;border-top:1px solid var(--stylo-border);margin:1em 0}._preview_1y1lh_268 .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-font-size:.9375rem;--stylo-font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;--stylo-font-family-mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;--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
2
|
/*$vite$:1*/
|
package/dist/stylo.js
CHANGED
|
@@ -1,47 +1,48 @@
|
|
|
1
|
-
import { i as e,
|
|
2
|
-
import { n, t as r } from "./useCodeMirror-
|
|
1
|
+
import { i as e, n as t } from "./frontmatter-DGUkb9z-.js";
|
|
2
|
+
import { n, t as r } from "./useCodeMirror-DtwupFgc.js";
|
|
3
3
|
import { n as i, r as a } from "./config-DTBxVhtH.js";
|
|
4
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
5
|
import { EditorView as m } from "@codemirror/view";
|
|
6
6
|
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
7
7
|
//#region src/editor/SourceView.tsx
|
|
8
|
-
function _({ value: t, onChange: n, readOnly: i, placeholder: a, codeLanguages: o, wikiLinkSource: s,
|
|
9
|
-
let
|
|
8
|
+
function _({ value: t, onChange: n, readOnly: i, placeholder: a, codeLanguages: o, wikiLinkSource: s, onResolveError: c, onSave: l, onViewChange: u }) {
|
|
9
|
+
let d = r({
|
|
10
10
|
value: t,
|
|
11
11
|
onChange: n,
|
|
12
12
|
readOnly: i,
|
|
13
13
|
placeholder: a,
|
|
14
14
|
codeLanguages: o,
|
|
15
15
|
wikiLinkSource: s,
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
onResolveError: c,
|
|
17
|
+
onSave: l,
|
|
18
|
+
onViewChange: u
|
|
18
19
|
});
|
|
19
20
|
return /* @__PURE__ */ h("div", {
|
|
20
21
|
className: e.source,
|
|
21
|
-
ref:
|
|
22
|
+
ref: d
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
25
|
//#endregion
|
|
25
26
|
//#region src/inplace/lazyInPlace.ts
|
|
26
|
-
var v = c(async () => ({ default: (await import("./InPlaceView-
|
|
27
|
+
var v = c(async () => ({ default: (await import("./InPlaceView-Bu91_3Ig.js")).InPlaceView })), y = c(async () => ({ default: (await import("./Preview-AaGGwLQP.js")).Preview }));
|
|
27
28
|
//#endregion
|
|
28
29
|
//#region src/SplitView.tsx
|
|
29
|
-
function b({ value: t, onChange: n, onWikiLinkClick: r, readOnly: i, placeholder: a, codeLanguages: s, wikiLinkSource: c,
|
|
30
|
-
let [
|
|
30
|
+
function b({ value: t, onChange: n, onWikiLinkClick: r, readOnly: i, placeholder: a, codeLanguages: s, wikiLinkSource: c, embedSource: u, onResolveError: d, frontmatter: m, onSave: v, onViewChange: b }) {
|
|
31
|
+
let [x, S] = p(null), C = f(null), w = f(!1);
|
|
31
32
|
return l(() => {
|
|
32
|
-
let e =
|
|
33
|
+
let e = x?.scrollDOM, t = C.current;
|
|
33
34
|
if (!e || !t) return;
|
|
34
35
|
let n = (e, t) => {
|
|
35
|
-
if (
|
|
36
|
+
if (w.current) return;
|
|
36
37
|
let n = e.scrollHeight - e.clientHeight;
|
|
37
|
-
n <= 0 || (
|
|
38
|
-
|
|
38
|
+
n <= 0 || (w.current = !0, t.scrollTop = e.scrollTop / n * (t.scrollHeight - t.clientHeight), requestAnimationFrame(() => {
|
|
39
|
+
w.current = !1;
|
|
39
40
|
}));
|
|
40
41
|
}, r = () => n(e, t), i = () => n(t, e);
|
|
41
42
|
return e.addEventListener("scroll", r, { passive: !0 }), t.addEventListener("scroll", i, { passive: !0 }), () => {
|
|
42
43
|
e.removeEventListener("scroll", r), t.removeEventListener("scroll", i);
|
|
43
44
|
};
|
|
44
|
-
}, [
|
|
45
|
+
}, [x]), /* @__PURE__ */ g("div", {
|
|
45
46
|
className: e.split,
|
|
46
47
|
children: [/* @__PURE__ */ h("div", {
|
|
47
48
|
className: e.splitPane,
|
|
@@ -52,14 +53,15 @@ function b({ value: t, onChange: n, onWikiLinkClick: r, readOnly: i, placeholder
|
|
|
52
53
|
placeholder: a,
|
|
53
54
|
codeLanguages: s,
|
|
54
55
|
wikiLinkSource: c,
|
|
55
|
-
|
|
56
|
+
onResolveError: d,
|
|
57
|
+
onSave: v,
|
|
56
58
|
onViewChange: (e) => {
|
|
57
|
-
|
|
59
|
+
S(e), b?.(e);
|
|
58
60
|
}
|
|
59
61
|
})
|
|
60
62
|
}), /* @__PURE__ */ h("div", {
|
|
61
63
|
className: e.splitPane,
|
|
62
|
-
ref:
|
|
64
|
+
ref: C,
|
|
63
65
|
children: /* @__PURE__ */ h(o, {
|
|
64
66
|
fallback: /* @__PURE__ */ h("div", {
|
|
65
67
|
className: e.preview,
|
|
@@ -67,7 +69,10 @@ function b({ value: t, onChange: n, onWikiLinkClick: r, readOnly: i, placeholder
|
|
|
67
69
|
}),
|
|
68
70
|
children: /* @__PURE__ */ h(y, {
|
|
69
71
|
value: t,
|
|
70
|
-
onWikiLinkClick: r
|
|
72
|
+
onWikiLinkClick: r,
|
|
73
|
+
embedSource: u,
|
|
74
|
+
onResolveError: d,
|
|
75
|
+
frontmatter: m
|
|
71
76
|
})
|
|
72
77
|
})
|
|
73
78
|
})]
|
|
@@ -188,65 +193,70 @@ function w({ view: t, items: n, icons: r, disabled: i, sticky: a, stickyVisibili
|
|
|
188
193
|
}
|
|
189
194
|
//#endregion
|
|
190
195
|
//#region src/Stylo.tsx
|
|
191
|
-
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, wikiLinkSource: O,
|
|
192
|
-
let
|
|
193
|
-
|
|
194
|
-
let
|
|
196
|
+
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, wikiLinkSource: O, embedSource: k, onResolveError: A, toolbar: j, icons: M, frontmatter: N }, P) {
|
|
197
|
+
let F = a === "preview" || a === "split" || a === "in-place" ? a : "source", [I, L] = p(null), R = F === "preview" ? null : i(j), z = j && typeof j == "object" ? j.render : void 0, B = j && typeof j == "object" ? j.sticky : void 0, V = B === "top" ? "top" : B ? "bottom" : !1, H = j && typeof j == "object" ? j.stickyVisibility : void 0, U = f(c);
|
|
198
|
+
U.current = c;
|
|
199
|
+
let W = f(A);
|
|
200
|
+
W.current = A;
|
|
201
|
+
let G = f((e, t) => {
|
|
202
|
+
W.current?.(e, t);
|
|
203
|
+
}).current, K = f(void 0);
|
|
195
204
|
l(() => {
|
|
196
205
|
let e = t(n)?.frontmatter ?? null;
|
|
197
|
-
e !==
|
|
198
|
-
}, [n]), u(
|
|
199
|
-
focus: () =>
|
|
200
|
-
getView: () =>
|
|
206
|
+
e !== K.current && (K.current = e, U.current?.(e));
|
|
207
|
+
}, [n]), u(P, () => ({
|
|
208
|
+
focus: () => I?.focus(),
|
|
209
|
+
getView: () => I,
|
|
201
210
|
insertAtCursor: (e) => {
|
|
202
|
-
|
|
211
|
+
I && (I.dispatch(I.state.replaceSelection(e)), I.focus());
|
|
203
212
|
},
|
|
204
213
|
scrollToHeading: (e) => {
|
|
205
|
-
if (!
|
|
206
|
-
let t = e.trim().toLowerCase(), { doc: n } =
|
|
214
|
+
if (!I) return !1;
|
|
215
|
+
let t = e.trim().toLowerCase(), { doc: n } = I.state;
|
|
207
216
|
for (let e = 1; e <= n.lines; e++) {
|
|
208
217
|
let r = n.line(e), i = /^ {0,3}#{1,6}[ \t]+(.*?)(?:[ \t]+#+)?[ \t]*$/.exec(r.text);
|
|
209
|
-
if (!(!i || i[1] === void 0) && i[1].trim().toLowerCase() === t) return
|
|
218
|
+
if (!(!i || i[1] === void 0) && i[1].trim().toLowerCase() === t) return I.dispatch({
|
|
210
219
|
selection: { anchor: r.from },
|
|
211
220
|
effects: m.scrollIntoView(r.from, { y: "start" })
|
|
212
|
-
}),
|
|
221
|
+
}), I.focus(), !0;
|
|
213
222
|
}
|
|
214
223
|
return !1;
|
|
215
224
|
}
|
|
216
|
-
}), [
|
|
217
|
-
let
|
|
225
|
+
}), [I]);
|
|
226
|
+
let q = [
|
|
218
227
|
e.root,
|
|
219
|
-
|
|
220
|
-
|
|
228
|
+
V === "bottom" && e.stickyToolbarRootBottom,
|
|
229
|
+
V === "top" && e.stickyToolbarRootTop,
|
|
221
230
|
"stylo",
|
|
222
231
|
T
|
|
223
232
|
].filter(Boolean).join(" ");
|
|
224
233
|
return /* @__PURE__ */ g("div", {
|
|
225
|
-
className:
|
|
226
|
-
"data-stylo-mode":
|
|
234
|
+
className: q,
|
|
235
|
+
"data-stylo-mode": F,
|
|
227
236
|
children: [
|
|
228
|
-
|
|
237
|
+
R && (() => {
|
|
229
238
|
let e = /* @__PURE__ */ h(w, {
|
|
230
|
-
view:
|
|
231
|
-
items:
|
|
232
|
-
icons:
|
|
239
|
+
view: I,
|
|
240
|
+
items: R,
|
|
241
|
+
icons: M,
|
|
233
242
|
disabled: S,
|
|
234
|
-
sticky:
|
|
235
|
-
stickyVisibility:
|
|
243
|
+
sticky: V,
|
|
244
|
+
stickyVisibility: H
|
|
236
245
|
});
|
|
237
|
-
return
|
|
246
|
+
return z ? z(e, { view: I }) : e;
|
|
238
247
|
})(),
|
|
239
|
-
|
|
248
|
+
F === "source" && /* @__PURE__ */ h(_, {
|
|
240
249
|
value: n,
|
|
241
250
|
onChange: r,
|
|
242
251
|
readOnly: S,
|
|
243
252
|
placeholder: C,
|
|
244
253
|
codeLanguages: D,
|
|
245
254
|
wikiLinkSource: O,
|
|
255
|
+
onResolveError: G,
|
|
246
256
|
onSave: s,
|
|
247
|
-
onViewChange:
|
|
257
|
+
onViewChange: L
|
|
248
258
|
}),
|
|
249
|
-
|
|
259
|
+
F === "preview" && /* @__PURE__ */ h(o, {
|
|
250
260
|
fallback: /* @__PURE__ */ h("div", {
|
|
251
261
|
className: e.preview,
|
|
252
262
|
"aria-busy": "true"
|
|
@@ -254,10 +264,12 @@ var T = s(function({ value: n, onChange: r, mode: a = "in-place", onSave: s, onF
|
|
|
254
264
|
children: /* @__PURE__ */ h(y, {
|
|
255
265
|
value: n,
|
|
256
266
|
onWikiLinkClick: d,
|
|
257
|
-
|
|
267
|
+
embedSource: k,
|
|
268
|
+
onResolveError: G,
|
|
269
|
+
frontmatter: N
|
|
258
270
|
})
|
|
259
271
|
}),
|
|
260
|
-
|
|
272
|
+
F === "split" && /* @__PURE__ */ h(b, {
|
|
261
273
|
value: n,
|
|
262
274
|
onChange: r,
|
|
263
275
|
onWikiLinkClick: d,
|
|
@@ -265,11 +277,13 @@ var T = s(function({ value: n, onChange: r, mode: a = "in-place", onSave: s, onF
|
|
|
265
277
|
placeholder: C,
|
|
266
278
|
codeLanguages: D,
|
|
267
279
|
wikiLinkSource: O,
|
|
268
|
-
|
|
280
|
+
embedSource: k,
|
|
281
|
+
onResolveError: G,
|
|
282
|
+
frontmatter: N,
|
|
269
283
|
onSave: s,
|
|
270
|
-
onViewChange:
|
|
284
|
+
onViewChange: L
|
|
271
285
|
}),
|
|
272
|
-
|
|
286
|
+
F === "in-place" && /* @__PURE__ */ h(o, {
|
|
273
287
|
fallback: /* @__PURE__ */ h("div", {
|
|
274
288
|
className: e.inplace,
|
|
275
289
|
"aria-busy": "true"
|
|
@@ -284,8 +298,10 @@ var T = s(function({ value: n, onChange: r, mode: a = "in-place", onSave: s, onF
|
|
|
284
298
|
inPlace: E,
|
|
285
299
|
codeLanguages: D,
|
|
286
300
|
wikiLinkSource: O,
|
|
301
|
+
embedSource: k,
|
|
302
|
+
onResolveError: G,
|
|
287
303
|
onSave: s,
|
|
288
|
-
onViewChange:
|
|
304
|
+
onViewChange: L
|
|
289
305
|
})
|
|
290
306
|
})
|
|
291
307
|
]
|
package/dist/stylo.js.map
CHANGED
|
@@ -1 +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, WikiLinkSource } 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 /** `[[wikilink]]` autocomplete source. Read once. */\n wikiLinkSource?: WikiLinkSource\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 wikiLinkSource,\n onSave,\n onViewChange,\n}: SourceViewProps) {\n const ref = useCodeMirror({\n value,\n onChange,\n readOnly,\n placeholder,\n codeLanguages,\n wikiLinkSource,\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, WikiLinkSource } 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 wikiLinkSource?: WikiLinkSource\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 wikiLinkSource,\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 wikiLinkSource={wikiLinkSource}\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 wikiLinkSource,\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 wikiLinkSource={wikiLinkSource}\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 wikiLinkSource={wikiLinkSource}\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 wikiLinkSource={wikiLinkSource}\n onSave={onSave}\n onViewChange={setView}\n />\n </Suspense>\n )}\n </div>\n )\n})\n\nStylo.displayName = \"Stylo\"\n"],"mappings":"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"}
|
|
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, ResolveErrorInfo, WikiLinkSource } 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 /** `[[wikilink]]` autocomplete source. Read once. */\n wikiLinkSource?: WikiLinkSource\n /** Notified when `wikiLinkSource` rejects. */\n onResolveError?: (error: unknown, info: ResolveErrorInfo) => void\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 wikiLinkSource,\n onResolveError,\n onSave,\n onViewChange,\n}: SourceViewProps) {\n const ref = useCodeMirror({\n value,\n onChange,\n readOnly,\n placeholder,\n codeLanguages,\n wikiLinkSource,\n onResolveError,\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 {\n CodeLanguages,\n EmbedSource,\n FrontmatterDisplay,\n ResolveErrorInfo,\n WikiLinkSource,\n} 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 wikiLinkSource?: WikiLinkSource\n embedSource?: EmbedSource\n onResolveError?: (error: unknown, info: ResolveErrorInfo) => void\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 wikiLinkSource,\n embedSource,\n onResolveError,\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 wikiLinkSource={wikiLinkSource}\n onResolveError={onResolveError}\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\n value={value}\n onWikiLinkClick={onWikiLinkClick}\n embedSource={embedSource}\n onResolveError={onResolveError}\n frontmatter={frontmatter}\n />\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 { ResolveErrorInfo, 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 wikiLinkSource,\n embedSource,\n onResolveError,\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\n // Stable wrapper so a changed `onResolveError` needs no editor rebuild and\n // does not thrash the embed effect. Passed to every surface.\n const onResolveErrorRef = useRef(onResolveError)\n onResolveErrorRef.current = onResolveError\n const resolveError = useRef((error: unknown, info: ResolveErrorInfo) => {\n onResolveErrorRef.current?.(error, info)\n }).current\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 wikiLinkSource={wikiLinkSource}\n onResolveError={resolveError}\n onSave={onSave}\n onViewChange={setView}\n />\n )}\n\n {resolved === \"preview\" && (\n <Suspense fallback={<div className={styles.preview} aria-busy=\"true\" />}>\n <LazyPreview\n value={value}\n onWikiLinkClick={onWikiLinkClick}\n embedSource={embedSource}\n onResolveError={resolveError}\n frontmatter={frontmatter}\n />\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 wikiLinkSource={wikiLinkSource}\n embedSource={embedSource}\n onResolveError={resolveError}\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 wikiLinkSource={wikiLinkSource}\n embedSource={embedSource}\n onResolveError={resolveError}\n onSave={onSave}\n onViewChange={setView}\n />\n </Suspense>\n )}\n </div>\n )\n})\n\nStylo.displayName = \"Stylo\"\n"],"mappings":"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"}
|
package/dist/toolbar/block.d.ts
CHANGED
|
@@ -2,14 +2,6 @@ import type { EditorState } from "@codemirror/state";
|
|
|
2
2
|
import type { EditorView } from "@codemirror/view";
|
|
3
3
|
/** The 1-based line numbers the primary selection touches. */
|
|
4
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
5
|
export interface LinePrefixSpec {
|
|
14
6
|
/** Detects this exact prefix; `match[0]` (from line start) is what gets stripped. */
|
|
15
7
|
match: RegExp;
|
|
@@ -35,22 +27,4 @@ export interface LinePrefixSpec {
|
|
|
35
27
|
export declare function toggleLinePrefix(view: EditorView, spec: LinePrefixSpec): boolean;
|
|
36
28
|
/** True when the primary caret's line carries the prefix. */
|
|
37
29
|
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
30
|
//# sourceMappingURL=block.d.ts.map
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"block.d.ts","sourceRoot":"","sources":["../../src/toolbar/block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAGlE;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"}
|