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
-
-
-
-```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/migration.svelte.md b/docs/src/pages/migration.svelte.md
index 72d25dc..1a2383c 100644
--- a/docs/src/pages/migration.svelte.md
+++ b/docs/src/pages/migration.svelte.md
@@ -24,8 +24,8 @@ 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-'] {
+ /* 👈 To be removed! */
/* ... */
}
```
@@ -38,7 +38,7 @@ Many exports have been renamed to make them less verbose:
- `CartaRenderer` -> `Markdown` (old one still supported);
- `CartaEvent` -> `Event`;
- `CartaEventType` -> `EventType`;
-- `CartaExtension` -> `Plugin`;
+- `CartaExtension` -> `Extension`;
- `CartaExtensionComponent` -> `ExtensionComponent`;
- `CartaOptions` -> `Options`;
- `CartaHistory` -> `TextAreaHistory`;
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/packages/carta-md/package.json b/packages/carta-md/package.json
index bc074ef..e1eeb05 100644
--- a/packages/carta-md/package.json
+++ b/packages/carta-md/package.json
@@ -39,7 +39,7 @@
"remark-gfm": "^4.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.0",
- "shiki": "^1.4.0",
+ "shiki": "^1.3.0",
"unified": "^11.0.4"
},
"peerDependencies": {
diff --git a/packages/carta-md/src/lib/internal/components/Input.svelte b/packages/carta-md/src/lib/internal/components/Input.svelte
index 3874591..c3fe8b6 100644
--- a/packages/carta-md/src/lib/internal/components/Input.svelte
+++ b/packages/carta-md/src/lib/internal/components/Input.svelte
@@ -63,14 +63,18 @@
}
};
+ const normalize = (text: string) => {
+ return text.replaceAll('\r\n', '\n');
+ };
+
const highlightNestedLanguages = debounce(async (text: string) => {
const highlighter = await carta.highlighter();
const { updated } = await loadNestedLanguages(highlighter, text);
if (updated) highlight(text);
}, 300);
- $: highlight(value).then(resize);
- $: highlightNestedLanguages(value);
+ $: highlight(normalize(value)).then(resize);
+ $: highlightNestedLanguages(normalize(value));
onMount(() => {
mounted = true;
diff --git a/packages/carta-md/src/lib/internal/highlight.ts b/packages/carta-md/src/lib/internal/highlight.ts
index 16cfddf..ed2df86 100644
--- a/packages/carta-md/src/lib/internal/highlight.ts
+++ b/packages/carta-md/src/lib/internal/highlight.ts
@@ -249,8 +249,6 @@ const findNestedLanguages = (text: string) => {
* @returns Whether the highlighter was updated with new languages.
*/
export const loadNestedLanguages = async (highlighter: Highlighter, text: string) => {
- text = text.replaceAll('\r\n', '\n'); // Normalize line endings
-
const languages = findNestedLanguages(text);
const loadedLanguages = highlighter.getLoadedLanguages();
let updated = false;
diff --git a/packages/plugin-anchor/src/lib/default.css b/packages/plugin-anchor/src/lib/default.css
index 1d18753..afbed18 100644
--- a/packages/plugin-anchor/src/lib/default.css
+++ b/packages/plugin-anchor/src/lib/default.css
@@ -1,18 +1,18 @@
-.carta-viewer h1,
-.carta-viewer h2,
-.carta-viewer h3,
-.carta-viewer h4,
-.carta-viewer h5,
-.carta-viewer h6 {
+.carta-renderer h1,
+.carta-renderer h2,
+.carta-renderer h3,
+.carta-renderer h4,
+.carta-renderer h5,
+.carta-renderer h6 {
position: relative;
}
-.carta-viewer h1 .icon.icon-link,
-.carta-viewer h2 .icon.icon-link,
-.carta-viewer h3 .icon.icon-link,
-.carta-viewer h4 .icon.icon-link,
-.carta-viewer h5 .icon.icon-link,
-.carta-viewer h6 .icon.icon-link {
+.carta-renderer h1 .icon.icon-link,
+.carta-renderer h2 .icon.icon-link,
+.carta-renderer h3 .icon.icon-link,
+.carta-renderer h4 .icon.icon-link,
+.carta-renderer h5 .icon.icon-link,
+.carta-renderer h6 .icon.icon-link {
opacity: 0;
content: url('./link.svg');
position: absolute;
@@ -22,11 +22,11 @@
transform: translateY(-50%);
}
-.carta-viewer h1:hover .icon-link,
-.carta-viewer h2:hover .icon-link,
-.carta-viewer h3:hover .icon-link,
-.carta-viewer h4:hover .icon-link,
-.carta-viewer h5:hover .icon-link,
-.carta-viewer h6:hover .icon-link {
+.carta-renderer h1:hover .icon-link,
+.carta-renderer h2:hover .icon-link,
+.carta-renderer h3:hover .icon-link,
+.carta-renderer h4:hover .icon-link,
+.carta-renderer h5:hover .icon-link,
+.carta-renderer h6:hover .icon-link {
opacity: 1;
}
diff --git a/packages/plugin-code/README.md b/packages/plugin-code/README.md
index cbb5c68..7eab23b 100644
--- a/packages/plugin-code/README.md
+++ b/packages/plugin-code/README.md
@@ -1,11 +1,13 @@
# Carta Code Plugin
-This plugin adds support for code blocks **syntax highlighting**. It uses the same highlighter from the core package(Shiki).
+This plugin adds support for code blocks **syntax highlighting**. Install it using:
```
npm i @cartamd/plugin-code
```
+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.
+
## Setup
### Styles
@@ -18,7 +20,7 @@ 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 (Shiki). If you want to use a theme different from the one used to highlight Markdown, you can specify it in the options. Remember to also have it loaded into the highlighter, by specifying it in `shikiOptions`.
+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
const carta = new Carta({
@@ -27,10 +29,7 @@ const carta = new Carta({
code({
theme: 'ayu-light'
})
- ],
- shikiOptions: {
- themes: ['ayu-light']
- }
+ ]
});
```
diff --git a/packages/plugin-code/package.json b/packages/plugin-code/package.json
index 0da3535..e57937b 100644
--- a/packages/plugin-code/package.json
+++ b/packages/plugin-code/package.json
@@ -16,7 +16,7 @@
"build": "tsc && tscp"
},
"devDependencies": {
- "@shikijs/rehype": "^1.4.0",
+ "@shikijs/rehype": "^1.3.0",
"@types/node": "^18.16.3",
"carta-md": "workspace:*",
"typescript": "^5.0.4",
@@ -30,7 +30,7 @@
],
"version": "4.0.0",
"dependencies": {
- "@shikijs/rehype": "^1.4.0",
+ "@shikijs/rehype": "^1.3.0",
"unified": "^11.0.4"
},
"keywords": [
diff --git a/packages/plugin-tikz/src/index.ts b/packages/plugin-tikz/src/index.ts
index 8ef05de..f46161b 100644
--- a/packages/plugin-tikz/src/index.ts
+++ b/packages/plugin-tikz/src/index.ts
@@ -86,20 +86,15 @@ const tikzTransformer: UnifiedPlugin<
hast.Root
> = ({ carta, options }) => {
return async function (tree) {
- visit(tree, (pre, index, parent) => {
+ visit(tree, (node, index, parent) => {
if (typeof document === 'undefined') {
// Cannot run outside the browser
return;
}
- if (pre.type !== 'element') return;
- const preElement = pre as hast.Element;
- if (preElement.tagName !== 'pre') return;
- const element = pre.children.at(0) as hast.Element | undefined;
- if (!element) return;
-
+ if (node.type !== 'element') return;
+ const element = node as hast.Element;
if (element.tagName !== 'code') return;
- if (!element.properties['className']) return;
if (!(element.properties['className'] as string[]).includes('language-tikz')) return;
// Element is a TikZ code block
@@ -134,7 +129,6 @@ const tikzTransformer: UnifiedPlugin<
}
const hastNode = fromDom(container) as hast.Element;
-
parent?.children.splice(index!, 1, hastNode);
return [SKIP, index!];
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1968455..f2eb6c4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -172,8 +172,8 @@ importers:
specifier: ^11.1.0
version: 11.1.0
shiki:
- specifier: ^1.4.0
- version: 1.4.0
+ specifier: ^1.3.0
+ version: 1.3.0
svelte:
specifier: ^3.54.0 || ^4.0.0
version: 4.2.2
@@ -297,8 +297,8 @@ importers:
packages/plugin-code:
dependencies:
'@shikijs/rehype':
- specifier: ^1.4.0
- version: 1.4.0
+ specifier: ^1.3.0
+ version: 1.3.0
unified:
specifier: ^11.0.4
version: 11.0.4
@@ -1422,25 +1422,25 @@ packages:
- supports-color
dev: true
- /@shikijs/core@1.4.0:
- resolution: {integrity: sha512-CxpKLntAi64h3j+TwWqVIQObPTED0FyXLHTTh3MKXtqiQNn2JGcMQQ362LftDbc9kYbDtrksNMNoVmVXzKFYUQ==}
+ /@shikijs/core@1.3.0:
+ resolution: {integrity: sha512-7fedsBfuILDTBmrYZNFI8B6ATTxhQAasUHllHmjvSZPnoq4bULWoTpHwmuQvZ8Aq03/tAa2IGo6RXqWtHdWaCA==}
dev: false
- /@shikijs/rehype@1.4.0:
- resolution: {integrity: sha512-Ba6QHYx+EIEvmqyNy/B49KAz3rXsTfAqYRY3KTZjPWonytokGOiJ1q/FV9l13D/ad6Qv+eWKhkAz6ITxx6ziFA==}
+ /@shikijs/rehype@1.3.0:
+ resolution: {integrity: sha512-CknEidx0ZTg3TeYAPU4ah8cr31a16neBbMyQ5kwAVdkloCe65uhQp+C/FEFs8NRir4eU5XCDA/+w2v5wnN6zgQ==}
dependencies:
- '@shikijs/transformers': 1.4.0
+ '@shikijs/transformers': 1.3.0
'@types/hast': 3.0.4
hast-util-to-string: 3.0.0
- shiki: 1.4.0
+ shiki: 1.3.0
unified: 11.0.4
unist-util-visit: 5.0.0
dev: false
- /@shikijs/transformers@1.4.0:
- resolution: {integrity: sha512-kzvlWmWYYSeaLKRce/kgmFFORUtBtFahfXRKndor0b60ocYiXufBQM6d6w1PlMuUkdk55aor9xLvy9wy7hTEJg==}
+ /@shikijs/transformers@1.3.0:
+ resolution: {integrity: sha512-3mlpg2I9CjhjE96dEWQOGeCWoPcyTov3s4aAsHmgvnTHa8MBknEnCQy8/xivJPSpD+olqOqIEoHnLfbNJK29AA==}
dependencies:
- shiki: 1.4.0
+ shiki: 1.3.0
dev: false
/@sveltejs/adapter-auto@3.1.1(@sveltejs/kit@2.5.4):
@@ -6435,10 +6435,10 @@ packages:
engines: {node: '>=8'}
dev: true
- /shiki@1.4.0:
- resolution: {integrity: sha512-5WIn0OL8PWm7JhnTwRWXniy6eEDY234mRrERVlFa646V2ErQqwIFd2UML7e0Pq9eqSKLoMa3Ke+xbsF+DAuy+Q==}
+ /shiki@1.3.0:
+ resolution: {integrity: sha512-9aNdQy/etMXctnPzsje1h1XIGm9YfRcSksKOGqZWXA/qP9G18/8fpz5Bjpma8bOgz3tqIpjERAd6/lLjFyzoww==}
dependencies:
- '@shikijs/core': 1.4.0
+ '@shikijs/core': 1.3.0
dev: false
/signal-exit@3.0.7: