comark-ui 0.0.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +69 -0
- package/README.md +69 -1
- package/dist/client-Bjd6VcGQ.mjs +241 -0
- package/dist/client.d.mts +22 -0
- package/dist/client.mjs +3 -0
- package/dist/editor-BAdok66p.mjs +835 -0
- package/dist/editor-CNivEuMH.d.mts +131 -0
- package/dist/editor-vue.d.mts +57 -0
- package/dist/editor-vue.mjs +85 -0
- package/dist/editor.d.mts +2 -0
- package/dist/editor.mjs +1 -0
- package/dist/html.d.mts +13 -0
- package/dist/html.mjs +108 -0
- package/dist/index-jkwx6SyG.d.mts +175 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +3 -0
- package/dist/nuxt-ui.d.mts +17 -0
- package/dist/nuxt-ui.mjs +488 -0
- package/dist/react.d.mts +8 -0
- package/dist/react.mjs +97 -0
- package/dist/skills/comark-ui/SKILL.md +138 -0
- package/dist/skills/comark-ui/agents/openai.yaml +3 -0
- package/dist/skills/comark-ui/references/annotate.md +65 -0
- package/dist/skills/comark-ui/references/badge.md +52 -0
- package/dist/skills/comark-ui/references/cards.md +57 -0
- package/dist/skills/comark-ui/references/carousel.md +31 -0
- package/dist/skills/comark-ui/references/cast.md +48 -0
- package/dist/skills/comark-ui/references/charts.md +80 -0
- package/dist/skills/comark-ui/references/chat.md +25 -0
- package/dist/skills/comark-ui/references/chips.md +33 -0
- package/dist/skills/comark-ui/references/cjk.md +23 -0
- package/dist/skills/comark-ui/references/code-notes.md +34 -0
- package/dist/skills/comark-ui/references/code.md +84 -0
- package/dist/skills/comark-ui/references/colors.md +33 -0
- package/dist/skills/comark-ui/references/critic.md +30 -0
- package/dist/skills/comark-ui/references/deflist.md +38 -0
- package/dist/skills/comark-ui/references/details.md +49 -0
- package/dist/skills/comark-ui/references/diagrams.md +51 -0
- package/dist/skills/comark-ui/references/diff.md +43 -0
- package/dist/skills/comark-ui/references/embed.md +40 -0
- package/dist/skills/comark-ui/references/fields.md +48 -0
- package/dist/skills/comark-ui/references/figure.md +25 -0
- package/dist/skills/comark-ui/references/file-tree.md +42 -0
- package/dist/skills/comark-ui/references/footnotes.md +26 -0
- package/dist/skills/comark-ui/references/github.md +44 -0
- package/dist/skills/comark-ui/references/icon.md +28 -0
- package/dist/skills/comark-ui/references/include.md +44 -0
- package/dist/skills/comark-ui/references/keys.md +28 -0
- package/dist/skills/comark-ui/references/links.md +43 -0
- package/dist/skills/comark-ui/references/lint.md +96 -0
- package/dist/skills/comark-ui/references/magic-move.md +34 -0
- package/dist/skills/comark-ui/references/mark.md +22 -0
- package/dist/skills/comark-ui/references/math.md +25 -0
- package/dist/skills/comark-ui/references/media.md +29 -0
- package/dist/skills/comark-ui/references/metrics.md +34 -0
- package/dist/skills/comark-ui/references/outline.md +44 -0
- package/dist/skills/comark-ui/references/properties.md +32 -0
- package/dist/skills/comark-ui/references/qr.md +20 -0
- package/dist/skills/comark-ui/references/quiz.md +45 -0
- package/dist/skills/comark-ui/references/reading-time.md +32 -0
- package/dist/skills/comark-ui/references/sources.md +24 -0
- package/dist/skills/comark-ui/references/steps.md +34 -0
- package/dist/skills/comark-ui/references/table.md +48 -0
- package/dist/skills/comark-ui/references/tabs.md +46 -0
- package/dist/skills/comark-ui/references/tests.md +59 -0
- package/dist/skills/comark-ui/references/ui.md +54 -0
- package/dist/skills/comark-ui/references/xref.md +48 -0
- package/dist/skills/comark-ui/references/zoom.md +35 -0
- package/dist/src-DROB05U6.mjs +119 -0
- package/dist/svelte/Mermaid.svelte +16 -0
- package/dist/svelte/Typeset.svelte +7 -0
- package/dist/svelte/index.d.ts +9 -0
- package/dist/svelte/index.js +80 -0
- package/dist/typeset-css-5aJ7cSmh.mjs +216 -0
- package/dist/typeset-css-DdOGL7WF.d.mts +10 -0
- package/dist/vue.d.mts +7 -0
- package/dist/vue.mjs +113 -0
- package/package.json +170 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 onmax
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
The icons are Lucide's (https://lucide.dev), under its license:
|
|
26
|
+
|
|
27
|
+
ISC License
|
|
28
|
+
|
|
29
|
+
Copyright (c) 2026 Lucide Icons and Contributors
|
|
30
|
+
|
|
31
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
32
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
33
|
+
copyright notice and this permission notice appear in all copies.
|
|
34
|
+
|
|
35
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
36
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
37
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
38
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
39
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
40
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
41
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
The following Lucide icons are derived from the Feather project:
|
|
46
|
+
|
|
47
|
+
airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out
|
|
48
|
+
|
|
49
|
+
The MIT License (MIT) (for the icons listed above)
|
|
50
|
+
|
|
51
|
+
Copyright (c) 2013-present Cole Bemis
|
|
52
|
+
|
|
53
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
54
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
55
|
+
in the Software without restriction, including without limitation the rights
|
|
56
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
57
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
58
|
+
furnished to do so, subject to the following conditions:
|
|
59
|
+
|
|
60
|
+
The above copyright notice and this permission notice shall be included in all
|
|
61
|
+
copies or substantial portions of the Software.
|
|
62
|
+
|
|
63
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
64
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
65
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
66
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
67
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
68
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
69
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,71 @@
|
|
|
1
1
|
# comark-ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://npmx.dev/package/comark-ui)
|
|
4
|
+
[](https://npmx.dev/package/comark-ui)
|
|
5
|
+
[](https://npmx.dev/package/comark-ui)
|
|
6
|
+
[](https://comark-ui.onmax.me/getting-started/)
|
|
7
|
+
|
|
8
|
+
Every [Comark](https://comark.dev) block from [comark-ui](https://comark-ui.onmax.me) in one install, registered in the order they need, with one icon resolver shared by `:icon`, cards, details, file trees, tabs, and code actions. It ships in lockstep with the plugins, so updating it brings their latest defaults.
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
pnpm add comark @comark/html comark-ui
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { createHtmlRenderer } from "@comark/html";
|
|
16
|
+
import comarkUi from "comark-ui/html";
|
|
17
|
+
|
|
18
|
+
const render = createHtmlRenderer(comarkUi());
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`comarkUi()` returns `{ plugins, components }`. Every plugin is on but `include`. Pass `false` to turn one off, which drops its components too, or an object to pass it options:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
comarkUi({
|
|
25
|
+
zoom: false,
|
|
26
|
+
code: { actions: [{ id: "run", label: "Run" }] },
|
|
27
|
+
github: { references: { repo: "owner/name" } },
|
|
28
|
+
include: { root: "content" },
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`include` is off because it reads files from disk; turn it on to paste files into a document. `cast`, `table`, and `tests` are on but read no files until you pass `{ root }` or `{ read }`. When you parse in a browser, pass `chips: { download: false }`, since a browser may not download another site's images. comark-critic has no component for its styles, so put the exported `criticCss` in the page. The text around the blocks (headings, paragraphs, lists, tables, code, and quotes) is styled too, from shadcn/typeset on the same tokens and scoped to the document; pass `typeset: false` when your site already styles Markdown. See [Typeset](https://comark-ui.onmax.me/guide/typeset/).
|
|
33
|
+
|
|
34
|
+
## Renderers
|
|
35
|
+
|
|
36
|
+
| Renderer | Import |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `@comark/html` | `comark-ui/html` |
|
|
39
|
+
| `@comark/vue` | `comark-ui/vue` |
|
|
40
|
+
| `@comark/react` | `comark-ui/react` |
|
|
41
|
+
| `@comark/svelte` | `comark-ui/svelte` |
|
|
42
|
+
|
|
43
|
+
Vue, React, and Svelte leave out annotate and diff, which render only to HTML.
|
|
44
|
+
|
|
45
|
+
## Editor
|
|
46
|
+
|
|
47
|
+
[Editor docs](https://comark-ui.onmax.me/guide/editor/) cover setup, Vue bindings, file uploads, and collaborative rooms.
|
|
48
|
+
|
|
49
|
+
`comark-ui/editor` is a [Tiptap](https://tiptap.dev) editor for Comark Markdown, with no DOM and no framework, so a server can convert documents with it too. Install `@tiptap/core`, `@tiptap/pm`, `@tiptap/starter-kit`, `@tiptap/extension-list`, `@tiptap/extension-image`, and `@tiptap/extension-table`.
|
|
50
|
+
|
|
51
|
+
| Export | What it does |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `ComarkKit` | The schema: StarterKit, task lists, inline images (drawn as players for video and audio), tables, and Comark's components, slots, and comments, each keeping the attributes it has no field for. |
|
|
54
|
+
| `fromMarkdown`, `toMarkdown` | Convert between Markdown and the editor's JSON with Comark's own parser and writer. Front matter stays beside the document. |
|
|
55
|
+
| `checkMarkdown(markdown, { plugins })` | Whether the editor can save the document without changing what readers see, judged with the plugins the page renders with. A document it can't keep opens read-only. |
|
|
56
|
+
| `Upload` | Uploads pasted and dropped files. Each shows a placeholder with its progress until `upload(file, progress)` returns its URL. The placeholder is a decoration, so a collaborative document never holds a file that hasn't arrived. |
|
|
57
|
+
| `createEditorCodec({ plugins })` | `{ extensions, fromMarkdown, toMarkdown, verify }` for a collaborative room, such as ViteHub Realtime's `editor` option. |
|
|
58
|
+
|
|
59
|
+
`comark-ui/editor/vue` binds the editor to a Markdown string with `useComarkEditor(markdown)` or `<ComarkEditor v-model="markdown" />`. Run `pnpm playground` in this package to try it.
|
|
60
|
+
|
|
61
|
+
## Agents
|
|
62
|
+
|
|
63
|
+
The package ships the [comark-ui agent skill](https://github.com/onmax/comark-ui/tree/main/skills/comark-ui): a `SKILL.md` that says how to install the plugins, which block shows what, and how to check the result, and a reference for each package. Install it with `npx skills add onmax/comark-ui`, or bundle it in your own app from `comark-ui/skills/comark-ui/`, which always matches the plugins it installed.
|
|
64
|
+
|
|
65
|
+
## Linting
|
|
66
|
+
|
|
67
|
+
[comark-linter](https://comark-ui.onmax.me/lint/) finds `comark-ui` when it's installed and checks documents with its plugins and components.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { theme } from "comark-zoom/theme";
|
|
2
|
+
//#region src/script.ts
|
|
3
|
+
/**
|
|
4
|
+
* Starts the Copy image and Copy source buttons of every `.cmm` Mermaid diagram on the page and returns a function that
|
|
5
|
+
* stops them. Every caller shares one set of listeners, which stop with the last caller. The function is
|
|
6
|
+
* self-contained: the HTML renderer inlines its source as a script, so it must not use anything from outside its body.
|
|
7
|
+
*/
|
|
8
|
+
function start() {
|
|
9
|
+
const running = window.__comarkMermaid;
|
|
10
|
+
if (running) {
|
|
11
|
+
running.users++;
|
|
12
|
+
return running.stop;
|
|
13
|
+
}
|
|
14
|
+
const listeners = new AbortController();
|
|
15
|
+
const signal = listeners.signal;
|
|
16
|
+
document.documentElement.setAttribute("data-mermaid-ready", "");
|
|
17
|
+
const links = /* @__PURE__ */ new Set();
|
|
18
|
+
const PROPERTIES = [
|
|
19
|
+
"fill",
|
|
20
|
+
"fill-opacity",
|
|
21
|
+
"stroke",
|
|
22
|
+
"stroke-opacity",
|
|
23
|
+
"stroke-width",
|
|
24
|
+
"stroke-dasharray",
|
|
25
|
+
"stroke-linecap",
|
|
26
|
+
"stroke-linejoin",
|
|
27
|
+
"opacity",
|
|
28
|
+
"color",
|
|
29
|
+
"rx",
|
|
30
|
+
"ry",
|
|
31
|
+
"font-family",
|
|
32
|
+
"font-size",
|
|
33
|
+
"font-weight",
|
|
34
|
+
"font-style",
|
|
35
|
+
"letter-spacing",
|
|
36
|
+
"text-anchor",
|
|
37
|
+
"dominant-baseline",
|
|
38
|
+
"paint-order"
|
|
39
|
+
];
|
|
40
|
+
function background(from) {
|
|
41
|
+
const probe = document.createElement("canvas").getContext("2d", { willReadFrequently: true });
|
|
42
|
+
for (let at = from; at && probe; at = at.parentElement) {
|
|
43
|
+
const color = getComputedStyle(at).backgroundColor;
|
|
44
|
+
probe.clearRect(0, 0, 1, 1);
|
|
45
|
+
probe.fillStyle = color;
|
|
46
|
+
probe.fillRect(0, 0, 1, 1);
|
|
47
|
+
if (probe.getImageData(0, 0, 1, 1).data[3] === 255) return color;
|
|
48
|
+
}
|
|
49
|
+
return "#fff";
|
|
50
|
+
}
|
|
51
|
+
function png(root) {
|
|
52
|
+
const svg = root.querySelector(".pele-diagram>svg");
|
|
53
|
+
if (!svg) return Promise.reject(/* @__PURE__ */ new Error("no diagram"));
|
|
54
|
+
const width = Number(svg.getAttribute("width")) || svg.viewBox.baseVal.width;
|
|
55
|
+
const height = Number(svg.getAttribute("height")) || svg.viewBox.baseVal.height;
|
|
56
|
+
if (!(width > 0 && height > 0)) return Promise.reject(/* @__PURE__ */ new Error("no size"));
|
|
57
|
+
const copy = svg.cloneNode(true);
|
|
58
|
+
if (!(copy instanceof SVGSVGElement)) return Promise.reject(/* @__PURE__ */ new Error("no diagram"));
|
|
59
|
+
const from = [svg, ...svg.querySelectorAll("*")];
|
|
60
|
+
const to = [copy, ...copy.querySelectorAll("*")];
|
|
61
|
+
from.forEach((node, index) => {
|
|
62
|
+
const into = to[index];
|
|
63
|
+
if (!(into instanceof SVGElement)) return;
|
|
64
|
+
const style = getComputedStyle(node);
|
|
65
|
+
for (const property of PROPERTIES) into.style.setProperty(property, style.getPropertyValue(property));
|
|
66
|
+
});
|
|
67
|
+
copy.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
68
|
+
copy.setAttribute("width", String(width));
|
|
69
|
+
copy.setAttribute("height", String(height));
|
|
70
|
+
copy.style.maxWidth = "none";
|
|
71
|
+
copy.style.width = `${width}px`;
|
|
72
|
+
copy.style.height = `${height}px`;
|
|
73
|
+
const scale = Math.min(2, 4096 / width, 4096 / height);
|
|
74
|
+
const canvas = document.createElement("canvas");
|
|
75
|
+
canvas.width = Math.max(1, Math.round(width * scale));
|
|
76
|
+
canvas.height = Math.max(1, Math.round(height * scale));
|
|
77
|
+
const fill = background(svg.parentElement ?? root);
|
|
78
|
+
const image = new Image();
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
image.onerror = () => reject(/* @__PURE__ */ new Error("not drawn"));
|
|
81
|
+
image.onload = () => {
|
|
82
|
+
const context = canvas.getContext("2d");
|
|
83
|
+
if (!context) return reject(/* @__PURE__ */ new Error("no canvas"));
|
|
84
|
+
context.fillStyle = fill;
|
|
85
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
86
|
+
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
87
|
+
canvas.toBlob((blob) => blob ? resolve(blob) : reject(/* @__PURE__ */ new Error("no image")), "image/png");
|
|
88
|
+
};
|
|
89
|
+
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(new XMLSerializer().serializeToString(copy))}`;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const part = (root, selector) => root.querySelector(`:scope>.cmm-bar>${selector}`);
|
|
93
|
+
function say(root, button, message, state) {
|
|
94
|
+
const status = part(root, ".cmm-status");
|
|
95
|
+
if (!status) return;
|
|
96
|
+
button.setAttribute("data-state", state);
|
|
97
|
+
status.textContent = message;
|
|
98
|
+
setTimeout(() => {
|
|
99
|
+
if (status.textContent !== message) return;
|
|
100
|
+
button.removeAttribute("data-state");
|
|
101
|
+
status.textContent = "";
|
|
102
|
+
}, state === "done" ? 1500 : 6e3);
|
|
103
|
+
}
|
|
104
|
+
function copyImage(root, button) {
|
|
105
|
+
const open = part(root, ".cmm-open");
|
|
106
|
+
if (open) open.hidden = true;
|
|
107
|
+
const image = png(root);
|
|
108
|
+
image.catch(() => {});
|
|
109
|
+
const clipboard = navigator.clipboard;
|
|
110
|
+
(clipboard && "ClipboardItem" in window ? clipboard.write([new ClipboardItem({ "image/png": image })]) : Promise.reject(/* @__PURE__ */ new Error("no clipboard"))).then(() => say(root, button, "Image copied", "done"), () => image.then((blob) => {
|
|
111
|
+
if (!open) return;
|
|
112
|
+
if (open.href) {
|
|
113
|
+
URL.revokeObjectURL(open.href);
|
|
114
|
+
links.delete(open.href);
|
|
115
|
+
}
|
|
116
|
+
open.href = URL.createObjectURL(blob);
|
|
117
|
+
links.add(open.href);
|
|
118
|
+
open.hidden = false;
|
|
119
|
+
say(root, button, "Couldn't copy the image. Open the PNG to save it.", "failed");
|
|
120
|
+
}, () => say(root, button, "Couldn't draw the image. Copy the source instead.", "failed")));
|
|
121
|
+
}
|
|
122
|
+
function copySource(root, button) {
|
|
123
|
+
const source = (root.getAttribute("data-source") ?? "").replace(/\n$/, "");
|
|
124
|
+
const longest = Math.max(2, ...(source.match(/`+/g) ?? []).map((run) => run.length));
|
|
125
|
+
const fence = "`".repeat(longest + 1);
|
|
126
|
+
const markdown = `${fence}mermaid\n${source}\n${fence}`;
|
|
127
|
+
const shown = root.querySelector(":scope>.cmm-markdown");
|
|
128
|
+
const failed = () => {
|
|
129
|
+
const pre = shown ?? document.createElement("pre");
|
|
130
|
+
pre.className = "cmm-markdown";
|
|
131
|
+
pre.textContent = markdown;
|
|
132
|
+
if (!shown) root.append(pre);
|
|
133
|
+
const range = document.createRange();
|
|
134
|
+
range.selectNodeContents(pre);
|
|
135
|
+
const selection = getSelection();
|
|
136
|
+
selection?.removeAllRanges();
|
|
137
|
+
selection?.addRange(range);
|
|
138
|
+
say(root, button, "Couldn't copy, so the source is selected. Press Ctrl+C or ⌘C.", "failed");
|
|
139
|
+
};
|
|
140
|
+
const clipboard = navigator.clipboard;
|
|
141
|
+
if (!clipboard) return failed();
|
|
142
|
+
clipboard.writeText(markdown).then(() => {
|
|
143
|
+
shown?.remove();
|
|
144
|
+
say(root, button, "Source copied", "done");
|
|
145
|
+
}, failed);
|
|
146
|
+
}
|
|
147
|
+
document.addEventListener("click", (event) => {
|
|
148
|
+
const button = event.target instanceof Element ? event.target.closest(".cmm>.cmm-bar>button") : null;
|
|
149
|
+
const root = button?.parentElement?.parentElement;
|
|
150
|
+
if (!button || !root) return;
|
|
151
|
+
if (button.classList.contains("cmm-image")) copyImage(root, button);
|
|
152
|
+
else if (button.classList.contains("cmm-source")) copySource(root, button);
|
|
153
|
+
}, { signal });
|
|
154
|
+
const state = {
|
|
155
|
+
users: 1,
|
|
156
|
+
stop: () => {
|
|
157
|
+
if (--state.users > 0) return;
|
|
158
|
+
listeners.abort();
|
|
159
|
+
for (const link of links) URL.revokeObjectURL(link);
|
|
160
|
+
document.documentElement.removeAttribute("data-mermaid-ready");
|
|
161
|
+
window.__comarkMermaid = void 0;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
window.__comarkMermaid = state;
|
|
165
|
+
return state.stop;
|
|
166
|
+
}
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/client.ts
|
|
169
|
+
/**
|
|
170
|
+
* comark-pele's diagrams on the shared `--comark-*` tokens, on a card like comark-diagrams'. Pele paints with its
|
|
171
|
+
* `--pele-*` variables, so setting them on the wrapper themes every diagram, light and dark, without drawing it
|
|
172
|
+
* again. The rules sit in the `comark` layer, so host `--pele-*` values are preserved and win over these fallbacks.
|
|
173
|
+
*/
|
|
174
|
+
const mermaidCss = `@layer comark{${[
|
|
175
|
+
theme(".cmm,.pele-diagram"),
|
|
176
|
+
theme(":root"),
|
|
177
|
+
`:where(:root){--pele-bg:var(--_comark-background);--pele-fg:var(--_comark-foreground);--pele-muted:var(--_comark-muted);--pele-line:var(--_comark-muted);--pele-surface:var(--_comark-accent);--pele-surface-alt:color-mix(in srgb,var(--_comark-accent),var(--_comark-foreground) 6%);--pele-border:var(--_comark-border);--pele-accent:var(--ui-primary,var(--primary,var(--_comark-foreground)));--pele-critical:var(--ui-error,var(--destructive,#dc2626));--pele-error:var(--ui-error,var(--destructive,#dc2626));--pele-font:var(--_comark-font);--pele-radius:calc(var(--_comark-radius) - 4px);--pele-series-1:var(--comark-chart-1,var(--ui-chart-1,var(--chart-1,#3b82f6)));--pele-series-2:var(--comark-chart-2,var(--ui-chart-2,var(--chart-2,#f97316)));--pele-series-3:var(--comark-chart-3,var(--ui-chart-3,var(--chart-3,#10b981)));--pele-series-4:var(--comark-chart-4,var(--ui-chart-4,var(--chart-4,#a855f7)));--pele-series-5:var(--comark-chart-5,var(--ui-chart-5,var(--chart-5,#f43f5e)));--pele-series-6:var(--comark-chart-6,var(--ui-chart-6,var(--chart-6,#eab308)));--pele-series-7:var(--comark-chart-7,var(--ui-chart-7,var(--chart-7,#06b6d4)));--pele-series-8:var(--comark-chart-8,var(--ui-chart-8,var(--chart-8,#ec4899)))}`,
|
|
178
|
+
".pele-diagram{box-sizing:border-box;margin-block:1.5rem;padding:1rem;overflow-x:auto;border:1px solid var(--_comark-border);border-radius:var(--_comark-radius);background:var(--_comark-background);color:var(--_comark-foreground);text-align:center}",
|
|
179
|
+
".pele-diagram>svg{min-width:min(28rem,var(--cmm-svg,28rem))}",
|
|
180
|
+
".pele-source{margin:0;color:var(--_comark-muted);text-align:left}",
|
|
181
|
+
".cmm{--comark-wide-width:calc(var(--cmm-svg) + 2rem + 2px);--pele-zoom-top:auto;--pele-zoom-bottom:.5rem;position:relative;margin-block:1.5rem}",
|
|
182
|
+
".cmm>.pele-diagram{margin-block:0}",
|
|
183
|
+
".cmm-bar{position:absolute;top:.375rem;right:.375rem;z-index:1;display:flex;align-items:center;gap:.125rem;padding:.125rem;border-radius:calc(var(--_comark-radius) - 2px);background:var(--_comark-background);opacity:0;transition:opacity .15s}",
|
|
184
|
+
"html:not([data-mermaid-ready]) .cmm-bar{display:none}",
|
|
185
|
+
".cmm:is(:hover,:focus-within)>.cmm-bar,.cmm-bar:has(.cmm-status:not(:empty),.cmm-open:not([hidden])){opacity:1}",
|
|
186
|
+
"@media (hover:none){.cmm-bar{opacity:1}}",
|
|
187
|
+
".cmm:has(>.pele-diagram[data-error]) .cmm-image{display:none}",
|
|
188
|
+
".cmm-bar>button{display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:0;padding:0;border:1px solid transparent;border-radius:calc(var(--_comark-radius) - 2px);background:transparent;color:var(--_comark-muted);position:relative;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;transition:background-color .15s ease,color .15s ease}",
|
|
189
|
+
"@media (hover:hover){.cmm-bar>button:hover{background:var(--_comark-accent);color:var(--_comark-foreground)}}",
|
|
190
|
+
"@media (prefers-reduced-motion:no-preference){.cmm-bar>button{transition:background-color .15s ease,color .15s ease,scale .15s ease-out}.cmm-bar>button:active{scale:.96}}",
|
|
191
|
+
".cmm-bar>button:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--_comark-ring) 50%,transparent)}",
|
|
192
|
+
".cmm-bar svg{width:.75rem;height:.75rem;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}",
|
|
193
|
+
".cmm-done,.cmm-bar>[data-state=done] .cmm-idle{display:none}",
|
|
194
|
+
".cmm-bar>[data-state=done] .cmm-done{display:block}",
|
|
195
|
+
".cmm-open{padding:0 .375rem;color:var(--_comark-foreground);font:500 .75rem/1.5rem var(--_comark-font);text-decoration:underline;text-underline-offset:2px}",
|
|
196
|
+
".cmm-open[hidden]{display:none}",
|
|
197
|
+
".cmm-status{position:absolute;top:calc(100% + .25rem);right:0;width:max-content;max-width:18rem;padding:.25rem .5rem;border-radius:calc(var(--_comark-radius) - 4px);background:var(--_comark-foreground);color:var(--_comark-background);font:.75rem/1.25rem var(--_comark-font);text-align:left;pointer-events:none}",
|
|
198
|
+
".cmm-status:empty{display:none}",
|
|
199
|
+
".cmm-markdown{margin:.5rem 0 0;padding:.75rem 1rem;overflow-x:auto;border:1px solid var(--_comark-border);border-radius:var(--_comark-radius);background:var(--_comark-accent);color:var(--_comark-foreground);font:.8125rem/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;white-space:pre}",
|
|
200
|
+
".cz-item>.pele-diagram>svg{min-width:0}",
|
|
201
|
+
"@media print{.pele-diagram{break-inside:avoid}.cmm-bar{display:none}}"
|
|
202
|
+
].join("")}}`;
|
|
203
|
+
/** Sizes Mermaid cards after Pele draws them in Vue, React, and Svelte, and adds the styles to the page once. */
|
|
204
|
+
function addMermaidStyles() {
|
|
205
|
+
for (const card of document.querySelectorAll(".pele-diagram")) {
|
|
206
|
+
const svg = card.querySelector(":scope > svg");
|
|
207
|
+
const width = Number(svg?.getAttribute("width"));
|
|
208
|
+
const block = card.parentElement?.classList.contains("cmm") ? card.parentElement : card;
|
|
209
|
+
if (Number.isFinite(width) && width > 0 && !card.querySelector(":scope > .pele-error")) {
|
|
210
|
+
block.dataset.wide = "";
|
|
211
|
+
block.style.setProperty("--cmm-svg", `${Math.ceil(width)}px`);
|
|
212
|
+
} else {
|
|
213
|
+
delete block.dataset.wide;
|
|
214
|
+
block.style.removeProperty("--cmm-svg");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (document.querySelector("style[data-comark-mermaid]")) return;
|
|
218
|
+
const style = document.createElement("style");
|
|
219
|
+
style.dataset.comarkMermaid = "";
|
|
220
|
+
style.textContent = mermaidCss;
|
|
221
|
+
document.head.append(style);
|
|
222
|
+
}
|
|
223
|
+
const icon = (name, path) => `<svg class="cmm-${name}" viewBox="0 0 24 24" aria-hidden="true">${path}</svg>`;
|
|
224
|
+
const done = icon("done", "<path d=\"M20 6 9 17l-5-5\"/>");
|
|
225
|
+
/**
|
|
226
|
+
* The inside of a diagram's bar, the same in every renderer: Copy image, Copy source, the link to the PNG for when the
|
|
227
|
+
* clipboard refuses it, and the status line that says whether the copy worked.
|
|
228
|
+
*/
|
|
229
|
+
function mermaidBar() {
|
|
230
|
+
return `<button type="button" class="cmm-image" aria-label="Copy image" title="Copy image">` + icon("idle", "<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/><path d=\"m21 15-5-5L5 21\"/>") + `${done}</button><button type="button" class="cmm-source" aria-label="Copy source" title="Copy source">` + icon("idle", "<path d=\"m16 18 6-6-6-6\"/><path d=\"m8 6-6 6 6 6\"/>") + `${done}</button><a class="cmm-open" target="_blank" rel="noopener" hidden>Open PNG</a><span class="cmm-status" role="status"></span>`;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Adds the Mermaid styles and starts the buttons of every diagram on the page, and returns a function that stops them.
|
|
234
|
+
* The Vue, React, and Svelte diagrams call it on mount and stop it on unmount.
|
|
235
|
+
*/
|
|
236
|
+
function startMermaid() {
|
|
237
|
+
addMermaidStyles();
|
|
238
|
+
return start();
|
|
239
|
+
}
|
|
240
|
+
//#endregion
|
|
241
|
+
export { start as a, startMermaid as i, mermaidBar as n, mermaidCss as r, addMermaidStyles as t };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { t as typesetCss } from "./typeset-css-DdOGL7WF.mjs";
|
|
2
|
+
//#region src/client.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* comark-pele's diagrams on the shared `--comark-*` tokens, on a card like comark-diagrams'. Pele paints with its
|
|
5
|
+
* `--pele-*` variables, so setting them on the wrapper themes every diagram, light and dark, without drawing it
|
|
6
|
+
* again. The rules sit in the `comark` layer, so host `--pele-*` values are preserved and win over these fallbacks.
|
|
7
|
+
*/
|
|
8
|
+
export declare const mermaidCss: string;
|
|
9
|
+
/** Sizes Mermaid cards after Pele draws them in Vue, React, and Svelte, and adds the styles to the page once. */
|
|
10
|
+
export declare function addMermaidStyles(): void;
|
|
11
|
+
/**
|
|
12
|
+
* The inside of a diagram's bar, the same in every renderer: Copy image, Copy source, the link to the PNG for when the
|
|
13
|
+
* clipboard refuses it, and the status line that says whether the copy worked.
|
|
14
|
+
*/
|
|
15
|
+
export declare function mermaidBar(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Adds the Mermaid styles and starts the buttons of every diagram on the page, and returns a function that stops them.
|
|
18
|
+
* The Vue, React, and Svelte diagrams call it on mount and stop it on unmount.
|
|
19
|
+
*/
|
|
20
|
+
export declare function startMermaid(): () => void;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { typesetCss };
|
package/dist/client.mjs
ADDED