diff --git a/docs/src/lib/components/header-tracker/HeaderTracker.svelte b/docs/src/lib/components/header-tracker/HeaderTracker.svelte index d584c97..f5a6b1d 100644 --- a/docs/src/lib/components/header-tracker/HeaderTracker.svelte +++ b/docs/src/lib/components/header-tracker/HeaderTracker.svelte @@ -1,7 +1,5 @@ - { - throttledHighlightHeader(); - debouncedHighlightHeader(); // So it is called at the end of the scroll event - }} -/> +
{#each headers as header, i} {@const margin = Number(header.tagName.split('')[1]) - 1} - {#key selectedHeaderIndex} - {#if header.children[0] instanceof HTMLAnchorElement && header.children[0].href} + {@const nextHeader = headers[i + 1]} + {#if header.children[0] instanceof HTMLAnchorElement && header.children[0].href} + {#key scrollY} {header.innerText} - {/if} - {/key} + {/key} + {/if} {/each}
diff --git a/docs/src/lib/components/sidebar/Sidebar.svelte b/docs/src/lib/components/sidebar/Sidebar.svelte index cb82c46..b02930c 100644 --- a/docs/src/lib/components/sidebar/Sidebar.svelte +++ b/docs/src/lib/components/sidebar/Sidebar.svelte @@ -45,12 +45,6 @@ Community Plugins - - - - Using Components - -

Plugins

diff --git a/docs/src/lib/styles/markdown.scss b/docs/src/lib/styles/markdown.scss index 61eb539..2e3c7cd 100644 --- a/docs/src/lib/styles/markdown.scss +++ b/docs/src/lib/styles/markdown.scss @@ -46,18 +46,7 @@ @apply rounded bg-neutral-800 px-1 text-neutral-50; } - p, - h1, - h2, - h3 { - code { - @apply px-1; - } - } - - .carta-editor code { + .carta-renderer code { font-family: 'Fira Code', monospace; - background: transparent; - padding: 0; } } diff --git a/docs/src/lib/utils.ts b/docs/src/lib/utils.ts index d2b288e..eba19d8 100644 --- a/docs/src/lib/utils.ts +++ b/docs/src/lib/utils.ts @@ -54,41 +54,3 @@ export const flyAndScale = ( easing: cubicOut }; }; - -export const throttle = ( - fn: (...args: A) => R, - delay: number -): [(...args: A) => R | undefined, () => void] => { - let wait = false; - let timeout: undefined | number; - let cancelled = false; - - return [ - (...args: A) => { - if (cancelled) return undefined; - if (wait) return undefined; - - const val = fn(...args); - - wait = true; - - timeout = window.setTimeout(() => { - wait = false; - }, delay); - - return val; - }, - () => { - cancelled = true; - clearTimeout(timeout); - } - ]; -}; - -export function debounce(cb: (...args: T) => unknown, wait = 1000) { - let timeout: NodeJS.Timeout; - return (...args: T) => { - clearTimeout(timeout); - timeout = setTimeout(() => cb(...args), wait); - }; -} diff --git a/docs/src/pages/editing-styles.svelte.md b/docs/src/pages/editing-styles.svelte.md index 81736ed..e54df5a 100644 --- a/docs/src/pages/editing-styles.svelte.md +++ b/docs/src/pages/editing-styles.svelte.md @@ -43,39 +43,11 @@ While the core styles are embedded in the Svelte components, the others can be s ### Using multiple themes -By using the `theme` property in `` you can differentiate the themes of multiple editors. +By using the `theme` property in the editor you can differentiate the themes of multiple editors. -## Dark mode +## Changing Markdown color theme -When using dark mode, there are two different themes that have to be changed: the editor theme and the one used for syntax highlighting: - - - -```css -/* Editor dark mode */ -/* Only if you are using the default theme */ -html.dark .carta-theme__default { - --border-color: var(--border-color-dark); - --selection-color: var(--selection-color-dark); - --focus-outline: var(--focus-outline-dark); - --hover-color: var(--hover-color-dark); - --caret-color: var(--caret-color-dark); - --text-color: var(--text-color-dark); -} - -/* Code dark mode */ -/* Only if you didn't specify a custom code theme */ -html.dark .shiki, -html.dark .shiki span { - color: var(--shiki-dark) !important; -} -``` - - - -## Changing Markdown input color theme - -Carta uses [Shiki](https://shiki.matsu.io/) for syntax highlighting. Two default themes are included in the core package, which are set as a [dual theme](https://shiki.matsu.io/guide/dual-themes) to support light and dark mode. If you plan to use a custom one with light/dark modes, make sure to use a dual theme as well. +Carta uses [Shiki](https://shiki.matsu.io/) for syntax highlighting. Two default themes are included in the core package, which are as a [dual theme](https://shiki.matsu.io/guide/dual-themes) used for light and dark mode. You can change theme in the options: @@ -90,7 +62,7 @@ const carta = new Carta({ -If you use a [custom theme](https://shiki.matsu.io/guide/load-theme)(or a custom language), you need to provide it inside the options, so that it gets loaded into the highlighter: +If you use a [custom theme](https://shiki.matsu.io/guide/load-theme)(or also a custom language), you need to specify it, so that it gets loaded into the highlighter: diff --git a/docs/src/pages/introduction.svelte.md b/docs/src/pages/introduction.svelte.md index 12e0af9..6c706f8 100644 --- a/docs/src/pages/introduction.svelte.md +++ b/docs/src/pages/introduction.svelte.md @@ -13,7 +13,7 @@ Carta is a lightweight, fast and extensible Svelte Markdown editor and viewer, d ## Features -- 🌈 Markdown syntax highlighting ([Shiki](https://shiki.style/)); +- 🌈 Markdown syntax highlighting ([shiki](https://shiki.style/)); - 🛠️ Toolbar (extensible); - ⌨️ Keyboard **shortcuts** (extensible); - 📦 Supports **[+150 plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins)** thanks to remark. diff --git a/docs/src/pages/migration.svelte.md b/docs/src/pages/migration.svelte.md index 72d25dc..ab94937 100644 --- a/docs/src/pages/migration.svelte.md +++ b/docs/src/pages/migration.svelte.md @@ -9,7 +9,7 @@ section: Overview Marked has been replaced with a combination of Unified, Remark and Rehype. If you previously used a custom plugin with it, you'll have to update it manually. Otherwise, all builtin plugins have already been updated. Make sure to **update** them! -Some plugins now have a different implementation and their options have changed. Those plugins are [plugin-math](https://beartocode.github.io/carta/plugins/math) and [plugin-anchor](https://beartocode.github.io/carta/plugins/anchor). +Some plugins now have a different implementation and their options have changed. Those plugins are [plugin-math](/plugins/math) and [plugin-anchor](/plugins/anchor). ## Syntax highlighter update @@ -21,24 +21,24 @@ Make sure to remove previous themes imports, as Shiki uses JS based ones. import 'carta-md/light.css'; // 👈 To be removed! ``` -And also update the default theme. Previous based selectors should be removed: +And also update the default theme. SHJ based selectors should be removed: ```css -/* 👇 To be removed! */ [class*='shj-lang-'] { + /* 👈 To be removed! */ /* ... */ } ``` ## Removed verbose prefixes -Many exports have been renamed to make them less verbose: +Many exports have been renamed to make them less verbose, here are them: - `CartaEditor` -> `MarkdownEditor` (old one still supported); - `CartaRenderer` -> `Markdown` (old one still supported); - `CartaEvent` -> `Event`; - `CartaEventType` -> `EventType`; -- `CartaExtension` -> `Plugin`; +- `CartaExtension` -> `Extension`; - `CartaExtensionComponent` -> `ExtensionComponent`; - `CartaOptions` -> `Options`; - `CartaHistory` -> `TextAreaHistory`; @@ -49,7 +49,7 @@ Many exports have been renamed to make them less verbose: - `CartaRenderer` -> `Renderer`; - `CartaLabels` -> `Labels`; -# Minor Changes +## Minor Changes - If you don't use a sanitizer, you need to explicitly set it to `false`; - Removed deprecated option `cartaRef` and `shjRef` for extensions; diff --git a/docs/src/pages/using-components.svelte.md b/docs/src/pages/using-components.svelte.md deleted file mode 100644 index f301c51..0000000 --- a/docs/src/pages/using-components.svelte.md +++ /dev/null @@ -1,217 +0,0 @@ ---- -title: Using Svelte Components -section: Overview ---- - - - -Svelte components can be embedded into the rendered HTML to make certain elements interactive. However, they require a bit more work, as Remark is configured to only render static HTML. To get around this, the idea is to do the following: - -1. Create a Unified plugin to isolate the targeted element; -2. Replace all the elements with the component, after every render. - -## Example - -Let's say we want to replace all hashtags, such as `#something`, with a custom component. Here is as example of how that could be achieved. - -### Parsing the hashtags - -First things first: we need to tell the parser that we want to parse hashtags as custom elements. To do this, it's useful to first install the following packages: - - - -```shell -npm i unist-util-visit -# Types -npm i -D unified hast -``` - - - -Let's create a Unified plugin. The basic structure of a plugin is the following: - - - -```ts -import type { Plugin as UnifiedPlugin } from 'unified' -import { SKIP, visit } from 'unist-util-visit' - -const unifiedPlugin: UnifiedPlugin<[], hast.Root> = () => { - return function (tree) { - // Visit every node in the syntax tree - visit(tree, (node, index, parent) => { - // Do something with the node - } - } -} -``` - - - -We now want to parse text nodes, so that words such as `#pizza` and `#123` are separated from the rest. This is a possible implementation: - - - -```ts -const unifiedPlugin: UnifiedPlugin<[], hast.Root> = () => { - return function (tree) { - visit(tree, (node, index, parent) => { - // Skip code blocks and their children - if (node.type === 'element' && node.tagName === 'pre') return [SKIP]; - // Skip non-text nodes - if (node.type !== 'text') return; - const text = node as hast.Text; - - // Parse the text node and replace hashtags with spans - const regex = /#(\w+)/g; - const children: (hast.Element | hast.Text)[] = []; - let lastIndex = 0; - let match; - while ((match = regex.exec(text.value))) { - const before = text.value.slice(lastIndex, match.index); - if (before) { - children.push({ type: 'text', value: before }); - } - children.push({ - type: 'element', - tagName: 'span', - properties: { type: 'hashtag', value: match[1] }, - children: [{ type: 'text', value: match[0] }] - }); - lastIndex = regex.lastIndex; - } - if (lastIndex < text.value.length) { - children.push({ type: 'text', value: text.value.slice(lastIndex) }); - } - - // Replace the text node with all the children - parent!.children.splice(index!, 1, ...children); - - // Skip the children - return [SKIP, index! + children.length]; - }); - }; -}; -``` - - - -If you want a more in-depth guide on writing Unified plugins, you can check out the official [documentation](https://unifiedjs.com/learn/guide/create-a-plugin/). - -Notice that hashtags are now replaced with the following: - -```html - #pizza -``` - -### Configuring the transformer - -Unified plugins need to be wrapped inside a `UnifiedTransformer` type, to be able to be used in Carta. - - - -```ts -import type { UnifiedTransformer } from 'carta-md'; - -const hashtagTransformer: UnifiedTransformer<'sync'> = { - execution: 'sync', // Sync, since the plugin is synchronous - type: 'rehype', // Rehype, since it operates on HTML - transform({ processor }) { - processor.use(unifiedPlugin); - } -}; -``` - - - -### Mounting the components - -We now want to replace the generated hashtag placeholders with the following element: - - - -```svelte - - - - -``` - - - -To do that, we create a listener that: - -1. Finds all the previous placeholders; -2. Mounts the component next to them; -3. Removes the placeholders. - - - -```ts -import type { Listener } from 'carta-md'; -import Hashtag from './Hashtag.svelte'; - -const convertHashtags: Listener<'carta-render'> = [ - 'carta-render', - function onRender({ detail: { carta } }) { - const rendererContainer = carta.renderer?.container; - if (!rendererContainer) return; - - // Find all hashtag spans and replace them with Svelte components - const hashtagSpans = rendererContainer.querySelectorAll('span[type="hashtag"]'); - for (const span of hashtagSpans) { - const hashtag = span.getAttribute('value') ?? ''; - - new Hashtag({ - target: span.parentElement!, - anchor: span, - props: { value: hashtag } - }); - - span.remove(); - } - } -]; -``` - - - -### Using the plugin - -Let's now create a Plugin with the transformer and the listener: - - - -```ts -import type { Plugin } from 'carta-md'; - -export const hashtag = (): Plugin => ({ - transformers: [hashtagTransformer], - listeners: [convertHashtags] -}); -``` - - - -We can now use the plugin with the following: - -```ts -import { Carta } from 'carta-md'; - -const carta = new Carta({ - // ... - extensions: [hashtag()] -}); -``` - -You can find the example source code [here](https://github.com/BearToCode/svelte-in-carta-example). diff --git a/docs/src/routes/+layout.svelte b/docs/src/routes/+layout.svelte index 723f83f..9dddf88 100644 --- a/docs/src/routes/+layout.svelte +++ b/docs/src/routes/+layout.svelte @@ -24,6 +24,6 @@