Compare commits

..

No commits in common. "slim" and "master" have entirely different histories.
slim ... master

63 changed files with 2900 additions and 678 deletions

1
.npmrc
View file

@ -1,3 +1,2 @@
engine-strict=true engine-strict=true
auto-install-peers=true auto-install-peers=true
publish-branch=slim

View file

@ -21,9 +21,7 @@
<div align="center"> <div align="center">
<a href="https://beartocode.github.io/carta/">📚 Documentation</a> <a href="https://beartocode.github.io/carta/">📚 Documentation</a>
<span> · </span> <span> · </span>
<a href="https://code.thetadev.de/ThetaDev/carta">Source code</a> <a href="https://github.com/BearToCode/carta">GitHub</a>
<span> · </span>
<a href="https://github.com/BearToCode/carta">Original GitHub</a>
</div> </div>
<br> <br>
@ -38,12 +36,6 @@
Carta is a **lightweight**, **fast** and **extensible** Svelte Markdown editor and viewer. It is powered by [unified](https://github.com/unifiedjs/unified), [remark](https://github.com/remarkjs/remark) and [rehype](https://github.com/rehypejs/rehype). Check out the [examples](http://beartocode.github.io/carta/examples) to see it in action. Carta is a **lightweight**, **fast** and **extensible** Svelte Markdown editor and viewer. It is powered by [unified](https://github.com/unifiedjs/unified), [remark](https://github.com/remarkjs/remark) and [rehype](https://github.com/rehypejs/rehype). Check out the [examples](http://beartocode.github.io/carta/examples) to see it in action.
Differently from most editors, Carta does not include a code editor, but it is _just_ a textarea with syntax highlighting, shortcuts and more. Differently from most editors, Carta does not include a code editor, but it is _just_ a textarea with syntax highlighting, shortcuts and more.
This is a fork of carta which applies the following changes:
- Replace Shiki syntax higlighter with Prism.js (much more lightweight, 90kB vs 385kB bundle size for minimal test application)
- Make markdown renderer class configurable and set it to `prose` by default (for Tailwind support)
- Make markdown input usable without JS
## Features ## Features
- 🌈 Markdown syntax highlighting ([Shiki](https://shiki.style/)); - 🌈 Markdown syntax highlighting ([Shiki](https://shiki.style/));
@ -108,7 +100,7 @@ npm i @cartamd/plugin-name
```svelte ```svelte
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
// Component default theme // Component default theme
import 'carta-md/default.css'; import 'carta-md/default.css';

View file

@ -43,7 +43,7 @@
"@cartamd/plugin-slash": "workspace:^", "@cartamd/plugin-slash": "workspace:^",
"@cartamd/plugin-tikz": "workspace:^", "@cartamd/plugin-tikz": "workspace:^",
"bits-ui": "^0.9.1", "bits-ui": "^0.9.1",
"@thetadev/carta-md": "workspace:^", "carta-md": "workspace:^",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"cmdk-sv": "^0.0.6", "cmdk-sv": "^0.0.6",
"flexsearch": "0.7.21", "flexsearch": "0.7.21",

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { emoji } from '@cartamd/plugin-emoji'; import { emoji } from '@cartamd/plugin-emoji';
import { code } from '@cartamd/plugin-code'; import { code } from '@cartamd/plugin-code';
import PlusCircled from './assets/PlusIcon.svelte'; import PlusCircled from './assets/PlusIcon.svelte';

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { attachment } from '@cartamd/plugin-attachment'; import { attachment } from '@cartamd/plugin-attachment';
import { emoji } from '@cartamd/plugin-emoji'; import { emoji } from '@cartamd/plugin-emoji';
import { slash } from '@cartamd/plugin-slash'; import { slash } from '@cartamd/plugin-slash';

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor, Markdown } from '@thetadev/carta-md'; import { Carta, MarkdownEditor, Markdown } from 'carta-md';
import placeholder from './math-stack-exchange-placeholder.tex?raw'; import placeholder from './math-stack-exchange-placeholder.tex?raw';
import { math } from '@cartamd/plugin-math'; import { math } from '@cartamd/plugin-math';
import { tikz } from '@cartamd/plugin-tikz'; import { tikz } from '@cartamd/plugin-tikz';

View file

@ -37,7 +37,7 @@ Setup a basic editor:
```svelte ```svelte
<script> <script>
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import 'carta-md/default.css'; /* Default theme */ import 'carta-md/default.css'; /* Default theme */
const carta = new Carta({ const carta = new Carta({
@ -68,7 +68,7 @@ Or, if you just want to render content:
```svelte ```svelte
<script> <script>
import { Carta, Markdown } from '@thetadev/carta-md'; import { Carta, Markdown } from 'carta-md';
const carta = new Carta({ const carta = new Carta({
/* ... */ /* ... */

View file

@ -39,7 +39,7 @@ import '@cartamd/plugin-anchor/default.css';
```svelte ```svelte
<script> <script>
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { anchor } from '@cartamd/plugin-anchor'; import { anchor } from '@cartamd/plugin-anchor';
const carta = new Carta({ const carta = new Carta({

View file

@ -35,7 +35,7 @@ import '@cartamd/plugin-attachment/default.css';
```svelte ```svelte
<script> <script>
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { attachment } from '@cartamd/plugin-attachment'; import { attachment } from '@cartamd/plugin-attachment';
const carta = new Carta({ const carta = new Carta({

View file

@ -62,7 +62,7 @@ It is no longer possible to specify a custom highlighter in this plugin. However
```svelte ```svelte
<script> <script>
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { code } from '@cartamd/plugin-code'; import { code } from '@cartamd/plugin-code';
const carta = new Carta({ const carta = new Carta({

View file

@ -39,7 +39,7 @@ import '@cartamd/plugin-emoji/default.css';
```svelte ```svelte
<script> <script>
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { emoji } from '@cartamd/plugin-emoji'; import { emoji } from '@cartamd/plugin-emoji';
const carta = new Carta({ const carta = new Carta({

View file

@ -5,7 +5,7 @@ title: Math
<script> <script>
import Code from '$lib/components/code/Code.svelte'; import Code from '$lib/components/code/Code.svelte';
import { Markdown, Carta } from '@thetadev/carta-md'; import { Markdown, Carta } from 'carta-md';
import { math } from '@cartamd/plugin-math'; import { math } from '@cartamd/plugin-math';
import 'katex/dist/katex.css'; import 'katex/dist/katex.css';
@ -78,7 +78,7 @@ or by using a content delivery network:
```svelte ```svelte
<script> <script>
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { math } from '@cartamd/plugin-math'; import { math } from '@cartamd/plugin-math';
const carta = new Carta({ const carta = new Carta({

View file

@ -39,7 +39,7 @@ import '@cartamd/plugin-slash/default.css';
```svelte ```svelte
<script> <script>
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { slash } from '@cartamd/plugin-slash'; import { slash } from '@cartamd/plugin-slash';
const carta = new Carta({ const carta = new Carta({

View file

@ -29,7 +29,7 @@ npm i @cartamd/plugin-tikz
```svelte ```svelte
<script> <script>
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { tikz } from '@cartamd/plugin-tikz'; import { tikz } from '@cartamd/plugin-tikz';
import '@cartamd/plugin-tikz/fonts.css'; import '@cartamd/plugin-tikz/fonts.css';

View file

@ -113,7 +113,7 @@ Unified plugins need to be wrapped inside a `UnifiedTransformer` type, to be abl
<Code> <Code>
```ts ```ts
import type { UnifiedTransformer } from '@thetadev/carta-md'; import type { UnifiedTransformer } from 'carta-md';
const hashtagTransformer: UnifiedTransformer<'sync'> = { const hashtagTransformer: UnifiedTransformer<'sync'> = {
execution: 'sync', // Sync, since the plugin is synchronous execution: 'sync', // Sync, since the plugin is synchronous
@ -158,7 +158,7 @@ To do that, we create a listener that:
<Code> <Code>
```ts ```ts
import type { Listener } from '@thetadev/carta-md'; import type { Listener } from 'carta-md';
import Hashtag from './Hashtag.svelte'; import Hashtag from './Hashtag.svelte';
const convertHashtags: Listener<'carta-render'> = [ const convertHashtags: Listener<'carta-render'> = [
@ -193,7 +193,7 @@ Let's now create a Plugin with the transformer and the listener:
<Code> <Code>
```ts ```ts
import type { Plugin } from '@thetadev/carta-md'; import type { Plugin } from 'carta-md';
export const hashtag = (): Plugin => ({ export const hashtag = (): Plugin => ({
transformers: [hashtagTransformer], transformers: [hashtagTransformer],
@ -206,7 +206,7 @@ export const hashtag = (): Plugin => ({
We can now use the plugin with the following: We can now use the plugin with the following:
```ts ```ts
import { Carta } from '@thetadev/carta-md'; import { Carta } from 'carta-md';
const carta = new Carta({ const carta = new Carta({
// ... // ...

View file

@ -100,7 +100,7 @@ npm i @cartamd/plugin-name
```svelte ```svelte
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
// Component default theme // Component default theme
import 'carta-md/default.css'; import 'carta-md/default.css';

View file

@ -1,12 +1,12 @@
{ {
"name": "@thetadev/carta-md", "name": "carta-md",
"description": "A lightweight, fully customizable, Markdown editor", "description": "A lightweight, fully customizable, Markdown editor",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://code.thetadev.de/ThetaDev/carta.git" "url": "git+https://github.com/BearToCode/carta.git"
}, },
"exports": { "exports": {
".": { ".": {
@ -14,10 +14,9 @@
"svelte": "./dist/index.js", "svelte": "./dist/index.js",
"import": "./dist/index.js" "import": "./dist/index.js"
}, },
"./default.css": "./dist/default.css", "./default.css": "./dist/default.css"
"./highlight.css": "./dist/highlight.css"
}, },
"version": "1.0.1", "version": "3.0.0",
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
"build": "svelte-kit sync && svelte-package", "build": "svelte-kit sync && svelte-package",
@ -29,7 +28,6 @@
"@sveltejs/kit": "^2.5.4", "@sveltejs/kit": "^2.5.4",
"@sveltejs/package": "^2.3.0", "@sveltejs/package": "^2.3.0",
"@sveltejs/vite-plugin-svelte": "^3.0.2", "@sveltejs/vite-plugin-svelte": "^3.0.2",
"@types/prismjs": "^1.26.4",
"svelte-check": "^3.6.7", "svelte-check": "^3.6.7",
"tslib": "^2.4.1", "tslib": "^2.4.1",
"typescript": "^5.1.6", "typescript": "^5.1.6",
@ -37,11 +35,11 @@
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"prismjs": "^1.29.0",
"rehype-stringify": "^10.0.0", "rehype-stringify": "^10.0.0",
"remark-gfm": "^4.0.0", "remark-gfm": "^4.0.0",
"remark-parse": "^11.0.0", "remark-parse": "^11.0.0",
"remark-rehype": "^11.1.0", "remark-rehype": "^11.1.0",
"shiki": "^1.4.0",
"unified": "^11.0.4" "unified": "^11.0.4"
}, },
"peerDependencies": { "peerDependencies": {

View file

@ -1,22 +1,30 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { type Carta } from '.'; import { loadNestedLanguages, type Carta } from '.';
export let carta: Carta; export let carta: Carta;
export let value: string; export let value: string;
export let theme = 'default'; export let theme = 'default';
export let renderCls = 'prose';
let elem: HTMLDivElement; let elem: HTMLDivElement;
let mounted = false; let mounted = false;
$: rendered = carta.renderSSR(value); let rendered = carta.renderSSR(value);
onMount(async () => { onMount(async () => {
carta.$setRenderer(elem);
// Load highlighting languages
const highlighter = await carta.highlighter();
await loadNestedLanguages(highlighter, value);
// Render using asynchronous renderer
rendered = await carta.render(value);
mounted = true; mounted = true;
}); });
</script> </script>
<div bind:this={elem} class="carta-viewer carta-theme__{theme} {renderCls}"> <div bind:this={elem} class="carta-viewer carta-theme__{theme} markdown-body">
<!-- eslint-disable-next-line svelte/no-at-html-tags --> <!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html rendered} {@html rendered}
{#if mounted} {#if mounted}

View file

@ -15,7 +15,6 @@
export let scroll: 'sync' | 'async' = 'sync'; export let scroll: 'sync' | 'async' = 'sync';
export let disableToolbar = false; export let disableToolbar = false;
export let placeholder = ''; export let placeholder = '';
export let renderCls = 'prose';
export let textarea: TextAreaProps = {}; export let textarea: TextAreaProps = {};
let userLabels: Partial<Labels> = {}; let userLabels: Partial<Labels> = {};
@ -125,7 +124,7 @@
</Input> </Input>
{/if} {/if}
{#if windowMode == 'split' || selectedTab == 'preview'} {#if windowMode == 'split' || selectedTab == 'preview'}
<Renderer {carta} {handleScroll} bind:value bind:elem={rendererElem} {renderCls}> <Renderer {carta} {handleScroll} bind:value bind:elem={rendererElem}>
<!-- Renderer extensions components --> <!-- Renderer extensions components -->
{#if mounted} {#if mounted}
{#each carta.components.filter(({ parent }) => [parent] {#each carta.components.filter(({ parent }) => [parent]

View file

@ -1,88 +0,0 @@
.carta-highlight {
color: #333333;
--hl-dark: #f8f8f2;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata,
.token.punctuation {
color: #6a737d;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.url {
color: #2396e3;
--hl-dark: #71d58a;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.url > .token.content {
color: #5abd60;
--hl-dark: #4dacfa;
}
.token.entity {
color: #6f42c1;
--hl-dark: #b392f0;
}
.token.atrule,
.token.attr-value,
.token.keyword,
.token.class-name,
.token.function,
.token.italic,
.token.deleted,
.token.list {
color: #e16;
--hl-dark: #ff7cc6;
}
.token.inserted {
color: #5abd60;
--hl-dark: #71d58a;
}
.token.regex,
.token.variable,
.token.bold {
color: #f60;
--hl-dark: #b581fd;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.strike {
text-decoration: line-through;
color: #f44;
--hl-dark: #ff5261;
}
.token.title {
font-weight: bold;
color: #212121;
--hl-dark: #e8e8e8;
}
.token.blockquote {
color: #999;
--hl-dark: #7d828b;
}
.token.code-snippet {
color: #2396e3;
--hl-dark: #4dacfa;
}

View file

@ -5,6 +5,7 @@ export type { Icon } from '$lib/internal/icons';
export type { KeyboardShortcut } from '$lib/internal/shortcuts'; export type { KeyboardShortcut } from '$lib/internal/shortcuts';
export type { Prefix } from '$lib/internal/prefixes'; export type { Prefix } from '$lib/internal/prefixes';
export * from '$lib/internal/carta'; export * from '$lib/internal/carta';
export * from '$lib/internal/highlight';
export * from '$lib/internal/textarea-props'; export * from '$lib/internal/textarea-props';
export * from '$lib/internal/labels'; export * from '$lib/internal/labels';
export * from './default.css?inline'; export * from './default.css?inline';

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,330 @@
import { type ThemeInput } from 'shiki';
const theme = {
displayName: 'Carta Dark' as const,
name: 'carta-dark' as const,
semanticHighlighting: true,
fg: '#f8f8f2',
bg: 'transparent',
tokenColors: [
{
scope: ['comment', 'punctuation.definition.comment', 'string.comment'],
settings: {
foreground: '#6a737d'
}
},
{
scope: ['variable.other.constant', 'variable.other.enummember', 'variable.language'],
settings: {
foreground: '#fff'
}
},
{
scope: ['constant', 'entity.name.constant'],
settings: {
foreground: '#71d58a'
}
},
{
scope: ['entity', 'entity.name'],
settings: {
foreground: '#b392f0'
}
},
{
scope: 'variable.parameter.function',
settings: {
foreground: '#e1e4e8'
}
},
{
scope: 'entity.name.tag',
settings: {
foreground: '#85e89d'
}
},
{
scope: ['keyword', 'punctuation.definition.template-expression'],
settings: {
foreground: '#ff7cc6'
}
},
{
scope: ['storage', 'storage.type'],
settings: {
foreground: '#ff7cc6'
}
},
{
scope: ['storage.modifier.package', 'storage.modifier.import', 'storage.type.java'],
settings: {
foreground: '#e1e4e8'
}
},
{
scope: [
'string',
'punctuation.definition.string',
'string punctuation.section.embedded source'
],
settings: {
foreground: '#4dacfa'
}
},
{
scope: 'support',
settings: {
foreground: '#71d58a'
}
},
{
scope: 'meta.property-name',
settings: {
foreground: '#71d58a'
}
},
{
scope: 'variable',
settings: {
foreground: '#b581fd'
}
},
{
scope: 'variable.other',
settings: {
foreground: '#e1e4e8'
}
},
{
scope: 'invalid.broken',
settings: {
fontStyle: 'italic',
foreground: '#fdaeb7'
}
},
{
scope: 'invalid.deprecated',
settings: {
fontStyle: 'italic',
foreground: '#fdaeb7'
}
},
{
scope: 'invalid.illegal',
settings: {
fontStyle: 'italic',
foreground: '#fdaeb7'
}
},
{
scope: 'invalid.unimplemented',
settings: {
fontStyle: 'italic',
foreground: '#fdaeb7'
}
},
{
scope: 'carriage-return',
settings: {
background: '#ff7cc6',
fontStyle: 'italic underline',
foreground: '#24292e'
}
},
{
scope: 'message.error',
settings: {
foreground: '#fdaeb7'
}
},
{
scope: 'string variable',
settings: {
foreground: '#71d58a'
}
},
{
scope: ['source.regexp', 'string.regexp'],
settings: {
foreground: '#4dacfa'
}
},
{
scope: [
'string.regexp.character-class',
'string.regexp constant.character.escape',
'string.regexp source.ruby.embedded',
'string.regexp string.regexp.arbitrary-repitition'
],
settings: {
foreground: '#4dacfa'
}
},
{
scope: 'string.regexp constant.character.escape',
settings: {
fontStyle: 'bold',
foreground: '#85e89d'
}
},
{
scope: 'support.constant',
settings: {
foreground: '#71d58a'
}
},
{
scope: 'support.variable',
settings: {
foreground: '#71d58a'
}
},
{
scope: 'meta.module-reference',
settings: {
foreground: '#71d58a'
}
},
{
scope: 'punctuation.definition.list.begin.markdown',
settings: {
foreground: '#ff7cc6'
}
},
{
scope: ['markup.heading', 'markup.heading entity.name'],
settings: {
fontStyle: 'bold',
foreground: '#e8e8e8'
}
},
{
scope: 'markup.quote',
settings: {
foreground: '#7d828b'
}
},
{
scope: 'markup.italic',
settings: {
fontStyle: 'italic',
foreground: '#ff7cc6'
}
},
{
scope: 'markup.bold',
settings: {
foreground: '#b581fd'
}
},
{
scope: ['markup.underline'],
settings: {
foreground: '#71d58a',
fontStyle: 'underline'
}
},
{
scope: ['markup.strikethrough'],
settings: {
foreground: '#ff5261',
fontStyle: 'strikethrough'
}
},
{
scope: 'markup.inline.raw',
settings: {
foreground: '#4dacfa'
}
},
{
scope: ['markup.deleted', 'meta.diff.header.from-file', 'punctuation.definition.deleted'],
settings: {
background: '#86181d',
foreground: '#fdaeb7'
}
},
{
scope: ['markup.inserted', 'meta.diff.header.to-file', 'punctuation.definition.inserted'],
settings: {
background: '#144620',
foreground: '#85e89d'
}
},
{
scope: ['markup.changed', 'punctuation.definition.changed'],
settings: {
background: '#c24e00',
foreground: '#b581fd'
}
},
{
scope: ['markup.ignored', 'markup.untracked'],
settings: {
background: '#71d58a',
foreground: '#2f363d'
}
},
{
scope: 'meta.diff.range',
settings: {
fontStyle: 'bold',
foreground: '#b392f0'
}
},
{
scope: 'meta.diff.header',
settings: {
foreground: '#71d58a'
}
},
{
scope: 'meta.separator',
settings: {
fontStyle: 'bold',
foreground: '#71d58a'
}
},
{
scope: 'meta.output',
settings: {
foreground: '#71d58a'
}
},
{
scope: [
'brackethighlighter.tag',
'brackethighlighter.curly',
'brackethighlighter.round',
'brackethighlighter.square',
'brackethighlighter.angle',
'brackethighlighter.quote'
],
settings: {
foreground: '#d1d5da'
}
},
{
scope: 'brackethighlighter.unmatched',
settings: {
foreground: '#fdaeb7'
}
},
{
scope: ['constant.other.reference.link', 'string.other.link'],
settings: {
fontStyle: 'underline',
foreground: '#4dacfa'
}
},
{
scope: ['punctuation.definition.markdown', 'fenced_code.block.language'],
settings: {
foreground: '#ff7cc6'
}
}
],
type: 'light'
} satisfies ThemeInput;
export default theme;

View file

@ -0,0 +1,329 @@
import { type ThemeInput } from 'shiki';
const theme = {
displayName: 'Carta Light' as const,
name: 'carta-light' as const,
semanticHighlighting: true,
fg: '#333333',
bg: 'transparent',
tokenColors: [
{
scope: ['comment', 'punctuation.definition.comment', 'string.comment'],
settings: {
foreground: '#6a737d'
}
},
{
scope: ['variable.other.constant', 'variable.other.enummember', 'variable.language'],
settings: {
foreground: '#000'
}
},
{
scope: ['constant', 'entity.name.constant'],
settings: {
foreground: '#3bf'
}
},
{
scope: ['entity', 'entity.name'],
settings: {
foreground: '#6f42c1'
}
},
{
scope: 'variable.parameter.function',
settings: {
foreground: '#24292e'
}
},
{
scope: 'entity.name.tag',
settings: {
foreground: '#22863a'
}
},
{
scope: ['keyword', 'punctuation.definition.template-expression'],
settings: {
foreground: '#e16'
}
},
{
scope: ['storage', 'storage.type'],
settings: {
foreground: '#e16'
}
},
{
scope: ['storage.modifier.package', 'storage.modifier.import', 'storage.type.java'],
settings: {
foreground: '#24292e'
}
},
{
scope: [
'string',
'punctuation.definition.string',
'string punctuation.section.embedded source'
],
settings: {
foreground: '#7d8'
}
},
{
scope: 'support',
settings: {
foreground: '#3bf'
}
},
{
scope: 'meta.property-name',
settings: {
foreground: '#3bf'
}
},
{
scope: 'variable',
settings: {
foreground: '#f60'
}
},
{
scope: 'variable.other',
settings: {
foreground: '#24292e'
}
},
{
scope: 'invalid.broken',
settings: {
fontStyle: 'italic',
foreground: '#b31d28'
}
},
{
scope: 'invalid.deprecated',
settings: {
fontStyle: 'italic',
foreground: '#b31d28'
}
},
{
scope: 'invalid.illegal',
settings: {
fontStyle: 'italic',
foreground: '#b31d28'
}
},
{
scope: 'invalid.unimplemented',
settings: {
fontStyle: 'italic',
foreground: '#b31d28'
}
},
{
scope: 'carriage-return',
settings: {
background: '#e16',
fontStyle: 'italic underline',
foreground: '#fafbfc'
}
},
{
scope: 'message.error',
settings: {
foreground: '#b31d28'
}
},
{
scope: 'string variable',
settings: {
foreground: '#3bf'
}
},
{
scope: ['source.regexp', 'string.regexp'],
settings: {
foreground: '#7d8'
}
},
{
scope: [
'string.regexp.character-class',
'string.regexp constant.character.escape',
'string.regexp source.ruby.embedded',
'string.regexp string.regexp.arbitrary-repitition'
],
settings: {
foreground: '#7d8'
}
},
{
scope: 'string.regexp constant.character.escape',
settings: {
fontStyle: 'bold',
foreground: '#22863a'
}
},
{
scope: 'support.constant',
settings: {
foreground: '#3bf'
}
},
{
scope: 'support.variable',
settings: {
foreground: '#3bf'
}
},
{
scope: 'meta.module-reference',
settings: {
foreground: '#3bf'
}
},
{
scope: 'punctuation.definition.list.begin.markdown',
settings: {
foreground: '#e16'
}
},
{
scope: ['markup.heading', 'markup.heading entity.name'],
settings: {
fontStyle: 'bold',
foreground: '#212121'
}
},
{
scope: 'markup.quote',
settings: {
foreground: '#999'
}
},
{
scope: 'markup.italic',
settings: {
foreground: '#e16'
}
},
{
scope: 'markup.bold',
settings: {
foreground: '#f60'
}
},
{
scope: ['markup.underline'],
settings: {
foreground: '#84f',
fontStyle: 'underline'
}
},
{
scope: ['markup.strikethrough'],
settings: {
foreground: '#f44',
fontStyle: 'strikethrough'
}
},
{
scope: 'markup.inline.raw',
settings: {
foreground: '#5af'
}
},
{
scope: ['markup.deleted', 'meta.diff.header.from-file', 'punctuation.definition.deleted'],
settings: {
background: '#ffeef0',
foreground: '#b31d28'
}
},
{
scope: ['markup.inserted', 'meta.diff.header.to-file', 'punctuation.definition.inserted'],
settings: {
background: '#f0fff4',
foreground: '#22863a'
}
},
{
scope: ['markup.changed', 'punctuation.definition.changed'],
settings: {
background: '#ffebda',
foreground: '#f60'
}
},
{
scope: ['markup.ignored', 'markup.untracked'],
settings: {
background: '#3bf',
foreground: '#f6f8fa'
}
},
{
scope: 'meta.diff.range',
settings: {
fontStyle: 'bold',
foreground: '#6f42c1'
}
},
{
scope: 'meta.diff.header',
settings: {
foreground: '#3bf'
}
},
{
scope: 'meta.separator',
settings: {
fontStyle: 'bold',
foreground: '#3bf'
}
},
{
scope: 'meta.output',
settings: {
foreground: '#3bf'
}
},
{
scope: [
'brackethighlighter.tag',
'brackethighlighter.curly',
'brackethighlighter.round',
'brackethighlighter.square',
'brackethighlighter.angle',
'brackethighlighter.quote'
],
settings: {
foreground: '#586069'
}
},
{
scope: 'brackethighlighter.unmatched',
settings: {
foreground: '#b31d28'
}
},
{
scope: ['constant.other.reference.link', 'string.other.link'],
settings: {
fontStyle: 'underline',
foreground: '#5af'
}
},
{
scope: ['punctuation.definition.markdown', 'fenced_code.block.language'],
settings: {
foreground: '#e16'
}
}
],
type: 'light'
} satisfies ThemeInput;
export default theme;

View file

@ -15,6 +15,16 @@ import { defaultIcons, type Icon, type DefaultIconId } from './icons';
import { defaultPrefixes, type DefaultPrefixId, type Prefix } from './prefixes'; import { defaultPrefixes, type DefaultPrefixId, type Prefix } from './prefixes';
import { Renderer } from './renderer'; import { Renderer } from './renderer';
import { CustomEvent, type MaybeArray } from './utils'; import { CustomEvent, type MaybeArray } from './utils';
import {
loadHighlighter,
loadDefaultTheme,
type Highlighter,
type GrammarRule,
type ShikiOptions,
type DualTheme,
type Theme,
type HighlightingRule
} from './highlight';
/** /**
* Carta-specific event with extra payload. * Carta-specific event with extra payload.
@ -98,6 +108,15 @@ export interface Options {
* HTML sanitizer. * HTML sanitizer.
*/ */
sanitizer: ((html: string) => string) | false; sanitizer: ((html: string) => string) | false;
/**
* Highlighter options.
*/
shikiOptions?: ShikiOptions;
/**
* ShikiJS theme
* @default 'carta-light' for light mode and 'carta-dark' for dark mode.
*/
theme?: Theme | DualTheme;
} }
/** /**
@ -148,6 +167,14 @@ export interface Plugin {
* elements absolutely. * elements absolutely.
*/ */
components?: ExtensionComponents; components?: ExtensionComponents;
/**
* Custom markdown grammar highlight rules for ShiKi.
*/
grammarRules?: GrammarRule[];
/**
* Custom markdown highlighting rules for ShiKi.
*/
highlightingRules?: HighlightingRule[];
/** /**
* Use this callback to execute code when one Carta instance loads the extension. * Use this callback to execute code when one Carta instance loads the extension.
* @param data General Carta related data. * @param data General Carta related data.
@ -158,10 +185,14 @@ export interface Plugin {
export class Carta { export class Carta {
public readonly sanitizer?: (html: string) => string; public readonly sanitizer?: (html: string) => string;
public readonly historyOptions?: TextAreaHistoryOptions; public readonly historyOptions?: TextAreaHistoryOptions;
public readonly theme?: Theme | DualTheme;
public readonly shikiOptions?: ShikiOptions;
public readonly rendererDebounce: number; public readonly rendererDebounce: number;
public readonly keyboardShortcuts: KeyboardShortcut[]; public readonly keyboardShortcuts: KeyboardShortcut[];
public readonly icons: Icon[]; public readonly icons: Icon[];
public readonly prefixes: Prefix[]; public readonly prefixes: Prefix[];
public readonly grammarRules: GrammarRule[];
public readonly highlightingRules: HighlightingRule[];
public readonly textareaListeners: Listeners; public readonly textareaListeners: Listeners;
public readonly cartaListeners: Listeners; public readonly cartaListeners: Listeners;
public readonly components: ExtensionComponents; public readonly components: ExtensionComponents;
@ -172,6 +203,7 @@ export class Carta {
private mElement: HTMLDivElement | undefined; private mElement: HTMLDivElement | undefined;
private mInput: InputEnhancer | undefined; private mInput: InputEnhancer | undefined;
private mRenderer: Renderer | undefined; private mRenderer: Renderer | undefined;
private mHighlighter: Highlighter | Promise<Highlighter> | undefined;
private mSyncTransformers: UnifiedTransformer<'sync'>[] = []; private mSyncTransformers: UnifiedTransformer<'sync'>[] = [];
private mAsyncTransformers: UnifiedTransformer<'async'>[] = []; private mAsyncTransformers: UnifiedTransformer<'async'>[] = [];
@ -185,6 +217,22 @@ export class Carta {
return this.mRenderer; return this.mRenderer;
} }
public async highlighter(): Promise<Highlighter> {
if (!this.mHighlighter) {
const promise = async () => {
return loadHighlighter({
theme: this.theme ?? (await loadDefaultTheme()),
grammarRules: this.grammarRules,
highlightingRules: this.highlightingRules,
shiki: this.shikiOptions
});
};
this.mHighlighter = promise();
this.mHighlighter = await this.mHighlighter;
}
return this.mHighlighter;
}
private elementsToBind: { private elementsToBind: {
elem: HTMLElement; elem: HTMLElement;
portal: HTMLElement; portal: HTMLElement;
@ -194,6 +242,8 @@ export class Carta {
public constructor(options?: Options) { public constructor(options?: Options) {
this.sanitizer = options?.sanitizer || undefined; this.sanitizer = options?.sanitizer || undefined;
this.historyOptions = options?.historyOptions; this.historyOptions = options?.historyOptions;
this.theme = options?.theme;
this.shikiOptions = options?.shikiOptions;
this.rendererDebounce = options?.rendererDebounce ?? 300; this.rendererDebounce = options?.rendererDebounce ?? 300;
// Load plugins // Load plugins
@ -203,6 +253,8 @@ export class Carta {
this.textareaListeners = []; this.textareaListeners = [];
this.cartaListeners = []; this.cartaListeners = [];
this.components = []; this.components = [];
this.grammarRules = [];
this.highlightingRules = [];
const listeners = []; const listeners = [];
for (const ext of options?.extensions ?? []) { for (const ext of options?.extensions ?? []) {
@ -210,6 +262,8 @@ export class Carta {
this.icons.push(...(ext.icons ?? [])); this.icons.push(...(ext.icons ?? []));
this.prefixes.push(...(ext.prefixes ?? [])); this.prefixes.push(...(ext.prefixes ?? []));
this.components.push(...(ext.components ?? [])); this.components.push(...(ext.components ?? []));
this.grammarRules.push(...(ext.grammarRules ?? []));
this.highlightingRules.push(...(ext.highlightingRules ?? []));
listeners.push(...(ext.listeners ?? [])); listeners.push(...(ext.listeners ?? []));
} }

View file

@ -2,8 +2,8 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import type { Carta } from '../carta'; import type { Carta } from '../carta';
import type { TextAreaProps } from '../textarea-props'; import type { TextAreaProps } from '../textarea-props';
import { debounce } from '../utils';
import { Prism, MARKDOWN } from '../highlight'; import { isSingleTheme, loadNestedLanguages } from '../highlight';
export let carta: Carta; export let carta: Carta;
export let value = ''; export let value = '';
@ -34,21 +34,43 @@
const setInput = () => { const setInput = () => {
carta.$setInput(textarea, elem, () => { carta.$setInput(textarea, elem, () => {
value = textarea.value; value = textarea.value;
highlightPrism(value); highlight(value);
}); });
}; };
const highlightPrism = (text: string) => { const highlight = async (text: string) => {
const html = Prism.highlight(text, MARKDOWN, 'md'); const highlighter = await carta.highlighter();
let html: string;
if (isSingleTheme(highlighter.theme)) {
// Single theme
html = highlighter.codeToHtml(text, {
lang: highlighter.lang,
theme: highlighter.theme
});
} else {
// Dual theme
html = highlighter.codeToHtml(text, {
lang: highlighter.lang,
themes: highlighter.theme
});
}
if (carta.sanitizer) { if (carta.sanitizer) {
highlighted = carta.sanitizer(html); highlighted = carta.sanitizer(html);
} else { } else {
highlighted = html; highlighted = html;
} }
resize();
}; };
$: highlightPrism(value); 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);
onMount(() => { onMount(() => {
mounted = true; mounted = true;
@ -69,7 +91,7 @@
class="carta-input" class="carta-input"
bind:this={elem} bind:this={elem}
> >
<div class="carta-input-wrapper" class:mounted> <div class="carta-input-wrapper">
<div <div
class="carta-highlight carta-font-code" class="carta-highlight carta-font-code"
tabindex="-1" tabindex="-1"
@ -116,26 +138,22 @@
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
min-height: 100%; min-height: 100%;
resize: none;
background: transparent; overflow-y: hidden;
resize: none;
padding: 0; padding: 0;
margin: 0; margin: 0;
border: 0; border: 0;
color: transparent;
background: transparent;
outline: none;
tab-size: 4; tab-size: 4;
} }
.mounted > textarea {
overflow-y: hidden;
outline: none;
color: transparent;
}
.carta-highlight { .carta-highlight {
display: none;
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
@ -153,11 +171,7 @@
word-break: break-word; word-break: break-word;
} }
.mounted > .carta-highlight { :global(.carta-highlight .shiki) {
display: block;
}
:global(.carta-highlight) {
margin: 0; margin: 0;
tab-size: 4; tab-size: 4;
background-color: transparent !important; background-color: transparent !important;

View file

@ -6,7 +6,6 @@
export let carta: Carta; export let carta: Carta;
export let value: string; export let value: string;
export let elem: HTMLDivElement; export let elem: HTMLDivElement;
export let renderCls = 'prose';
export let handleScroll: (e: UIEvent) => void; export let handleScroll: (e: UIEvent) => void;
let mounted = false; let mounted = false;
@ -26,7 +25,7 @@
onMount(() => (mounted = true)); onMount(() => (mounted = true));
</script> </script>
<div bind:this={elem} on:scroll={handleScroll} class="carta-renderer {renderCls}"> <div bind:this={elem} on:scroll={handleScroll} class="carta-renderer markdown-body">
<!-- eslint-disable-next-line svelte/no-at-html-tags --> <!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html renderedHtml} {@html renderedHtml}
{#if mounted} {#if mounted}

View file

@ -1,14 +1,268 @@
// @ts-expect-error no type definitions import {
import PrismImport from 'prismjs/components/prism-core'; getHighlighter,
import type PrismType from 'prismjs'; type BundledTheme,
type ThemeInput,
type StringLiteralUnion,
type BundledLanguage,
type SpecialLanguage,
type LanguageInput,
type LanguageRegistration,
type HighlighterGeneric,
bundledLanguages,
bundledThemes,
type ThemeRegistration
} from 'shiki';
import type { Intellisense } from './utils';
const Prism: typeof PrismType = PrismImport; /**
* Custom TextMate grammar rule for the highlighter.
*/
export type GrammarRule = {
name: string;
type: 'block' | 'inline';
definition: LanguageRegistration['repository'][string];
};
globalThis.Prism = Prism; /**
import 'prismjs/components/prism-markup'; * Custom TextMate highlighting rule for the highlighter.
import prismMarkdown from './prism-markdown'; */
prismMarkdown(Prism); export type HighlightingRule = {
light: NonNullable<ThemeRegistration['tokenColors']>[number];
dark: NonNullable<ThemeRegistration['tokenColors']>[number];
};
const MARKDOWN = Prism.languages['md']; /**
* Shiki options for the highlighter.
*/
export type ShikiOptions = {
themes?: Array<ThemeInput | StringLiteralUnion<BundledTheme>>;
langs?: (LanguageInput | StringLiteralUnion<BundledLanguage> | SpecialLanguage)[];
};
export { Prism, MARKDOWN }; type CustomMarkdownLangName = Awaited<(typeof import('./assets/markdown'))['default']['name']>;
type DefaultLightThemeName = Awaited<(typeof import('./assets/theme-light'))['default']['name']>;
type DefaultDarkThemeName = Awaited<(typeof import('./assets/theme-dark'))['default']['name']>;
export const customMarkdownLangName: CustomMarkdownLangName = 'cartamd';
export const defaultLightThemeName: DefaultLightThemeName = 'carta-light';
export const defaultDarkThemeName: DefaultDarkThemeName = 'carta-dark';
export const loadDefaultTheme = async (): Promise<{
light: ThemeRegistration;
dark: ThemeRegistration;
}> => ({
light: structuredClone((await import('./assets/theme-light')).default),
dark: structuredClone((await import('./assets/theme-dark')).default)
});
/**
* Language for the highlighter.
*/
export type Language = Intellisense<BundledLanguage | CustomMarkdownLangName>;
/**
* Theme name for the highlighter.
*/
export type ThemeName = Intellisense<BundledTheme | DefaultLightThemeName | DefaultDarkThemeName>;
/**
* Theme for the highlighter.
*/
export type Theme = ThemeName | ThemeRegistration;
/**
* Dual theme for light and dark mode.
*/
export type DualTheme = {
light: Theme;
dark: Theme;
};
/**
* Options for the highlighter.
*/
export type HighlighterOptions = {
grammarRules: GrammarRule[];
highlightingRules: HighlightingRule[];
theme: Theme | DualTheme;
shiki?: ShikiOptions;
};
/**
* Loads the highlighter instance, with custom rules and options. Uses Shiki under the hood.
* @param rules Custom rules for the highlighter, from plugins.
* @param options Custom options for the highlighter.
* @returns The highlighter instance.
*/
export async function loadHighlighter({
grammarRules,
highlightingRules,
theme,
shiki
}: HighlighterOptions): Promise<Highlighter> {
// Inject rules into the custom markdown language
const injectGrammarRules = (
lang: Awaited<(typeof import('./assets/markdown'))['default']>,
rules: GrammarRule[]
) => {
lang.repository = {
...langDefinition.repository,
...Object.fromEntries(rules.map(({ name, definition }) => [name, definition]))
};
for (const rule of rules) {
if (rule.type === 'block') {
lang.repository.block.patterns.unshift({ include: `#${rule.name}` });
} else {
lang.repository.inline.patterns.unshift({ include: `#${rule.name}` });
}
}
};
const injectHighlightRules = (theme: ThemeRegistration, rules: HighlightingRule[]) => {
if (theme.type === 'light') {
theme.tokenColors ||= [];
theme.tokenColors.unshift(...rules.map(({ light }) => light));
} else {
theme.tokenColors ||= [];
theme.tokenColors.unshift(...rules.map(({ dark }) => dark));
}
};
// Additional themes and languages provided by the user
const themes = shiki?.themes ?? [];
const langs = shiki?.langs ?? [];
const highlighter: HighlighterGeneric<BundledLanguage, BundledTheme> = await getHighlighter({
themes,
langs
});
// Custom markdown language
const langDefinition = (await import('./assets/markdown')).default;
injectGrammarRules(langDefinition, grammarRules);
await highlighter.loadLanguage(langDefinition);
// Custom themes
if (isSingleTheme(theme)) {
let registration: ThemeRegistration;
if (isThemeRegistration(theme)) {
registration = theme;
} else {
registration = (await bundledThemes[theme as BundledTheme]()).default;
}
injectHighlightRules(registration, highlightingRules);
await highlighter.loadTheme(registration);
} else {
const { light, dark } = theme;
let lightRegistration: ThemeRegistration;
let darkRegistration: ThemeRegistration;
if (isThemeRegistration(light)) {
lightRegistration = light;
} else {
lightRegistration = (await bundledThemes[light as BundledTheme]()).default;
}
if (isThemeRegistration(dark)) {
darkRegistration = dark;
} else {
darkRegistration = (await bundledThemes[dark as BundledTheme]()).default;
}
injectHighlightRules(lightRegistration, highlightingRules);
injectHighlightRules(darkRegistration, highlightingRules);
await highlighter.loadTheme(lightRegistration);
await highlighter.loadTheme(darkRegistration);
}
return {
theme,
lang: customMarkdownLangName,
...highlighter
};
}
export interface Highlighter extends HighlighterGeneric<BundledLanguage, BundledTheme> {
/**
* The language specified for the highlighter.
*/
theme: Theme | DualTheme;
/**
* The theme specified for the highlighter.
*/
lang: Language;
}
/**
* Checks if a language is a bundled language.
* @param lang The language to check.
* @returns Whether the language is a bundled language.
*/
export const isBundleLanguage = (lang: string): lang is BundledLanguage =>
Object.keys(bundledLanguages).includes(lang);
/**
* Checks if a theme is a bundled theme.
* @param theme The theme to check.
* @returns Whether the theme is a bundled theme.
*/
export const isBundleTheme = (theme: string): theme is BundledTheme =>
Object.keys(bundledThemes).includes(theme);
/**
* 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);
/**
* Checks if a theme is a theme registration.
* @param theme The theme to check.
* @returns Whether the theme is a theme registration.
*/
export const isThemeRegistration = (theme: Theme): theme is ThemeRegistration =>
typeof theme == 'object';
/**
* Find all nested languages in the markdown text and load them into the highlighter.
* @param text Markdown text to parse for nested languages.
* @returns The set of nested languages found in the text.
*/
const findNestedLanguages = (text: string) => {
const languages = new Set<string>();
const regex = /```([a-z]+)\n([\s\S]+?)\n```/g;
let match: RegExpExecArray | null;
while ((match = regex.exec(text))) {
languages.add(match[1]);
}
return languages;
};
/**
* Load all nested languages found in the markdown text into the highlighter.
* @param highlighter The highlighter instance.
* @param text The text to parse for nested languages.
* @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;
for (const lang of languages) {
if (isBundleLanguage(lang) && !loadedLanguages.includes(lang)) {
await highlighter.loadLanguage(lang);
loadedLanguages.push(lang);
updated = true;
}
}
return {
updated
};
};

View file

@ -1,439 +0,0 @@
// Original source: https://github.com/PrismJS/prism/blob/master/components/prism-markdown.js
export default function (Prism) {
// Allow only one line break
const inner = /(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;
/**
* This function is intended for the creation of the bold or italic pattern.
*
* This also adds a lookbehind group to the given pattern to ensure that the pattern is not backslash-escaped.
*
* _Note:_ Keep in mind that this adds a capturing group.
*
* @param {string} pattern
* @returns {RegExp}
*/
function createInline(pattern) {
pattern = pattern.replace(/<inner>/g, function () {
return inner;
});
return RegExp(/((?:^|[^\\])(?:\\{2})*)/.source + '(?:' + pattern + ')');
}
const tableCell = /(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source;
const tableRow = /\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(
/__/g,
function () {
return tableCell;
}
);
const tableLine = /\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;
Prism.languages.markdown = Prism.languages.extend('markup', {});
Prism.languages.insertBefore('markdown', 'prolog', {
'front-matter-block': {
pattern: /(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,
lookbehind: true,
greedy: true,
inside: {
punctuation: /^---|---$/,
'front-matter': {
pattern: /\S+(?:\s+\S+)*/,
alias: ['yaml', 'language-yaml'],
inside: Prism.languages.yaml
}
}
},
blockquote: {
// > ...
pattern: /^>(?:[\t ]*>)*/m,
alias: 'punctuation'
},
table: {
pattern: RegExp('^' + tableRow + tableLine + '(?:' + tableRow + ')*', 'm'),
inside: {
'table-data-rows': {
pattern: RegExp('^(' + tableRow + tableLine + ')(?:' + tableRow + ')*$'),
lookbehind: true,
inside: {
'table-data': {
pattern: RegExp(tableCell),
inside: Prism.languages.markdown
},
punctuation: /\|/
}
},
'table-line': {
pattern: RegExp('^(' + tableRow + ')' + tableLine + '$'),
lookbehind: true,
inside: {
punctuation: /\||:?-{3,}:?/
}
},
'table-header-row': {
pattern: RegExp('^' + tableRow + '$'),
inside: {
'table-header': {
pattern: RegExp(tableCell),
alias: 'important',
inside: Prism.languages.markdown
},
punctuation: /\|/
}
}
}
},
code: [
{
// Prefixed by 4 spaces or 1 tab and preceded by an empty line
pattern:
/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,
lookbehind: true,
alias: 'keyword'
},
{
// ```optional language
// code block
// ```
pattern: /^```[\s\S]*?^```$/m,
greedy: true,
inside: {
'code-block': {
pattern: /^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,
lookbehind: true
},
'code-language': {
pattern: /^(```).+/,
lookbehind: true
},
punctuation: /```/
}
}
],
title: [
{
// title 1
// =======
// title 2
// -------
pattern: /\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,
alias: 'important',
inside: {
punctuation: /==+$|--+$/
}
},
{
// # title 1
// ###### title 6
pattern: /(^\s*)#.+/m,
lookbehind: true,
alias: 'important',
inside: {
punctuation: /^#+|#+$/
}
}
],
hr: {
// ***
// ---
// * * *
// -----------
pattern: /(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,
lookbehind: true,
alias: 'punctuation'
},
list: {
// * item
// + item
// - item
// 1. item
pattern: /(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,
lookbehind: true,
alias: 'punctuation'
},
'url-reference': {
// [id]: http://example.com "Optional title"
// [id]: http://example.com 'Optional title'
// [id]: http://example.com (Optional title)
// [id]: <http://example.com> "Optional title"
pattern:
/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,
inside: {
variable: {
pattern: /^(!?\[)[^\]]+/,
lookbehind: true
},
string: /(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,
punctuation: /^[[\]!:]|[<>]/
},
alias: 'url'
},
bold: {
// **strong**
// __strong__
// allow one nested instance of italic text using the same delimiter
pattern: createInline(
/\b__(?:(?!_)<inner>|_(?:(?!_)<inner>)+_)+__\b|\*\*(?:(?!\*)<inner>|\*(?:(?!\*)<inner>)+\*)+\*\*/
.source
),
lookbehind: true,
greedy: true,
inside: {
content: {
pattern: /(^..)[\s\S]+(?=..$)/,
lookbehind: true,
inside: {} // see below
},
punctuation: /\*\*|__/
}
},
italic: {
// *em*
// _em_
// allow one nested instance of bold text using the same delimiter
pattern: createInline(
/\b_(?:(?!_)<inner>|__(?:(?!_)<inner>)+__)+_\b|\*(?:(?!\*)<inner>|\*\*(?:(?!\*)<inner>)+\*\*)+\*/
.source
),
lookbehind: true,
greedy: true,
inside: {
content: {
pattern: /(^.)[\s\S]+(?=.$)/,
lookbehind: true,
inside: {} // see below
},
punctuation: /[*_]/
}
},
strike: {
// ~~strike through~~
// ~strike~
pattern: createInline(/(~~?)(?:(?!~)<inner>)+\2/.source),
lookbehind: true,
greedy: true,
inside: {
content: {
pattern: /(^~~?)[\s\S]+(?=\1$)/,
lookbehind: true,
inside: {} // see below
},
punctuation: /~~?/
}
},
'code-snippet': {
// `code`
// ``code``
pattern: /(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,
lookbehind: true,
greedy: true,
alias: ['code', 'keyword']
},
url: {
// [example](http://example.com "Optional title")
// [example][id]
// [example] [id]
pattern: createInline(
/!?\[(?:(?!\])<inner>)+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\])<inner>)+\])/
.source
),
lookbehind: true,
greedy: true,
inside: {
operator: /^!/,
content: {
pattern: /(^\[)[^\]]+(?=\])/,
lookbehind: true,
inside: {} // see below
},
variable: {
pattern: /(^\][ \t]?\[)[^\]]+(?=\]$)/,
lookbehind: true
},
url: {
pattern: /(^\]\()[^\s)]+/,
lookbehind: true
},
string: {
pattern: /(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,
lookbehind: true
}
}
},
'url-autolink': {
pattern: /https?:\/\/(?:[^\\\n\r >])+/,
lookbehind: true,
greedy: true,
alias: 'url'
}
});
['url', 'bold', 'italic', 'strike'].forEach(function (token) {
['url', 'bold', 'italic', 'strike', 'code-snippet'].forEach(function (inside) {
if (token !== inside) {
Prism.languages.markdown[token].inside.content.inside[inside] =
Prism.languages.markdown[inside];
}
});
});
Prism.hooks.add('after-tokenize', function (env) {
if (env.language !== 'markdown' && env.language !== 'md') {
return;
}
function walkTokens(tokens) {
if (!tokens || typeof tokens === 'string') {
return;
}
for (let i = 0, l = tokens.length; i < l; i++) {
const token = tokens[i];
if (token.type !== 'code') {
walkTokens(token.content);
continue;
}
/*
* Add the correct `language-xxxx` class to this code block. Keep in mind that the `code-language` token
* is optional. But the grammar is defined so that there is only one case we have to handle:
*
* token.content = [
* <span class="punctuation">```</span>,
* <span class="code-language">xxxx</span>,
* '\n', // exactly one new lines (\r or \n or \r\n)
* <span class="code-block">...</span>,
* '\n', // exactly one new lines again
* <span class="punctuation">```</span>
* ];
*/
const codeLang = token.content[1];
const codeBlock = token.content[3];
if (
codeLang &&
codeBlock &&
codeLang.type === 'code-language' &&
codeBlock.type === 'code-block' &&
typeof codeLang.content === 'string'
) {
// this might be a language that Prism does not support
// do some replacements to support C++, C#, and F#
let lang = codeLang.content.replace(/\b#/g, 'sharp').replace(/\b\+\+/g, 'pp');
// only use the first word
lang = (/[a-z][\w-]*/i.exec(lang) || [''])[0].toLowerCase();
const alias = 'language-' + lang;
// add alias
if (!codeBlock.alias) {
codeBlock.alias = [alias];
} else if (typeof codeBlock.alias === 'string') {
codeBlock.alias = [codeBlock.alias, alias];
} else {
codeBlock.alias.push(alias);
}
}
}
}
walkTokens(env.tokens);
});
Prism.hooks.add('wrap', function (env) {
if (env.type !== 'code-block') {
return;
}
let codeLang = '';
for (let i = 0, l = env.classes.length; i < l; i++) {
const cls = env.classes[i];
const match = /language-(.+)/.exec(cls);
if (match) {
codeLang = match[1];
break;
}
}
const grammar = Prism.languages[codeLang];
if (!grammar) {
if (codeLang && codeLang !== 'none' && Prism.plugins.autoloader) {
const id = 'md-' + new Date().valueOf() + '-' + Math.floor(Math.random() * 1e16);
env.attributes['id'] = id;
Prism.plugins.autoloader.loadLanguages(codeLang, function () {
const ele = document.getElementById(id);
if (ele) {
ele.innerHTML = Prism.highlight(ele.textContent, Prism.languages[codeLang], codeLang);
}
});
}
} else {
env.content = Prism.highlight(textContent(env.content), grammar, codeLang);
}
});
const tagPattern = RegExp(Prism.languages.markup.tag.pattern.source, 'gi');
/**
* A list of known entity names.
*
* This will always be incomplete to save space. The current list is the one used by lowdash's unescape function.
*
* @see {@link https://github.com/lodash/lodash/blob/2da024c3b4f9947a48517639de7560457cd4ec6c/unescape.js#L2}
*/
const KNOWN_ENTITY_NAMES = {
amp: '&',
lt: '<',
gt: '>',
quot: '"'
};
// IE 11 doesn't support `String.fromCodePoint`
const fromCodePoint = String.fromCodePoint || String.fromCharCode;
/**
* Returns the text content of a given HTML source code string.
*
* @param {string} html
* @returns {string}
*/
function textContent(html) {
// remove all tags
let text = html.replace(tagPattern, '');
// decode known entities
text = text.replace(/&(\w{1,8}|#x?[\da-f]{1,8});/gi, function (m, code) {
code = code.toLowerCase();
if (code[0] === '#') {
let value;
if (code[1] === 'x') {
value = parseInt(code.slice(2), 16);
} else {
value = Number(code.slice(1));
}
return fromCodePoint(value);
} else {
const known = KNOWN_ENTITY_NAMES[code];
if (known) {
return known;
}
// unable to decode
return m;
}
});
return text;
}
Prism.languages.md = Prism.languages.markdown;
}

View file

@ -4,7 +4,6 @@
import ToggleTheme from './ToggleTheme.svelte'; import ToggleTheme from './ToggleTheme.svelte';
import sampleText from './sample.md?raw'; import sampleText from './sample.md?raw';
import '$lib/default.css'; import '$lib/default.css';
import '$lib/highlight.css';
const carta = new Carta(); const carta = new Carta();
</script> </script>

View file

@ -50,7 +50,7 @@
background: #1b1b1f; background: #1b1b1f;
} }
:global(html.dark .prose) { :global(html.dark .markdown-body) {
color: #fff; color: #fff;
} }
@ -67,8 +67,8 @@
/* Code dark mode */ /* Code dark mode */
:global(html.dark .carta-highlight), :global(html.dark .shiki),
:global(html.dark .carta-highlight span) { :global(html.dark .shiki span) {
color: var(--hl-dark) !important; color: var(--shiki-dark) !important;
} }
</style> </style>

View file

@ -20,7 +20,7 @@ import '@cartamd/plugin-anchor/default.css';
```svelte ```svelte
<script> <script>
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { anchor } from '@cartamd/plugin-anchor'; import { anchor } from '@cartamd/plugin-anchor';
const carta = new Carta({ const carta = new Carta({

View file

@ -36,7 +36,7 @@
"rehype-slug": "^6.0.0" "rehype-slug": "^6.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@thetadev/carta-md": "^4.0.0", "carta-md": "^4.0.0",
"svelte": "^3.54.0 || ^4.0.0" "svelte": "^3.54.0 || ^4.0.0"
}, },
"devDependencies": { "devDependencies": {
@ -44,7 +44,7 @@
"@sveltejs/kit": "^2.5.4", "@sveltejs/kit": "^2.5.4",
"@sveltejs/package": "^2.3.0", "@sveltejs/package": "^2.3.0",
"@sveltejs/vite-plugin-svelte": "^3.0.2", "@sveltejs/vite-plugin-svelte": "^3.0.2",
"@thetadev/carta-md": "workspace:*", "carta-md": "workspace:*",
"marked": "^9.1.5", "marked": "^9.1.5",
"publint": "^0.1.9", "publint": "^0.1.9",
"svelte": "^3.54.0 || ^4.0.0", "svelte": "^3.54.0 || ^4.0.0",

View file

@ -1,6 +1,6 @@
import rehypeSlug, { type Options as SlugOptions } from 'rehype-slug'; import rehypeSlug, { type Options as SlugOptions } from 'rehype-slug';
import rehypeAutolinkHeadings, { type Options as AutolinkOptions } from 'rehype-autolink-headings'; import rehypeAutolinkHeadings, { type Options as AutolinkOptions } from 'rehype-autolink-headings';
import type { Plugin } from '@thetadev/carta-md'; import type { Plugin } from 'carta-md';
export * from './default.css?inline'; export * from './default.css?inline';
export interface AnchorExtensionOptions { export interface AnchorExtensionOptions {

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { anchor } from '$lib'; import { anchor } from '$lib';
import 'carta-md/default.css'; import 'carta-md/default.css';
import '$lib/default.css'; import '$lib/default.css';

View file

@ -20,7 +20,7 @@ import '@cartamd/plugin-attachment/default.css';
```svelte ```svelte
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { attachment } from '@cartamd/plugin-attachment'; import { attachment } from '@cartamd/plugin-attachment';
const carta = new Carta({ const carta = new Carta({

View file

@ -34,7 +34,7 @@
"!dist/**/*.spec.*" "!dist/**/*.spec.*"
], ],
"peerDependencies": { "peerDependencies": {
"@thetadev/carta-md": "^4.0.0", "carta-md": "^4.0.0",
"marked": "^9.1.5", "marked": "^9.1.5",
"svelte": "^3.54.0 || ^4.0.0" "svelte": "^3.54.0 || ^4.0.0"
}, },
@ -44,7 +44,7 @@
"@sveltejs/package": "^2.3.0", "@sveltejs/package": "^2.3.0",
"@sveltejs/vite-plugin-svelte": "^3.0.2", "@sveltejs/vite-plugin-svelte": "^3.0.2",
"@types/node-emoji": "^1.8.2", "@types/node-emoji": "^1.8.2",
"@thetadev/carta-md": "workspace:*", "carta-md": "workspace:*",
"marked": "^9.1.5", "marked": "^9.1.5",
"publint": "^0.1.9", "publint": "^0.1.9",
"svelte": "^4.2.12", "svelte": "^4.2.12",

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Carta } from '@thetadev/carta-md'; import type { Carta } from 'carta-md';
import type { Writable } from 'svelte/store'; import type { Writable } from 'svelte/store';
import UploadIcon from './icons/UploadIcon.svelte'; import UploadIcon from './icons/UploadIcon.svelte';
import type { SvelteComponent } from 'svelte'; import type { SvelteComponent } from 'svelte';

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Carta } from '@thetadev/carta-md'; import type { Carta } from 'carta-md';
import SpinnerIcon from './icons/SpinnerIcon.svelte'; import SpinnerIcon from './icons/SpinnerIcon.svelte';
import type { Writable } from 'svelte/store'; import type { Writable } from 'svelte/store';
import type { SvelteComponent } from 'svelte'; import type { SvelteComponent } from 'svelte';

View file

@ -1,4 +1,4 @@
import type { Carta, Plugin, Listener } from '@thetadev/carta-md'; import type { Carta, Plugin, Listener } from 'carta-md';
import { get, writable, type Writable } from 'svelte/store'; import { get, writable, type Writable } from 'svelte/store';
import type { SvelteComponent } from 'svelte'; import type { SvelteComponent } from 'svelte';
import DropOverlay from './DropOverlay.svelte'; import DropOverlay from './DropOverlay.svelte';

View file

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { attachment } from '$lib'; import { attachment } from '$lib';
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import 'carta-md/default.css'; import 'carta-md/default.css';
import '$lib/default.css'; import '$lib/default.css';

View file

@ -42,7 +42,7 @@ It is no longer possible to specify a custom highlighter in this plugin. However
```svelte ```svelte
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { code } from '@cartamd/plugin-code'; import { code } from '@cartamd/plugin-code';
const carta = new Carta({ const carta = new Carta({

View file

@ -18,12 +18,12 @@
"devDependencies": { "devDependencies": {
"@shikijs/rehype": "^1.4.0", "@shikijs/rehype": "^1.4.0",
"@types/node": "^18.16.3", "@types/node": "^18.16.3",
"@thetadev/carta-md": "workspace:*", "carta-md": "workspace:*",
"typescript": "^5.0.4", "typescript": "^5.0.4",
"typescript-cp": "^0.1.8" "typescript-cp": "^0.1.8"
}, },
"peerDependencies": { "peerDependencies": {
"@thetadev/carta-md": "^4.0.0" "carta-md": "^4.0.0"
}, },
"files": [ "files": [
"dist" "dist"

View file

@ -1,4 +1,4 @@
import type { DualTheme, Theme, Plugin } from '@thetadev/carta-md'; import type { DualTheme, Theme, Plugin } from 'carta-md';
import type { RehypeShikiOptions } from '@shikijs/rehype'; import type { RehypeShikiOptions } from '@shikijs/rehype';
import rehypeShikiFromHighlighter from '@shikijs/rehype/core'; import rehypeShikiFromHighlighter from '@shikijs/rehype/core';

View file

@ -20,7 +20,7 @@ import '@cartamd/plugin-emoji/default.css';
```svelte ```svelte
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { emoji } from '@cartamd/plugin-emoji'; import { emoji } from '@cartamd/plugin-emoji';
const carta = new Carta({ const carta = new Carta({

View file

@ -37,7 +37,7 @@
"remark-gemoji": "^8.0.0" "remark-gemoji": "^8.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@thetadev/carta-md": "^4.0.0", "carta-md": "^4.0.0",
"svelte": "^3.54.0 || ^4.0.0" "svelte": "^3.54.0 || ^4.0.0"
}, },
"devDependencies": { "devDependencies": {
@ -46,7 +46,7 @@
"@sveltejs/package": "^2.3.0", "@sveltejs/package": "^2.3.0",
"@sveltejs/vite-plugin-svelte": "^3.0.2", "@sveltejs/vite-plugin-svelte": "^3.0.2",
"@types/node-emoji": "^1.8.2", "@types/node-emoji": "^1.8.2",
"@thetadev/carta-md": "workspace:*", "carta-md": "workspace:*",
"marked": "^9.1.5", "marked": "^9.1.5",
"publint": "^0.1.9", "publint": "^0.1.9",
"svelte": "^4.2.12", "svelte": "^4.2.12",

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Carta } from '@thetadev/carta-md'; import type { Carta } from 'carta-md';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import nodeEmoji from 'node-emoji'; import nodeEmoji from 'node-emoji';
import type { TransitionConfig } from 'svelte/transition'; import type { TransitionConfig } from 'svelte/transition';

View file

@ -1,4 +1,4 @@
import type { Plugin, ExtensionComponent, GrammarRule, HighlightingRule } from '@thetadev/carta-md'; import type { Plugin, ExtensionComponent, GrammarRule, HighlightingRule } from 'carta-md';
import remarkGemoji from 'remark-gemoji'; import remarkGemoji from 'remark-gemoji';
import { fade, scale, type TransitionConfig } from 'svelte/transition'; import { fade, scale, type TransitionConfig } from 'svelte/transition';
import Emoji from './Emoji.svelte'; import Emoji from './Emoji.svelte';

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { emoji } from '$lib'; import { emoji } from '$lib';
import 'carta-md/default.css'; import 'carta-md/default.css';
import '$lib/default.css'; import '$lib/default.css';

View file

@ -38,7 +38,7 @@ or by using a content delivery network:
```svelte ```svelte
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { math } from '@cartamd/plugin-math'; import { math } from '@cartamd/plugin-math';
const carta = new Carta({ const carta = new Carta({

View file

@ -17,11 +17,11 @@
"build": "tsc" "build": "tsc"
}, },
"devDependencies": { "devDependencies": {
"@thetadev/carta-md": "workspace:*", "carta-md": "workspace:*",
"typescript": "^5.0.4" "typescript": "^5.0.4"
}, },
"peerDependencies": { "peerDependencies": {
"@thetadev/carta-md": "^4.0.0" "carta-md": "^4.0.0"
}, },
"files": [ "files": [
"dist" "dist"

View file

@ -1,4 +1,4 @@
import type { Plugin } from '@thetadev/carta-md'; import type { Plugin } from 'carta-md';
import remarkMath, { type Options as RemarkMathOptions } from 'remark-math'; import remarkMath, { type Options as RemarkMathOptions } from 'remark-math';
import rehypeKatex, { type Options as RehypeKatexOptions } from 'rehype-katex'; import rehypeKatex, { type Options as RehypeKatexOptions } from 'rehype-katex';

View file

@ -20,7 +20,7 @@ import '@cartamd/plugin-slash/default.css';
```svelte ```svelte
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { slash } from '@cartamd/plugin-slash'; import { slash } from '@cartamd/plugin-slash';
const carta = new Carta({ const carta = new Carta({

View file

@ -35,7 +35,7 @@
"bezier-easing": "^2.1.0" "bezier-easing": "^2.1.0"
}, },
"peerDependencies": { "peerDependencies": {
"@thetadev/carta-md": "^4.0.0", "carta-md": "^4.0.0",
"svelte": "^3.54.0 || ^4.0.0" "svelte": "^3.54.0 || ^4.0.0"
}, },
"devDependencies": { "devDependencies": {
@ -44,7 +44,7 @@
"@sveltejs/package": "^2.3.0", "@sveltejs/package": "^2.3.0",
"@sveltejs/vite-plugin-svelte": "^3.0.2", "@sveltejs/vite-plugin-svelte": "^3.0.2",
"@types/node-emoji": "^1.8.2", "@types/node-emoji": "^1.8.2",
"@thetadev/carta-md": "workspace:*", "carta-md": "workspace:*",
"marked": "^9.1.5", "marked": "^9.1.5",
"publint": "^0.1.9", "publint": "^0.1.9",
"svelte": "^4.2.12", "svelte": "^4.2.12",

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Carta } from '@thetadev/carta-md'; import type { Carta } from 'carta-md';
import type { SlashSnippet } from './snippets'; import type { SlashSnippet } from './snippets';
import type { TransitionConfig } from 'svelte/transition'; import type { TransitionConfig } from 'svelte/transition';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';

View file

@ -1,6 +1,6 @@
import { fade, scale, type TransitionConfig } from 'svelte/transition'; import { fade, scale, type TransitionConfig } from 'svelte/transition';
import SlashComponent from './Slash.svelte'; import SlashComponent from './Slash.svelte';
import type { Plugin, ExtensionComponent } from '@thetadev/carta-md'; import type { Plugin, ExtensionComponent } from 'carta-md';
import BezierEasing from 'bezier-easing'; import BezierEasing from 'bezier-easing';
import { defaultSnippets, type DefaultSnippetId, type SlashSnippet } from './snippets'; import { defaultSnippets, type DefaultSnippetId, type SlashSnippet } from './snippets';
export * from './default.css?inline'; export * from './default.css?inline';

View file

@ -1,4 +1,4 @@
import type { InputEnhancer } from '@thetadev/carta-md'; import type { InputEnhancer } from 'carta-md';
export interface SlashSnippet { export interface SlashSnippet {
/** /**

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { slash } from '$lib'; import { slash } from '$lib';
import 'carta-md/default.css'; import 'carta-md/default.css';
import '$lib/default.css'; import '$lib/default.css';

View file

@ -16,7 +16,7 @@ npm i @cartamd/plugin-tikz
```svelte ```svelte
<script lang="ts"> <script lang="ts">
import { Carta, MarkdownEditor } from '@thetadev/carta-md'; import { Carta, MarkdownEditor } from 'carta-md';
import { tikz } from '@cartamd/plugin-tikz'; import { tikz } from '@cartamd/plugin-tikz';
import '@cartamd/plugin-tikz/fonts.css'; import '@cartamd/plugin-tikz/fonts.css';

View file

@ -20,7 +20,7 @@
"devDependencies": { "devDependencies": {
"@types/hast": "^3.0.4", "@types/hast": "^3.0.4",
"@types/md5": "^2.3.2", "@types/md5": "^2.3.2",
"@thetadev/carta-md": "workspace:*", "carta-md": "workspace:*",
"md5": "^2.3.0", "md5": "^2.3.0",
"typescript": "^5.0.4", "typescript": "^5.0.4",
"unified": "^11.0.4", "unified": "^11.0.4",
@ -29,7 +29,7 @@
"vite-raw-plugin": "^1.0.2" "vite-raw-plugin": "^1.0.2"
}, },
"peerDependencies": { "peerDependencies": {
"@thetadev/carta-md": "^4.0.0" "carta-md": "^4.0.0"
}, },
"files": [ "files": [
"dist" "dist"

View file

@ -1,4 +1,4 @@
import type { Carta, Event, Plugin } from '@thetadev/carta-md'; import type { Carta, Event, Plugin } from 'carta-md';
import type { Plugin as UnifiedPlugin } from 'unified'; import type { Plugin as UnifiedPlugin } from 'unified';
import { visit, SKIP } from 'unist-util-visit'; import { visit, SKIP } from 'unist-util-visit';
import { fromDom } from 'hast-util-from-dom'; import { fromDom } from 'hast-util-from-dom';

View file

@ -77,12 +77,12 @@ importers:
'@cartamd/plugin-tikz': '@cartamd/plugin-tikz':
specifier: workspace:^ specifier: workspace:^
version: link:../packages/plugin-tikz version: link:../packages/plugin-tikz
'@thetadev/carta-md':
specifier: workspace:^
version: link:../packages/carta-md
bits-ui: bits-ui:
specifier: ^0.9.1 specifier: ^0.9.1
version: 0.9.4(svelte@4.2.12) version: 0.9.4(svelte@4.2.12)
carta-md:
specifier: workspace:^
version: link:../packages/carta-md
clsx: clsx:
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.0.0 version: 2.0.0
@ -159,9 +159,6 @@ importers:
packages/carta-md: packages/carta-md:
dependencies: dependencies:
prismjs:
specifier: ^1.29.0
version: 1.29.0
rehype-stringify: rehype-stringify:
specifier: ^10.0.0 specifier: ^10.0.0
version: 10.0.0 version: 10.0.0
@ -174,6 +171,9 @@ importers:
remark-rehype: remark-rehype:
specifier: ^11.1.0 specifier: ^11.1.0
version: 11.1.0 version: 11.1.0
shiki:
specifier: ^1.4.0
version: 1.4.0
svelte: svelte:
specifier: ^3.54.0 || ^4.0.0 specifier: ^3.54.0 || ^4.0.0
version: 4.2.2 version: 4.2.2
@ -193,9 +193,6 @@ importers:
'@sveltejs/vite-plugin-svelte': '@sveltejs/vite-plugin-svelte':
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2(svelte@4.2.2)(vite@5.1.6) version: 3.0.2(svelte@4.2.2)(vite@5.1.6)
'@types/prismjs':
specifier: ^1.26.4
version: 1.26.4
svelte-check: svelte-check:
specifier: ^3.6.7 specifier: ^3.6.7
version: 3.6.7(postcss@8.4.31)(svelte@4.2.2) version: 3.6.7(postcss@8.4.31)(svelte@4.2.2)
@ -230,7 +227,7 @@ importers:
'@sveltejs/vite-plugin-svelte': '@sveltejs/vite-plugin-svelte':
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2(svelte@4.2.2)(vite@5.1.6) version: 3.0.2(svelte@4.2.2)(vite@5.1.6)
'@thetadev/carta-md': carta-md:
specifier: workspace:* specifier: workspace:*
version: link:../carta-md version: link:../carta-md
marked: marked:
@ -269,12 +266,12 @@ importers:
'@sveltejs/vite-plugin-svelte': '@sveltejs/vite-plugin-svelte':
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2(svelte@4.2.12)(vite@5.1.6) version: 3.0.2(svelte@4.2.12)(vite@5.1.6)
'@thetadev/carta-md':
specifier: workspace:*
version: link:../carta-md
'@types/node-emoji': '@types/node-emoji':
specifier: ^1.8.2 specifier: ^1.8.2
version: 1.8.2 version: 1.8.2
carta-md:
specifier: workspace:*
version: link:../carta-md
marked: marked:
specifier: ^9.1.5 specifier: ^9.1.5
version: 9.1.5 version: 9.1.5
@ -306,12 +303,12 @@ importers:
specifier: ^11.0.4 specifier: ^11.0.4
version: 11.0.4 version: 11.0.4
devDependencies: devDependencies:
'@thetadev/carta-md':
specifier: workspace:*
version: link:../carta-md
'@types/node': '@types/node':
specifier: ^18.16.3 specifier: ^18.16.3
version: 18.16.3 version: 18.16.3
carta-md:
specifier: workspace:*
version: link:../carta-md
typescript: typescript:
specifier: ^5.0.4 specifier: ^5.0.4
version: 5.0.4 version: 5.0.4
@ -343,12 +340,12 @@ importers:
'@sveltejs/vite-plugin-svelte': '@sveltejs/vite-plugin-svelte':
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2(svelte@4.2.12)(vite@5.1.6) version: 3.0.2(svelte@4.2.12)(vite@5.1.6)
'@thetadev/carta-md':
specifier: workspace:*
version: link:../carta-md
'@types/node-emoji': '@types/node-emoji':
specifier: ^1.8.2 specifier: ^1.8.2
version: 1.8.2 version: 1.8.2
carta-md:
specifier: workspace:*
version: link:../carta-md
marked: marked:
specifier: ^9.1.5 specifier: ^9.1.5
version: 9.1.5 version: 9.1.5
@ -380,7 +377,7 @@ importers:
specifier: ^6.0.0 specifier: ^6.0.0
version: 6.0.0 version: 6.0.0
devDependencies: devDependencies:
'@thetadev/carta-md': carta-md:
specifier: workspace:* specifier: workspace:*
version: link:../carta-md version: link:../carta-md
typescript: typescript:
@ -405,12 +402,12 @@ importers:
'@sveltejs/vite-plugin-svelte': '@sveltejs/vite-plugin-svelte':
specifier: ^3.0.2 specifier: ^3.0.2
version: 3.0.2(svelte@4.2.12)(vite@5.1.6) version: 3.0.2(svelte@4.2.12)(vite@5.1.6)
'@thetadev/carta-md':
specifier: workspace:*
version: link:../carta-md
'@types/node-emoji': '@types/node-emoji':
specifier: ^1.8.2 specifier: ^1.8.2
version: 1.8.2 version: 1.8.2
carta-md:
specifier: workspace:*
version: link:../carta-md
marked: marked:
specifier: ^9.1.5 specifier: ^9.1.5
version: 9.1.5 version: 9.1.5
@ -442,15 +439,15 @@ importers:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.0.0 version: 5.0.0
devDependencies: devDependencies:
'@thetadev/carta-md':
specifier: workspace:*
version: link:../carta-md
'@types/hast': '@types/hast':
specifier: ^3.0.4 specifier: ^3.0.4
version: 3.0.4 version: 3.0.4
'@types/md5': '@types/md5':
specifier: ^2.3.2 specifier: ^2.3.2
version: 2.3.2 version: 2.3.2
carta-md:
specifier: workspace:*
version: link:../carta-md
md5: md5:
specifier: ^2.3.0 specifier: ^2.3.0
version: 2.3.0 version: 2.3.0
@ -1738,10 +1735,6 @@ packages:
resolution: {integrity: sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==} resolution: {integrity: sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==}
dev: true dev: true
/@types/prismjs@1.26.4:
resolution: {integrity: sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==}
dev: true
/@types/pug@2.0.6: /@types/pug@2.0.6:
resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==}
dev: true dev: true
@ -5881,6 +5874,7 @@ packages:
/prismjs@1.29.0: /prismjs@1.29.0:
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true
/process-nextick-args@2.0.1: /process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}

View file

@ -34,7 +34,7 @@ export const execAsync = (command, cwd = undefined, options = { showLog: false }
* List of all the packages. * List of all the packages.
*/ */
export const packages = [ export const packages = [
'@thetadev/carta-md', 'carta-md',
'plugin-math', 'plugin-math',
'plugin-slash', 'plugin-slash',
'plugin-emoji', 'plugin-emoji',