From e52e12f7f1f4e6dea493d05eed80bd68e37c6c97 Mon Sep 17 00:00:00 2001 From: BearToCode Date: Tue, 14 Nov 2023 20:51:22 +0100 Subject: [PATCH 001/110] fix(core): correct row height calculation --- packages/carta-md/src/lib/internal/input.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/carta-md/src/lib/internal/input.ts b/packages/carta-md/src/lib/internal/input.ts index 533b1a9..a1aad0e 100644 --- a/packages/carta-md/src/lib/internal/input.ts +++ b/packages/carta-md/src/lib/internal/input.ts @@ -514,12 +514,26 @@ export class CartaInput { */ public getRowHeight() { // Turns out calculating line height is quite tricky - const lineHeight = parseFloat(getComputedStyle(this.container).lineHeight); + const rawLineHeight = getComputedStyle(this.container).lineHeight; + + const lineHeight = parseFloat(rawLineHeight); const fontSize = parseFloat(getComputedStyle(this.container).fontSize); + if (isNaN(lineHeight)) { // "normal" => use default 1.2 value for all modern browser return Math.ceil(fontSize * 1.2); } + if (rawLineHeight.endsWith('em')) { + return Math.ceil(lineHeight * fontSize); + } + if (rawLineHeight.endsWith('%')) { + return Math.ceil((lineHeight / 100) * fontSize); + } + if (rawLineHeight.endsWith('px')) { + return Math.ceil(lineHeight); + } + + // Line height can also be a multiplier of the font size return Math.ceil(fontSize * lineHeight); } } From b070f5dbe262e7648e0085b158d7481072379b9f Mon Sep 17 00:00:00 2001 From: BearToCode Date: Tue, 14 Nov 2023 21:15:22 +0100 Subject: [PATCH 002/110] fix(core): allow any html element as portal for `bindToCaret` --- packages/carta-md/src/lib/internal/carta.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/carta-md/src/lib/internal/carta.ts b/packages/carta-md/src/lib/internal/carta.ts index bc3c3f1..8662845 100644 --- a/packages/carta-md/src/lib/internal/carta.ts +++ b/packages/carta-md/src/lib/internal/carta.ts @@ -359,10 +359,7 @@ export class Carta { * * ``` */ - public bindToCaret( - element: HTMLElement, - portal = document.querySelector('body') as HTMLBodyElement - ) { + public bindToCaret(element: HTMLElement, portal = document.querySelector('body') as HTMLElement) { if (this.input) { return this.input.$bindToCaret(element, portal); } else { From cf623603b61fe13d3e415a532d7706dbb3064842 Mon Sep 17 00:00:00 2001 From: BearToCode Date: Tue, 14 Nov 2023 22:27:32 +0100 Subject: [PATCH 003/110] fix(core): lost of elements reference on input change --- packages/carta-md/src/lib/internal/carta.ts | 26 +++++++-------- .../internal/components/CartaRenderer.svelte | 10 +++--- .../internal/components/MarkdownInput.svelte | 32 ++++++++++--------- packages/carta-md/src/lib/internal/input.ts | 3 +- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/packages/carta-md/src/lib/internal/carta.ts b/packages/carta-md/src/lib/internal/carta.ts index 8662845..f388569 100644 --- a/packages/carta-md/src/lib/internal/carta.ts +++ b/packages/carta-md/src/lib/internal/carta.ts @@ -330,7 +330,6 @@ export class Carta { this.elementsToBind.forEach((it) => { it.callback = this.input?.$bindToCaret(it.elem, it.portal).destroy; }); - this.elementsToBind = []; } /** @@ -360,21 +359,18 @@ export class Carta { * ``` */ public bindToCaret(element: HTMLElement, portal = document.querySelector('body') as HTMLElement) { - if (this.input) { - return this.input.$bindToCaret(element, portal); - } else { - let callback: (() => void) | undefined; + let callback: (() => void) | undefined; - // Bind the element later, when the input is ready - this.elementsToBind.push({ elem: element, portal, callback }); + if (this.input) callback = this.input.$bindToCaret(element, portal).destroy; - return { - destroy() { - if (callback) { - callback(); - } - } - }; - } + // Bind the element later, when the input is ready + this.elementsToBind.push({ elem: element, portal, callback }); + + return { + destroy: () => { + callback && callback(); + this.elementsToBind = this.elementsToBind.filter((it) => it.elem != element); + } + }; } } diff --git a/packages/carta-md/src/lib/internal/components/CartaRenderer.svelte b/packages/carta-md/src/lib/internal/components/CartaRenderer.svelte index c615348..472a4d3 100644 --- a/packages/carta-md/src/lib/internal/components/CartaRenderer.svelte +++ b/packages/carta-md/src/lib/internal/components/CartaRenderer.svelte @@ -8,6 +8,7 @@ export let elem: HTMLDivElement; export let handleScroll: (e: UIEvent) => void; + let mounted = false; let renderedHtml = carta.renderSSR(value); const debouncedRenderer = debounce(() => { @@ -20,15 +21,16 @@ debouncedRenderer(); } - onMount(() => { - carta.$setRenderer(elem); - }); + onMount(() => carta.$setRenderer(elem)); + onMount(() => (mounted = true));
{@html renderedHtml} - + {#if mounted} + + {/if}
+``` + + + +## Sanitization + +By default Carta does **not** sanitize user input, which can include malicious code that could lead to [XSS attacks](https://en.wikipedia.org/wiki/Cross-site_scripting). For this reason it is _strongly recommended_ to install a package that handles that for you. + +Since Carta operates both on the server and the client, you'd need a sanitizer able to work in both environments, for example [isomorphic-dompurify](https://www.npmjs.com/package/isomorphic-dompurify) or [sanitize-html](https://www.npmjs.com/package/sanitize-html). Here is an example using the former, which requires minimum configuration. + + + +```svelte + + + +``` + + diff --git a/docs/src/pages/introduction.svelte.md b/docs/src/pages/introduction.svelte.md index 25c1cf2..db98b34 100644 --- a/docs/src/pages/introduction.svelte.md +++ b/docs/src/pages/introduction.svelte.md @@ -17,7 +17,7 @@ Carta is a lightweight, fast and extensible Svelte Markdown editor and viewer, d - **Lightweight**: no code editor is included, just a textarea with syntax highlighting, with Markdown related utilities. - **SSR compatible**: works great with SvelteKit. - **Keyboard shortcuts**: extensible and configurable. -- **Toolbar**: add or remove according to your needs. +- **Toolbar**: add or remove buttons according to your needs. - **Plugins friendly**: easily create your own extension. ## Official Plugins diff --git a/docs/src/routes/[...slug]/+page.svelte b/docs/src/routes/[...slug]/+page.svelte index 7881c21..2f09d00 100644 --- a/docs/src/routes/[...slug]/+page.svelte +++ b/docs/src/routes/[...slug]/+page.svelte @@ -1,9 +1,11 @@ -
+
{@html rendered} {#if mounted} From 471d5d5eae1baf1e647138ed7d40f28efb5bb23d Mon Sep 17 00:00:00 2001 From: BearToCode Date: Thu, 16 Nov 2023 19:31:54 +0100 Subject: [PATCH 014/110] docs: add math stack exchange example --- docs/src/lib/examples/GitHubExample.svelte | 2 +- .../examples/MathStackExchangeExample.svelte | 38 +++++++++ .../math-stack-exchange-placeholder.tex | 23 ++++++ docs/src/lib/styles/math-stack-exchange.scss | 77 +++++++++++++++++++ docs/src/pages/examples.svelte.md | 10 ++- docs/src/pages/getting-started.svelte.md | 17 ++++ docs/src/pages/introduction.svelte.md | 8 ++ 7 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 docs/src/lib/examples/MathStackExchangeExample.svelte create mode 100644 docs/src/lib/examples/math-stack-exchange-placeholder.tex create mode 100644 docs/src/lib/styles/math-stack-exchange.scss diff --git a/docs/src/lib/examples/GitHubExample.svelte b/docs/src/lib/examples/GitHubExample.svelte index 5d80239..444f718 100644 --- a/docs/src/lib/examples/GitHubExample.svelte +++ b/docs/src/lib/examples/GitHubExample.svelte @@ -20,7 +20,7 @@ ] }); - export let value = ''; + export let value = 'This is an example inspired by [GitHub](https://github.com)'; diff --git a/docs/src/lib/examples/MathStackExchangeExample.svelte b/docs/src/lib/examples/MathStackExchangeExample.svelte new file mode 100644 index 0000000..907ef09 --- /dev/null +++ b/docs/src/lib/examples/MathStackExchangeExample.svelte @@ -0,0 +1,38 @@ + + +
+ + + {#key value} + + {/key} +
diff --git a/docs/src/lib/examples/math-stack-exchange-placeholder.tex b/docs/src/lib/examples/math-stack-exchange-placeholder.tex new file mode 100644 index 0000000..c63338d --- /dev/null +++ b/docs/src/lib/examples/math-stack-exchange-placeholder.tex @@ -0,0 +1,23 @@ +> Here is a formula: + +$$ +\dfrac{\partial}{\partial t}( + \dfrac{\partial \mathcal{L}}{\partial \dot{q}_k} +) - \dfrac{\partial \mathcal{L}}{\partial q_k} = 0 +$$ + +> And here is a circuit: + +```tikz +\usepackage{circuitikz} + +\begin{document} + \begin{circuitikz} \draw + (0,0) to[battery] (0,4) + to[ammeter] (4,4) -- (4,0) + to[lamp] (0,0) + ; + \end{circuitikz} +\end{document} +``` + diff --git a/docs/src/lib/styles/math-stack-exchange.scss b/docs/src/lib/styles/math-stack-exchange.scss new file mode 100644 index 0000000..73ec59e --- /dev/null +++ b/docs/src/lib/styles/math-stack-exchange.scss @@ -0,0 +1,77 @@ +.math-stack-exchange-container { + background-color: #252526; + border-radius: 0.25rem; +} + +.carta-theme__math-stack-exchange { + // Core styles + $background: #252526; + $background-light: #333333; + $border: #333333; + $accent: #2e8acb; + + &.carta-editor { + background-color: $background; + border: 1px solid $border; + border-radius: 0.25rem; + + .carta-wrapper { + padding: 1rem; + flex-grow: 1; + overflow-y: auto; + border-bottom-left-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + + &:focus-within { + outline: 3px solid rgba($accent, 0.5); + } + + .carta-container { + height: 200px; + } + } + + .carta-font-code { + font-family: 'Fira Code', monospace; + caret-color: white; + } + + .carta-toolbar { + height: 2.5rem; + + background-color: $background-light; + border-bottom: 1px solid $border; + + padding-right: 12px; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; + + .carta-icon { + width: 2rem; + height: 2rem; + + &:hover { + color: white; + background-color: $border; + } + } + } + + .carta-toolbar-left { + display: none; + } + + .carta-toolbar-right { + justify-content: flex-start; + } + + [class*='shj-lang-'] { + background: transparent; + } + } + + &.carta-viewer { + min-height: 60px; + padding: 1rem; + } +} diff --git a/docs/src/pages/examples.svelte.md b/docs/src/pages/examples.svelte.md index e358449..dcdd61e 100644 --- a/docs/src/pages/examples.svelte.md +++ b/docs/src/pages/examples.svelte.md @@ -6,13 +6,14 @@ section: Overview Here is a list of examples made using Carta and some of its plugins. ## GitHub - +
Plugins used: code, slash, emoji, attachment. View source on [GitHub](https://github.com/BearToCode/carta/blob/master/docs/src/lib/examples/GitHubExample.svelte). @@ -23,3 +24,10 @@ Plugins used: code, slash, emoji, attachment. View source on [GitHub](https://gi
Plugins used: code, emoji. View source on [GitHub](https://github.com/BearToCode/carta/blob/master/docs/src/lib/examples/DiscordExample.svelte). + +## Math Stack Exchange + + +
+ +Plugins used: math, tikz. View source on [GitHub](https://github.com/BearToCode/carta/blob/master/docs/src/lib/examples/MathStackExchangeExample.svelte). diff --git a/docs/src/pages/getting-started.svelte.md b/docs/src/pages/getting-started.svelte.md index 517ab73..ee88451 100644 --- a/docs/src/pages/getting-started.svelte.md +++ b/docs/src/pages/getting-started.svelte.md @@ -43,6 +43,7 @@ Setup a basic editor: const carta = new Carta({ // Remember to use a sanitizer to prevent XSS attacks! + // More on that below // sanitizer: ... }); @@ -59,6 +60,22 @@ Setup a basic editor: ``` +Or, if you just want to render content: + +```svelte + + + +``` + ## Sanitization diff --git a/docs/src/pages/introduction.svelte.md b/docs/src/pages/introduction.svelte.md index db98b34..e2d9fa0 100644 --- a/docs/src/pages/introduction.svelte.md +++ b/docs/src/pages/introduction.svelte.md @@ -102,4 +102,12 @@ A list of examples inspired by popular platforms. + + + +Math Stack Exchange +Inspired by Math Stack Exchange. + + +
From 7d7f69ee0a3e8a3d1a2ee660d741d1b0fdd0a625 Mon Sep 17 00:00:00 2001 From: BearToCode Date: Sat, 18 Nov 2023 19:59:33 +0100 Subject: [PATCH 015/110] docs: add remaining pages --- .vscode/settings.json | 3 +- .../header-tracker/HeaderTracker.svelte | 2 + docs/src/lib/components/navbar/Navbar.svelte | 2 +- .../src/lib/components/sidebar/Sidebar.svelte | 19 +- docs/src/lib/styles/github.scss | 46 ++++ docs/src/lib/styles/markdown.scss | 20 +- docs/src/pages/api/core.svelte.md | 131 +++++++++++ docs/src/pages/api/extension.svelte.md | 221 ++++++++++++++++++ docs/src/pages/api/utilities.svelte.md | 42 ++++ docs/src/pages/editing-styles.svelte.md | 54 +++++ docs/src/pages/getting-started.svelte.md | 4 + docs/src/pages/plugins/attachment.svelte.md | 91 ++++++++ docs/src/pages/plugins/code.svelte.md | 117 ++++++++++ docs/src/pages/plugins/emoji.svelte.md | 70 ++++++ docs/src/pages/plugins/math.svelte.md | 166 +++++++++++++ docs/src/pages/plugins/slash.svelte.md | 78 +++++++ docs/src/pages/plugins/tikz.svelte.md | 71 ++++++ docs/src/routes/+layout.svelte | 4 +- docs/src/routes/[...slug]/+page.svelte | 1 + 19 files changed, 1120 insertions(+), 22 deletions(-) create mode 100644 docs/src/pages/api/core.svelte.md create mode 100644 docs/src/pages/api/extension.svelte.md create mode 100644 docs/src/pages/api/utilities.svelte.md create mode 100644 docs/src/pages/editing-styles.svelte.md create mode 100644 docs/src/pages/plugins/attachment.svelte.md create mode 100644 docs/src/pages/plugins/code.svelte.md create mode 100644 docs/src/pages/plugins/emoji.svelte.md create mode 100644 docs/src/pages/plugins/math.svelte.md create mode 100644 docs/src/pages/plugins/slash.svelte.md create mode 100644 docs/src/pages/plugins/tikz.svelte.md diff --git a/.vscode/settings.json b/.vscode/settings.json index 0ce3589..95dddcf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,7 +10,8 @@ "oldschool", "rehype", "tikz", - "tikzjax" + "tikzjax", + "typeof" ], "typescript.tsdk": "node_modules\\typescript\\lib", "[svelte]": { diff --git a/docs/src/lib/components/header-tracker/HeaderTracker.svelte b/docs/src/lib/components/header-tracker/HeaderTracker.svelte index ee43660..f5a6b1d 100644 --- a/docs/src/lib/components/header-tracker/HeaderTracker.svelte +++ b/docs/src/lib/components/header-tracker/HeaderTracker.svelte @@ -38,10 +38,12 @@
{#each headers as header, i} + {@const margin = Number(header.tagName.split('')[1]) - 1} {@const nextHeader = headers[i + 1]} {#if header.children[0] instanceof HTMLAnchorElement && header.children[0].href} {#key scrollY}
- carta logo + carta logo diff --git a/docs/src/lib/components/sidebar/Sidebar.svelte b/docs/src/lib/components/sidebar/Sidebar.svelte index a010136..2e3c06e 100644 --- a/docs/src/lib/components/sidebar/Sidebar.svelte +++ b/docs/src/lib/components/sidebar/Sidebar.svelte @@ -72,7 +72,7 @@ - + TikZ @@ -85,14 +85,9 @@

API

- - - Render - - - - - Render SSR + + + Utilities @@ -100,8 +95,8 @@ Core - - - Extensions + + + Extension
diff --git a/docs/src/lib/styles/github.scss b/docs/src/lib/styles/github.scss index 78c26c3..b83daaf 100644 --- a/docs/src/lib/styles/github.scss +++ b/docs/src/lib/styles/github.scss @@ -113,4 +113,50 @@ &.carta-emoji button.carta-active { background: $border; } + + // Plugin slash + &.carta-slash { + width: 18rem; + max-height: 14rem; + overflow-y: scroll; + border-radius: 4px; + font-family: inherit; + background-color: $background; + padding: 6px; + scroll-padding: 6px; + } + + &.carta-slash span { + width: fit-content; + } + + &.carta-slash button { + background: none; + width: 100%; + padding: 10px; + border: 0; + border-radius: 4px; + } + + &.carta-slash .carta-slash-group { + padding: 0 4px 0 4px; + margin-bottom: 4px; + font-size: 0.8rem; + } + + &.carta-slash button.carta-active, + &.carta-slash button:hover { + background: $background-light; + cursor: pointer; + } + + &.carta-slash .carta-snippet-title { + font-size: 0.85rem; + font-weight: 600; + } + + &.carta-slash .carta-snippet-description { + font-size: 0.8rem; + text-overflow: ellipsis; + } } diff --git a/docs/src/lib/styles/markdown.scss b/docs/src/lib/styles/markdown.scss index 2b54175..d42cd16 100644 --- a/docs/src/lib/styles/markdown.scss +++ b/docs/src/lib/styles/markdown.scss @@ -4,20 +4,25 @@ } h1 { - @apply mb-4 mt-6 text-4xl font-bold text-white first:mt-0; + @apply mb-4 mt-8 text-4xl font-bold text-white first:mt-0; } h2 { - @apply mb-4 mt-6 text-3xl font-semibold text-white first:mt-0; + @apply mb-4 mt-8 text-3xl font-semibold text-white first:mt-0; } h3 { - @apply mb-3 mt-4 text-2xl font-medium text-white first:mt-0; + @apply mb-3 mt-7 text-2xl font-medium text-white first:mt-0; + } + + h4 { + @apply mb-3 mt-3 text-lg font-medium text-white first:mt-0; } h1, h2, - h3 { + h3, + h4 { @apply scroll-mt-16; } @@ -33,11 +38,14 @@ @apply text-neutral-300; } - a:not(h1 > a, h2 > a, h3 > a, .bg-card) { + a:not(h1 > a, h2 > a, h3 > a, h4 > a, .bg-card) { @apply text-sky-300 underline; } code { - @apply text-neutral-50; + @apply rounded; + &:not([class*='language-']) { + @apply bg-neutral-800 px-1 text-neutral-100; + } } } diff --git a/docs/src/pages/api/core.svelte.md b/docs/src/pages/api/core.svelte.md new file mode 100644 index 0000000..25b1a2f --- /dev/null +++ b/docs/src/pages/api/core.svelte.md @@ -0,0 +1,131 @@ +--- +section: API +title: Core +--- + +# `Carta` options + +List of options that can be used when creating `Carta`: + +```ts +new Carta({ + /* ... */ +}); +``` + +### `extensions` + +Type: `CartaExtension[]` + +List of extensions(plugins) to use. + +### `rendererDebounce` + +Type: `number` + +Rendering debouncing timeout, in milliseconds. +Defaults to 300ms. + +### `disableShortcuts` + +Type: `DefaultShortcutId[] | true` + +Remove shortcuts by id. You can use `true` to disable all of them. + +### `disablePrefixes` + +Type: `DefaultPrefixId[] | true` + +Remove prefixes by id. You can use `true` to disable all of them. + +### `historyOptions` + +History management options. + +#### `historyOptions.minInterval` + +Type: `number` + +Minimum interval between save states in milliseconds. +Defaults to 300ms. + +#### `historyOptions.maxSize` + +Type: `number` + +Maximum history size in bytes. +Defaults to 1MB. + +### `sanitizer` + +Type: `(html: string) => void` + +HTML sanitizer. See [here](/getting-started#sanitization) for more details. + +# `CartaEditor` options + +List of options that can be used in the `` component. + +### `carta` + +Type: `Carta` + +Carta manager to use for this editor. + +### `theme` + +Type: `string` + +The theme of this editor. The editor and related elements will have the `carta-theme__` as a class. + +### `value` + +Type: `string` + +Current Markdown input value. + +### `mode` + +Type: `'tabs' | 'split' | 'auto'` + +Editor windows mode. With `auto` it will split when the window size is greater than 768px. + +### `scroll` + +Type: `'sync' | 'async'` + +Scroll synchronization. + +### `disableToolbar` + +Type: `boolean` + +Option to disable the toolbar. + +### `placeholder` + +Type: `string` + +Set the textarea placeholder. + +# `CartaViewer` options + +List of options that can be used in the `` component. + +### `carta` + +Type: `Carta` + +Carta manager to use for this editor. + +### `theme` + +Type: `string` + +The theme of this editor. The viewer and related elements will have the `carta-theme__` as a class. + +### `value` + +Type: `string` + +Current Markdown input value. diff --git a/docs/src/pages/api/extension.svelte.md b/docs/src/pages/api/extension.svelte.md new file mode 100644 index 0000000..a871742 --- /dev/null +++ b/docs/src/pages/api/extension.svelte.md @@ -0,0 +1,221 @@ +--- +section: API +title: Extension +--- + + + +# `CartaExtension` properties + +You can easily extend Carta by creating custom plugins. + + + +```ts +const ext: CartaExtension = { + // ... +}; + +const carta = new Carta({ + extensions: [ext] +}); +``` + + + +Here are all the `CartaExtension` properties: + +### `markedExtensions` + +List of marked extensions. For more information check out [Marked docs](https://marked.js.org/using_pro). + +### `shortcuts` + +Type: `KeyboardShortcut[]` + +Additional keyboards shortcut. For example: + + + +```ts +const shortcut: KeyboardShortcut = { + id: 'bold', + combination: new Set(['control', 'b']), + action: (input) => input.toggleSelectionSurrounding('**') +}; +``` + + + +#### `KeyboardShortcut.id` + +Type: `string` + +Id of the shortcut. + +#### `KeyboardShortcut.combination` + +Type: `Set` + +Set of keys, corresponding to the `e.key` of `KeyboardEvent`s, but lowercase. + +#### `KeyboardShortcut.action` + +Type: `(input: CartaInput) => void` + +Shortcut callback. + +#### `KeyboardShortcut.preventSave` + +Prevent saving the current state in history. + +### `icons` + +Type: `CartaIcon[]` + +Additional toolbar icons. For example: + + + +```ts +const icon: CartaIcon = { + id: 'heading', + action: (input) => input.toggleLinePrefix('###'), + component: HeadingIcon +}; +``` + + + +#### `CartaIcon.id` + +Type: `string` + +Id of the icon. + +#### `CartaIcon.action` + +Type: `(input: CartaInput) => void` + +Click callback. + +#### `CartaIcon.component` + +Type: `ComponentType` (SvelteComponent) + +The Icon as a Svelte component. + +### `prefixes` + +Type: `Prefix[]` + +Text prefixes, default ones include the `- ` for bulleted lists, `1. ` for numbered lists, `- [ ]` for task lists. + + + +```ts +const prefix: Prefix = { + id: 'bulletedList', + match: (line) => { + const prefix = line.slice(0, 2); + if (prefix === '- ') return prefix; + }, + maker: () => '- ' +}; +``` + + + +#### `Prefix.id` + +Type: `string` + +Id of the prefix. + +#### `Prefix.match` + +Type: `(line: string) => string | undefined` + +Function that returns the prefix, if it is present. + +#### `Prefix.maker` + +Type: `(previousMatch: string, previousLine: string) => string` + +Function that returns the prefix for the new line. + +Example: + + + +```ts +const prefix: Prefix = { + id: 'numberedList', + match: (line) => line.match(/^\d+\./)?.at(0), + maker: (prev) => `${Number(prev.slice(0, -1)) + 1}. ` +}; +``` + + + +### `listeners` + +Type: `CartaListener[]` + +Textarea event listeners. Has an additional `carta-render` and `carta-render-ssr` events keys. + + + +```ts +const click: CartaListener = ['click', () => console.log('I was clicked!')]; +const render: CartaListener = [ + 'carta-render', + (e) => { + const carta = e.detail.carta; + // ... + }, + { + once: true + } +]; +``` + + + +### `components` + +Type: `CartaExtensionComponent[]` + +Additional components to be added to the editor or viewer. + +#### `CartaExtensionComponent.component` + +Type: `typeof SvelteComponentTyped` + +Svelte components that exports `carta: Carta` and all the other properties specified as the generic parameter and in `props`. + +#### `CartaExtensionComponent.props` + +Type: `T` + +Properties that will be handed to the component. + +#### `CartaExtensionComponent.parent` + +Type: `MaybeArray<'editor' | 'input' | 'renderer' | 'preview'>` + +Where the element will be placed. + +### `highlightRules` + +Type: `ShjLanguageDefinition` + +Custom markdown highlighting rules. See [Speed-Highlight Wiki](https://github.com/speed-highlight/core/wiki/Create-or-suggest-new-languages) for more info. + +### `onLoad` + +Type: `(data: { carta: Carta; highlight: HighlightFunctions }) => void` + +Use this callback to execute code when one Carta instance loads the extension. diff --git a/docs/src/pages/api/utilities.svelte.md b/docs/src/pages/api/utilities.svelte.md new file mode 100644 index 0000000..c641125 --- /dev/null +++ b/docs/src/pages/api/utilities.svelte.md @@ -0,0 +1,42 @@ +--- +section: API +title: Utilities +--- + +## `Carta.render` + +Allows you to render Markdown asynchronously. + +```ts +const carta = new Carta({ + /* ... */ +}); +const markdown = '# Some Markdown'; +const html = await carta.render(markdown); +``` + +## `Carta.renderSSR` + +Allows you to render Markdown synchronously, suitable for Server Side Rendering. Note that particular extensions that add content asynchronously will not work in this configuration. + +```ts +const carta = new Carta({ + /* ... */ +}); +const markdown = '# Some Markdown'; +const html = carta.renderSSR(markdown); +``` + +## `Carta.bindToCaret` + +Svelte action that allows you to bind a specific element to the caret position. Used, for example, in `plugin-emoji` and `plugin-slash`. + +```svelte + + +
+ +
+``` diff --git a/docs/src/pages/editing-styles.svelte.md b/docs/src/pages/editing-styles.svelte.md new file mode 100644 index 0000000..1752008 --- /dev/null +++ b/docs/src/pages/editing-styles.svelte.md @@ -0,0 +1,54 @@ +--- +title: Editing Styles +section: Overview +--- + + + +## Customizing editor styles + +While the core styles are embedded in the Svelte components, the others can be set in a custom stylesheet. Here is what the final rendered HTML looks like. + + + +```html +
+
+
+ +
+
+ + +
+
+ +
+
+
+
+