diff --git a/docs/src/lib/examples/assets/PlusIcon.svelte b/docs/src/lib/examples/assets/PlusIcon.svelte
new file mode 100644
index 0000000..aaa4ef9
--- /dev/null
+++ b/docs/src/lib/examples/assets/PlusIcon.svelte
@@ -0,0 +1,3 @@
+
+
+
diff --git a/docs/src/lib/search/index.ts b/docs/src/lib/search/index.ts
index 511f862..7a1623f 100644
--- a/docs/src/lib/search/index.ts
+++ b/docs/src/lib/search/index.ts
@@ -1,4 +1,4 @@
-import { Document } from 'flexsearch';
+import flexsearch from 'flexsearch';
import type { SvelteComponent } from 'svelte';
export interface SearchResult {
@@ -19,7 +19,7 @@ export type EnrichedSearchResult = SearchResult & {
};
export async function initializeSearch() {
- const indexedPages = new Document({
+ const indexedPages = new flexsearch.Document({
tokenize: 'full',
cache: true,
context: true,
diff --git a/docs/src/lib/styles/discord.scss b/docs/src/lib/styles/discord.scss
index ad88fd9..60d8d3b 100644
--- a/docs/src/lib/styles/discord.scss
+++ b/docs/src/lib/styles/discord.scss
@@ -27,6 +27,7 @@
.carta-font-code {
font-family: 'Fira Code', monospace;
caret-color: white;
+ font-size: 1.1rem;
}
.carta-toolbar {
@@ -41,10 +42,6 @@
height: 1.75rem;
transform: translateX(-50%) translateY(-50%);
}
-
- [class*='shj-lang-'] {
- background: transparent;
- }
}
// Plugin emoji
@@ -79,3 +76,8 @@
background: $background-contrast;
}
}
+
+html.dark .shiki,
+html.dark .shiki span {
+ color: var(--shiki-dark) !important;
+}
diff --git a/docs/src/lib/styles/github.scss b/docs/src/lib/styles/github.scss
index b83daaf..7d2b961 100644
--- a/docs/src/lib/styles/github.scss
+++ b/docs/src/lib/styles/github.scss
@@ -28,15 +28,14 @@
.carta-font-code {
font-family: 'Fira Code', monospace;
caret-color: white;
+ font-size: 1.1rem;
}
.carta-toolbar {
height: 2.5rem;
background-color: $background-light;
- border-bottom: 1px solid $border;
- padding-right: 12px;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
@@ -51,6 +50,12 @@
}
}
+ .carta-toolbar-left button,
+ .carta-toolbar-right,
+ .carta-filler {
+ border-bottom: 1px solid $border;
+ }
+
.carta-toolbar-left {
& > *:first-child {
border-top-left-radius: 0.5rem;
@@ -62,10 +67,12 @@
font-size: 0.95rem;
}
- .carta-active {
+ button {
height: 100%;
+ }
+
+ .carta-active {
background-color: $background;
- transform: translateY(1px);
color: white;
border-right: 1px solid $border;
@@ -77,8 +84,37 @@
}
}
- [class*='shj-lang-'] {
- background: transparent;
+ .carta-toolbar-right {
+ padding-right: 12px;
+ }
+
+ .carta-icons-menu {
+ padding: 8px;
+ border: 1px solid $border;
+ border-radius: 6px;
+ min-width: 180px;
+ background: $background;
+
+ .carta-icon-full {
+ padding-left: 6px;
+ padding-right: 6px;
+
+ margin-top: 2px;
+ &:first-child {
+ margin-top: 0;
+ }
+
+ &:hover {
+ color: white;
+ background-color: $border;
+ }
+
+ span {
+ margin-left: 6px;
+ color: white;
+ font-size: 0.85rem;
+ }
+ }
}
}
@@ -160,3 +196,8 @@
text-overflow: ellipsis;
}
}
+
+html.dark .shiki,
+html.dark .shiki span {
+ color: var(--shiki-dark) !important;
+}
diff --git a/docs/src/lib/styles/markdown.scss b/docs/src/lib/styles/markdown.scss
index d42cd16..61eb539 100644
--- a/docs/src/lib/styles/markdown.scss
+++ b/docs/src/lib/styles/markdown.scss
@@ -43,9 +43,21 @@
}
code {
- @apply rounded;
- &:not([class*='language-']) {
- @apply bg-neutral-800 px-1 text-neutral-100;
+ @apply rounded bg-neutral-800 px-1 text-neutral-50;
+ }
+
+ p,
+ h1,
+ h2,
+ h3 {
+ code {
+ @apply px-1;
}
}
+
+ .carta-editor code {
+ font-family: 'Fira Code', monospace;
+ background: transparent;
+ padding: 0;
+ }
}
diff --git a/docs/src/lib/styles/math-stack-exchange.scss b/docs/src/lib/styles/math-stack-exchange.scss
index 73ec59e..1354a4e 100644
--- a/docs/src/lib/styles/math-stack-exchange.scss
+++ b/docs/src/lib/styles/math-stack-exchange.scss
@@ -34,6 +34,7 @@
.carta-font-code {
font-family: 'Fira Code', monospace;
caret-color: white;
+ font-size: 1.1rem;
}
.carta-toolbar {
@@ -64,9 +65,34 @@
.carta-toolbar-right {
justify-content: flex-start;
}
+ }
- [class*='shj-lang-'] {
- background: transparent;
+ .carta-icons-menu {
+ padding: 8px;
+ border: 1px solid $border;
+ border-radius: 6px;
+ min-width: 180px;
+ background: $background;
+
+ .carta-icon-full {
+ padding-left: 6px;
+ padding-right: 6px;
+
+ margin-top: 2px;
+ &:first-child {
+ margin-top: 0;
+ }
+
+ &:hover {
+ color: white;
+ background-color: $border;
+ }
+
+ span {
+ margin-left: 6px;
+ color: white;
+ font-size: 0.85rem;
+ }
}
}
@@ -75,3 +101,8 @@
padding: 1rem;
}
}
+
+html.dark .shiki,
+html.dark .shiki span {
+ color: var(--shiki-dark) !important;
+}
diff --git a/docs/src/lib/utils.ts b/docs/src/lib/utils.ts
index eba19d8..d2b288e 100644
--- a/docs/src/lib/utils.ts
+++ b/docs/src/lib/utils.ts
@@ -54,3 +54,41 @@ 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/api/core.svelte.md b/docs/src/pages/api/core.svelte.md
index 91dc87f..62163f9 100644
--- a/docs/src/pages/api/core.svelte.md
+++ b/docs/src/pages/api/core.svelte.md
@@ -17,9 +17,15 @@ new Carta({
});
```
+### `gfmOptions`
+
+Type: `GfmOptions`
+
+GitHub Flavored Markdown options.
+
### `extensions`
-Type: `CartaExtension[]`
+Type: `Extension[]`
List of extensions(plugins) to use.
@@ -34,13 +40,19 @@ Defaults to 300ms.
Type: `DefaultShortcutId[] | true`
-Remove shortcuts by id. You can use `true` to disable all of them.
+Remove default shortcuts by id. You can use `true` to disable all of them.
+
+### `disableIcons`
+
+Type: `DefaultIconId[] | true`
+
+Remove default icons 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.
+Remove default prefixes by id. You can use `true` to disable all of them.
### `historyOptions`
@@ -66,15 +78,21 @@ Type: `(html: string) => void`
HTML sanitizer. See [here]({base}/getting-started#sanitization) for more details.
-### `labels`
+### `shikiOptions`
-Type: `Partial`
+Type: `ShikiOptions`
-Can be used to provide custom text for labels in the editor.
+Highlighter(Shiki) options.
-# `CartaEditor` options
+### `theme`
-List of options that can be used in the `` component.
+Type: `Theme | DualTheme`
+
+Shiki theme to use to highlight Markdown.
+
+# `MarkdownEditor` options
+
+List of options that can be used in the `` component.
### `carta`
@@ -126,9 +144,15 @@ Additional properties that will be used in the textarea used under the hood in t
`class`, `placeholder` and `value` are not allowed. Use the corresponding editor properties
instead.
-# `CartaViewer` options
+### `labels`
-List of options that can be used in the `` component.
+Type: `Partial`
+
+Can be used to provide custom text for labels in the editor.
+
+# `Markdown` options
+
+List of options that can be used in the `` component.
### `carta`
diff --git a/docs/src/pages/api/extension.svelte.md b/docs/src/pages/api/extension.svelte.md
index a871742..f39da6a 100644
--- a/docs/src/pages/api/extension.svelte.md
+++ b/docs/src/pages/api/extension.svelte.md
@@ -7,14 +7,14 @@ title: Extension
import Code from '$lib/components/code/Code.svelte';
-# `CartaExtension` properties
+# `Plugin` properties
You can easily extend Carta by creating custom plugins.
```ts
-const ext: CartaExtension = {
+const ext: Plugin = {
// ...
};
@@ -25,11 +25,47 @@ const carta = new Carta({
-Here are all the `CartaExtension` properties:
+Here are all the `Plugin` properties:
-### `markedExtensions`
+### `transformers`
-List of marked extensions. For more information check out [Marked docs](https://marked.js.org/using_pro).
+Type: `UnifiedTransformer`
+
+Remark or Rehype transformers.
+
+#### `UnifiedTransformer.execution`
+
+Type: `'sync' | 'async'`
+
+If you specify async, this transformer won't be available for SSR.
+
+#### `UnifiedTransformer.type`
+
+Type: `'remark' | 'rehype'`
+
+This determines at which step the transformer will operate, whether on Remark, on a Markdown-based syntax tree, or Rehype, on a HTML-based one.
+
+#### `UnifiedTransformer.transform`
+
+Type: `({ processor, carta }) => void`
+
+The actual processor, can be async if the execution is specified as such.
+
+
+
+```ts
+{
+ execution: 'sync',
+ type: 'rehype',
+ transform({ processor }) {
+ processor
+ .use(rehypeSlug)
+ .use(rehypeAutolinkHeadings);
+ }
+}
+```
+
+
### `shortcuts`
@@ -63,7 +99,7 @@ Set of keys, corresponding to the `e.key` of `KeyboardEvent`s, but lowercase.
#### `KeyboardShortcut.action`
-Type: `(input: CartaInput) => void`
+Type: `(input: InputEnhancer) => void`
Shortcut callback.
@@ -73,14 +109,14 @@ Prevent saving the current state in history.
### `icons`
-Type: `CartaIcon[]`
+Type: `Icon[]`
Additional toolbar icons. For example:
```ts
-const icon: CartaIcon = {
+const icon: Icon = {
id: 'heading',
action: (input) => input.toggleLinePrefix('###'),
component: HeadingIcon
@@ -89,19 +125,19 @@ const icon: CartaIcon = {
-#### `CartaIcon.id`
+#### `Icon.id`
Type: `string`
Id of the icon.
-#### `CartaIcon.action`
+#### `Icon.action`
-Type: `(input: CartaInput) => void`
+Type: `(input: InputEnhancer) => void`
Click callback.
-#### `CartaIcon.component`
+#### `Icon.component`
Type: `ComponentType` (SvelteComponent)
@@ -162,15 +198,15 @@ const prefix: Prefix = {
### `listeners`
-Type: `CartaListener[]`
+Type: `Listener[]`
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 = [
+const click: Listener = ['click', () => console.log('I was clicked!')];
+const render: Listener = [
'carta-render',
(e) => {
const carta = e.detail.carta;
@@ -186,33 +222,39 @@ const render: CartaListener = [
### `components`
-Type: `CartaExtensionComponent[]`
+Type: `ExtensionComponent[]`
Additional components to be added to the editor or viewer.
-#### `CartaExtensionComponent.component`
+#### `ExtensionComponent.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`
+#### `ExtensionComponent.props`
Type: `T`
Properties that will be handed to the component.
-#### `CartaExtensionComponent.parent`
+#### `ExtensionComponent.parent`
Type: `MaybeArray<'editor' | 'input' | 'renderer' | 'preview'>`
Where the element will be placed.
+### `grammarRules`
+
+Type: `GrammarRule[]`
+
+Custom Markdown TextMate grammar rules for Shiki. They will be injected into the language.
+
### `highlightRules`
-Type: `ShjLanguageDefinition`
+Type: `HighlightingRule[]`
-Custom markdown highlighting rules. See [Speed-Highlight Wiki](https://github.com/speed-highlight/core/wiki/Create-or-suggest-new-languages) for more info.
+Custom highlighting rules for ShiKi. They will be injected into the selected theme.
### `onLoad`
diff --git a/docs/src/pages/api/utilities.svelte.md b/docs/src/pages/api/utilities.svelte.md
index c641125..273796f 100644
--- a/docs/src/pages/api/utilities.svelte.md
+++ b/docs/src/pages/api/utilities.svelte.md
@@ -40,3 +40,52 @@ Svelte action that allows you to bind a specific element to the caret position.
```
+
+## `Carta.highlighter`
+
+Get the Shiki highlighter.
+
+```ts
+const highlighter = await carta.highlighter();
+const userTheme = carta.theme;
+```
+
+Here are some other highlight related utilities:
+
+### `isBundleLanguage`
+
+Checks if a language is a bundled language.
+
+```ts
+export const isBundleLanguage = (lang: string): lang is BundledLanguage;
+```
+
+### `isBundleTheme`
+
+Checks if a theme is a bundled theme.
+
+```ts
+export const isBundleTheme = (theme: string): theme is BundledTheme;
+```
+
+### `isDualTheme`
+
+Checks if a theme is a dual theme.
+
+```ts
+export const isDualTheme = (theme: Theme | DualTheme): theme is DualTheme;
+```
+
+### `isSingleTheme`
+
+```ts
+export const isSingleTheme = (theme: Theme | DualTheme): theme is Theme;
+```
+
+### `isThemeRegistration`
+
+Checks if a theme is a theme registration.
+
+```ts
+export const isThemeRegistration = (theme: Theme): theme is ThemeRegistration;
+```
diff --git a/docs/src/pages/community-plugins.svelte.md b/docs/src/pages/community-plugins.svelte.md
new file mode 100644
index 0000000..c30ce68
--- /dev/null
+++ b/docs/src/pages/community-plugins.svelte.md
@@ -0,0 +1,83 @@
+---
+title: Community Plugins
+section: Overview
+---
+
+
+
+Here are is a list of several plugins developed by the community:
+
+
+
+### `carta-plugin-video`
+
+
+
+> Adds ability to render online video from Youtube or Vimeo.
+
+
+
+```
+npm i carta-plugin-video
+```
+
+
+
+
+
+### `carta-plugin-imsize`
+
+
+
+> Adds ability to render images in specific sizes.
+
+
+
+```
+npm i carta-plugin-imsize
+```
+
+
+
+
+
+### `carta-plugin-ins-del`
+
+
+
+> `` and `` tags support
+
+
+
+```
+npm i carta-plugin-ins-del
+```
+
+
+
+
+
+### `carta-plugin-subscript`
+
+
+
+> Adds ability to render subscripts and superscripts.
+
+
+
+```
+npm i carta-plugin-subscript
+```
+
+
diff --git a/docs/src/pages/editing-styles.svelte.md b/docs/src/pages/editing-styles.svelte.md
index 1752008..81736ed 100644
--- a/docs/src/pages/editing-styles.svelte.md
+++ b/docs/src/pages/editing-styles.svelte.md
@@ -43,11 +43,68 @@ While the core styles are embedded in the Svelte components, the others can be s
### Using multiple themes
-By using the `theme` property in the editor you can differentiate the themes of multiple editors.
+By using the `theme` property in `` you can differentiate the themes of multiple editors.
-## Changing Markdown color theme
+## Dark mode
-Carta uses [Speed Highlight JS](https://github.com/speed-highlight/core) for syntax highlighting. Two default themes are included in the core package, `light.css` and `dark.css`, and others can be found on the Speed Highlight [GitHub](https://github.com/speed-highlight/core/tree/main/src/themes), but you can also easily create your own.
+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.
+
+You can change theme in the options:
+
+
+
+```ts
+const carta = new Carta({
+ // ...
+ theme: 'github-dark'
+});
+```
+
+
+
+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:
+
+
+
+```ts
+const carta = new Carta({
+ // ...
+ shikiOptions: {
+ langs: // ...
+ themes: // ...
+ }
+})
+```
+
+
## Markdown stylesheets
diff --git a/docs/src/pages/getting-started.svelte.md b/docs/src/pages/getting-started.svelte.md
index 71ed52d..81b7662 100644
--- a/docs/src/pages/getting-started.svelte.md
+++ b/docs/src/pages/getting-started.svelte.md
@@ -37,9 +37,8 @@ Setup a basic editor:
```svelte
-
+
```
@@ -68,7 +68,7 @@ Or, if you just want to render content:
```svelte
-
+
```
@@ -93,16 +93,16 @@ Since Carta operates both on the server and the client, you'd need a sanitizer a
```svelte
-
+
```
diff --git a/docs/src/pages/introduction.svelte.md b/docs/src/pages/introduction.svelte.md
index 2de6e88..12e0af9 100644
--- a/docs/src/pages/introduction.svelte.md
+++ b/docs/src/pages/introduction.svelte.md
@@ -5,21 +5,21 @@ section: Overview
-> Swiftly edit and render Markdown, with no overhead.
+> Modern, lightweight, powerful Markdown Editor.
Carta is a lightweight, fast and extensible Svelte Markdown editor and viewer, designed for flexibility. It works natively in SvelteKit, and supports Server Side Rendering.
## Features
-- **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 buttons according to your needs.
-- **Plugins friendly**: easily create your own extension.
-- **Accessibility**: includes ARIA roles, arrow keys navigation and labels.
+- ๐ 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.
+- ๐ Scroll sync;
+- โ Accessibility friendly;
+- ๐ฅ๏ธ **SSR** compatible;
## Official Plugins
@@ -31,7 +31,7 @@ Carta comes with a set of official plugins for the most common use cases.
-
+MathSupport for KaTex expressions.
@@ -39,7 +39,7 @@ Carta comes with a set of official plugins for the most common use cases.
-
+CodeCode blocks syntax highlighting.
@@ -47,7 +47,7 @@ Carta comes with a set of official plugins for the most common use cases.
-
+EmojiEmbed emojis in Markdown.
@@ -55,7 +55,7 @@ Carta comes with a set of official plugins for the most common use cases.
-
+SlashSupport for slash commands.
@@ -63,7 +63,7 @@ Carta comes with a set of official plugins for the most common use cases.
-
+TikZSupport for TikZ/PgfPlots diagrams.
@@ -71,12 +71,28 @@ Carta comes with a set of official plugins for the most common use cases.
-
+AttachmentHandle text attachments.
+
+
+
+Anchor
+Add anchor links to headings.
+
+
+
+
+
+
+Community Plugins
+Explore plugins from the community.
+
+
+
## Examples
@@ -89,7 +105,7 @@ A list of examples inspired by popular platforms.
-
+GitHubInspired by GitHub.
@@ -97,7 +113,7 @@ A list of examples inspired by popular platforms.
-
+DiscordInspired by Discord.
@@ -105,7 +121,7 @@ A list of examples inspired by popular platforms.
-
+Math Stack ExchangeInspired by Math Stack Exchange.
diff --git a/docs/src/pages/migration.svelte.md b/docs/src/pages/migration.svelte.md
new file mode 100644
index 0000000..72d25dc
--- /dev/null
+++ b/docs/src/pages/migration.svelte.md
@@ -0,0 +1,57 @@
+---
+title: Migration Guide
+section: Overview
+---
+
+# Major Changes
+
+## Removal of Marked
+
+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).
+
+## Syntax highlighter update
+
+SpeedHighlight has been replaced with [Shiki](https://shiki.matsu.io/). It now offers support for more languages, themes, and extensibility.
+
+Make sure to remove previous themes imports, as Shiki uses JS based ones.
+
+```ts
+import 'carta-md/light.css'; // ๐ To be removed!
+```
+
+And also update the default theme. Previous based selectors should be removed:
+
+```css
+/* ๐ To be removed! */
+[class*='shj-lang-'] {
+ /* ... */
+}
+```
+
+## Removed verbose prefixes
+
+Many exports have been renamed to make them less verbose:
+
+- `CartaEditor` -> `MarkdownEditor` (old one still supported);
+- `CartaRenderer` -> `Markdown` (old one still supported);
+- `CartaEvent` -> `Event`;
+- `CartaEventType` -> `EventType`;
+- `CartaExtension` -> `Plugin`;
+- `CartaExtensionComponent` -> `ExtensionComponent`;
+- `CartaOptions` -> `Options`;
+- `CartaHistory` -> `TextAreaHistory`;
+- `CartaHistoryOptions` -> `TextAreaHistoryOptions`;
+- `CartaIcon` -> `Icon`;
+- `CartaListener` -> `Listener`;
+- `CartaInput` -> `InputEnhancer`;
+- `CartaRenderer` -> `Renderer`;
+- `CartaLabels` -> `Labels`;
+
+# 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;
+- Removed deprecated options `postProcess` for `plugin-tikz`;
+- `Carta.options` are no longer available.
diff --git a/docs/src/pages/plugins/anchor.svelte.md b/docs/src/pages/plugins/anchor.svelte.md
new file mode 100644
index 0000000..4b17150
--- /dev/null
+++ b/docs/src/pages/plugins/anchor.svelte.md
@@ -0,0 +1,70 @@
+---
+section: Plugins
+title: Anchor
+---
+
+
+
+This plugin adds `id` attributes and permalinks to headings.
+
+## Installation
+
+
+
+```
+npm i @cartamd/plugin-anchor
+```
+
+
+
+## Setup
+
+### Styles
+
+Import the default theme, or create you own:
+
+
+
+```ts
+import '@cartamd/plugin-anchor/default.css';
+```
+
+
+
+### Extension
+
+
+
+```svelte
+
+
+
+```
+
+
+
+## Options
+
+Here are the options you can pass to `anchor()`:
+
+```ts
+export interface AnchorExtensionOptions {
+ /**
+ * rehype-slug options.
+ */
+ slug?: SlugOptions;
+ /**
+ * rehype-autolink-headings options.
+ */
+ autolink?: AutolinkOptions;
+}
+```
diff --git a/docs/src/pages/plugins/attachment.svelte.md b/docs/src/pages/plugins/attachment.svelte.md
index 2543c3d..bbd0eca 100644
--- a/docs/src/pages/plugins/attachment.svelte.md
+++ b/docs/src/pages/plugins/attachment.svelte.md
@@ -35,7 +35,7 @@ import '@cartamd/plugin-attachment/default.css';
```svelte
-
+
```
diff --git a/docs/src/pages/plugins/code.svelte.md b/docs/src/pages/plugins/code.svelte.md
index 1462531..d1c2c82 100644
--- a/docs/src/pages/plugins/code.svelte.md
+++ b/docs/src/pages/plugins/code.svelte.md
@@ -7,8 +7,7 @@ title: Code
import Code from '$lib/components/code/Code.svelte';
-This plugin adds support for code blocks **syntax highlighting**.
-This is done using [Speed-highlight JS](https://github.com/speed-highlight/core), which supports dynamic imports. This way, languages definitions are only imported at the moment of need.
+This plugin adds support for code blocks **syntax highlighting**. It uses the same highlighter from the core package(Shiki).
## Installation
@@ -36,34 +35,34 @@ import '@cartamd/plugin-code/default.css';
### Using the default highlighter
-Carta comes with a default highlighter that matches the one used to highlight Markdown in the editor and is used by default.
-The theme is the same as the one used in the main Carta package (`carta-md/light.css` or `carta-md/dark.css`).
-[Here](https://github.com/speed-highlight/core/tree/main/src/themes) you can find other themes.
-
-### Using a custom highlighter
-
-You can also provide a custom highlighter, that can be either sync or async.
+Carta comes with a default highlighter that matches the one used to highlight markdown in the editor and is used by default (Shiki). If you want to use a theme different from the one used to highlight Markdown, you can specify it in the options.
```ts
-code({
- customHighlight: {
- highlighter: (code, lang) => myCustomHighlighter(code, lang),
- langPrefix: 'my-highlighter-'
- }
+const carta = new Carta({
+ // ...
+ extensions: [
+ code({
+ theme: 'ayu-light'
+ })
+ ]
});
```
+### Using a custom highlighter
+
+It is no longer possible to specify a custom highlighter in this plugin. However, there are many different [Remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins) that provide syntax highlighting.
+
### Extension
```svelte
-
+
```
## Options
-Here are the options you can pass to `code()`:
-
-```ts
-interface CodeExtensionOptions {
- /**
- * Default language when none is provided.
- */
- defaultLanguage?: string;
- /**
- * Whether to autodetect a language when none is provided.
- * Overwritten by `defaultLanguage`.
- */
- autoDetect?: string;
- /**
- * Line numbering.
- * @defaults false.
- */
- lineNumbering?: boolean;
-
- /**
- * Options for custom syntax highlighting.
- */
- customHighlight?: {
- /**
- * Custom highlight function. Beware that you'll have to provide your own styles.
- * This function needs to convert a string of code into html.
- */
- highlighter: (code: string, lang: string) => string | Promise;
- /**
- * The language tag found immediately after the code block opening marker is
- * appended to this to form the class attribute added to the `` element.
- */
- langPrefix: string;
- };
-}
-```
+The options you can pass to `code()` extend the ones provided by [Shiki](https://shiki.matsu.io/guide/transformers).
diff --git a/docs/src/pages/plugins/emoji.svelte.md b/docs/src/pages/plugins/emoji.svelte.md
index df7fa01..e7162bd 100644
--- a/docs/src/pages/plugins/emoji.svelte.md
+++ b/docs/src/pages/plugins/emoji.svelte.md
@@ -39,7 +39,7 @@ import '@cartamd/plugin-emoji/default.css';
```svelte
-
+
```
diff --git a/docs/src/pages/plugins/math.svelte.md b/docs/src/pages/plugins/math.svelte.md
index a7691fd..7d4cc5c 100644
--- a/docs/src/pages/plugins/math.svelte.md
+++ b/docs/src/pages/plugins/math.svelte.md
@@ -5,7 +5,7 @@ title: Math
-
+
```
@@ -103,7 +103,7 @@ Pythagorean theorem: $a^2+b^2=c^2$
-
+
@@ -119,7 +119,7 @@ $$
-
+
## Options
@@ -131,7 +131,6 @@ interface MathExtensionOptions {
* Options for inline katex, eg: $a^2+b^2=c^2$
*/
inline?: {
- katexOptions?: KatexOptions;
/**
* @default control+m
*/
@@ -144,23 +143,18 @@ interface MathExtensionOptions {
* $$
*/
block?: {
- /**
- * Tag the generated katex will be put into. Must have `display: block`.
- */
- tag?: string;
- /**
- * Whether to center the generated expression.
- * @default true
- */
- center?: boolean;
- /**
- * Class for generated katex.
- */
- class?: string;
/**
* @default ctrl+shift+m
*/
shortcut?: Set;
- katexOptions?: KatexOptions;
};
+ /**
+ * Options for remark-math
+ */
+ remarkMath?: RemarkMathOptions;
+ /**
+ * Options for rehype-katex
+ */
+ rehypeKatex?: RehypeKatexOptions;
+}
```
diff --git a/docs/src/pages/plugins/slash.svelte.md b/docs/src/pages/plugins/slash.svelte.md
index cc029cb..5593a72 100644
--- a/docs/src/pages/plugins/slash.svelte.md
+++ b/docs/src/pages/plugins/slash.svelte.md
@@ -39,7 +39,7 @@ import '@cartamd/plugin-slash/default.css';
```svelte
-
+
```
diff --git a/docs/src/pages/plugins/tikz.svelte.md b/docs/src/pages/plugins/tikz.svelte.md
index 7557c42..c878f8a 100644
--- a/docs/src/pages/plugins/tikz.svelte.md
+++ b/docs/src/pages/plugins/tikz.svelte.md
@@ -20,7 +20,7 @@ npm i @cartamd/plugin-tikz
## Important Notes
1. This plugin requires the import of a **heavy** library (~7Mb), which is dynamically imported at runtime;
-2. Generated images are **not ssr compatible**, as they are rendered in the browser;
+2. Generated images are **not SSR compatible**, as they are rendered in the browser;
3. You need to update your sanitizer to allow the specific tag: `
`.
## Setup
@@ -29,7 +29,7 @@ npm i @cartamd/plugin-tikz
```svelte
-
+
```
diff --git a/docs/src/pages/using-components.svelte.md b/docs/src/pages/using-components.svelte.md
new file mode 100644
index 0000000..f301c51
--- /dev/null
+++ b/docs/src/pages/using-components.svelte.md
@@ -0,0 +1,217 @@
+---
+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 91e45c3..723f83f 100644
--- a/docs/src/routes/+layout.svelte
+++ b/docs/src/routes/+layout.svelte
@@ -4,8 +4,8 @@
import Navbar from '$lib/components/navbar/Navbar.svelte';
import Sidebar from '$lib/components/sidebar/Sidebar.svelte';
import Footer from '$lib/components/footer/Footer.svelte';
- import '../app.postcss';
import { base } from '$app/paths';
+ import '../app.postcss';
@@ -24,6 +24,6 @@
-
+
diff --git a/docs/src/routes/+layout.ts b/docs/src/routes/+layout.ts
index 189f71e..a4c5a95 100644
--- a/docs/src/routes/+layout.ts
+++ b/docs/src/routes/+layout.ts
@@ -1 +1,3 @@
+import 'iconify-icon'; // Register iconify web components
+
export const prerender = true;
diff --git a/docs/src/routes/[...slug]/+page.svelte b/docs/src/routes/[...slug]/+page.svelte
index a0df694..449ea7b 100644
--- a/docs/src/routes/[...slug]/+page.svelte
+++ b/docs/src/routes/[...slug]/+page.svelte
@@ -2,10 +2,10 @@
import type { PageData } from './$types';
import { onMount, type SvelteComponent } from 'svelte';
import { page } from '$app/stores';
+ import { base } from '$app/paths';
import '$lib/styles/markdown.scss';
import '$lib/styles/coldark.scss';
- import { base } from '$app/paths';
export let data: PageData;
diff --git a/docs/svelte.config.js b/docs/svelte.config.js
index 0cdd882..3f812da 100644
--- a/docs/svelte.config.js
+++ b/docs/svelte.config.js
@@ -1,7 +1,7 @@
import mdsvexConfig from './mdsvex.config.js';
import adapter from '@sveltejs/adapter-static';
import { mdsvex } from 'mdsvex';
-import { vitePreprocess } from '@sveltejs/kit/vite';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
diff --git a/package.json b/package.json
index 26acf64..85b8d68 100644
--- a/package.json
+++ b/package.json
@@ -53,13 +53,5 @@
"@semantic-release/npm",
"@semantic-release/github"
]
- },
- "pnpm": {
- "overrides": {
- "@adobe/css-tools@<4.3.1": ">=4.3.1",
- "semver@>=7.0.0 <7.5.2": ">=7.5.2",
- "postcss@<8.4.31": ">=8.4.31",
- "undici@<5.26.2": ">=5.26.2"
- }
}
}
diff --git a/packages/carta-md/README.md b/packages/carta-md/README.md
index aadc55b..2b4495b 100644
--- a/packages/carta-md/README.md
+++ b/packages/carta-md/README.md
@@ -1,31 +1,25 @@