Compare commits
127 commits
@cartamd/p
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
2177545454 | ||
|
a303834127 | ||
|
3fd18a6796 | ||
|
486a885704 | ||
|
8e5803a1c4 | ||
|
0ff9e5b124 | ||
|
eb114a7311 | ||
|
34d4f400e6 | ||
|
cc79b25288 | ||
|
a44b8e971d | ||
|
bafc86d8d3 | ||
|
a086ece088 | ||
|
dddaad1f1b | ||
|
7261d295e2 | ||
|
482fd4b8af | ||
|
dfc8812123 | ||
|
54358b649b | ||
|
7ad874e6aa | ||
|
545864a202 | ||
|
0cef0b94ed | ||
|
20d58a856b | ||
|
1a61f58e7d | ||
|
7d4034c316 | ||
|
26241b5bcd | ||
|
3785fd01c0 | ||
|
c9be45a1ee | ||
|
c4428b8150 | ||
|
036af273f9 | ||
|
58a7a8848f | ||
|
255913bd35 | ||
|
e175e87bcc | ||
|
7e4a387523 | ||
|
342c8d24d0 | ||
|
9e1b0d1850 | ||
|
b30b2af6d9 | ||
|
c6a81acedd | ||
|
d1590ea384 | ||
|
24be57a10f | ||
|
eb647b416f | ||
|
ae15b5b590 | ||
|
6e9cb68141 | ||
|
f0abf195b8 | ||
|
8f4188936e | ||
|
bed8ba5d47 | ||
|
b7fecea206 | ||
|
aeac8073b3 | ||
|
e430bf035a | ||
|
f0aaaed536 | ||
|
3e8e7417d4 | ||
|
a36b57f0c4 | ||
|
46a58adbec | ||
|
b0deb50a25 | ||
|
db3770cece | ||
|
00d67f3cbf | ||
|
0af2e23f25 | ||
|
893742d90e | ||
|
d33285251a | ||
|
4693658089 | ||
|
d3f3b4cc09 | ||
|
4843c27c67 | ||
|
8f98e2ad36 | ||
|
2884ceeff1 | ||
|
5ddddbdc2e | ||
|
5b0ce03e6d | ||
|
2a7c77e757 | ||
|
6fa30f2d3e | ||
|
b7c8704de3 | ||
|
4e097b81ab | ||
|
502e564f77 | ||
|
dc270ffce9 | ||
|
68350779f6 | ||
|
56e1572a75 | ||
|
1b75fe794e | ||
|
14be70bb74 | ||
|
661cd137d9 | ||
|
4075b8a76f | ||
|
f7f49a513a | ||
|
c9002efa7e | ||
|
7bdd027b0a | ||
|
0bef4b8d37 | ||
|
345c987273 | ||
|
32a9862c4c | ||
|
d2e82deadd | ||
|
fd189175cd | ||
|
e3200b93a3 | ||
|
3420f1893f | ||
|
c5f5a8ed1a | ||
|
1600e4ec23 | ||
|
1d78d5b7b7 | ||
|
18ca2902ab | ||
|
71d41f6ee7 | ||
|
e510e913df | ||
|
515c98fe0e | ||
|
a56b05b36a | ||
|
9ce2539e3d | ||
|
6d80e55cfb | ||
|
06cd281170 | ||
|
39df745e6e | ||
|
26fe1f6cba | ||
|
4ae846ed08 | ||
|
a0325eb73d | ||
|
0501342b4c | ||
|
08d52652d1 | ||
|
389f09739c | ||
|
7d7f69ee0a | ||
|
471d5d5eae | ||
|
c063d314d6 | ||
|
eba352fe15 | ||
|
75a535eed9 | ||
|
521fadaa35 | ||
|
ef1a15f4fa | ||
|
be8a860bcb | ||
|
b187a6a904 | ||
|
a61e04987c | ||
|
4cdd7ed551 | ||
|
1c099e8f3c | ||
|
cf623603b6 | ||
|
b070f5dbe2 | ||
|
e52e12f7f1 | ||
|
e14f9fc666 | ||
|
e5e4ad4f06 | ||
|
20bc2819c9 | ||
|
1db7fc42b7 | ||
|
0d7442aff6 | ||
|
25144519d7 | ||
|
3bcbe058f2 | ||
|
92ec673efa |
189 changed files with 11476 additions and 3610 deletions
|
@ -1 +1,2 @@
|
||||||
dist
|
dist
|
||||||
|
.svelte-kit
|
2
.github/workflows/code-quality.yml
vendored
2
.github/workflows/code-quality.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
- uses: pnpm/action-setup@v2
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 6
|
version: 8
|
||||||
|
|
||||||
- name: Install dependendencies
|
- name: Install dependendencies
|
||||||
run: pnpm i
|
run: pnpm i
|
||||||
|
|
8
.github/workflows/gh-pages.yml
vendored
8
.github/workflows/gh-pages.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
- uses: pnpm/action-setup@v2
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 6
|
version: 8
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm i
|
run: pnpm i
|
||||||
|
@ -25,12 +25,12 @@ jobs:
|
||||||
- name: Build packages
|
- name: Build packages
|
||||||
run: pnpm run build
|
run: pnpm run build
|
||||||
|
|
||||||
- name: Build demo
|
- name: Build docs
|
||||||
run: pnpm run build
|
run: pnpm run build
|
||||||
working-directory: demo
|
working-directory: docs
|
||||||
|
|
||||||
- name: Deploy pages
|
- name: Deploy pages
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
publish_dir: ./demo/build
|
publish_dir: ./docs/build
|
||||||
|
|
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
|
@ -16,7 +16,7 @@ jobs:
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
- uses: pnpm/action-setup@v2
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 6
|
version: 8
|
||||||
|
|
||||||
- name: Install dependendencies
|
- name: Install dependendencies
|
||||||
run: pnpm i
|
run: pnpm i
|
||||||
|
@ -45,7 +45,7 @@ jobs:
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
- uses: pnpm/action-setup@v2
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 6
|
version: 8
|
||||||
|
|
||||||
- name: Install dependendencies
|
- name: Install dependendencies
|
||||||
run: pnpm i
|
run: pnpm i
|
||||||
|
@ -53,9 +53,6 @@ jobs:
|
||||||
- name: Build all packages
|
- name: Build all packages
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
- name: Verify the integrity of provenance attestations and registry signatures for installed dependencies
|
|
||||||
run: npm audit signatures
|
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
run: npm run publish
|
run: npm run publish
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"plugins": ["prettier-plugin-svelte"],
|
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
||||||
"pluginSearchDirs": ["."],
|
|
||||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||||
}
|
}
|
||||||
|
|
23
.vscode/settings.json
vendored
23
.vscode/settings.json
vendored
|
@ -1,7 +1,26 @@
|
||||||
{
|
{
|
||||||
"cSpell.words": ["Carta", "cartamd", "Katex", "tikzjax", "tikz"],
|
"cSpell.words": [
|
||||||
|
"Carta",
|
||||||
|
"cartamd",
|
||||||
|
"coldark",
|
||||||
|
"dompurify",
|
||||||
|
"flexsearch",
|
||||||
|
"Gemoji",
|
||||||
|
"gruvbox",
|
||||||
|
"iconify",
|
||||||
|
"Katex",
|
||||||
|
"mdsvex",
|
||||||
|
"oldschool",
|
||||||
|
"rehype",
|
||||||
|
"shiki",
|
||||||
|
"shikijs",
|
||||||
|
"tikz",
|
||||||
|
"tikzjax",
|
||||||
|
"typeof"
|
||||||
|
],
|
||||||
"typescript.tsdk": "node_modules\\typescript\\lib",
|
"typescript.tsdk": "node_modules\\typescript\\lib",
|
||||||
"[svelte]": {
|
"[svelte]": {
|
||||||
"editor.defaultFormatter": "svelte.svelte-vscode"
|
"editor.defaultFormatter": "svelte.svelte-vscode"
|
||||||
}
|
},
|
||||||
|
"css.customData": [".vscode/tailwind.json"]
|
||||||
}
|
}
|
||||||
|
|
55
.vscode/tailwind.json
vendored
Normal file
55
.vscode/tailwind.json
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"version": 1.1,
|
||||||
|
"atDirectives": [
|
||||||
|
{
|
||||||
|
"name": "@tailwind",
|
||||||
|
"description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@apply",
|
||||||
|
"description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#apply"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@responsive",
|
||||||
|
"description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@screen",
|
||||||
|
"description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#screen"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@variants",
|
||||||
|
"description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#variants"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
196
README.md
196
README.md
|
@ -1,42 +1,88 @@
|
||||||
|
<div align="right">
|
||||||
|
<a href="https://www.npmjs.com/package/carta-md">
|
||||||
|
<img src="https://img.shields.io/npm/v/carta-md?color=ff7cc6&labelColor=171d27&logo=npm&logoColor=white" alt="npm">
|
||||||
|
</a>
|
||||||
|
<a href="https://bundlephobia.com/package/carta-md">
|
||||||
|
<img src="https://img.shields.io/bundlephobia/min/carta-md?color=4dacfa&labelColor=171d27&logo=javascript&logoColor=white" alt="bundle">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/BearToCode/carta/blob/master/LICENSE">
|
||||||
|
<img src="https://img.shields.io/npm/l/carta-md?color=71d58a&labelColor=171d27&logo=git&logoColor=white" alt="license">
|
||||||
|
</a>
|
||||||
|
<a href="http://beartocode.github.io/carta/">
|
||||||
|
<img src="https://img.shields.io/readthedocs/carta?logo=svelte&color=b581fd&logoColor=ffffff&labelColor=171d27" alt="docs">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
[](https://beartocode.github.io/carta/)
|
||||||
|
|
||||||
|
<h1 align="center"><strong>Carta</strong></h1>
|
||||||
|
<div align="center">Modern, lightweight, powerful Markdown Editor.</div>
|
||||||
|
<br />
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="https://see.fontimg.com/api/renderfont4/lemD/eyJyIjoiZnMiLCJoIjoxMjMsInciOjEyNTAsImZzIjo5OCwiZmdjIjoiIzQ2RUJFNyIsImJnYyI6IiNGRkZGRkYiLCJ0IjoxfQ/Q2FydGE/bukhari-script.png" width="240" alt="logo">
|
<a href="https://beartocode.github.io/carta/">📚 Documentation</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://github.com/BearToCode/carta">GitHub</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div align="center">
|
# Introduction
|
||||||
<a href="https://www.npmjs.com/package/carta-md"><img src="https://img.shields.io/npm/v/carta-md?color=16b57c&labelColor=171d27&logo=npm&logoColor=white" alt="npm"></a>
|
|
||||||
<a href="https://bundlephobia.com/package/carta-md"><img src="https://img.shields.io/bundlephobia/min/carta-md?color=16b57c&labelColor=171d27&logo=javascript&logoColor=white" alt="bundle"></a>
|
|
||||||
<a href="https://github.com/BearToCode/carta/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/carta-md?color=16b57c&labelColor=171d27&logo=git&logoColor=white" alt="license"></a>
|
|
||||||
<a href="http://beartocode.github.io/carta/"><img src="https://img.shields.io/badge/available-red?label=demo&color=16b57c&labelColor=171d27&logo=svelte&logoColor=white" alt="demo"></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
> [!NOTE]
|
||||||
|
> Carta has recently been updated to `v4`, which features numerous major changes.
|
||||||
|
>
|
||||||
|
> Follow the [Migration Guide](http://beartocode.github.io/carta/migration) to update your project.
|
||||||
|
|
||||||
Carta is a **lightweight**, **fast** and **extensible** Svelte Markdown editor and viewer, based on [Marked](https://github.com/markedjs/marked). Check out the [demo](http://beartocode.github.io/carta/) 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 includes neither ProseMirror nor CodeMirror, allowing for an extremely small bundle size and fast loading time.
|
Differently from most editors, Carta does not include a code editor, but it is _just_ a textarea with syntax highlighting, shortcuts and more.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Keyboard **shortcuts** (extensible);
|
- 🌈 Markdown syntax highlighting ([Shiki](https://shiki.style/));
|
||||||
- Toolbar (extensible);
|
- 🛠️ Toolbar (extensible);
|
||||||
- Markdown syntax highlighting;
|
- ⌨️ Keyboard **shortcuts** (extensible);
|
||||||
- Scroll sync;
|
- 📦 Supports **[150+ plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins)** thanks to remark;
|
||||||
- **SSR** compatible;
|
- 🔀 Scroll sync;
|
||||||
- **Katex** support (plugin);
|
- ✅ Accessibility friendly;
|
||||||
- **Slash** commands (plugin);
|
- 🖥️ **SSR** compatible;
|
||||||
- **Emojis**, with included search (plugin);
|
- ⚗️ **KaTeX** support (plugin);
|
||||||
- **Tikz** support(plugin);
|
- 🔨 **Slash** commands (plugin);
|
||||||
- **Attachment** support(plugin);
|
- 😄 **Emojis**, with included search (plugin);
|
||||||
- Code blocks **syntax highlighting** (plugin).
|
- ✏️ **TikZ** support (plugin);
|
||||||
|
- 📂 **Attachment** support (plugin);
|
||||||
|
- ⚓ **Anchor** links in headings (plugin);
|
||||||
|
- 🌈 Code blocks **syntax highlighting** (plugin).
|
||||||
|
|
||||||
## Getting started
|
## Packages
|
||||||
|
|
||||||
> **Warning**
|
| Package | Status | Docs |
|
||||||
|
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||||
|
| [carta-md](https://www.npmjs.com/package/carta-md) |  | [/](https://beartocode.github.io/carta/introduction) |
|
||||||
|
| [plugin-math](https://www.npmjs.com/package/@cartamd/plugin-math) |  | [/plugins/math](https://beartocode.github.io/carta/plugins/math) |
|
||||||
|
| [plugin-code](https://www.npmjs.com/package/@cartamd/plugin-code) |  | [/plugins/code](https://beartocode.github.io/carta/plugins/code) |
|
||||||
|
| [plugin-emoji](https://www.npmjs.com/package/@cartamd/plugin-emoji) |  | [/plugins/emoji](https://beartocode.github.io/carta/plugins/emoji) |
|
||||||
|
| [plugin-slash](https://www.npmjs.com/package/@cartamd/plugin-slash) |  | [/plugins/slash](https://beartocode.github.io/carta/plugins/slash) |
|
||||||
|
| [plugin-tikz](https://www.npmjs.com/package/@cartamd/plugin-tikz) |  | [/plugins/tikz](https://beartocode.github.io/carta/plugins/tikz) |
|
||||||
|
| [plugin-attachment](https://www.npmjs.com/package/@cartamd/plugin-attachment) |  | [/plugins/attachment](https://beartocode.github.io/carta/plugins/attachment) |
|
||||||
|
| [plugin-anchor](https://www.npmjs.com/package/@cartamd/plugin-anchor) |  | [/plugins/anchor](https://beartocode.github.io/carta/plugins/anchor) |
|
||||||
|
|
||||||
|
## Community plugins
|
||||||
|
|
||||||
|
| Plugin | Description |
|
||||||
|
| ----------------------------------------------------------------------------- | ---------------------------------- |
|
||||||
|
| [carta-plugin-video](https://github.com/maisonsmd/carta-plugin-video) | Render online videos |
|
||||||
|
| [carta-plugin-imsize](https://github.com/maisonsmd/carta-plugin-imsize) | Render images in specific sizes |
|
||||||
|
| [carta-plugin-subscript](https://github.com/maisonsmd/carta-plugin-subscript) | Render subscripts and superscripts |
|
||||||
|
| [carta-plugin-ins-del](https://github.com/maisonsmd/carta-plugin-ins-del) | `<ins>` and `<del>` tags support |
|
||||||
|
|
||||||
|
# Getting started
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
> Sanitization is not dealt with by Carta. You need to provide a `sanitizer` in the options.
|
> Sanitization is not dealt with by Carta. You need to provide a `sanitizer` in the options.
|
||||||
> Common sanitizers are [isomorphic-dompurify](https://www.npmjs.com/package/isomorphic-dompurify) (suggested) and [sanitize-html](https://www.npmjs.com/package/sanitize-html).
|
> Common sanitizers are [isomorphic-dompurify](https://www.npmjs.com/package/isomorphic-dompurify) (suggested) and [sanitize-html](https://www.npmjs.com/package/sanitize-html).
|
||||||
|
> Checkout the documentation for an example.
|
||||||
|
|
||||||
### Installation
|
## Installation
|
||||||
|
|
||||||
Core package:
|
Core package:
|
||||||
|
|
||||||
|
@ -50,15 +96,13 @@ Plugins:
|
||||||
npm i @cartamd/plugin-name
|
npm i @cartamd/plugin-name
|
||||||
```
|
```
|
||||||
|
|
||||||
### Basic configuration
|
## Basic configuration
|
||||||
|
|
||||||
```svelte
|
```svelte
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Carta, CartaEditor } from 'carta-md';
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
// Component default theme
|
// Component default theme
|
||||||
import 'carta-md/default.css';
|
import 'carta-md/default.css';
|
||||||
// Markdown input theme (Speed Highlight)
|
|
||||||
import 'carta-md/light.css';
|
|
||||||
|
|
||||||
const carta = new Carta({
|
const carta = new Carta({
|
||||||
// Remember to use a sanitizer to prevent XSS attacks
|
// Remember to use a sanitizer to prevent XSS attacks
|
||||||
|
@ -66,85 +110,40 @@ npm i @cartamd/plugin-name
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CartaEditor {carta} />
|
<MarkdownEditor {carta} />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Or in global stylesheet */
|
/* Or in global stylesheet */
|
||||||
/* Set your custom monospace font */
|
/* Set your custom monospace font */
|
||||||
:global(.carta-font-code) {
|
:global(.carta-font-code) {
|
||||||
font-family: '...', monospace;
|
font-family: '...', monospace;
|
||||||
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
```
|
```
|
||||||
|
|
||||||
Editor component exported properties:
|
# Documentation
|
||||||
|
|
||||||
| Name | Type | Description |
|
For the full documentation, examples, guides and more checkout the [website](https://beartocode.github.io/carta/).
|
||||||
| ---------------- | ----------------------------- | ----------------------------------------- |
|
|
||||||
| `carta` | `Carta` | Carta Editor |
|
|
||||||
| `theme` | `string` | For custom css themes, see below for more |
|
|
||||||
| `value` | `string` | Markdown input |
|
|
||||||
| `placeholder` | `string` | Placeholder text for textarea |
|
|
||||||
| `mode` | `'tabs' \| 'split' \| 'auto'` | Tabs settings |
|
|
||||||
| `disableToolbar` | `boolean` | Option to disable the toolbar |
|
|
||||||
|
|
||||||
### Plugins
|
- [Introduction](https://beartocode.github.io/carta/introduction)
|
||||||
|
- [Examples](https://beartocode.github.io/carta/examples)
|
||||||
|
- [Getting Started](https://beartocode.github.io/carta/getting-started)
|
||||||
|
- [Editing Styles](https://beartocode.github.io/carta/editing-styles)
|
||||||
|
- Plugins:
|
||||||
|
- [Math](https://beartocode.github.io/carta/plugins/math)
|
||||||
|
- [Code](https://beartocode.github.io/carta/plugins/code)
|
||||||
|
- [Emoji](https://beartocode.github.io/carta/plugins/emoji)
|
||||||
|
- [Slash](https://beartocode.github.io/carta/plugins/slash)
|
||||||
|
- [TikZ](https://beartocode.github.io/carta/plugins/tikz)
|
||||||
|
- [Attachment](https://beartocode.github.io/carta/plugins/attachment)
|
||||||
|
- [Anchor](https://beartocode.github.io/carta/plugins/anchor)
|
||||||
|
- API:
|
||||||
|
- [Utilities](https://beartocode.github.io/carta/api/utilities)
|
||||||
|
- [Core](https://beartocode.github.io/carta/api/core)
|
||||||
|
- [Extension](https://beartocode.github.io/carta/api/extension)
|
||||||
|
|
||||||
Each plugin's _readme_ includes a guide on its use.
|
# Contributing & Development
|
||||||
|
|
||||||
| Name | Description |
|
|
||||||
| ----------------------------------------------------------------------------------------------- | --------------------------------------- |
|
|
||||||
| [plugin-math](https://github.com/BearToCode/carta/tree/master/packages/plugin-math) | Katex support |
|
|
||||||
| [plugin-slash](https://github.com/BearToCode/carta/tree/master/packages/plugin-slash) | Slash commands support |
|
|
||||||
| [plugin-emoji](https://github.com/BearToCode/carta/tree/master/packages/plugin-emoji) | Emojis support, including inline search |
|
|
||||||
| [plugin-code](https://github.com/BearToCode/carta/tree/master/packages/plugin-code) | Code blocks syntax highlighting |
|
|
||||||
| [plugin-tikz](https://github.com/BearToCode/carta/tree/master/packages/plugin-tikz) | TikZ support using TikZJax |
|
|
||||||
| [plugin-attachment](https://github.com/BearToCode/carta/tree/master/packages/plugin-attachment) | Attachments support |
|
|
||||||
|
|
||||||
## Themes customization
|
|
||||||
|
|
||||||
By using the `theme` property in `CartaEditor` and `CartaPreview` you can change their classes to `carta-editor__{theme}` and `carta-viewer__{theme}`.
|
|
||||||
|
|
||||||
Check out the [default theme](https://github.com/BearToCode/carta/blob/master/packages/carta-md/src/lib/default.css) to customize it.
|
|
||||||
|
|
||||||
If you are using a plugin, look at its _readme_ for its customization.
|
|
||||||
|
|
||||||
Markdown highlighting is done using **Speed Highlight JS**, [here](https://github.com/speed-highlight/core/tree/main/src/themes) you can find more themes.
|
|
||||||
|
|
||||||
You can find complete Markdown stylesheet online. For example [github-markdown-css](https://github.com/sindresorhus/github-markdown-css)(used in the demo), or [tailwind-typography](https://tailwindcss.com/docs/typography-plugin).
|
|
||||||
|
|
||||||
## Extensibility
|
|
||||||
|
|
||||||
### Options
|
|
||||||
|
|
||||||
Carta options:
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------------------ | ------------------------------ | ----------------------------------------------- |
|
|
||||||
| `extensions` | `CartaExtension[]` | Editor/viewer extensions |
|
|
||||||
| `rendererDebounce` | `number` | Renderer debouncing timeout, in ms (def. 300ms) |
|
|
||||||
| `disableShortcuts` | `DefaultShortcutId[] \| true` | Remove default shortcuts by ids |
|
|
||||||
| `disableIcons` | `DefaultIconId[] \| true` | Remove default icons by ids |
|
|
||||||
| `disablePrefixes` | `DefaultPrefixId[] \| true` | Remove default prefixes by ids |
|
|
||||||
| `historyOptions` | `Partial<CartaHistoryOptions>` | History (Undo/Redo) options |
|
|
||||||
| `sanitizer` | `(html: string) => string` | HTML sanitizer |
|
|
||||||
|
|
||||||
You can easily extend Carta by creating custom plugins. Here are all the `CartaExtension` properties:
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
| ------------------ | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `markedExtensions` | `marked.MarkedExtension[]` | Marked extensions, more on that [here](https://marked.js.org/using_advanced) |
|
|
||||||
| `shortcuts` | `KeyboardShortcut[]` | Additional keyboard shortcuts |
|
|
||||||
| `icons` | `CartaIcon[]` | Additional icons |
|
|
||||||
| `prefixes` | `Prefix[]` | Additional prefixes |
|
|
||||||
| `listeners` | `CartaListener[]` | Textarea event listeners |
|
|
||||||
| `components` | `CartaExtensionComponents` | Additional components, that will be put after the editor. All components are given a `carta: Carta`. prop The editor has a `relative` position, so you can position elements absolutely |
|
|
||||||
| `highlightRules` | `HighlightRule[]` | Custom markdown highlight rules. See [Speed-Highlight Wiki](https://github.com/speed-highlight/core/wiki/Create-or-suggest-new-languages). |
|
|
||||||
| `onLoad` | `(data: { carta:Carta, ... }) => void` | Use this callback to execute code when one Carta instance loads the extension. |
|
|
||||||
|
|
||||||
If you created a plugin and want to share it, you can open an _issue_ and we will consider sponsoring it on this guide.
|
|
||||||
|
|
||||||
## Contributions
|
|
||||||
|
|
||||||
Every contribution is well accepted. If you have a feature request you can open a new issue.
|
Every contribution is well accepted. If you have a feature request you can open a new issue.
|
||||||
|
|
||||||
|
@ -159,3 +158,12 @@ npm run commit
|
||||||
# or, if you have commitizen installed globally
|
# or, if you have commitizen installed globally
|
||||||
git cz
|
git cz
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Running docs
|
||||||
|
|
||||||
|
If you want to preview the docs:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd docs
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
engine-strict=true
|
|
||||||
resolution-mode=highest
|
|
|
@ -1,144 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import { Carta, CartaEditor } from 'carta-md';
|
|
||||||
import { math } from '@cartamd/plugin-math';
|
|
||||||
import { slash } from '@cartamd/plugin-slash';
|
|
||||||
import { emoji } from '@cartamd/plugin-emoji';
|
|
||||||
import { code } from '@cartamd/plugin-code';
|
|
||||||
import { tikz } from '@cartamd/plugin-tikz';
|
|
||||||
import { placeholderText } from './placeholder';
|
|
||||||
|
|
||||||
import 'carta-md/default.css';
|
|
||||||
import 'carta-md/light.css';
|
|
||||||
|
|
||||||
import 'katex/dist/katex.css';
|
|
||||||
|
|
||||||
import '@cartamd/plugin-code/default.css';
|
|
||||||
import '@cartamd/plugin-slash/default.css';
|
|
||||||
import '@cartamd/plugin-emoji/default.css';
|
|
||||||
import '@cartamd/plugin-tikz/fonts.css';
|
|
||||||
|
|
||||||
const carta = new Carta({
|
|
||||||
extensions: [
|
|
||||||
tikz({
|
|
||||||
debug: true
|
|
||||||
}),
|
|
||||||
slash(),
|
|
||||||
emoji(),
|
|
||||||
code(),
|
|
||||||
math()
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
let syncScroll = true;
|
|
||||||
let mode: 'tabs' | 'split' | 'auto' = 'auto';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<!-- Custom fonts -->
|
|
||||||
<!-- Fira font -->
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;600;700&display=swap"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
<!-- Inter -->
|
|
||||||
<link rel="preconnect" href="https://rsms.me/" />
|
|
||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="options">
|
|
||||||
<fieldset>
|
|
||||||
<legend>Scroll</legend>
|
|
||||||
|
|
||||||
<input bind:checked={syncScroll} type="checkbox" id="scroll" name="scroll" />
|
|
||||||
<label for="scroll">Sync scroll</label>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>Editor mode</legend>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input type="radio" id="auto" name="drone" value="auto" bind:group={mode} />
|
|
||||||
<label for="auto">Auto</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input type="radio" id="tabs" name="drone" value="tabs" bind:group={mode} />
|
|
||||||
<label for="tabs">Tabs</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input type="radio" id="split" name="drone" value="split" bind:group={mode} />
|
|
||||||
<label for="split">Split</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CartaEditor value={placeholderText} scroll={syncScroll ? 'sync' : 'async'} {carta} {mode} />
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
:global(body) {
|
|
||||||
margin: 0;
|
|
||||||
font-family: 'Inter var', sans-serif;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.carta-font-code, code) {
|
|
||||||
font-family: 'Fira Code', monospace;
|
|
||||||
font-variant-ligatures: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(input, textarea, button) {
|
|
||||||
font-family: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
max-width: 1536px;
|
|
||||||
margin: 0 auto 0 auto;
|
|
||||||
padding: 2rem 0 2rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.options {
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive main */
|
|
||||||
|
|
||||||
@media screen and (max-width: 640px) {
|
|
||||||
main {
|
|
||||||
width: 95%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 640px) and (max-width: 767px) {
|
|
||||||
main {
|
|
||||||
width: 640px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 767px) and (max-width: 1023px) {
|
|
||||||
main {
|
|
||||||
width: 768px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1023px) and (max-width: 1279px) {
|
|
||||||
main {
|
|
||||||
width: 1024px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1279px) and (max-width: 1535px) {
|
|
||||||
main {
|
|
||||||
width: 1280px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1 +0,0 @@
|
||||||
export const prerender = true;
|
|
|
@ -1,122 +0,0 @@
|
||||||
export const placeholderText = `# Carta Demo
|
|
||||||
|
|
||||||
Far out in the uncharted backwaters of the unfashionable end of the western spiral arm of the **Galaxy** lies a small unregarded yellow sun.
|
|
||||||
Orbiting this at a distance of roughly _ninety-two million miles_ is an utterly insignificant little blue green planet :earth_africa: whose ape-descended life forms are so amazingly primitive that they still think digital watches are a pretty neat idea.
|
|
||||||
|
|
||||||
\`\`\`tikz
|
|
||||||
\\usepackage{pgfplots}
|
|
||||||
\\pgfplotsset{compat=1.16}
|
|
||||||
|
|
||||||
\\pgfplotsset{width=7cm,compat=1.8}
|
|
||||||
\\begin{document}
|
|
||||||
\\begin{tikzpicture}[thick,scale=1.2, every node/.style={scale=1.2}]
|
|
||||||
\\begin{axis}
|
|
||||||
\\addplot3[
|
|
||||||
surf,
|
|
||||||
colormap/viridis,
|
|
||||||
samples=20,
|
|
||||||
domain=0:2*pi,
|
|
||||||
y domain=0:2*pi,
|
|
||||||
z buffer=sort
|
|
||||||
]
|
|
||||||
( {(2+cos(deg(x)))*cos(deg(y+pi/2))},
|
|
||||||
{(2+cos(deg(x)))*sin(deg(y+pi/2))},
|
|
||||||
{sin(deg(x))}
|
|
||||||
);
|
|
||||||
\\end{axis}
|
|
||||||
\\end{tikzpicture}
|
|
||||||
\\end{document}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
# Basic Markdown
|
|
||||||
|
|
||||||
You can have different types of text: **bold**, _italic_, \`code\` and ~~strikethrough~~. This is a [link](http://beartocode.github.io/carta/) to the page you are currently viewing.
|
|
||||||
|
|
||||||
Here is a quote:
|
|
||||||
|
|
||||||
> Time is an illusion. Lunchtime doubly so.
|
|
||||||
|
|
||||||
And then some lists:
|
|
||||||
|
|
||||||
- This is a bulleted list
|
|
||||||
- Which does not have an order
|
|
||||||
- but only some dots
|
|
||||||
|
|
||||||
1. And then that's one with numbers
|
|
||||||
2. That just keep going
|
|
||||||
3. and going
|
|
||||||
|
|
||||||
- [ ] And finally this is a task list
|
|
||||||
- [ ] Where you can keep track of to-dos
|
|
||||||
- [x] by putting an _x_ inside the brackets
|
|
||||||
|
|
||||||
You can also create tables, like so:
|
|
||||||
|
|
||||||
| Item | Price | Origin |
|
|
||||||
| ------- | ----- | ------ |
|
|
||||||
| :apple: | 2.1 | Italy |
|
|
||||||
| :banana:| 42 | Brazil |
|
|
||||||
| :lemon: | 18 | Spain |
|
|
||||||
|
|
||||||
# Official Plugins
|
|
||||||
|
|
||||||
## \`plugin-code\`
|
|
||||||
|
|
||||||
This plugin adds support for **syntax highlighting** on your code blocks:
|
|
||||||
|
|
||||||
\`\`\`rs
|
|
||||||
fn visit_mars() {
|
|
||||||
let spaceship = get_spaceship();
|
|
||||||
|
|
||||||
spaceship.liftoff();
|
|
||||||
spaceship.head_to(Planet::Mars);
|
|
||||||
thread::sleep(Time::Month(6));
|
|
||||||
spaceship.land();
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## \`plugin-math\`
|
|
||||||
|
|
||||||
|
|
||||||
With this plugin you can write beautiful Katex expressions, both _inline_ $\\underline{v}=A-\\lambda I_d$, and as _block_ equations:
|
|
||||||
|
|
||||||
$$
|
|
||||||
{\\displaystyle {\\boldsymbol {\\sigma }}=\\zeta (\\nabla \\cdot \\mathbf {u} )\\mathbf {I} +\\mu \\left[\\nabla \\mathbf {u} +(\\nabla \\mathbf {u} )^{\\mathrm {T} }-{\\tfrac {2}{3}}(\\nabla \\cdot \\mathbf {u} )\\mathbf {I} \\right]}
|
|
||||||
$$
|
|
||||||
|
|
||||||
## \`plugin-emoji\`
|
|
||||||
|
|
||||||
Adds support for **emojis**, as well as an inline emoji search, that appears after typing a colon. :smile_cat:
|
|
||||||
|
|
||||||
Try typing an icon to see the inline search. :alien:
|
|
||||||
|
|
||||||
## \`plugin-slash\`
|
|
||||||
|
|
||||||
Use a **slash** / to use commands while writing markdown. You can add your custom commands too. Also includes a inline command search.
|
|
||||||
|
|
||||||
## \`plugin-tikz\`
|
|
||||||
|
|
||||||
|
|
||||||
\`\`\`tikz
|
|
||||||
\\usepackage{pgfplots}
|
|
||||||
\\pgfplotsset{compat=1.16}
|
|
||||||
\\pgfplotsset{width=7cm}
|
|
||||||
|
|
||||||
\\begin{document}
|
|
||||||
\\begin{tikzpicture}[thick,scale=1.2, every node/.style={scale=1.2}]
|
|
||||||
\\begin{axis}[domain=-1:1,y domain=-1:1]
|
|
||||||
\\addplot3[
|
|
||||||
surf,
|
|
||||||
colormap/viridis,
|
|
||||||
samples = 18
|
|
||||||
]
|
|
||||||
{x*y*exp(x+2*y-9*x^2-9*y^2)};
|
|
||||||
\\end{axis}
|
|
||||||
\\end{tikzpicture}
|
|
||||||
\\end{document}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Adds support for **TikZ**, thanks to [TikZJax](https://tikzjax.com/).
|
|
||||||
|
|
||||||
|
|
||||||
`;
|
|
File diff suppressed because it is too large
Load diff
1
demo/.gitignore → docs/.gitignore
vendored
1
demo/.gitignore → docs/.gitignore
vendored
|
@ -1,6 +1,7 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
/build
|
/build
|
||||||
|
/dist
|
||||||
/.svelte-kit
|
/.svelte-kit
|
||||||
/package
|
/package
|
||||||
.env
|
.env
|
13
docs/components.json
Normal file
13
docs/components.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.js",
|
||||||
|
"css": "src/app.postcss",
|
||||||
|
"baseColor": "neutral"
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "$lib/components",
|
||||||
|
"utils": "$lib/utils"
|
||||||
|
}
|
||||||
|
}
|
16
docs/mdsvex.config.js
Normal file
16
docs/mdsvex.config.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { defineMDSveXConfig as defineConfig } from 'mdsvex';
|
||||||
|
import rehypeSlug from 'rehype-slug';
|
||||||
|
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
||||||
|
|
||||||
|
const config = defineConfig({
|
||||||
|
extensions: ['.svelte.md', '.md', '.svx'],
|
||||||
|
|
||||||
|
smartypants: {
|
||||||
|
dashes: 'oldschool'
|
||||||
|
},
|
||||||
|
|
||||||
|
remarkPlugins: [],
|
||||||
|
rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: 'wrap' }]]
|
||||||
|
});
|
||||||
|
|
||||||
|
export default config;
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "demo",
|
"name": "docs",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -15,24 +15,40 @@
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^2.0.0",
|
"@sveltejs/adapter-auto": "^3.1.1",
|
||||||
"@sveltejs/adapter-static": "1.0.0-next.50",
|
"@sveltejs/adapter-static": "3.0.1",
|
||||||
"@sveltejs/kit": "^1.5.0",
|
"@sveltejs/kit": "^2.5.4",
|
||||||
|
"@sveltejs/package": "^2.3.0",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^3.0.2",
|
||||||
|
"@types/flexsearch": "^0.7.6",
|
||||||
"@types/katex": "^0.16.0",
|
"@types/katex": "^0.16.0",
|
||||||
"svelte": "^3.54.0 || ^4.0.0",
|
"autoprefixer": "^10.4.16",
|
||||||
"svelte-check": "^3.0.1",
|
"mdsvex": "^0.11.0",
|
||||||
|
"rehype-autolink-headings": "^7.1.0",
|
||||||
|
"rehype-slug": "^6.0.0",
|
||||||
|
"sass": "^1.69.5",
|
||||||
|
"svelte": "^4.2.12",
|
||||||
|
"svelte-check": "^3.6.7",
|
||||||
|
"tailwindcss": "^3.3.5",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^4.3.9"
|
"vite": "^5.1.6"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@cartamd/plugin-attachment": "workspace:^",
|
||||||
"@cartamd/plugin-code": "workspace:^",
|
"@cartamd/plugin-code": "workspace:^",
|
||||||
"@cartamd/plugin-emoji": "workspace:^",
|
"@cartamd/plugin-emoji": "workspace:^",
|
||||||
"@cartamd/plugin-math": "workspace:^",
|
"@cartamd/plugin-math": "workspace:^",
|
||||||
"@cartamd/plugin-slash": "workspace:^",
|
"@cartamd/plugin-slash": "workspace:^",
|
||||||
"@cartamd/plugin-tikz": "workspace:^",
|
"@cartamd/plugin-tikz": "workspace:^",
|
||||||
|
"bits-ui": "^0.9.1",
|
||||||
"carta-md": "workspace:^",
|
"carta-md": "workspace:^",
|
||||||
"katex": "^0.16.7"
|
"clsx": "^2.0.0",
|
||||||
|
"cmdk-sv": "^0.0.6",
|
||||||
|
"flexsearch": "0.7.21",
|
||||||
|
"iconify-icon": "^2.0.0",
|
||||||
|
"katex": "^0.16.10",
|
||||||
|
"tailwind-merge": "^2.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
8
docs/postcss.config.cjs
Normal file
8
docs/postcss.config.cjs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const tailwindcss = require('tailwindcss');
|
||||||
|
const autoprefixer = require('autoprefixer');
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
plugins: [tailwindcss(), autoprefixer]
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
0
demo/src/app.d.ts → docs/src/app.d.ts
vendored
0
demo/src/app.d.ts → docs/src/app.d.ts
vendored
25
docs/src/app.html
Normal file
25
docs/src/app.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html class="dark" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
%sveltekit.head%
|
||||||
|
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<!-- Inter -->
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<!-- Fira Code -->
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;600;700&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
84
docs/src/app.postcss
Normal file
84
docs/src/app.postcss
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 0 0% 3.9%;
|
||||||
|
|
||||||
|
--muted: 0 0% 96.1%;
|
||||||
|
--muted-foreground: 0 0% 45.1%;
|
||||||
|
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 0 0% 3.9%;
|
||||||
|
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 0 0% 3.9%;
|
||||||
|
|
||||||
|
--border: 0 0% 89.8%;
|
||||||
|
--input: 0 0% 89.8%;
|
||||||
|
|
||||||
|
--primary: 0 0% 9%;
|
||||||
|
--primary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--secondary: 0 0% 96.1%;
|
||||||
|
--secondary-foreground: 0 0% 9%;
|
||||||
|
|
||||||
|
--accent: 0 0% 96.1%;
|
||||||
|
--accent-foreground: 0 0% 9%;
|
||||||
|
|
||||||
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--ring: 0 0% 3.9%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
|
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 0 0% 3.9%;
|
||||||
|
--foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--muted: 0 0% 14.9%;
|
||||||
|
--muted-foreground: 0 0% 63.9%;
|
||||||
|
|
||||||
|
--popover: 0 0% 3.9%;
|
||||||
|
--popover-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--card: 0 0% 3.9%;
|
||||||
|
--card-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--border: 0 0% 14.9%;
|
||||||
|
--input: 0 0% 14.9%;
|
||||||
|
|
||||||
|
--primary: 0 0% 98%;
|
||||||
|
--primary-foreground: 0 0% 9%;
|
||||||
|
|
||||||
|
--secondary: 0 0% 14.9%;
|
||||||
|
--secondary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--accent: 0 0% 14.9%;
|
||||||
|
--accent-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--ring: 0 0% 83.1%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nav div[data-portal] {
|
||||||
|
display: none;
|
||||||
|
}
|
20
docs/src/lib/components/code/Code.svelte
Normal file
20
docs/src/lib/components/code/Code.svelte
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<script lang="ts">
|
||||||
|
let elem: HTMLElement;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={elem} class="relative">
|
||||||
|
<slot />
|
||||||
|
<!-- Copy button -->
|
||||||
|
<button
|
||||||
|
title="Copy"
|
||||||
|
on:click={() => {
|
||||||
|
navigator.clipboard.writeText(elem.innerText);
|
||||||
|
}}
|
||||||
|
class="
|
||||||
|
absolute right-4 top-[min(50%_,_32px)] aspect-square -translate-y-1/2 transform
|
||||||
|
rounded hover:bg-neutral-800 hover:text-neutral-300 active:text-sky-300
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<iconify-icon icon="octicon:copy-16" class="p-2 text-lg"></iconify-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
9
docs/src/lib/components/footer/Footer.svelte
Normal file
9
docs/src/lib/components/footer/Footer.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<div class="mt-12 w-full border-t border-neutral-800 text-end">
|
||||||
|
<span class="mt-6 block text-sm italic text-neutral-400"
|
||||||
|
>Handmade by <a
|
||||||
|
target="_blank"
|
||||||
|
class="hover:text-sky-300 hover:underline"
|
||||||
|
href="https://github.com/BearToCode">Davide</a
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</div>
|
66
docs/src/lib/components/header-tracker/HeaderTracker.svelte
Normal file
66
docs/src/lib/components/header-tracker/HeaderTracker.svelte
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onNavigate } from '$app/navigation';
|
||||||
|
import { debounce, throttle } from '$lib/utils';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
const PADDING = 80;
|
||||||
|
|
||||||
|
export { className as class };
|
||||||
|
|
||||||
|
let className = '';
|
||||||
|
let headers: HTMLElement[] = [];
|
||||||
|
let selectedHeaderIndex = 0;
|
||||||
|
|
||||||
|
function retrieveHeaders() {
|
||||||
|
headers = Array.from(
|
||||||
|
document.querySelectorAll('.markdown > h1, .markdown > h2, .markdown > h3')
|
||||||
|
) as HTMLElement[];
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [throttledHighlightHeader] = throttle(highlightHeader, 100);
|
||||||
|
const debouncedHighlightHeader = debounce(highlightHeader, 100);
|
||||||
|
|
||||||
|
onNavigate(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
retrieveHeaders();
|
||||||
|
highlightHeader();
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
onMount(retrieveHeaders);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<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}
|
||||||
|
{#key selectedHeaderIndex}
|
||||||
|
{#if header.children[0] instanceof HTMLAnchorElement && header.children[0].href}
|
||||||
|
<a
|
||||||
|
style="margin-left: {margin * 0.75}rem;"
|
||||||
|
class="block text-sm {selectedHeaderIndex === i
|
||||||
|
? 'font-medium text-sky-300'
|
||||||
|
: 'text-neutral-400'}"
|
||||||
|
href={header.children[0].href}>{header.innerText}</a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{/key}
|
||||||
|
{/each}
|
||||||
|
</div>
|
17
docs/src/lib/components/link/Link.svelte
Normal file
17
docs/src/lib/components/link/Link.svelte
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
|
export let href = '';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a
|
||||||
|
{...$$restProps}
|
||||||
|
href={href.startsWith('/') ? `${base}${href}` : href}
|
||||||
|
on:click
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</a>
|
22
docs/src/lib/components/link/PluginLink.svelte
Normal file
22
docs/src/lib/components/link/PluginLink.svelte
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let npmLink: string;
|
||||||
|
export let githubLink: string;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="plugin-link mb-2 mt-6 flex items-end space-x-3">
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<a href={githubLink} class="flex aspect-square">
|
||||||
|
<iconify-icon icon="mdi:github" class="text-3xl text-white hover:text-sky-300"></iconify-icon>
|
||||||
|
</a>
|
||||||
|
<a href={npmLink} class="flex aspect-square">
|
||||||
|
<iconify-icon icon="gg:npm" class="text-3xl text-white hover:text-sky-300"></iconify-icon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:global(.markdown .plugin-link > h1, .markdown .plugin-link > h2, .markdown .plugin-link > h3) {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
34
docs/src/lib/components/mobile-sidebar/MobileSidebar.svelte
Normal file
34
docs/src/lib/components/mobile-sidebar/MobileSidebar.svelte
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Sidebar from '../sidebar/Sidebar.svelte';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
|
export { className as class };
|
||||||
|
|
||||||
|
let className = '';
|
||||||
|
let enabled = false;
|
||||||
|
|
||||||
|
$: {
|
||||||
|
$page.url;
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container mb-4 w-full border-b border-neutral-800 px-4 pb-1 sm:px-6 {className}">
|
||||||
|
<button on:click={() => (enabled = !enabled)} class="text-neutral-500 hover:text-neutral-200">
|
||||||
|
<iconify-icon icon="ci:hamburger-lg" class="text-3xl"></iconify-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if enabled}
|
||||||
|
<div class="fixed bottom-0 left-0 right-0 top-0 z-10 bg-neutral-950 bg-opacity-50"></div>
|
||||||
|
|
||||||
|
<div class="fixed bottom-0 left-0 top-0 z-10 rounded-r-xl bg-neutral-900">
|
||||||
|
<Sidebar class="w-[20rem] px-4 py-4" />
|
||||||
|
<button
|
||||||
|
on:click={() => (enabled = false)}
|
||||||
|
class="absolute right-4 top-4 text-neutral-500 hover:text-neutral-200"
|
||||||
|
>
|
||||||
|
<iconify-icon icon="charm:cross" class="text-2xl"></iconify-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
33
docs/src/lib/components/navbar/GitHub.svelte
Normal file
33
docs/src/lib/components/navbar/GitHub.svelte
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
export { className as class };
|
||||||
|
|
||||||
|
let className = '';
|
||||||
|
let loading = true;
|
||||||
|
let stars: number;
|
||||||
|
onMount(async () => {
|
||||||
|
const res = await fetch('https://api.github.com/repos/BearToCode/carta');
|
||||||
|
const json = await res.json();
|
||||||
|
stars = json.stargazers_count;
|
||||||
|
loading = false;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="https://github.com/BearToCode/carta"
|
||||||
|
class="flex h-12 items-center space-x-2 p-2 {className}"
|
||||||
|
>
|
||||||
|
<iconify-icon icon="mdi:github" class="text-2xl"></iconify-icon>
|
||||||
|
<div class="hidden h-min flex-col justify-center space-y-1 md:flex">
|
||||||
|
<p class="text-[0.9rem] font-semibold leading-3">BearToCode/carta</p>
|
||||||
|
{#if loading}
|
||||||
|
<div class="pulse my-1.5 h-3 w-[80px] rounded-full bg-neutral-800" />
|
||||||
|
{:else}
|
||||||
|
<div class="inline-flex items-center space-x-1">
|
||||||
|
<iconify-icon icon="ic:round-star" class="h-3 w-3"></iconify-icon>
|
||||||
|
<span class="mt-1 text-[0.8rem] leading-3">{stars}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</a>
|
28
docs/src/lib/components/navbar/Navbar.svelte
Normal file
28
docs/src/lib/components/navbar/Navbar.svelte
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
import Link from '../link/Link.svelte';
|
||||||
|
import GitHub from './GitHub.svelte';
|
||||||
|
import Search from './Search.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav
|
||||||
|
class="fixed left-0 right-0 top-0 z-10 bg-neutral-900 bg-opacity-50 backdrop-blur-2xl backdrop-filter"
|
||||||
|
>
|
||||||
|
<div class="container mx-auto flex items-center justify-between px-4 py-1 sm:px-6">
|
||||||
|
<Link href="/">
|
||||||
|
<img src="{base}/logo.png" class="h-8" alt="carta logo" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div class="flex-grow" />
|
||||||
|
|
||||||
|
<Search class="mx-auto" />
|
||||||
|
|
||||||
|
<div class="hidden flex-grow md:block" />
|
||||||
|
|
||||||
|
<GitHub class="ml-auto" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-neutral-900 via-neutral-700 to-neutral-900"
|
||||||
|
/>
|
||||||
|
</nav>
|
111
docs/src/lib/components/navbar/Search.svelte
Normal file
111
docs/src/lib/components/navbar/Search.svelte
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import * as Command from '$lib/components/ui/command';
|
||||||
|
import type { Document } from 'flexsearch';
|
||||||
|
import {
|
||||||
|
enrichResult,
|
||||||
|
initializeSearch,
|
||||||
|
type EnrichedSearchResult,
|
||||||
|
type SearchResult
|
||||||
|
} from '$lib/search';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
|
export { className as class };
|
||||||
|
|
||||||
|
let open = false;
|
||||||
|
let value = '';
|
||||||
|
let className = '';
|
||||||
|
let index: Document<SearchResult, true>;
|
||||||
|
let results: EnrichedSearchResult[] = [];
|
||||||
|
|
||||||
|
function handleKeydown(e: KeyboardEvent) {
|
||||||
|
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
|
||||||
|
e.preventDefault();
|
||||||
|
open = !open;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(query: string) {
|
||||||
|
if (!index || !query) {
|
||||||
|
results = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pages = new Map<string, SearchResult>();
|
||||||
|
const searchResult = index
|
||||||
|
.search(query, 5, { enrich: true })
|
||||||
|
.map((res) => res.result)
|
||||||
|
.flat();
|
||||||
|
for (const res of searchResult) {
|
||||||
|
pages.set(res.doc.path, enrichResult(res.doc, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
results = Array.from(pages.values()).slice(0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
index = await initializeSearch();
|
||||||
|
});
|
||||||
|
|
||||||
|
$: search(value);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:keydown={handleKeydown} />
|
||||||
|
|
||||||
|
<button on:click={() => (open = !open)} class="mr-2 block aspect-square md:hidden">
|
||||||
|
<iconify-icon icon="ion:search" class="text-2xl text-neutral-200"></iconify-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="hidden w-[360px] items-center justify-between rounded-lg border border-neutral-700 bg-neutral-900 px-2 py-1.5 text-sm md:flex {className}"
|
||||||
|
on:click={() => (open = !open)}
|
||||||
|
>
|
||||||
|
<div class="inline-flex items-center space-x-2">
|
||||||
|
<iconify-icon icon="ion:search" class="text-xl text-neutral-500"></iconify-icon>
|
||||||
|
<span class="text-neutral-500">Search...</span>
|
||||||
|
</div>
|
||||||
|
<kbd
|
||||||
|
class="bg-muted text-muted-foreground pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border px-1.5 font-mono text-[10px] font-medium opacity-100"
|
||||||
|
>
|
||||||
|
<span class="text-xs">⌘</span>K
|
||||||
|
</kbd>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Command.Dialog shouldFilter={false} bind:open>
|
||||||
|
<Command.Input bind:value placeholder="Search anything..." />
|
||||||
|
<Command.List>
|
||||||
|
{#if value}
|
||||||
|
<Command.Empty>No results found.</Command.Empty>
|
||||||
|
<Command.Group>
|
||||||
|
{#each results as result}
|
||||||
|
<Command.Item
|
||||||
|
onSelect={() => {
|
||||||
|
if (result.match?.heading) goto(`${base}/${result.path}#${result.match.heading.id}`);
|
||||||
|
else goto(`${base}/${result.path}`);
|
||||||
|
open = false;
|
||||||
|
}}
|
||||||
|
class="group"
|
||||||
|
value={result.title}
|
||||||
|
>
|
||||||
|
<h2 class="text-base font-medium">
|
||||||
|
{result.title}
|
||||||
|
|
||||||
|
{#if result.match?.heading}
|
||||||
|
<span class="text-neutral-400"> - {result.match.heading.text}</span>
|
||||||
|
{/if}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{#if result.match}
|
||||||
|
<p class="line-clamp-1 pr-8 text-sm text-neutral-400">{result.match.text}</p>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="absolute right-2 top-1/2 hidden -translate-y-1/2 group-aria-selected:block">
|
||||||
|
<iconify-icon icon="mi:enter" class="text-xl text-neutral-400"></iconify-icon>
|
||||||
|
</div>
|
||||||
|
</Command.Item>
|
||||||
|
{/each}
|
||||||
|
</Command.Group>
|
||||||
|
{/if}
|
||||||
|
</Command.List>
|
||||||
|
</Command.Dialog>
|
114
docs/src/lib/components/sidebar/Sidebar.svelte
Normal file
114
docs/src/lib/components/sidebar/Sidebar.svelte
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import SidebarLink from './SidebarLink.svelte';
|
||||||
|
|
||||||
|
export { className as class };
|
||||||
|
|
||||||
|
let className = '';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="h-full {className}">
|
||||||
|
<h3 class="mb-3 ml-4 mt-6 text-sm font-medium first:mt-0 last:mb-0">Overview</h3>
|
||||||
|
|
||||||
|
<!-- Introduction -->
|
||||||
|
<SidebarLink href="/introduction">
|
||||||
|
<iconify-icon icon="radix-icons:dashboard" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Introduction</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Examples -->
|
||||||
|
<SidebarLink href="/examples">
|
||||||
|
<iconify-icon icon="ph:codesandbox-logo" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Examples</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Getting Started -->
|
||||||
|
<SidebarLink href="/getting-started">
|
||||||
|
<iconify-icon icon="ic:round-download" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Getting Started</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Editing Styles -->
|
||||||
|
<SidebarLink href="/editing-styles">
|
||||||
|
<iconify-icon icon="lucide:palette" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Editing Styles</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Migration -->
|
||||||
|
<SidebarLink href="/migration">
|
||||||
|
<iconify-icon icon="material-symbols:upgrade" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Migration</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Community Plugins -->
|
||||||
|
<SidebarLink href="/community-plugins">
|
||||||
|
<iconify-icon icon="ph:stack-fill" class="text-xl"></iconify-icon>
|
||||||
|
<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 -->
|
||||||
|
<SidebarLink href="/plugins/math">
|
||||||
|
<iconify-icon icon="tabler:math" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Math</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Code -->
|
||||||
|
<SidebarLink href="/plugins/code">
|
||||||
|
<iconify-icon icon="fluent:code-16-filled" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Code</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Emoji -->
|
||||||
|
<SidebarLink href="/plugins/emoji">
|
||||||
|
<iconify-icon icon="mingcute:emoji-line" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Emoji</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Slash -->
|
||||||
|
<SidebarLink href="/plugins/slash">
|
||||||
|
<iconify-icon icon="tabler:slash" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Slash</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- TikZ -->
|
||||||
|
<SidebarLink href="/plugins/tikz">
|
||||||
|
<iconify-icon icon="mdi:draw-pen" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">TikZ</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Attachment -->
|
||||||
|
<SidebarLink href="/plugins/attachment">
|
||||||
|
<iconify-icon icon="tdesign:attach" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Attachment</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Anchor -->
|
||||||
|
<SidebarLink href="/plugins/anchor">
|
||||||
|
<iconify-icon icon="mingcute:link-fill" class="text-xl"></iconify-icon>
|
||||||
|
<span class="text-[0.95rem]">Anchor</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<h3 class="mb-3 ml-4 mt-6 text-sm font-medium first:mt-0 last:mb-0">API</h3>
|
||||||
|
|
||||||
|
<!-- Utilities -->
|
||||||
|
<SidebarLink href="/api/utilities">
|
||||||
|
<span class="text-[0.95rem]">Utilities</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Core -->
|
||||||
|
<SidebarLink href="/api/core">
|
||||||
|
<span class="text-[0.95rem]">Core</span>
|
||||||
|
</SidebarLink>
|
||||||
|
|
||||||
|
<!-- Extension -->
|
||||||
|
<SidebarLink href="/api/extension">
|
||||||
|
<span class="text-[0.95rem]">Extension</span>
|
||||||
|
</SidebarLink>
|
||||||
|
</div>
|
19
docs/src/lib/components/sidebar/SidebarLink.svelte
Normal file
19
docs/src/lib/components/sidebar/SidebarLink.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import Link from '../link/Link.svelte';
|
||||||
|
|
||||||
|
export let href: string;
|
||||||
|
|
||||||
|
let currentHref: string;
|
||||||
|
$: currentHref = $page.url.pathname;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
{href}
|
||||||
|
class="
|
||||||
|
{currentHref === `${base}${href}`
|
||||||
|
? 'bg-sky-400 bg-opacity-10 font-medium text-sky-300'
|
||||||
|
: 'text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300'}
|
||||||
|
inline-flex w-full items-center space-x-2 rounded-lg px-4 py-1.5"><slot /></Link
|
||||||
|
>
|
13
docs/src/lib/components/ui/card/card-content.svelte
Normal file
13
docs/src/lib/components/ui/card/card-content.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn('p-6 pt-0', className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
13
docs/src/lib/components/ui/card/card-description.svelte
Normal file
13
docs/src/lib/components/ui/card/card-description.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLParagraphElement>;
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p class={cn('text-muted-foreground text-base', className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</p>
|
13
docs/src/lib/components/ui/card/card-footer.svelte
Normal file
13
docs/src/lib/components/ui/card/card-footer.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn('flex items-center p-6 pt-0', className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
13
docs/src/lib/components/ui/card/card-header.svelte
Normal file
13
docs/src/lib/components/ui/card/card-header.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn('flex flex-col space-y-1.5 p-6', className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
21
docs/src/lib/components/ui/card/card-title.svelte
Normal file
21
docs/src/lib/components/ui/card/card-title.svelte
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
import type { HeadingLevel } from '.';
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLHeadingElement> & {
|
||||||
|
tag?: HeadingLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export let tag: $$Props['tag'] = 'h3';
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:element
|
||||||
|
this={tag}
|
||||||
|
class={cn('font-semibold leading-none tracking-tight', className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</svelte:element>
|
24
docs/src/lib/components/ui/card/card.svelte
Normal file
24
docs/src/lib/components/ui/card/card.svelte
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Link from '$lib/components/link/Link.svelte';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
let className = '';
|
||||||
|
export { className as class };
|
||||||
|
export let href: string;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
{href}
|
||||||
|
class={cn(
|
||||||
|
'bg-card text-card-foreground block rounded-xl border bg-opacity-30 shadow hover:border-sky-300',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</Link>
|
24
docs/src/lib/components/ui/card/index.ts
Normal file
24
docs/src/lib/components/ui/card/index.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import Root from './card.svelte';
|
||||||
|
import Content from './card-content.svelte';
|
||||||
|
import Description from './card-description.svelte';
|
||||||
|
import Footer from './card-footer.svelte';
|
||||||
|
import Header from './card-header.svelte';
|
||||||
|
import Title from './card-title.svelte';
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Title,
|
||||||
|
//
|
||||||
|
Root as Card,
|
||||||
|
Content as CardContent,
|
||||||
|
Description as CardDescription,
|
||||||
|
Footer as CardFooter,
|
||||||
|
Header as CardHeader,
|
||||||
|
Title as CardTitle
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
23
docs/src/lib/components/ui/command/command-dialog.svelte
Normal file
23
docs/src/lib/components/ui/command/command-dialog.svelte
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Command from './command.svelte';
|
||||||
|
import * as Dialog from '$lib/components/ui/dialog';
|
||||||
|
import type { Dialog as DialogPrimitive } from 'bits-ui';
|
||||||
|
import type { Command as CommandPrimitive } from 'cmdk-sv';
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.Props & CommandPrimitive.CommandProps;
|
||||||
|
|
||||||
|
export let open: $$Props['open'] = false;
|
||||||
|
export let value: $$Props['value'] = undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dialog.Root bind:open {...$$restProps}>
|
||||||
|
<Dialog.Content class="overflow-hidden p-0">
|
||||||
|
<Command
|
||||||
|
class="[&_[data-cmdk-group-heading]]:text-muted-foreground [&_[data-cmdk-group-heading]]:px-2 [&_[data-cmdk-group-heading]]:font-medium [&_[data-cmdk-group]:not([hidden])_~[data-cmdk-group]]:pt-0 [&_[data-cmdk-group]]:px-2 [&_[data-cmdk-input-wrapper]_svg]:h-5 [&_[data-cmdk-input-wrapper]_svg]:w-5 [&_[data-cmdk-input]]:h-12 [&_[data-cmdk-item]]:px-2 [&_[data-cmdk-item]]:py-3 [&_[data-cmdk-item]_svg]:h-5 [&_[data-cmdk-item]_svg]:w-5"
|
||||||
|
{...$$restProps}
|
||||||
|
bind:value
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</Command>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
12
docs/src/lib/components/ui/command/command-empty.svelte
Normal file
12
docs/src/lib/components/ui/command/command-empty.svelte
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
// type $$Props = CommandPrimitive.EmptyProps;
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Empty class={cn('py-6 text-center text-sm', className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</CommandPrimitive.Empty>
|
18
docs/src/lib/components/ui/command/command-group.svelte
Normal file
18
docs/src/lib/components/ui/command/command-group.svelte
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
// type $$Props = CommandPrimitive.GroupProps;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Group
|
||||||
|
class={cn(
|
||||||
|
'text-foreground [&_[data-cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[data-cmdk-group-heading]]:px-2 [&_[data-cmdk-group-heading]]:py-1.5 [&_[data-cmdk-group-heading]]:text-xs [&_[data-cmdk-group-heading]]:font-medium',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CommandPrimitive.Group>
|
22
docs/src/lib/components/ui/command/command-input.svelte
Normal file
22
docs/src/lib/components/ui/command/command-input.svelte
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
// type $$Props = CommandPrimitive.InputProps;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export let value = '';
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex items-center border-b px-3" data-cmdk-input-wrapper="">
|
||||||
|
<iconify-icon icon="ion:search" class="mr-2 shrink-0 text-xl opacity-50"></iconify-icon>
|
||||||
|
<CommandPrimitive.Input
|
||||||
|
bind:value
|
||||||
|
class={cn(
|
||||||
|
'placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
||||||
|
</div>
|
19
docs/src/lib/components/ui/command/command-item.svelte
Normal file
19
docs/src/lib/components/ui/command/command-item.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
// type $$Props = CommandPrimitive.ItemProps;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Item
|
||||||
|
class={cn(
|
||||||
|
'aria-selected:bg-accent aria-selected:text-accent-foreground relative cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CommandPrimitive.Item>
|
15
docs/src/lib/components/ui/command/command-list.svelte
Normal file
15
docs/src/lib/components/ui/command/command-list.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
// type $$Props = CommandPrimitive.ListProps;
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.List
|
||||||
|
class={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CommandPrimitive.List>
|
10
docs/src/lib/components/ui/command/command-separator.svelte
Normal file
10
docs/src/lib/components/ui/command/command-separator.svelte
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
// type $$Props = CommandPrimitive.SeparatorProps;
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Separator class={cn('bg-border -mx-1 h-px', className)} {...$$restProps} />
|
15
docs/src/lib/components/ui/command/command-shortcut.svelte
Normal file
15
docs/src/lib/components/ui/command/command-shortcut.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
// type $$Props = HTMLAttributes<HTMLSpanElement>;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</span>
|
22
docs/src/lib/components/ui/command/command.svelte
Normal file
22
docs/src/lib/components/ui/command/command.svelte
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
type $$Props = CommandPrimitive.CommandProps;
|
||||||
|
|
||||||
|
export let value: $$Props['value'] = undefined;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Root
|
||||||
|
class={cn(
|
||||||
|
'bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
bind:value
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CommandPrimitive.Root>
|
37
docs/src/lib/components/ui/command/index.ts
Normal file
37
docs/src/lib/components/ui/command/index.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||||
|
|
||||||
|
import Root from './command.svelte';
|
||||||
|
import Dialog from './command-dialog.svelte';
|
||||||
|
import Empty from './command-empty.svelte';
|
||||||
|
import Group from './command-group.svelte';
|
||||||
|
import Item from './command-item.svelte';
|
||||||
|
import Input from './command-input.svelte';
|
||||||
|
import List from './command-list.svelte';
|
||||||
|
import Separator from './command-separator.svelte';
|
||||||
|
import Shortcut from './command-shortcut.svelte';
|
||||||
|
|
||||||
|
const Loading = CommandPrimitive.Loading;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Dialog,
|
||||||
|
Empty,
|
||||||
|
Group,
|
||||||
|
Item,
|
||||||
|
Input,
|
||||||
|
List,
|
||||||
|
Separator,
|
||||||
|
Shortcut,
|
||||||
|
Loading,
|
||||||
|
//
|
||||||
|
Root as Command,
|
||||||
|
Dialog as CommandDialog,
|
||||||
|
Empty as CommandEmpty,
|
||||||
|
Group as CommandGroup,
|
||||||
|
Item as CommandItem,
|
||||||
|
Input as CommandInput,
|
||||||
|
List as CommandList,
|
||||||
|
Separator as CommandSeparator,
|
||||||
|
Shortcut as CommandShortcut,
|
||||||
|
Loading as CommandLoading
|
||||||
|
};
|
35
docs/src/lib/components/ui/dialog/dialog-content.svelte
Normal file
35
docs/src/lib/components/ui/dialog/dialog-content.svelte
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||||
|
import * as Dialog from '.';
|
||||||
|
import { cn, flyAndScale } from '$lib/utils';
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.ContentProps;
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export let transition: $$Props['transition'] = flyAndScale;
|
||||||
|
export let transitionConfig: $$Props['transitionConfig'] = {
|
||||||
|
duration: 200
|
||||||
|
};
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dialog.Portal>
|
||||||
|
<Dialog.Overlay />
|
||||||
|
<DialogPrimitive.Content
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
class={cn(
|
||||||
|
'bg-background fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg sm:rounded-lg md:w-full',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<DialogPrimitive.Close
|
||||||
|
class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-3 my-auto aspect-square rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none"
|
||||||
|
>
|
||||||
|
<iconify-icon icon="basil:cross-solid" class="text-2xl"></iconify-icon>
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
</DialogPrimitive.Close>
|
||||||
|
</DialogPrimitive.Content>
|
||||||
|
</Dialog.Portal>
|
16
docs/src/lib/components/ui/dialog/dialog-description.svelte
Normal file
16
docs/src/lib/components/ui/dialog/dialog-description.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.DescriptionProps;
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Description
|
||||||
|
class={cn('text-muted-foreground text-sm', className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DialogPrimitive.Description>
|
16
docs/src/lib/components/ui/dialog/dialog-footer.svelte
Normal file
16
docs/src/lib/components/ui/dialog/dialog-footer.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
13
docs/src/lib/components/ui/dialog/dialog-header.svelte
Normal file
13
docs/src/lib/components/ui/dialog/dialog-header.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
21
docs/src/lib/components/ui/dialog/dialog-overlay.svelte
Normal file
21
docs/src/lib/components/ui/dialog/dialog-overlay.svelte
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.OverlayProps;
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export let transition: $$Props['transition'] = fade;
|
||||||
|
export let transitionConfig: $$Props['transitionConfig'] = {
|
||||||
|
duration: 150
|
||||||
|
};
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Overlay
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
class={cn('bg-background/80 fixed inset-0 z-50 backdrop-blur-sm ', className)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
9
docs/src/lib/components/ui/dialog/dialog-portal.svelte
Normal file
9
docs/src/lib/components/ui/dialog/dialog-portal.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||||
|
|
||||||
|
// type $$Props = DialogPrimitive.PortalProps;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Portal {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</DialogPrimitive.Portal>
|
16
docs/src/lib/components/ui/dialog/dialog-title.svelte
Normal file
16
docs/src/lib/components/ui/dialog/dialog-title.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.TitleProps;
|
||||||
|
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Title
|
||||||
|
class={cn('text-lg font-semibold leading-none tracking-tight', className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DialogPrimitive.Title>
|
34
docs/src/lib/components/ui/dialog/index.ts
Normal file
34
docs/src/lib/components/ui/dialog/index.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||||
|
|
||||||
|
const Root = DialogPrimitive.Root;
|
||||||
|
const Trigger = DialogPrimitive.Trigger;
|
||||||
|
|
||||||
|
import Title from './dialog-title.svelte';
|
||||||
|
import Portal from './dialog-portal.svelte';
|
||||||
|
import Footer from './dialog-footer.svelte';
|
||||||
|
import Header from './dialog-header.svelte';
|
||||||
|
import Overlay from './dialog-overlay.svelte';
|
||||||
|
import Content from './dialog-content.svelte';
|
||||||
|
import Description from './dialog-description.svelte';
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Title,
|
||||||
|
Portal,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Trigger,
|
||||||
|
Overlay,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
//
|
||||||
|
Root as Dialog,
|
||||||
|
Title as DialogTitle,
|
||||||
|
Portal as DialogPortal,
|
||||||
|
Footer as DialogFooter,
|
||||||
|
Header as DialogHeader,
|
||||||
|
Trigger as DialogTrigger,
|
||||||
|
Overlay as DialogOverlay,
|
||||||
|
Content as DialogContent,
|
||||||
|
Description as DialogDescription
|
||||||
|
};
|
28
docs/src/lib/examples/DiscordExample.svelte
Normal file
28
docs/src/lib/examples/DiscordExample.svelte
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import { emoji } from '@cartamd/plugin-emoji';
|
||||||
|
import { code } from '@cartamd/plugin-code';
|
||||||
|
import PlusCircled from './assets/PlusIcon.svelte';
|
||||||
|
|
||||||
|
import '$lib/styles/discord.scss';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
sanitizer: false,
|
||||||
|
disableIcons: true,
|
||||||
|
extensions: [
|
||||||
|
emoji(),
|
||||||
|
code(),
|
||||||
|
{
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
component: PlusCircled,
|
||||||
|
parent: 'input',
|
||||||
|
props: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor placeholder="Send a message to @someone" mode="tabs" theme="discord" {carta} />
|
30
docs/src/lib/examples/GitHubExample.svelte
Normal file
30
docs/src/lib/examples/GitHubExample.svelte
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import { attachment } from '@cartamd/plugin-attachment';
|
||||||
|
import { emoji } from '@cartamd/plugin-emoji';
|
||||||
|
import { slash } from '@cartamd/plugin-slash';
|
||||||
|
import { code } from '@cartamd/plugin-code';
|
||||||
|
|
||||||
|
import '$lib/styles/github.scss';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
sanitizer: false,
|
||||||
|
extensions: [
|
||||||
|
attachment({
|
||||||
|
async upload() {
|
||||||
|
return 'some-url-from-server.xyz';
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
emoji(),
|
||||||
|
slash(),
|
||||||
|
code()
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export let value = `This is an example inspired by [GitHub](https://github.com)
|
||||||
|
\`\`\`js
|
||||||
|
console.log('Hello, World!');
|
||||||
|
\`\`\``;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor bind:value mode="tabs" theme="github" {carta} />
|
39
docs/src/lib/examples/MathStackExchangeExample.svelte
Normal file
39
docs/src/lib/examples/MathStackExchangeExample.svelte
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Carta, MarkdownEditor, Markdown } from 'carta-md';
|
||||||
|
import placeholder from './math-stack-exchange-placeholder.tex?raw';
|
||||||
|
import { math } from '@cartamd/plugin-math';
|
||||||
|
import { tikz } from '@cartamd/plugin-tikz';
|
||||||
|
|
||||||
|
import '$lib/styles/math-stack-exchange.scss';
|
||||||
|
import 'katex/dist/katex.min.css';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
sanitizer: false,
|
||||||
|
extensions: [
|
||||||
|
math(),
|
||||||
|
tikz({
|
||||||
|
postProcessing: (html) => {
|
||||||
|
// Simple dark mode support
|
||||||
|
return html
|
||||||
|
.replaceAll('#000000', '~~~')
|
||||||
|
.replaceAll('#000', '~~~')
|
||||||
|
.replaceAll('black', '~~~')
|
||||||
|
.replaceAll('#ffffff', '#000')
|
||||||
|
.replaceAll('#fff', '#000')
|
||||||
|
.replaceAll('white', '#000')
|
||||||
|
.replaceAll('~~~', '#fff');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export let value = placeholder;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="math-stack-exchange-container">
|
||||||
|
<MarkdownEditor bind:value mode="tabs" theme="math-stack-exchange" {carta} />
|
||||||
|
|
||||||
|
{#key value}
|
||||||
|
<Markdown theme="math-stack-exchange" {value} {carta} />
|
||||||
|
{/key}
|
||||||
|
</div>
|
3
docs/src/lib/examples/assets/PlusIcon.svelte
Normal file
3
docs/src/lib/examples/assets/PlusIcon.svelte
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<div class="discord-plus-icon">
|
||||||
|
<iconify-icon icon="radix-icons:plus-circled" class="text-2xl"></iconify-icon>
|
||||||
|
</div>
|
23
docs/src/lib/examples/math-stack-exchange-placeholder.tex
Normal file
23
docs/src/lib/examples/math-stack-exchange-placeholder.tex
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
> Here is a formula:
|
||||||
|
|
||||||
|
$$
|
||||||
|
\dfrac{\partial}{\partial t}(
|
||||||
|
\dfrac{\partial \mathcal{L}}{\partial \dot{q}_k}
|
||||||
|
) - \dfrac{\partial \mathcal{L}}{\partial q_k} = 0
|
||||||
|
$$
|
||||||
|
|
||||||
|
> And here is a circuit:
|
||||||
|
|
||||||
|
```tikz
|
||||||
|
\usepackage{circuitikz}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\begin{circuitikz} \draw
|
||||||
|
(0,0) to[battery] (0,4)
|
||||||
|
to[ammeter] (4,4) -- (4,0)
|
||||||
|
to[lamp] (0,0)
|
||||||
|
;
|
||||||
|
\end{circuitikz}
|
||||||
|
\end{document}
|
||||||
|
```
|
||||||
|
|
96
docs/src/lib/search/index.ts
Normal file
96
docs/src/lib/search/index.ts
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import flexsearch from 'flexsearch';
|
||||||
|
import type { SvelteComponent } from 'svelte';
|
||||||
|
|
||||||
|
export interface SearchResult {
|
||||||
|
path: string;
|
||||||
|
content: string;
|
||||||
|
html: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EnrichedSearchResult = SearchResult & {
|
||||||
|
match?: {
|
||||||
|
heading?: {
|
||||||
|
text: string;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function initializeSearch() {
|
||||||
|
const indexedPages = new flexsearch.Document<SearchResult, true>({
|
||||||
|
tokenize: 'full',
|
||||||
|
cache: true,
|
||||||
|
context: true,
|
||||||
|
document: {
|
||||||
|
id: 'path',
|
||||||
|
index: ['title', 'content'],
|
||||||
|
store: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const pages = import.meta.glob('../../pages/**/*.svelte.md');
|
||||||
|
await Promise.all(
|
||||||
|
Object.keys(pages).map(async (page) => {
|
||||||
|
const module = (await pages[page]()) as {
|
||||||
|
default: typeof SvelteComponent;
|
||||||
|
metadata: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
const elem = document.createElement('div');
|
||||||
|
new module.default({ target: elem });
|
||||||
|
|
||||||
|
const path = page.replace('../../pages/', '').replace('.svelte.md', '');
|
||||||
|
const title = module.metadata.title as string;
|
||||||
|
const html = elem.innerHTML;
|
||||||
|
const content = extractText(module.default);
|
||||||
|
|
||||||
|
indexedPages.add({
|
||||||
|
path,
|
||||||
|
html,
|
||||||
|
title,
|
||||||
|
content
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return indexedPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractText(component: typeof SvelteComponent) {
|
||||||
|
const parentElem = document.createElement('div');
|
||||||
|
new component({ target: parentElem });
|
||||||
|
|
||||||
|
const text = parentElem.textContent ?? '';
|
||||||
|
// Remove extra spaces
|
||||||
|
return text.replace(/\s+/g, ' ').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enrichResult(result: SearchResult, query: string): EnrichedSearchResult {
|
||||||
|
let heading: HTMLHeadingElement | null = null;
|
||||||
|
|
||||||
|
const parentElem = document.createElement('div');
|
||||||
|
parentElem.innerHTML = result.html;
|
||||||
|
|
||||||
|
for (const node of parentElem.childNodes) {
|
||||||
|
const content = node.textContent?.replaceAll('\n', '').replace(/\s+/g, ' ').trim() ?? '';
|
||||||
|
if (['h1', 'h2', 'h3'].includes(node.nodeName.toLowerCase())) {
|
||||||
|
heading = node as HTMLHeadingElement;
|
||||||
|
}
|
||||||
|
if (content?.toLowerCase().includes(query.toLowerCase())) {
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
match: {
|
||||||
|
heading: heading
|
||||||
|
? {
|
||||||
|
text: heading.textContent ?? '',
|
||||||
|
id: heading.id
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
text: content
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
317
docs/src/lib/styles/coldark.scss
Normal file
317
docs/src/lib/styles/coldark.scss
Normal file
|
@ -0,0 +1,317 @@
|
||||||
|
/**
|
||||||
|
* Coldark Theme for Prism.js
|
||||||
|
* Theme variation: Dark
|
||||||
|
* Tested with HTML, CSS, JS, JSON, PHP, YAML, Bash script
|
||||||
|
* @author Armand Philippot <contact@armandphilippot.com>
|
||||||
|
* @homepage https://github.com/ArmandPhilippot/coldark-prism
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
code[class*='language-'],
|
||||||
|
pre[class*='language-'] {
|
||||||
|
color: #e3eaf2;
|
||||||
|
background: none;
|
||||||
|
font-family: 'Fira Code', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*='language-']::-moz-selection,
|
||||||
|
pre[class*='language-'] ::-moz-selection,
|
||||||
|
code[class*='language-']::-moz-selection,
|
||||||
|
code[class*='language-'] ::-moz-selection {
|
||||||
|
background: #3c526d;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*='language-']::selection,
|
||||||
|
pre[class*='language-'] ::selection,
|
||||||
|
code[class*='language-']::selection,
|
||||||
|
code[class*='language-'] ::selection {
|
||||||
|
background: #3c526d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
pre[class*='language-'] {
|
||||||
|
padding: 1em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(pre) > code[class*='language-'],
|
||||||
|
pre[class*='language-'] {
|
||||||
|
@apply rounded-md border border-neutral-800 bg-neutral-950 bg-opacity-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code */
|
||||||
|
:not(pre) > code[class*='language-'] {
|
||||||
|
padding: 0.1em 0.3em;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.prolog,
|
||||||
|
.token.doctype,
|
||||||
|
.token.cdata {
|
||||||
|
color: #8da1b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.punctuation {
|
||||||
|
color: #e3eaf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.delimiter.important,
|
||||||
|
.token.selector .parent,
|
||||||
|
.token.tag,
|
||||||
|
.token.tag .token.punctuation {
|
||||||
|
color: #66cccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-name,
|
||||||
|
.token.boolean,
|
||||||
|
.token.boolean.important,
|
||||||
|
.token.number,
|
||||||
|
.token.constant,
|
||||||
|
.token.selector .token.attribute {
|
||||||
|
color: #e6d37a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.class-name,
|
||||||
|
.token.key,
|
||||||
|
.token.parameter,
|
||||||
|
.token.property,
|
||||||
|
.token.property-access,
|
||||||
|
.token.variable {
|
||||||
|
color: #6cb8e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-value,
|
||||||
|
.token.inserted,
|
||||||
|
.token.color,
|
||||||
|
.token.selector .token.value,
|
||||||
|
.token.string,
|
||||||
|
.token.string .token.url-link {
|
||||||
|
color: #91d076;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.builtin,
|
||||||
|
.token.keyword-array,
|
||||||
|
.token.package,
|
||||||
|
.token.regex {
|
||||||
|
color: #f4adf4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.function,
|
||||||
|
.token.selector .token.class,
|
||||||
|
.token.selector .token.id {
|
||||||
|
color: #c699e3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.atrule .token.rule,
|
||||||
|
.token.combinator,
|
||||||
|
.token.keyword,
|
||||||
|
.token.operator,
|
||||||
|
.token.pseudo-class,
|
||||||
|
.token.pseudo-element,
|
||||||
|
.token.selector,
|
||||||
|
.token.unit {
|
||||||
|
color: #e9ae7e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.deleted,
|
||||||
|
.token.important {
|
||||||
|
color: #cd6660;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.keyword-this,
|
||||||
|
.token.this {
|
||||||
|
color: #6cb8e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important,
|
||||||
|
.token.keyword-this,
|
||||||
|
.token.this,
|
||||||
|
.token.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.delimiter.important {
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.title,
|
||||||
|
.language-markdown .token.title .token.punctuation {
|
||||||
|
color: #6cb8e6;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.blockquote.punctuation {
|
||||||
|
color: #f4adf4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.code {
|
||||||
|
color: #66cccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.hr.punctuation {
|
||||||
|
color: #6cb8e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.url .token.content {
|
||||||
|
color: #91d076;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.url-link {
|
||||||
|
color: #e6d37a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.list.punctuation {
|
||||||
|
color: #f4adf4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-markdown .token.table-header {
|
||||||
|
color: #e3eaf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-json .token.operator {
|
||||||
|
color: #e3eaf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-scss .token.variable {
|
||||||
|
color: #66cccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Show Invisibles plugin
|
||||||
|
* https://prismjs.com/plugins/show-invisibles/
|
||||||
|
*/
|
||||||
|
.token.token.tab:not(:empty):before,
|
||||||
|
.token.token.cr:before,
|
||||||
|
.token.token.lf:before,
|
||||||
|
.token.token.space:before {
|
||||||
|
color: #8da1b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Toolbar plugin
|
||||||
|
* https://prismjs.com/plugins/toolbar/
|
||||||
|
*/
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > a,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > button {
|
||||||
|
color: #111b27;
|
||||||
|
background: #6cb8e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:hover,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:focus,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:hover,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:focus {
|
||||||
|
color: #111b27;
|
||||||
|
background: #6cb8e6da;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > span,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:hover,
|
||||||
|
div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:focus {
|
||||||
|
color: #111b27;
|
||||||
|
background: #8da1b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Line Highlight plugin
|
||||||
|
* http://prismjs.com/plugins/line-highlight/
|
||||||
|
*/
|
||||||
|
.line-highlight.line-highlight {
|
||||||
|
background: #3c526d5f;
|
||||||
|
background: linear-gradient(to right, #3c526d5f 70%, #3c526d55);
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-highlight.line-highlight:before,
|
||||||
|
.line-highlight.line-highlight[data-end]:after {
|
||||||
|
background-color: #8da1b9;
|
||||||
|
color: #111b27;
|
||||||
|
box-shadow: 0 1px #3c526d;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[id].linkable-line-numbers.linkable-line-numbers span.line-numbers-rows > span:hover:before {
|
||||||
|
background-color: #8da1b918;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Line Numbers plugin
|
||||||
|
* http://prismjs.com/plugins/line-numbers/
|
||||||
|
*/
|
||||||
|
.line-numbers.line-numbers .line-numbers-rows {
|
||||||
|
border-right: 1px solid #0b121b;
|
||||||
|
background: #0b121b7a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-numbers .line-numbers-rows > span:before {
|
||||||
|
color: #8da1b9da;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Match Braces plugin
|
||||||
|
* https://prismjs.com/plugins/match-braces/
|
||||||
|
*/
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-1,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-5,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-9 {
|
||||||
|
color: #e6d37a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-2,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-6,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-10 {
|
||||||
|
color: #f4adf4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-3,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-7,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-11 {
|
||||||
|
color: #6cb8e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-4,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-8,
|
||||||
|
.rainbow-braces .token.token.punctuation.brace-level-12 {
|
||||||
|
color: #c699e3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Diff Highlight plugin
|
||||||
|
* https://prismjs.com/plugins/diff-highlight/
|
||||||
|
*/
|
||||||
|
pre.diff-highlight > code .token.token.deleted:not(.prefix),
|
||||||
|
pre > code.diff-highlight .token.token.deleted:not(.prefix) {
|
||||||
|
background-color: #cd66601f;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.diff-highlight > code .token.token.inserted:not(.prefix),
|
||||||
|
pre > code.diff-highlight .token.token.inserted:not(.prefix) {
|
||||||
|
background-color: #91d0761f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Command Line plugin
|
||||||
|
* https://prismjs.com/plugins/command-line/
|
||||||
|
*/
|
||||||
|
.command-line .command-line-prompt {
|
||||||
|
border-right: 1px solid #0b121b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-line .command-line-prompt > span:before {
|
||||||
|
color: #8da1b9da;
|
||||||
|
}
|
83
docs/src/lib/styles/discord.scss
Normal file
83
docs/src/lib/styles/discord.scss
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
.carta-theme__discord {
|
||||||
|
// Core styles
|
||||||
|
$background: #2f3136;
|
||||||
|
$background-light: #161b22;
|
||||||
|
$background-contrast: #46484b;
|
||||||
|
|
||||||
|
&.carta-editor {
|
||||||
|
background-color: $background;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
|
::placeholder {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-wrapper {
|
||||||
|
padding: 1rem;
|
||||||
|
padding-left: 56px;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.carta-container {
|
||||||
|
min-height: 32px;
|
||||||
|
max-height: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-font-code {
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
caret-color: white;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-toolbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discord-plus-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
left: -24px;
|
||||||
|
width: 1.75rem;
|
||||||
|
height: 1.75rem;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin emoji
|
||||||
|
&.carta-emoji {
|
||||||
|
width: 18rem;
|
||||||
|
max-height: 14rem;
|
||||||
|
overflow-y: scroll;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: $background;
|
||||||
|
padding: 6px;
|
||||||
|
scroll-padding: 6px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-emoji button {
|
||||||
|
background: $background-light;
|
||||||
|
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-emoji button:hover,
|
||||||
|
&.carta-emoji button.carta-active {
|
||||||
|
background: $background-contrast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .shiki,
|
||||||
|
html.dark .shiki span {
|
||||||
|
color: var(--shiki-dark) !important;
|
||||||
|
}
|
203
docs/src/lib/styles/github.scss
Normal file
203
docs/src/lib/styles/github.scss
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
.carta-theme__github {
|
||||||
|
// Core styles
|
||||||
|
$background: #0d1117;
|
||||||
|
$background-light: #161b22;
|
||||||
|
$border: #2b3138;
|
||||||
|
$accent: #1f6feb;
|
||||||
|
|
||||||
|
&.carta-editor {
|
||||||
|
background-color: $background;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
outline: 2px solid $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-wrapper {
|
||||||
|
padding: 1rem;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.carta-container {
|
||||||
|
min-height: 120px;
|
||||||
|
max-height: 160px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-font-code {
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
caret-color: white;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-toolbar {
|
||||||
|
height: 2.5rem;
|
||||||
|
|
||||||
|
background-color: $background-light;
|
||||||
|
|
||||||
|
border-top-left-radius: 0.5rem;
|
||||||
|
border-top-right-radius: 0.5rem;
|
||||||
|
|
||||||
|
.carta-icon {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
background-color: $border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-toolbar-left button,
|
||||||
|
.carta-toolbar-right,
|
||||||
|
.carta-filler {
|
||||||
|
border-bottom: 1px solid $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-toolbar-left {
|
||||||
|
& > *:first-child {
|
||||||
|
border-top-left-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-active {
|
||||||
|
background-color: $background;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
border-right: 1px solid $border;
|
||||||
|
border-bottom: 1px solid $background;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-left: 1px solid $border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-toolbar-right {
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-icons-menu {
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 6px;
|
||||||
|
min-width: 180px;
|
||||||
|
background: $background;
|
||||||
|
|
||||||
|
.carta-icon-full {
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
|
||||||
|
margin-top: 2px;
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
background-color: $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-left: 6px;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin emoji
|
||||||
|
&.carta-emoji {
|
||||||
|
width: 18rem;
|
||||||
|
max-height: 14rem;
|
||||||
|
overflow-y: scroll;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: $background;
|
||||||
|
padding: 6px;
|
||||||
|
scroll-padding: 6px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-emoji button {
|
||||||
|
background: $background-light;
|
||||||
|
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-emoji button:hover,
|
||||||
|
&.carta-emoji button.carta-active {
|
||||||
|
background: $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugin slash
|
||||||
|
&.carta-slash {
|
||||||
|
width: 18rem;
|
||||||
|
max-height: 14rem;
|
||||||
|
overflow-y: scroll;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: $background;
|
||||||
|
padding: 6px;
|
||||||
|
scroll-padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-slash span {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-slash button {
|
||||||
|
background: none;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-slash .carta-slash-group {
|
||||||
|
padding: 0 4px 0 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-slash button.carta-active,
|
||||||
|
&.carta-slash button:hover {
|
||||||
|
background: $background-light;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-slash .carta-snippet-title {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-slash .carta-snippet-description {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .shiki,
|
||||||
|
html.dark .shiki span {
|
||||||
|
color: var(--shiki-dark) !important;
|
||||||
|
}
|
63
docs/src/lib/styles/markdown.scss
Normal file
63
docs/src/lib/styles/markdown.scss
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
.markdown {
|
||||||
|
& {
|
||||||
|
@apply text-[1.05rem] leading-7 text-neutral-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@apply mb-4 mt-8 text-4xl font-bold text-white first:mt-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
@apply mb-4 mt-8 text-3xl font-semibold text-white first:mt-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
@apply mb-3 mt-7 text-2xl font-medium text-white first:mt-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
@apply mb-3 mt-3 text-lg font-medium text-white first:mt-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4 {
|
||||||
|
@apply scroll-mt-16;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
@apply mb-3 text-lg italic text-neutral-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
@apply ml-4 list-disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
@apply text-neutral-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not(h1 > a, h2 > a, h3 > a, h4 > a, .bg-card) {
|
||||||
|
@apply text-sky-300 underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
@apply rounded bg-neutral-800 px-1 text-neutral-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
code {
|
||||||
|
@apply px-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-editor code {
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
108
docs/src/lib/styles/math-stack-exchange.scss
Normal file
108
docs/src/lib/styles/math-stack-exchange.scss
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
.math-stack-exchange-container {
|
||||||
|
background-color: #252526;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-theme__math-stack-exchange {
|
||||||
|
// Core styles
|
||||||
|
$background: #252526;
|
||||||
|
$background-light: #333333;
|
||||||
|
$border: #333333;
|
||||||
|
$accent: #2e8acb;
|
||||||
|
|
||||||
|
&.carta-editor {
|
||||||
|
background-color: $background;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
.carta-wrapper {
|
||||||
|
padding: 1rem;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
border-bottom-left-radius: 0.25rem;
|
||||||
|
border-bottom-right-radius: 0.25rem;
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
outline: 3px solid rgba($accent, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-container {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-font-code {
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
caret-color: white;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-toolbar {
|
||||||
|
height: 2.5rem;
|
||||||
|
|
||||||
|
background-color: $background-light;
|
||||||
|
border-bottom: 1px solid $border;
|
||||||
|
|
||||||
|
padding-right: 12px;
|
||||||
|
border-top-left-radius: 0.25rem;
|
||||||
|
border-top-right-radius: 0.25rem;
|
||||||
|
|
||||||
|
.carta-icon {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
background-color: $border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-toolbar-left {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-toolbar-right {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.carta-icons-menu {
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 6px;
|
||||||
|
min-width: 180px;
|
||||||
|
background: $background;
|
||||||
|
|
||||||
|
.carta-icon-full {
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
|
||||||
|
margin-top: 2px;
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
background-color: $border;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-left: 6px;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.carta-viewer {
|
||||||
|
min-height: 60px;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .shiki,
|
||||||
|
html.dark .shiki span {
|
||||||
|
color: var(--shiki-dark) !important;
|
||||||
|
}
|
94
docs/src/lib/utils.ts
Normal file
94
docs/src/lib/utils.ts
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import { type ClassValue, clsx } from 'clsx';
|
||||||
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
import { cubicOut } from 'svelte/easing';
|
||||||
|
import type { TransitionConfig } from 'svelte/transition';
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlyAndScaleParams = {
|
||||||
|
y?: number;
|
||||||
|
x?: number;
|
||||||
|
start?: number;
|
||||||
|
duration?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const flyAndScale = (
|
||||||
|
node: Element,
|
||||||
|
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
|
||||||
|
): TransitionConfig => {
|
||||||
|
const style = getComputedStyle(node);
|
||||||
|
const transform = style.transform === 'none' ? '' : style.transform;
|
||||||
|
|
||||||
|
const scaleConversion = (valueA: number, scaleA: [number, number], scaleB: [number, number]) => {
|
||||||
|
const [minA, maxA] = scaleA;
|
||||||
|
const [minB, maxB] = scaleB;
|
||||||
|
|
||||||
|
const percentage = (valueA - minA) / (maxA - minA);
|
||||||
|
const valueB = percentage * (maxB - minB) + minB;
|
||||||
|
|
||||||
|
return valueB;
|
||||||
|
};
|
||||||
|
|
||||||
|
const styleToString = (style: Record<string, number | string | undefined>): string => {
|
||||||
|
return Object.keys(style).reduce((str, key) => {
|
||||||
|
if (style[key] === undefined) return str;
|
||||||
|
return str + `${key}:${style[key]};`;
|
||||||
|
}, '');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
duration: params.duration ?? 200,
|
||||||
|
delay: 0,
|
||||||
|
css: (t) => {
|
||||||
|
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
|
||||||
|
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
|
||||||
|
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
|
||||||
|
|
||||||
|
return styleToString({
|
||||||
|
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
|
||||||
|
opacity: t
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
}
|
173
docs/src/pages/api/core.svelte.md
Normal file
173
docs/src/pages/api/core.svelte.md
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
---
|
||||||
|
section: API
|
||||||
|
title: Core
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
# `Carta` options
|
||||||
|
|
||||||
|
List of options that can be used when creating `Carta`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
new Carta({
|
||||||
|
/* ... */
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### `gfmOptions`
|
||||||
|
|
||||||
|
Type: `GfmOptions`
|
||||||
|
|
||||||
|
GitHub Flavored Markdown options.
|
||||||
|
|
||||||
|
### `extensions`
|
||||||
|
|
||||||
|
Type: `Extension[]`
|
||||||
|
|
||||||
|
List of extensions(plugins) to use.
|
||||||
|
|
||||||
|
### `rendererDebounce`
|
||||||
|
|
||||||
|
Type: `number`
|
||||||
|
|
||||||
|
Rendering debouncing timeout, in milliseconds.
|
||||||
|
Defaults to 300ms.
|
||||||
|
|
||||||
|
### `disableShortcuts`
|
||||||
|
|
||||||
|
Type: `DefaultShortcutId[] | true`
|
||||||
|
|
||||||
|
Remove default shortcuts by id. You can use `true` to disable all of them.
|
||||||
|
|
||||||
|
### `disableIcons`
|
||||||
|
|
||||||
|
Type: `DefaultIconId[] | true`
|
||||||
|
|
||||||
|
Remove default icons by id. You can use `true` to disable all of them.
|
||||||
|
|
||||||
|
### `disablePrefixes`
|
||||||
|
|
||||||
|
Type: `DefaultPrefixId[] | true`
|
||||||
|
|
||||||
|
Remove default prefixes by id. You can use `true` to disable all of them.
|
||||||
|
|
||||||
|
### `historyOptions`
|
||||||
|
|
||||||
|
History management options.
|
||||||
|
|
||||||
|
#### `historyOptions.minInterval`
|
||||||
|
|
||||||
|
Type: `number`
|
||||||
|
|
||||||
|
Minimum interval between save states in milliseconds.
|
||||||
|
Defaults to 300ms.
|
||||||
|
|
||||||
|
#### `historyOptions.maxSize`
|
||||||
|
|
||||||
|
Type: `number`
|
||||||
|
|
||||||
|
Maximum history size in bytes.
|
||||||
|
Defaults to 1MB.
|
||||||
|
|
||||||
|
### `sanitizer`
|
||||||
|
|
||||||
|
Type: `(html: string) => void`
|
||||||
|
|
||||||
|
HTML sanitizer. See [here]({base}/getting-started#sanitization) for more details.
|
||||||
|
|
||||||
|
### `shikiOptions`
|
||||||
|
|
||||||
|
Type: `ShikiOptions`
|
||||||
|
|
||||||
|
Highlighter(Shiki) options.
|
||||||
|
|
||||||
|
### `theme`
|
||||||
|
|
||||||
|
Type: `Theme | DualTheme`
|
||||||
|
|
||||||
|
Shiki theme to use to highlight Markdown.
|
||||||
|
|
||||||
|
# `MarkdownEditor` options
|
||||||
|
|
||||||
|
List of options that can be used in the `<MarkdownEditor>` component.
|
||||||
|
|
||||||
|
### `carta`
|
||||||
|
|
||||||
|
Type: `Carta`
|
||||||
|
|
||||||
|
Carta manager to use for this editor.
|
||||||
|
|
||||||
|
### `theme`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The theme of this editor. The editor and related elements will have the `carta-theme__<theme>` as a class.
|
||||||
|
|
||||||
|
### `value`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
Current Markdown input value.
|
||||||
|
|
||||||
|
### `mode`
|
||||||
|
|
||||||
|
Type: `'tabs' | 'split' | 'auto'`
|
||||||
|
|
||||||
|
Editor windows mode. With `auto` it will split when the window size is greater than 768px.
|
||||||
|
|
||||||
|
### `scroll`
|
||||||
|
|
||||||
|
Type: `'sync' | 'async'`
|
||||||
|
|
||||||
|
Scroll synchronization.
|
||||||
|
|
||||||
|
### `disableToolbar`
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Option to disable the toolbar.
|
||||||
|
|
||||||
|
### `placeholder`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
Set the textarea placeholder.
|
||||||
|
|
||||||
|
### `textarea`
|
||||||
|
|
||||||
|
Type: `TextAreaProps` (extends `Record<string, unknown>`)
|
||||||
|
|
||||||
|
Additional properties that will be used in the textarea used under the hood in the editor.
|
||||||
|
`class`, `placeholder` and `value` are not allowed. Use the corresponding editor properties
|
||||||
|
instead.
|
||||||
|
|
||||||
|
### `labels`
|
||||||
|
|
||||||
|
Type: `Partial<Labels>`
|
||||||
|
|
||||||
|
Can be used to provide custom text for labels in the editor.
|
||||||
|
|
||||||
|
# `Markdown` options
|
||||||
|
|
||||||
|
List of options that can be used in the `<Markdown>` component.
|
||||||
|
|
||||||
|
### `carta`
|
||||||
|
|
||||||
|
Type: `Carta`
|
||||||
|
|
||||||
|
Carta manager to use for this editor.
|
||||||
|
|
||||||
|
### `theme`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The theme of this editor. The viewer and related elements will have the `carta-theme__<theme>` as a class.
|
||||||
|
|
||||||
|
### `value`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
Current Markdown input value.
|
263
docs/src/pages/api/extension.svelte.md
Normal file
263
docs/src/pages/api/extension.svelte.md
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
---
|
||||||
|
section: API
|
||||||
|
title: Extension
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
# `Plugin` properties
|
||||||
|
|
||||||
|
You can easily extend Carta by creating custom plugins.
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const ext: Plugin = {
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
extensions: [ext]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
Here are all the `Plugin` properties:
|
||||||
|
|
||||||
|
### `transformers`
|
||||||
|
|
||||||
|
Type: `UnifiedTransformer`
|
||||||
|
|
||||||
|
Remark or Rehype transformers.
|
||||||
|
|
||||||
|
#### `UnifiedTransformer.execution`
|
||||||
|
|
||||||
|
Type: `'sync' | 'async'`
|
||||||
|
|
||||||
|
If you specify async, this transformer won't be available for SSR.
|
||||||
|
|
||||||
|
#### `UnifiedTransformer.type`
|
||||||
|
|
||||||
|
Type: `'remark' | 'rehype'`
|
||||||
|
|
||||||
|
This determines at which step the transformer will operate, whether on Remark, on a Markdown-based syntax tree, or Rehype, on a HTML-based one.
|
||||||
|
|
||||||
|
#### `UnifiedTransformer.transform`
|
||||||
|
|
||||||
|
Type: `({ processor, carta }) => void`
|
||||||
|
|
||||||
|
The actual processor, can be async if the execution is specified as such.
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
{
|
||||||
|
execution: 'sync',
|
||||||
|
type: 'rehype',
|
||||||
|
transform({ processor }) {
|
||||||
|
processor
|
||||||
|
.use(rehypeSlug)
|
||||||
|
.use(rehypeAutolinkHeadings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### `shortcuts`
|
||||||
|
|
||||||
|
Type: `KeyboardShortcut[]`
|
||||||
|
|
||||||
|
Additional keyboards shortcut. For example:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const shortcut: KeyboardShortcut = {
|
||||||
|
id: 'bold',
|
||||||
|
combination: new Set(['control', 'b']),
|
||||||
|
action: (input) => input.toggleSelectionSurrounding('**')
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
#### `KeyboardShortcut.id`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
Id of the shortcut.
|
||||||
|
|
||||||
|
#### `KeyboardShortcut.combination`
|
||||||
|
|
||||||
|
Type: `Set<string>`
|
||||||
|
|
||||||
|
Set of keys, corresponding to the `e.key` of `KeyboardEvent`s, but lowercase.
|
||||||
|
|
||||||
|
#### `KeyboardShortcut.action`
|
||||||
|
|
||||||
|
Type: `(input: InputEnhancer) => void`
|
||||||
|
|
||||||
|
Shortcut callback.
|
||||||
|
|
||||||
|
#### `KeyboardShortcut.preventSave`
|
||||||
|
|
||||||
|
Prevent saving the current state in history.
|
||||||
|
|
||||||
|
### `icons`
|
||||||
|
|
||||||
|
Type: `Icon[]`
|
||||||
|
|
||||||
|
Additional toolbar icons. For example:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const icon: Icon = {
|
||||||
|
id: 'heading',
|
||||||
|
action: (input) => input.toggleLinePrefix('###'),
|
||||||
|
component: HeadingIcon
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
#### `Icon.id`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
Id of the icon.
|
||||||
|
|
||||||
|
#### `Icon.action`
|
||||||
|
|
||||||
|
Type: `(input: InputEnhancer) => void`
|
||||||
|
|
||||||
|
Click callback.
|
||||||
|
|
||||||
|
#### `Icon.component`
|
||||||
|
|
||||||
|
Type: `ComponentType` (SvelteComponent)
|
||||||
|
|
||||||
|
The Icon as a Svelte component.
|
||||||
|
|
||||||
|
### `prefixes`
|
||||||
|
|
||||||
|
Type: `Prefix[]`
|
||||||
|
|
||||||
|
Text prefixes, default ones include the `- ` for bulleted lists, `1. ` for numbered lists, `- [ ]` for task lists.
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const prefix: Prefix = {
|
||||||
|
id: 'bulletedList',
|
||||||
|
match: (line) => {
|
||||||
|
const prefix = line.slice(0, 2);
|
||||||
|
if (prefix === '- ') return prefix;
|
||||||
|
},
|
||||||
|
maker: () => '- '
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
#### `Prefix.id`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
Id of the prefix.
|
||||||
|
|
||||||
|
#### `Prefix.match`
|
||||||
|
|
||||||
|
Type: `(line: string) => string | undefined`
|
||||||
|
|
||||||
|
Function that returns the prefix, if it is present.
|
||||||
|
|
||||||
|
#### `Prefix.maker`
|
||||||
|
|
||||||
|
Type: `(previousMatch: string, previousLine: string) => string`
|
||||||
|
|
||||||
|
Function that returns the prefix for the new line.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const prefix: Prefix = {
|
||||||
|
id: 'numberedList',
|
||||||
|
match: (line) => line.match(/^\d+\./)?.at(0),
|
||||||
|
maker: (prev) => `${Number(prev.slice(0, -1)) + 1}. `
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### `listeners`
|
||||||
|
|
||||||
|
Type: `Listener[]`
|
||||||
|
|
||||||
|
Textarea event listeners. Has an additional `carta-render` and `carta-render-ssr` events keys.
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const click: Listener = ['click', () => console.log('I was clicked!')];
|
||||||
|
const render: Listener = [
|
||||||
|
'carta-render',
|
||||||
|
(e) => {
|
||||||
|
const carta = e.detail.carta;
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
{
|
||||||
|
once: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### `components`
|
||||||
|
|
||||||
|
Type: `ExtensionComponent[]`
|
||||||
|
|
||||||
|
Additional components to be added to the editor or viewer.
|
||||||
|
|
||||||
|
#### `ExtensionComponent<T>.component`
|
||||||
|
|
||||||
|
Type: `typeof SvelteComponentTyped<T & { carta: Carta }>`
|
||||||
|
|
||||||
|
Svelte components that exports `carta: Carta` and all the other properties specified as the generic parameter and in `props`.
|
||||||
|
|
||||||
|
#### `ExtensionComponent<T>.props`
|
||||||
|
|
||||||
|
Type: `T`
|
||||||
|
|
||||||
|
Properties that will be handed to the component.
|
||||||
|
|
||||||
|
#### `ExtensionComponent<T>.parent`
|
||||||
|
|
||||||
|
Type: `MaybeArray<'editor' | 'input' | 'renderer' | 'preview'>`
|
||||||
|
|
||||||
|
Where the element will be placed.
|
||||||
|
|
||||||
|
### `grammarRules`
|
||||||
|
|
||||||
|
Type: `GrammarRule[]`
|
||||||
|
|
||||||
|
Custom Markdown TextMate grammar rules for Shiki. They will be injected into the language.
|
||||||
|
|
||||||
|
### `highlightRules`
|
||||||
|
|
||||||
|
Type: `HighlightingRule[]`
|
||||||
|
|
||||||
|
Custom highlighting rules for ShiKi. They will be injected into the selected theme.
|
||||||
|
|
||||||
|
### `onLoad`
|
||||||
|
|
||||||
|
Type: `(data: { carta: Carta; highlight: HighlightFunctions }) => void`
|
||||||
|
|
||||||
|
Use this callback to execute code when one Carta instance loads the extension.
|
91
docs/src/pages/api/utilities.svelte.md
Normal file
91
docs/src/pages/api/utilities.svelte.md
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
---
|
||||||
|
section: API
|
||||||
|
title: Utilities
|
||||||
|
---
|
||||||
|
|
||||||
|
## `Carta.render`
|
||||||
|
|
||||||
|
Allows you to render Markdown asynchronously.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const carta = new Carta({
|
||||||
|
/* ... */
|
||||||
|
});
|
||||||
|
const markdown = '# Some Markdown';
|
||||||
|
const html = await carta.render(markdown);
|
||||||
|
```
|
||||||
|
|
||||||
|
## `Carta.renderSSR`
|
||||||
|
|
||||||
|
Allows you to render Markdown synchronously, suitable for Server Side Rendering. Note that particular extensions that add content asynchronously will not work in this configuration.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const carta = new Carta({
|
||||||
|
/* ... */
|
||||||
|
});
|
||||||
|
const markdown = '# Some Markdown';
|
||||||
|
const html = carta.renderSSR(markdown);
|
||||||
|
```
|
||||||
|
|
||||||
|
## `Carta.bindToCaret`
|
||||||
|
|
||||||
|
Svelte action that allows you to bind a specific element to the caret position. Used, for example, in `plugin-emoji` and `plugin-slash`.
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
export let carta;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div use:carta.bindToCaret>
|
||||||
|
<!-- ... -->
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
## `Carta.highlighter`
|
||||||
|
|
||||||
|
Get the Shiki highlighter.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const highlighter = await carta.highlighter();
|
||||||
|
const userTheme = carta.theme;
|
||||||
|
```
|
||||||
|
|
||||||
|
Here are some other highlight related utilities:
|
||||||
|
|
||||||
|
### `isBundleLanguage`
|
||||||
|
|
||||||
|
Checks if a language is a bundled language.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export const isBundleLanguage = (lang: string): lang is BundledLanguage;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `isBundleTheme`
|
||||||
|
|
||||||
|
Checks if a theme is a bundled theme.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export const isBundleTheme = (theme: string): theme is BundledTheme;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `isDualTheme`
|
||||||
|
|
||||||
|
Checks if a theme is a dual theme.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export const isDualTheme = (theme: Theme | DualTheme): theme is DualTheme;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `isSingleTheme`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export const isSingleTheme = (theme: Theme | DualTheme): theme is Theme;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `isThemeRegistration`
|
||||||
|
|
||||||
|
Checks if a theme is a theme registration.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export const isThemeRegistration = (theme: Theme): theme is ThemeRegistration;
|
||||||
|
```
|
83
docs/src/pages/community-plugins.svelte.md
Normal file
83
docs/src/pages/community-plugins.svelte.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
---
|
||||||
|
title: Community Plugins
|
||||||
|
section: Overview
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PluginLink from '$lib/components/link/PluginLink.svelte';
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
Here are is a list of several plugins developed by the community:
|
||||||
|
|
||||||
|
<PluginLink
|
||||||
|
npmLink="https://www.npmjs.com/package/carta-plugin-video"
|
||||||
|
githubLink="https://github.com/maisonsmd/carta-plugin-video">
|
||||||
|
|
||||||
|
### `carta-plugin-video`
|
||||||
|
|
||||||
|
</PluginLink>
|
||||||
|
|
||||||
|
> Adds ability to render online video from Youtube or Vimeo.
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i carta-plugin-video
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
<PluginLink
|
||||||
|
npmLink="https://www.npmjs.com/package/carta-plugin-imsize"
|
||||||
|
githubLink="https://github.com/maisonsmd/carta-plugin-imsize">
|
||||||
|
|
||||||
|
### `carta-plugin-imsize`
|
||||||
|
|
||||||
|
</PluginLink>
|
||||||
|
|
||||||
|
> Adds ability to render images in specific sizes.
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i carta-plugin-imsize
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
<PluginLink
|
||||||
|
npmLink="https://www.npmjs.com/package/carta-plugin-ins-del"
|
||||||
|
githubLink="https://github.com/maisonsmd/carta-plugin-ins-del">
|
||||||
|
|
||||||
|
### `carta-plugin-ins-del`
|
||||||
|
|
||||||
|
</PluginLink>
|
||||||
|
|
||||||
|
> `<ins>` and `<del>` tags support
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i carta-plugin-ins-del
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
<PluginLink
|
||||||
|
npmLink="https://www.npmjs.com/package/carta-plugin-subscript"
|
||||||
|
githubLink="https://github.com/maisonsmd/carta-plugin-subscript">
|
||||||
|
|
||||||
|
### `carta-plugin-subscript`
|
||||||
|
|
||||||
|
</PluginLink>
|
||||||
|
|
||||||
|
> Adds ability to render subscripts and superscripts.
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i carta-plugin-subscript
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
111
docs/src/pages/editing-styles.svelte.md
Normal file
111
docs/src/pages/editing-styles.svelte.md
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
---
|
||||||
|
title: Editing Styles
|
||||||
|
section: Overview
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
## Customizing editor styles
|
||||||
|
|
||||||
|
While the core styles are embedded in the Svelte components, the others can be set in a custom stylesheet. Here is what the final rendered HTML looks like.
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="carta-editor carta-theme__<theme>">
|
||||||
|
<div class="carta-toolbar">
|
||||||
|
<div class="carta-toolbar-left">
|
||||||
|
<!-- ... -->
|
||||||
|
</div>
|
||||||
|
<div class="carta-toolbar-right">
|
||||||
|
<button class="carta-icon"><!-- ... --></button>
|
||||||
|
<!-- Other icons -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="carta-wrapper">
|
||||||
|
<div class="carta-container mode-<split|tabs>">
|
||||||
|
<div class="carta-input-wrapper">
|
||||||
|
<pre class="carta-font-code"><!-- ... --></pre>
|
||||||
|
<textarea class="carta-font-code" id="md" />
|
||||||
|
</div>
|
||||||
|
<div class="carta-renderer">
|
||||||
|
<!-- Rendered Markdown -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### Using multiple themes
|
||||||
|
|
||||||
|
By using the `theme` property in `<MarkdownEditor>` you can differentiate the themes of multiple editors.
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const carta = new Carta({
|
||||||
|
// ...
|
||||||
|
theme: 'github-dark'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const carta = new Carta({
|
||||||
|
// ...
|
||||||
|
shikiOptions: {
|
||||||
|
langs: // ...
|
||||||
|
themes: // ...
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Markdown stylesheets
|
||||||
|
|
||||||
|
Markdown is converted into standard HTML, so you can edit the final styles by using standard CSS rules. If you do not wish to create one from the ground up, you can use some already complete stylesheets, like [github-markdown-css](https://github.com/sindresorhus/github-markdown-css) or [Tailwind Typography](https://tailwindcss.com/docs/typography-plugin).
|
33
docs/src/pages/examples.svelte.md
Normal file
33
docs/src/pages/examples.svelte.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
title: Examples
|
||||||
|
section: Overview
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import GitHubExample from '$lib/examples/GitHubExample.svelte';
|
||||||
|
import DiscordExample from '$lib/examples/DiscordExample.svelte';
|
||||||
|
import MathStackExchangeExample from '$lib/examples/MathStackExchangeExample.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
Here is a list of examples made using Carta and some of its plugins.
|
||||||
|
|
||||||
|
## GitHub
|
||||||
|
|
||||||
|
<GitHubExample />
|
||||||
|
<br>
|
||||||
|
|
||||||
|
Plugins used: code, slash, emoji, attachment. View source on [GitHub](https://github.com/BearToCode/carta/blob/master/docs/src/lib/examples/GitHubExample.svelte).
|
||||||
|
|
||||||
|
## Discord
|
||||||
|
|
||||||
|
<DiscordExample />
|
||||||
|
<br>
|
||||||
|
|
||||||
|
Plugins used: code, emoji. View source on [GitHub](https://github.com/BearToCode/carta/blob/master/docs/src/lib/examples/DiscordExample.svelte).
|
||||||
|
|
||||||
|
## Math Stack Exchange
|
||||||
|
|
||||||
|
<MathStackExchangeExample />
|
||||||
|
<br>
|
||||||
|
|
||||||
|
Plugins used: math, tikz. View source on [GitHub](https://github.com/BearToCode/carta/blob/master/docs/src/lib/examples/MathStackExchangeExample.svelte).
|
108
docs/src/pages/getting-started.svelte.md
Normal file
108
docs/src/pages/getting-started.svelte.md
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
---
|
||||||
|
title: Getting Started
|
||||||
|
section: Overview
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Installing the core package:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i carta-md
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
Installing plugins:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i @cartamd/plugin-name
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Setup a basic editor:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import 'carta-md/default.css'; /* Default theme */
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
// Remember to use a sanitizer to prevent XSS attacks!
|
||||||
|
// More on that below
|
||||||
|
// sanitizer: ...
|
||||||
|
});
|
||||||
|
|
||||||
|
let value = '';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor {carta} bind:value />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Set your custom monospace font */
|
||||||
|
:global(.carta-font-code) {
|
||||||
|
font-family: '...', monospace;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
Or, if you just want to render content:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { Carta, Markdown } from 'carta-md';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
/* ... */
|
||||||
|
});
|
||||||
|
|
||||||
|
let value = '...';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Markdown {carta} {value} />
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Sanitization
|
||||||
|
|
||||||
|
By default Carta does **not** sanitize user input, which can include malicious code that could lead to [XSS attacks](https://en.wikipedia.org/wiki/Cross-site_scripting). For this reason it is _strongly recommended_ to install a package that handles that for you.
|
||||||
|
|
||||||
|
Since Carta operates both on the server and the client, you'd need a sanitizer able to work in both environments, for example [isomorphic-dompurify](https://www.npmjs.com/package/isomorphic-dompurify) or [sanitize-html](https://www.npmjs.com/package/sanitize-html). Here is an example using the former, which requires minimum configuration.
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
// Your other stuff...
|
||||||
|
import DOMPurify from 'isomorphic-dompurify';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
sanitizer: DOMPurify.sanitize
|
||||||
|
});
|
||||||
|
|
||||||
|
let value = '';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor {carta} bind:value />
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
130
docs/src/pages/introduction.svelte.md
Normal file
130
docs/src/pages/introduction.svelte.md
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
---
|
||||||
|
title: Carta
|
||||||
|
section: Overview
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as Card from "$lib/components/ui/card";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
> Modern, lightweight, powerful Markdown Editor.
|
||||||
|
|
||||||
|
Carta is a lightweight, fast and extensible Svelte Markdown editor and viewer, designed for flexibility. It works natively in SvelteKit, and supports Server Side Rendering.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- 🌈 Markdown syntax highlighting ([Shiki](https://shiki.style/));
|
||||||
|
- 🛠️ Toolbar (extensible);
|
||||||
|
- ⌨️ Keyboard **shortcuts** (extensible);
|
||||||
|
- 📦 Supports **[+150 plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins)** thanks to remark.
|
||||||
|
- 🔀 Scroll sync;
|
||||||
|
- ✅ Accessibility friendly;
|
||||||
|
- 🖥️ **SSR** compatible;
|
||||||
|
|
||||||
|
## Official Plugins
|
||||||
|
|
||||||
|
Carta comes with a set of official plugins for the most common use cases.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div class="w-full grid sm:grid-cols-2 gap-4">
|
||||||
|
|
||||||
|
<Card.Root href="/plugins/math">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="tabler:math" class="text-3xl text-sky-300"></iconify-icon>
|
||||||
|
<Card.Title>Math</Card.Title>
|
||||||
|
<Card.Description>Support for KaTex expressions.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
<Card.Root href="/plugins/code">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="fluent:code-16-filled" class="text-3xl text-sky-300"></iconify-icon>
|
||||||
|
<Card.Title>Code</Card.Title>
|
||||||
|
<Card.Description>Code blocks syntax highlighting.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
<Card.Root href="/plugins/emoji">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="mingcute:emoji-line" class="text-3xl text-sky-300"></iconify-icon>
|
||||||
|
<Card.Title>Emoji</Card.Title>
|
||||||
|
<Card.Description>Embed emojis in Markdown.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
<Card.Root href="/plugins/slash">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="tabler:slash" class="text-3xl text-sky-300"></iconify-icon>
|
||||||
|
<Card.Title>Slash</Card.Title>
|
||||||
|
<Card.Description>Support for slash commands.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
<Card.Root href="/plugins/tikz">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="mdi:draw-pen" class="text-3xl text-sky-300"></iconify-icon>
|
||||||
|
<Card.Title>TikZ</Card.Title>
|
||||||
|
<Card.Description>Support for TikZ/PgfPlots diagrams.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
<Card.Root href="/plugins/attachment">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="tdesign:attach" class="text-3xl text-sky-300"></iconify-icon>
|
||||||
|
<Card.Title>Attachment</Card.Title>
|
||||||
|
<Card.Description>Handle text attachments.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
<Card.Root href="/plugins/anchor">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="mingcute:link-fill" class="text-3xl text-sky-300"></iconify-icon>
|
||||||
|
<Card.Title>Anchor</Card.Title>
|
||||||
|
<Card.Description>Add anchor links to headings.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
<Card.Root href="/community-plugins">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="ph:stack-fill" class="text-3xl text-sky-300"></iconify-icon>
|
||||||
|
<Card.Title>Community Plugins</Card.Title>
|
||||||
|
<Card.Description>Explore plugins from the community.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
A list of examples inspired by popular platforms.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="w-full grid sm:grid-cols-2 gap-4">
|
||||||
|
|
||||||
|
<Card.Root href="/examples#github">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="mdi:github" class="text-3xl text-sky-300" ></iconify-icon>
|
||||||
|
<Card.Title>GitHub</Card.Title>
|
||||||
|
<Card.Description>Inspired by GitHub.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
<Card.Root href="/examples#discord">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="ic:baseline-discord" class="text-3xl text-sky-300" ></iconify-icon>
|
||||||
|
<Card.Title>Discord</Card.Title>
|
||||||
|
<Card.Description>Inspired by Discord.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
<Card.Root href="/examples#math-stack-exchange">
|
||||||
|
<Card.Header>
|
||||||
|
<iconify-icon icon="fluent:math-formula-16-filled" class="text-3xl text-sky-300" ></iconify-icon>
|
||||||
|
<Card.Title>Math Stack Exchange</Card.Title>
|
||||||
|
<Card.Description>Inspired by Math Stack Exchange.</Card.Description>
|
||||||
|
</Card.Header>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
|
</div>
|
57
docs/src/pages/migration.svelte.md
Normal file
57
docs/src/pages/migration.svelte.md
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
---
|
||||||
|
title: Migration Guide
|
||||||
|
section: Overview
|
||||||
|
---
|
||||||
|
|
||||||
|
# Major Changes
|
||||||
|
|
||||||
|
## Removal of Marked
|
||||||
|
|
||||||
|
Marked has been replaced with a combination of Unified, Remark and Rehype. If you previously used a custom plugin with it, you'll have to update it manually. Otherwise, all builtin plugins have already been updated. Make sure to **update** them!
|
||||||
|
|
||||||
|
Some plugins now have a different implementation and their options have changed. Those plugins are [plugin-math](https://beartocode.github.io/carta/plugins/math) and [plugin-anchor](https://beartocode.github.io/carta/plugins/anchor).
|
||||||
|
|
||||||
|
## Syntax highlighter update
|
||||||
|
|
||||||
|
SpeedHighlight has been replaced with [Shiki](https://shiki.matsu.io/). It now offers support for more languages, themes, and extensibility.
|
||||||
|
|
||||||
|
Make sure to remove previous themes imports, as Shiki uses JS based ones.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import 'carta-md/light.css'; // 👈 To be removed!
|
||||||
|
```
|
||||||
|
|
||||||
|
And also update the default theme. Previous based selectors should be removed:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* 👇 To be removed! */
|
||||||
|
[class*='shj-lang-'] {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Removed verbose prefixes
|
||||||
|
|
||||||
|
Many exports have been renamed to make them less verbose:
|
||||||
|
|
||||||
|
- `CartaEditor` -> `MarkdownEditor` (old one still supported);
|
||||||
|
- `CartaRenderer` -> `Markdown` (old one still supported);
|
||||||
|
- `CartaEvent` -> `Event`;
|
||||||
|
- `CartaEventType` -> `EventType`;
|
||||||
|
- `CartaExtension` -> `Plugin`;
|
||||||
|
- `CartaExtensionComponent` -> `ExtensionComponent`;
|
||||||
|
- `CartaOptions` -> `Options`;
|
||||||
|
- `CartaHistory` -> `TextAreaHistory`;
|
||||||
|
- `CartaHistoryOptions` -> `TextAreaHistoryOptions`;
|
||||||
|
- `CartaIcon` -> `Icon`;
|
||||||
|
- `CartaListener` -> `Listener`;
|
||||||
|
- `CartaInput` -> `InputEnhancer`;
|
||||||
|
- `CartaRenderer` -> `Renderer`;
|
||||||
|
- `CartaLabels` -> `Labels`;
|
||||||
|
|
||||||
|
# Minor Changes
|
||||||
|
|
||||||
|
- If you don't use a sanitizer, you need to explicitly set it to `false`;
|
||||||
|
- Removed deprecated option `cartaRef` and `shjRef` for extensions;
|
||||||
|
- Removed deprecated options `postProcess` for `plugin-tikz`;
|
||||||
|
- `Carta.options` are no longer available.
|
70
docs/src/pages/plugins/anchor.svelte.md
Normal file
70
docs/src/pages/plugins/anchor.svelte.md
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
---
|
||||||
|
section: Plugins
|
||||||
|
title: Anchor
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
This plugin adds `id` attributes and permalinks to headings.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i @cartamd/plugin-anchor
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Styles
|
||||||
|
|
||||||
|
Import the default theme, or create you own:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import '@cartamd/plugin-anchor/default.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### Extension
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import { anchor } from '@cartamd/plugin-anchor';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
extensions: [anchor()]
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor {carta} />
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
Here are the options you can pass to `anchor()`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface AnchorExtensionOptions {
|
||||||
|
/**
|
||||||
|
* rehype-slug options.
|
||||||
|
*/
|
||||||
|
slug?: SlugOptions;
|
||||||
|
/**
|
||||||
|
* rehype-autolink-headings options.
|
||||||
|
*/
|
||||||
|
autolink?: AutolinkOptions;
|
||||||
|
}
|
||||||
|
```
|
91
docs/src/pages/plugins/attachment.svelte.md
Normal file
91
docs/src/pages/plugins/attachment.svelte.md
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
---
|
||||||
|
section: Plugins
|
||||||
|
title: Attachment
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
This plugin adds support for attachments.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i @cartamd/plugin-attachment
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Styles
|
||||||
|
|
||||||
|
Import the default theme, or create you own:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import '@cartamd/plugin-attachment/default.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### Extension
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import { attachment } from '@cartamd/plugin-attachment';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
extensions: [
|
||||||
|
attachment({
|
||||||
|
upload(file) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor {carta} />
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
Here are the options you can pass to `attachment()`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface AttachmentExtensionOptions {
|
||||||
|
/**
|
||||||
|
* Upload a file to the server. Return the url of the uploaded file.
|
||||||
|
* If an error occurs, return null. This function does **not** handle errors.
|
||||||
|
* @param file The file to upload
|
||||||
|
* @returns The uploaded file url, or null if it failed
|
||||||
|
*/
|
||||||
|
upload: (file: File) => Promise<string | null>;
|
||||||
|
/**
|
||||||
|
* Supported mime types.
|
||||||
|
*
|
||||||
|
* @default ['image/png', 'image/jpeg', 'image/gif', 'image/svg+xml'].
|
||||||
|
*/
|
||||||
|
supportedMimeTypes?: string[];
|
||||||
|
/**
|
||||||
|
* Whether to disable the attach icon.
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
disableIcon?: boolean;
|
||||||
|
/**
|
||||||
|
* Custom drop overlay component. Use `false` to disable the overlay.
|
||||||
|
*/
|
||||||
|
dropOverlay?: false | typeof SvelteComponent;
|
||||||
|
/**
|
||||||
|
* Custom loading overlay component. Use `false` to disable the overlay.
|
||||||
|
*/
|
||||||
|
loadingOverlay?: false | typeof SvelteComponent<{ uploadingFiles: Writable<File[]> }>;
|
||||||
|
}
|
||||||
|
```
|
80
docs/src/pages/plugins/code.svelte.md
Normal file
80
docs/src/pages/plugins/code.svelte.md
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
---
|
||||||
|
section: Plugins
|
||||||
|
title: Code
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
This plugin adds support for code blocks **syntax highlighting**. It uses the same highlighter from the core package(Shiki).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i @cartamd/plugin-code
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Styles
|
||||||
|
|
||||||
|
Import the default styles:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import '@cartamd/plugin-code/default.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const carta = new Carta({
|
||||||
|
// ...
|
||||||
|
extensions: [
|
||||||
|
code({
|
||||||
|
theme: 'ayu-light'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### Using a custom highlighter
|
||||||
|
|
||||||
|
It is no longer possible to specify a custom highlighter in this plugin. However, there are many different [Remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins) that provide syntax highlighting.
|
||||||
|
|
||||||
|
### Extension
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import { code } from '@cartamd/plugin-code';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
extensions: [code()]
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor {carta} />
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
The options you can pass to `code()` extend the ones provided by [Shiki](https://shiki.matsu.io/guide/transformers).
|
70
docs/src/pages/plugins/emoji.svelte.md
Normal file
70
docs/src/pages/plugins/emoji.svelte.md
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
---
|
||||||
|
section: Plugins
|
||||||
|
title: Emoji
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
This plugin adds support for **Emojis**.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i @cartamd/plugin-emoji
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Styles
|
||||||
|
|
||||||
|
Import the default theme, or create you own:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import '@cartamd/plugin-emoji/default.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### Extension
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import { emoji } from '@cartamd/plugin-emoji';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
extensions: [emoji()]
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor {carta} />
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
Here are the options you can pass to `emoji()`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface EmojiExtensionOptions {
|
||||||
|
/**
|
||||||
|
* Custom in transition. See https://svelte.dev/docs#run-time-svelte-transition.
|
||||||
|
*/
|
||||||
|
inTransition?: (node: Element) => TransitionConfig;
|
||||||
|
/**
|
||||||
|
* Custom out transition. See https://svelte.dev/docs#run-time-svelte-transition.
|
||||||
|
*/
|
||||||
|
outTransition?: (node: Element) => TransitionConfig;
|
||||||
|
}
|
||||||
|
```
|
160
docs/src/pages/plugins/math.svelte.md
Normal file
160
docs/src/pages/plugins/math.svelte.md
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
---
|
||||||
|
section: Plugins
|
||||||
|
title: Math
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
import { Markdown, Carta } from 'carta-md';
|
||||||
|
import { math } from '@cartamd/plugin-math';
|
||||||
|
import 'katex/dist/katex.css';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
extensions: [math()]
|
||||||
|
})
|
||||||
|
export let inline = "$a^2+b^2=c^2$";
|
||||||
|
export let block = `
|
||||||
|
$$
|
||||||
|
\\mathcal{L}\\{f\\}(s) = \\int_0^{\\infty} {f(t)e^{-st}dt}
|
||||||
|
$$
|
||||||
|
`;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
This plugins adds support for [KaTeX](https://katex.org/) expressions.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i @cartamd/plugin-math
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Styles
|
||||||
|
|
||||||
|
You need to get access to the katex **stylesheet**,
|
||||||
|
to do so, you can either install katex using:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i katex
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
and then adding this import to your app:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import 'katex/dist/katex.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
or by using a content delivery network:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/katex@0.16.7/dist/katex.min.css"
|
||||||
|
integrity="sha384-3UiQGuEI4TTMaFmGIZumfRPtfKQ3trwQE2JgosJxCnGmQpL/lJdjpcHkaaFwHlcI"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### Extension
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import { math } from '@cartamd/plugin-math';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
extensions: [math()]
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor {carta} />
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Inline:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
Pythagorean theorem: $a^2+b^2=c^2$
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
<Markdown {carta} value={inline} />
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
Block:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
$$
|
||||||
|
\\mathcal{L}\\{f\\}(s) = \\int_0^{\\infty} {f(t)e^{-st}dt}
|
||||||
|
$$
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
<Markdown {carta} value={block} />
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
Here are the options you can pass to `math()`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface MathExtensionOptions {
|
||||||
|
/**
|
||||||
|
* Options for inline katex, eg: $a^2+b^2=c^2$
|
||||||
|
*/
|
||||||
|
inline?: {
|
||||||
|
/**
|
||||||
|
* @default control+m
|
||||||
|
*/
|
||||||
|
shortcut?: Set<string>;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Option for block katex, eg:
|
||||||
|
* $$
|
||||||
|
* a^2+b^2=c^2
|
||||||
|
* $$
|
||||||
|
*/
|
||||||
|
block?: {
|
||||||
|
/**
|
||||||
|
* @default ctrl+shift+m
|
||||||
|
*/
|
||||||
|
shortcut?: Set<string>;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Options for remark-math
|
||||||
|
*/
|
||||||
|
remarkMath?: RemarkMathOptions;
|
||||||
|
/**
|
||||||
|
* Options for rehype-katex
|
||||||
|
*/
|
||||||
|
rehypeKatex?: RehypeKatexOptions;
|
||||||
|
}
|
||||||
|
```
|
78
docs/src/pages/plugins/slash.svelte.md
Normal file
78
docs/src/pages/plugins/slash.svelte.md
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
---
|
||||||
|
section: Plugins
|
||||||
|
title: Slash
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
This plugin adds support for **Slash** commands.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i @cartamd/plugin-slash
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Styles
|
||||||
|
|
||||||
|
Import the default theme, or create you own:
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import '@cartamd/plugin-slash/default.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
### Extension
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import { slash } from '@cartamd/plugin-slash';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
extensions: [slash()]
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor {carta} />
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
Here are the options you can pass to `slash()`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface SlashExtensionOptions {
|
||||||
|
/**
|
||||||
|
* List of default snippets to disable.
|
||||||
|
*/
|
||||||
|
disableDefaultSnippets?: DefaultSnippetId[] | true;
|
||||||
|
/**
|
||||||
|
* Additional snippets.
|
||||||
|
*/
|
||||||
|
snippets?: SlashSnippet[];
|
||||||
|
/**
|
||||||
|
* Custom in transition. See https://svelte.dev/docs#run-time-svelte-transition.
|
||||||
|
*/
|
||||||
|
inTransition?: (node: Element) => TransitionConfig;
|
||||||
|
/**
|
||||||
|
* Custom out transition. See https://svelte.dev/docs#run-time-svelte-transition.
|
||||||
|
*/
|
||||||
|
outTransition?: (node: Element) => TransitionConfig;
|
||||||
|
}
|
||||||
|
```
|
71
docs/src/pages/plugins/tikz.svelte.md
Normal file
71
docs/src/pages/plugins/tikz.svelte.md
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
---
|
||||||
|
section: Plugins
|
||||||
|
title: TikZ
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Code from '$lib/components/code/Code.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
This plugin adds support for **PGF/TikZ** illustrations thanks to [TikZJax](https://tikzjax.com/). It uses the code generated for the [Obsidian-TikZ plugin](https://github.com/artisticat1/obsidian-tikzjax).
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i @cartamd/plugin-tikz
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
1. This plugin requires the import of a **heavy** library (~7Mb), which is dynamically imported at runtime;
|
||||||
|
2. Generated images are **not SSR compatible**, as they are rendered in the browser;
|
||||||
|
3. You need to update your sanitizer to allow the specific tag: `<div type="text/tikz">`.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
<Code>
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<script>
|
||||||
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import { tikz } from '@cartamd/plugin-tikz';
|
||||||
|
import '@cartamd/plugin-tikz/fonts.css';
|
||||||
|
|
||||||
|
const carta = new Carta({
|
||||||
|
extensions: [tikz()]
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<MarkdownEditor {carta} />
|
||||||
|
```
|
||||||
|
|
||||||
|
</Code>
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
Here are the options you can pass to `tikz()`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface TikzExtensionOptions {
|
||||||
|
/**
|
||||||
|
* Enables Tikzjax console output.
|
||||||
|
*/
|
||||||
|
debug?: boolean;
|
||||||
|
/**
|
||||||
|
* Class for generated svg div container.
|
||||||
|
*/
|
||||||
|
class?: string;
|
||||||
|
/**
|
||||||
|
* Whether to center the generated expression.
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
center?: boolean;
|
||||||
|
/**
|
||||||
|
* Post processing function for html.
|
||||||
|
* This also runs on stored html.
|
||||||
|
*/
|
||||||
|
postProcessing?: (html: string) => string;
|
||||||
|
}
|
||||||
|
```
|
217
docs/src/pages/using-components.svelte.md
Normal file
217
docs/src/pages/using-components.svelte.md
Normal 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).
|
29
docs/src/routes/+layout.svelte
Normal file
29
docs/src/routes/+layout.svelte
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import HeaderTracker from '$lib/components/header-tracker/HeaderTracker.svelte';
|
||||||
|
import MobileSidebar from '$lib/components/mobile-sidebar/MobileSidebar.svelte';
|
||||||
|
import Navbar from '$lib/components/navbar/Navbar.svelte';
|
||||||
|
import Sidebar from '$lib/components/sidebar/Sidebar.svelte';
|
||||||
|
import Footer from '$lib/components/footer/Footer.svelte';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
import '../app.postcss';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Navbar />
|
||||||
|
|
||||||
|
<div class="filter-blur-xl min-h-screen w-full py-16 xl:py-24">
|
||||||
|
<div
|
||||||
|
class="fixed bottom-0 left-0 right-0 top-0 z-[-1] backdrop-blur-2xl backdrop-filter"
|
||||||
|
style="background: url({base}/background.png) no-repeat center center; background-size: cover;"
|
||||||
|
></div>
|
||||||
|
<MobileSidebar class="xl:hidden" />
|
||||||
|
<div class="container relative mx-auto flex px-4 sm:px-6">
|
||||||
|
<Sidebar class="sticky top-24 hidden xl:block" />
|
||||||
|
<main
|
||||||
|
class="container max-w-4xl flex-shrink-0 flex-grow px-0 xl:max-w-3xl xl:px-4 2xl:max-w-4xl"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<Footer />
|
||||||
|
</main>
|
||||||
|
<HeaderTracker class="sticky top-24 hidden w-[15rem] flex-shrink-0 xl:block" />
|
||||||
|
</div>
|
||||||
|
</div>
|
3
docs/src/routes/+layout.ts
Normal file
3
docs/src/routes/+layout.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import 'iconify-icon'; // Register iconify web components
|
||||||
|
|
||||||
|
export const prerender = true;
|
9
docs/src/routes/+page.svelte
Normal file
9
docs/src/routes/+page.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
goto(`${base}/introduction`);
|
||||||
|
});
|
||||||
|
</script>
|
23
docs/src/routes/[...slug]/+page.server.ts
Normal file
23
docs/src/routes/[...slug]/+page.server.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import type { SvelteComponent } from 'svelte';
|
||||||
|
import type { PageServerLoad } from './$types';
|
||||||
|
import { error } from '@sveltejs/kit';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async (event) => {
|
||||||
|
const pages = import.meta.glob('../../pages/**/*.svelte.md');
|
||||||
|
const path = event.url.pathname.slice(base.length).slice(1);
|
||||||
|
const match = pages[`../../pages/${path}.svelte.md`];
|
||||||
|
if (!match) throw error(404, 'Not found');
|
||||||
|
|
||||||
|
const Markdown = (await match()) as {
|
||||||
|
default: SvelteComponent;
|
||||||
|
metadata: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
const content = Markdown.default.render();
|
||||||
|
const metadata = Markdown.metadata;
|
||||||
|
|
||||||
|
return {
|
||||||
|
content,
|
||||||
|
metadata
|
||||||
|
};
|
||||||
|
};
|
51
docs/src/routes/[...slug]/+page.svelte
Normal file
51
docs/src/routes/[...slug]/+page.svelte
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from './$types';
|
||||||
|
import { onMount, type SvelteComponent } from 'svelte';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
|
import '$lib/styles/markdown.scss';
|
||||||
|
import '$lib/styles/coldark.scss';
|
||||||
|
|
||||||
|
export let data: PageData;
|
||||||
|
|
||||||
|
let mounted = false;
|
||||||
|
let clientSideComponent: typeof SvelteComponent | null = null;
|
||||||
|
async function renderClientSideComponent() {
|
||||||
|
// Load a reactive version of the page to keep reactivity
|
||||||
|
const pages = import.meta.glob('../../pages/**/*.svelte.md');
|
||||||
|
const path = $page.url.pathname.slice(base.length).slice(1);
|
||||||
|
const match = pages[`../../pages/${path}.svelte.md`];
|
||||||
|
clientSideComponent = ((await match()) as { default: typeof SvelteComponent }).default;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
mounted = true;
|
||||||
|
renderClientSideComponent();
|
||||||
|
});
|
||||||
|
|
||||||
|
$: if (mounted) {
|
||||||
|
$page.url;
|
||||||
|
clientSideComponent = null;
|
||||||
|
renderClientSideComponent();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{data.metadata['title']} - Carta</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<h3 class="mb-2 font-semibold text-sky-300">{data.metadata['section']}</h3>
|
||||||
|
|
||||||
|
<h1 class="mb-4 text-5xl font-bold text-white">{data.metadata['title']}</h1>
|
||||||
|
|
||||||
|
<div class="markdown">
|
||||||
|
{#if clientSideComponent}
|
||||||
|
<svelte:component this={clientSideComponent} />
|
||||||
|
{:else}
|
||||||
|
{#key $page.url}
|
||||||
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||||
|
{@html data.content.html}
|
||||||
|
{/key}
|
||||||
|
{/if}
|
||||||
|
</div>
|
BIN
docs/static/background.png
vendored
Normal file
BIN
docs/static/background.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 474 KiB |
BIN
docs/static/favicon.png
vendored
Normal file
BIN
docs/static/favicon.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 934 B |
BIN
docs/static/logo-small.png
vendored
Normal file
BIN
docs/static/logo-small.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
BIN
docs/static/logo.png
vendored
Normal file
BIN
docs/static/logo.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
|
@ -1,11 +1,15 @@
|
||||||
|
import mdsvexConfig from './mdsvex.config.js';
|
||||||
import adapter from '@sveltejs/adapter-static';
|
import adapter from '@sveltejs/adapter-static';
|
||||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
import { mdsvex } from 'mdsvex';
|
||||||
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
|
extensions: ['.svelte', ...mdsvexConfig.extensions],
|
||||||
|
|
||||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||||
// for more information about preprocessors
|
// for more information about preprocessors
|
||||||
preprocess: vitePreprocess(),
|
preprocess: [vitePreprocess(), mdsvex(mdsvexConfig)],
|
||||||
|
|
||||||
kit: {
|
kit: {
|
||||||
adapter: adapter({
|
adapter: adapter({
|
59
docs/tailwind.config.js
Normal file
59
docs/tailwind.config.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
const config = {
|
||||||
|
darkMode: 'class',
|
||||||
|
content: ['./src/**/*.{html,js,svelte,ts,md}'],
|
||||||
|
safelist: ['dark'],
|
||||||
|
theme: {
|
||||||
|
container: {
|
||||||
|
center: true,
|
||||||
|
padding: '2rem',
|
||||||
|
screens: {
|
||||||
|
'2xl': '1400px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
border: 'hsl(var(--border) / <alpha-value>)',
|
||||||
|
input: 'hsl(var(--input) / <alpha-value>)',
|
||||||
|
ring: 'hsl(var(--ring) / <alpha-value>)',
|
||||||
|
background: 'hsl(var(--background) / <alpha-value>)',
|
||||||
|
foreground: 'hsl(var(--foreground) / <alpha-value>)',
|
||||||
|
primary: {
|
||||||
|
DEFAULT: 'hsl(var(--primary) / <alpha-value>)',
|
||||||
|
foreground: 'hsl(var(--primary-foreground) / <alpha-value>)'
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
DEFAULT: 'hsl(var(--secondary) / <alpha-value>)',
|
||||||
|
foreground: 'hsl(var(--secondary-foreground) / <alpha-value>)'
|
||||||
|
},
|
||||||
|
destructive: {
|
||||||
|
DEFAULT: 'hsl(var(--destructive) / <alpha-value>)',
|
||||||
|
foreground: 'hsl(var(--destructive-foreground) / <alpha-value>)'
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
DEFAULT: 'hsl(var(--muted) / <alpha-value>)',
|
||||||
|
foreground: 'hsl(var(--muted-foreground) / <alpha-value>)'
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
DEFAULT: 'hsl(var(--accent) / <alpha-value>)',
|
||||||
|
foreground: 'hsl(var(--accent-foreground) / <alpha-value>)'
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
DEFAULT: 'hsl(var(--popover) / <alpha-value>)',
|
||||||
|
foreground: 'hsl(var(--popover-foreground) / <alpha-value>)'
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
DEFAULT: 'hsl(var(--card) / <alpha-value>)',
|
||||||
|
foreground: 'hsl(var(--card-foreground) / <alpha-value>)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
lg: 'var(--radius)',
|
||||||
|
md: 'calc(var(--radius) - 2px)',
|
||||||
|
sm: 'calc(var(--radius) - 4px)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue