Compare commits

...

28 commits
v4 ... master

Author SHA1 Message Date
BearToCode
2177545454 docs: fix grammar 2024-05-02 19:28:12 +02:00
BearToCode
a303834127 build: fix shiki version difference 2024-05-02 19:26:54 +02:00
BearToCode
3fd18a6796 fix: update shiki, fix latex highlighting issues 2024-05-02 17:26:17 +02:00
BearToCode
486a885704 docs: specify to load the language into the highlighter 2024-05-02 17:11:26 +02:00
BearToCode
8e5803a1c4 docs: fix style check issue 2024-05-02 11:30:35 +02:00
BearToCode
0ff9e5b124 docs: add guide on using Svelte components 2024-05-02 11:26:12 +02:00
BearToCode
eb114a7311 docs: small fix in style 2024-05-01 18:21:40 +02:00
BearToCode
34d4f400e6 docs: fix wrong name for Plugins 2024-05-01 18:15:42 +02:00
BearToCode
cc79b25288 docs: add missing copy button 2024-05-01 16:35:47 +02:00
BearToCode
a44b8e971d docs: add dark mode chapter 2024-05-01 16:30:13 +02:00
BearToCode
bafc86d8d3 fix: handle caret endings in Markdown component
fix: #56
2024-04-28 15:46:51 +02:00
BearToCode
a086ece088 docs: update old plugin code readme 2024-04-20 20:05:27 +02:00
BearToCode
dddaad1f1b fix(plugin-anchor): wrong class in default stylesheet 2024-04-20 19:46:00 +02:00
BearToCode
7261d295e2 docs: fix editor code having padding 2024-04-18 12:49:11 +02:00
BearToCode
482fd4b8af fix(plugin-tikz): remove wrapping pre 2024-04-15 18:16:43 +02:00
BearToCode
dfc8812123 fix(plugin-tikz): prevent crash when className property is not set 2024-04-14 18:37:48 +02:00
BearToCode
54358b649b fix(plugin-code): do not import isSingleTheme for main package
fix #50
2024-04-14 18:24:18 +02:00
BearToCode
7ad874e6aa fix(plugin-code): module not found error 2024-04-14 18:08:33 +02:00
BearToCode
545864a202 docs: fix grammar/style 2024-04-13 19:59:43 +02:00
BearToCode
0cef0b94ed docs: fix laggy header tracker 2024-04-12 20:14:11 +02:00
BearToCode
20d58a856b docs: fix code background 2024-04-12 16:34:34 +02:00
BearToCode
1a61f58e7d fix(plugin-tikz): invalid version 2024-04-12 16:22:49 +02:00
BearToCode
7d4034c316 fix(plugin-code): invalid version 2024-04-12 16:18:06 +02:00
BearToCode
26241b5bcd fix(plugin-emoji): invalid version 2024-04-12 16:12:56 +02:00
BearToCode
3785fd01c0 fix(plugin-slash): invalid version 2024-04-12 16:07:48 +02:00
BearToCode
c9be45a1ee fix(plugin-math): invalid version 2024-04-12 16:02:45 +02:00
BearToCode
c4428b8150 docs: fix relative links 2024-04-12 16:00:39 +02:00
Davide
036af273f9
v4 (#46) 2024-04-12 15:55:06 +02:00
22 changed files with 431 additions and 97 deletions

View file

@ -1,5 +1,7 @@
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
import { onNavigate } from '$app/navigation';
import { debounce, throttle } from '$lib/utils';
import { onMount } from 'svelte';
const PADDING = 80;
@ -7,49 +9,58 @@
let className = '';
let headers: HTMLElement[] = [];
let scrollY = 0;
let selectedHeaderIndex = 0;
function retrieveHeaders() {
const markdownContainer = document.querySelector('.markdown');
if (!markdownContainer) return;
headers = Array.from(markdownContainer.querySelectorAll('h1, h2, h3')) as HTMLElement[];
headers = Array.from(
document.querySelectorAll('.markdown > h1, .markdown > h2, .markdown > h3')
) as HTMLElement[];
}
function highlightHeader(header: HTMLElement, nextHeader: HTMLElement | null, index: number) {
const headerHasReachedTop = header.getBoundingClientRect().top <= PADDING || index == 0;
const nextHeaderReachedTop = nextHeader && nextHeader.getBoundingClientRect().top <= PADDING;
return !nextHeaderReachedTop && headerHasReachedTop;
}
const highlightHeader = () => {
for (let index = headers.length - 1; index >= 0; index--) {
const header = headers[index];
const rect = header.getBoundingClientRect();
if (rect.top < PADDING) {
selectedHeaderIndex = index;
return;
}
}
selectedHeaderIndex = 0;
};
let observer: MutationObserver;
const [throttledHighlightHeader] = throttle(highlightHeader, 100);
const debouncedHighlightHeader = debounce(highlightHeader, 100);
onMount(() => {
observer = new MutationObserver(retrieveHeaders);
observer.observe(document.body, { childList: true, subtree: true });
retrieveHeaders();
});
onDestroy(() => {
observer?.disconnect();
onNavigate(() => {
setTimeout(() => {
retrieveHeaders();
highlightHeader();
}, 300);
});
onMount(retrieveHeaders);
</script>
<svelte:window bind:scrollY />
<svelte:window
on:scroll={() => {
throttledHighlightHeader();
debouncedHighlightHeader(); // So it is called at the end of the scroll event
}}
/>
<div class="h-full space-y-3 {className}">
{#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}
{#key selectedHeaderIndex}
{#if header.children[0] instanceof HTMLAnchorElement && header.children[0].href}
<a
style="margin-left: {margin * 0.75}rem;"
class="block text-sm {highlightHeader(header, nextHeader, i)
class="block text-sm {selectedHeaderIndex === i
? 'font-medium text-sky-300'
: 'text-neutral-400'}"
href={header.children[0].href}>{header.innerText}</a
>
{/key}
{/if}
{/if}
{/key}
{/each}
</div>

View file

@ -45,6 +45,12 @@
<span class="text-[0.95rem]">Community Plugins</span>
</SidebarLink>
<!-- Using Svelte Components -->
<SidebarLink href="/using-components">
<iconify-icon icon="ri:svelte-fill" class="text-xl"></iconify-icon>
<span class="text-[0.95rem]">Using Components</span>
</SidebarLink>
<h3 class="mb-3 ml-4 mt-6 text-sm font-medium first:mt-0 last:mb-0">Plugins</h3>
<!-- Math -->

View file

@ -46,7 +46,18 @@
@apply rounded bg-neutral-800 px-1 text-neutral-50;
}
.carta-renderer code {
p,
h1,
h2,
h3 {
code {
@apply px-1;
}
}
.carta-editor code {
font-family: 'Fira Code', monospace;
background: transparent;
padding: 0;
}
}

View file

@ -54,3 +54,41 @@ export const flyAndScale = (
easing: cubicOut
};
};
export const throttle = <R, A extends unknown[]>(
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<T extends unknown[]>(cb: (...args: T) => unknown, wait = 1000) {
let timeout: NodeJS.Timeout;
return (...args: T) => {
clearTimeout(timeout);
timeout = setTimeout(() => cb(...args), wait);
};
}

View file

@ -43,11 +43,39 @@ 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 `<MarkdownEditor>` you can differentiate the themes of multiple editors.
## Changing Markdown color theme
## Dark mode
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.
When using dark mode, there are two different themes that have to be changed: the editor theme and the one used for syntax highlighting:
<Code>
```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;
}
```
</Code>
## 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:
@ -62,7 +90,7 @@ const carta = new Carta({
</Code>
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:
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:
<Code>

View file

@ -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.

View file

@ -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](/plugins/math) and [plugin-anchor](/plugins/anchor).
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
@ -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. SHJ based selectors should be removed:
And also update the default theme. Previous 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, here are them:
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` -> `Extension`;
- `CartaExtension` -> `Plugin`;
- `CartaExtensionComponent` -> `ExtensionComponent`;
- `CartaOptions` -> `Options`;
- `CartaHistory` -> `TextAreaHistory`;
@ -49,7 +49,7 @@ Many exports have been renamed to make them less verbose, here are them:
- `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;

View file

@ -0,0 +1,217 @@
---
title: Using Svelte Components
section: Overview
---
<script>
import Code from '$lib/components/code/Code.svelte';
</script>
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:
<Code>
```shell
npm i unist-util-visit
# Types
npm i -D unified hast
```
</Code>
Let's create a Unified plugin. The basic structure of a plugin is the following:
<Code>
```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
}
}
}
```
</Code>
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:
<Code>
```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];
});
};
};
```
</Code>
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
<span type="hashtag" value="pizza"> #pizza </span>
```
### Configuring the transformer
Unified plugins need to be wrapped inside a `UnifiedTransformer` type, to be able to be used in Carta.
<Code>
```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);
}
};
```
</Code>
### Mounting the components
We now want to replace the generated hashtag placeholders with the following element:
<Code>
```svelte
<!-- Hashtag.svelte -->
<script>
export let value;
</script>
<button
on:click={() => {
console.log('Hashtag clicked!');
}}
>
#{value}
</button>
```
</Code>
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.
<Code>
```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();
}
}
];
```
</Code>
### Using the plugin
Let's now create a Plugin with the transformer and the listener:
<Code>
```ts
import type { Plugin } from 'carta-md';
export const hashtag = (): Plugin => ({
transformers: [hashtagTransformer],
listeners: [convertHashtags]
});
```
</Code>
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).

View file

@ -24,6 +24,6 @@
<slot />
<Footer />
</main>
<HeaderTracker class="sticky top-24 hidden w-[30rem] xl:block" />
<HeaderTracker class="sticky top-24 hidden w-[15rem] flex-shrink-0 xl:block" />
</div>
</div>

View file

@ -39,7 +39,7 @@
"remark-gfm": "^4.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.0",
"shiki": "^1.3.0",
"shiki": "^1.4.0",
"unified": "^11.0.4"
},
"peerDependencies": {

View file

@ -63,18 +63,14 @@
}
};
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(normalize(value)).then(resize);
$: highlightNestedLanguages(normalize(value));
$: highlight(value).then(resize);
$: highlightNestedLanguages(value);
onMount(() => {
mounted = true;

View file

@ -249,6 +249,8 @@ 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;

View file

@ -1,18 +1,18 @@
.carta-renderer h1,
.carta-renderer h2,
.carta-renderer h3,
.carta-renderer h4,
.carta-renderer h5,
.carta-renderer h6 {
.carta-viewer h1,
.carta-viewer h2,
.carta-viewer h3,
.carta-viewer h4,
.carta-viewer h5,
.carta-viewer h6 {
position: relative;
}
.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 {
.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 {
opacity: 0;
content: url('./link.svg');
position: absolute;
@ -22,11 +22,11 @@
transform: translateY(-50%);
}
.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 {
.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 {
opacity: 1;
}

View file

@ -1,13 +1,11 @@
# Carta Code Plugin
This plugin adds support for code blocks **syntax highlighting**. Install it using:
This plugin adds support for code blocks **syntax highlighting**. It uses the same highlighter from the core package(Shiki).
```
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
@ -20,7 +18,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.
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`.
```ts
const carta = new Carta({
@ -29,7 +27,10 @@ const carta = new Carta({
code({
theme: 'ayu-light'
})
]
],
shikiOptions: {
themes: ['ayu-light']
}
});
```

View file

@ -16,7 +16,7 @@
"build": "tsc && tscp"
},
"devDependencies": {
"@shikijs/rehype": "^1.3.0",
"@shikijs/rehype": "^1.4.0",
"@types/node": "^18.16.3",
"carta-md": "workspace:*",
"typescript": "^5.0.4",
@ -28,9 +28,9 @@
"files": [
"dist"
],
"version": "3.0.0",
"version": "4.0.0",
"dependencies": {
"@shikijs/rehype": "^1.3.0",
"@shikijs/rehype": "^1.4.0",
"unified": "^11.0.4"
},
"keywords": [

View file

@ -1,11 +1,28 @@
import { DualTheme, Theme, isSingleTheme, type Plugin } from 'carta-md';
import { type RehypeShikiOptions } from '@shikijs/rehype';
import type { DualTheme, Theme, Plugin } from 'carta-md';
import type { RehypeShikiOptions } from '@shikijs/rehype';
import rehypeShikiFromHighlighter from '@shikijs/rehype/core';
export type CodeExtensionOptions = Omit<RehypeShikiOptions, 'theme' | 'themes'> & {
theme?: Theme | DualTheme;
};
// FIXME: find a better solution then copy-pasting these functions in next version.
// However, when importing from carta-md, this causes a MODULE_NOT_FOUND error
// for some reason.
/**
* Checks if a theme is a dual theme.
* @param theme The theme to check.
* @returns Whether the theme is a dual theme.
*/
export const isDualTheme = (theme: Theme | DualTheme): theme is DualTheme =>
typeof theme == 'object' && 'light' in theme && 'dark' in theme;
/**
* Checks if a theme is a single theme.
* @param theme The theme to check.
* @returns Whether the theme is a single theme.
*/
export const isSingleTheme = (theme: Theme | DualTheme): theme is Theme => !isDualTheme(theme);
/**
* Carta code highlighting plugin. Themes available on [GitHub](https://github.com/speed-highlight/core/tree/main/dist/themes).
*/
@ -17,6 +34,7 @@ export const code = (options?: CodeExtensionOptions): Plugin => {
type: 'rehype',
async transform({ processor, carta }) {
let theme = options?.theme;
const highlighter = await carta.highlighter();
if (!theme) {
theme = highlighter.theme; // Use the theme specified in the highlighter

View file

@ -1,6 +1,6 @@
{
"name": "@cartamd/plugin-emoji",
"version": "3.0.0",
"version": "4.0.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View file

@ -30,7 +30,7 @@
"rehype-katex": "^7.0.0",
"remark-math": "^6.0.0"
},
"version": "3.0.0",
"version": "4.0.1",
"keywords": [
"carta",
"markdown",

View file

@ -1,6 +1,6 @@
{
"name": "@cartamd/plugin-slash",
"version": "3.0.0",
"version": "4.0.1",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View file

@ -34,7 +34,7 @@
"files": [
"dist"
],
"version": "3.0.0",
"version": "4.0.0",
"keywords": [
"carta",
"markdown",

View file

@ -86,15 +86,20 @@ const tikzTransformer: UnifiedPlugin<
hast.Root
> = ({ carta, options }) => {
return async function (tree) {
visit(tree, (node, index, parent) => {
visit(tree, (pre, index, parent) => {
if (typeof document === 'undefined') {
// Cannot run outside the browser
return;
}
if (node.type !== 'element') return;
const element = node as hast.Element;
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 (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
@ -129,6 +134,7 @@ const tikzTransformer: UnifiedPlugin<
}
const hastNode = fromDom(container) as hast.Element;
parent?.children.splice(index!, 1, hastNode);
return [SKIP, index!];

View file

@ -172,8 +172,8 @@ importers:
specifier: ^11.1.0
version: 11.1.0
shiki:
specifier: ^1.3.0
version: 1.3.0
specifier: ^1.4.0
version: 1.4.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.3.0
version: 1.3.0
specifier: ^1.4.0
version: 1.4.0
unified:
specifier: ^11.0.4
version: 11.0.4
@ -1422,25 +1422,25 @@ packages:
- supports-color
dev: true
/@shikijs/core@1.3.0:
resolution: {integrity: sha512-7fedsBfuILDTBmrYZNFI8B6ATTxhQAasUHllHmjvSZPnoq4bULWoTpHwmuQvZ8Aq03/tAa2IGo6RXqWtHdWaCA==}
/@shikijs/core@1.4.0:
resolution: {integrity: sha512-CxpKLntAi64h3j+TwWqVIQObPTED0FyXLHTTh3MKXtqiQNn2JGcMQQ362LftDbc9kYbDtrksNMNoVmVXzKFYUQ==}
dev: false
/@shikijs/rehype@1.3.0:
resolution: {integrity: sha512-CknEidx0ZTg3TeYAPU4ah8cr31a16neBbMyQ5kwAVdkloCe65uhQp+C/FEFs8NRir4eU5XCDA/+w2v5wnN6zgQ==}
/@shikijs/rehype@1.4.0:
resolution: {integrity: sha512-Ba6QHYx+EIEvmqyNy/B49KAz3rXsTfAqYRY3KTZjPWonytokGOiJ1q/FV9l13D/ad6Qv+eWKhkAz6ITxx6ziFA==}
dependencies:
'@shikijs/transformers': 1.3.0
'@shikijs/transformers': 1.4.0
'@types/hast': 3.0.4
hast-util-to-string: 3.0.0
shiki: 1.3.0
shiki: 1.4.0
unified: 11.0.4
unist-util-visit: 5.0.0
dev: false
/@shikijs/transformers@1.3.0:
resolution: {integrity: sha512-3mlpg2I9CjhjE96dEWQOGeCWoPcyTov3s4aAsHmgvnTHa8MBknEnCQy8/xivJPSpD+olqOqIEoHnLfbNJK29AA==}
/@shikijs/transformers@1.4.0:
resolution: {integrity: sha512-kzvlWmWYYSeaLKRce/kgmFFORUtBtFahfXRKndor0b60ocYiXufBQM6d6w1PlMuUkdk55aor9xLvy9wy7hTEJg==}
dependencies:
shiki: 1.3.0
shiki: 1.4.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.3.0:
resolution: {integrity: sha512-9aNdQy/etMXctnPzsje1h1XIGm9YfRcSksKOGqZWXA/qP9G18/8fpz5Bjpma8bOgz3tqIpjERAd6/lLjFyzoww==}
/shiki@1.4.0:
resolution: {integrity: sha512-5WIn0OL8PWm7JhnTwRWXniy6eEDY234mRrERVlFa646V2ErQqwIFd2UML7e0Pq9eqSKLoMa3Ke+xbsF+DAuy+Q==}
dependencies:
'@shikijs/core': 1.3.0
'@shikijs/core': 1.4.0
dev: false
/signal-exit@3.0.7: