@riebeckite/plugin-excalidraw 0.0.21 → 0.0.23
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/README.md +31 -10
- package/README_ja.md +23 -2
- package/dist/index.js +118 -2
- package/dist/src/render.d.ts +6 -0
- package/package.json +6 -4
- package/style.css +19 -0
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @riebeckite/plugin-excalidraw
|
|
2
2
|
|
|
3
|
+
<!-- Generated from docs/docs/plugins/excalidraw.md. Edit the canonical documentation in docs/docs/plugins and run `pnpm docs:sync`. -->
|
|
4
|
+
|
|
3
5
|
Excalidraw drawing rendering for Obsidian wikilinks.
|
|
4
6
|
|
|
5
7
|
[日本語](./README_ja.md)
|
|
@@ -49,20 +51,22 @@ fenced code block is extracted and supports both `json` and lz-string
|
|
|
49
51
|
`.excalidraw` or `.excalidraw.md`; everything else returns `null` and falls
|
|
50
52
|
through to the attachment plugin
|
|
51
53
|
- Reads the file under `config.content.directory` (path-traversal safe)
|
|
52
|
-
- Parses the scene
|
|
53
|
-
|
|
54
|
-
```html
|
|
55
|
-
<figure class="rr-excalidraw" data-excalidraw="pending" data-excalidraw-lazy="true">
|
|
56
|
-
<div class="rr-excalidraw__canvas" role="img" aria-label="drawing.excalidraw"></div>
|
|
57
|
-
<script type="application/json" class="rr-excalidraw__payload">{"elements":[...],"appState":{...},"files":{...}}</script>
|
|
58
|
-
</figure>
|
|
59
|
-
```
|
|
60
|
-
|
|
54
|
+
- Parses the scene before rendering it.
|
|
61
55
|
- Wikilink aliases can set a size: `![[drawing.excalidraw|800]]` (width) or
|
|
62
56
|
`![[drawing.excalidraw|800x600]]` (width x height)
|
|
63
57
|
- Missing files, invalid scenes, or out-of-directory paths render an error
|
|
64
58
|
placeholder and log to the console
|
|
65
59
|
|
|
60
|
+
#### Live example
|
|
61
|
+
|
|
62
|
+
The following source is rendered as an SVG drawing on this page.
|
|
63
|
+
|
|
64
|
+
````md
|
|
65
|
+
![[HW]]
|
|
66
|
+
````
|
|
67
|
+
|
|
68
|
+
![[HW]]
|
|
69
|
+
|
|
66
70
|
### Client (`initExcalidraw`)
|
|
67
71
|
|
|
68
72
|
- Renders pending figures to SVG with `exportToSvg` from
|
|
@@ -72,6 +76,23 @@ fenced code block is extracted and supports both `json` and lz-string
|
|
|
72
76
|
- Success → `data-excalidraw="ready"` (the SVG replaces the empty canvas)
|
|
73
77
|
- Failure → `data-excalidraw="error"` plus a placeholder message
|
|
74
78
|
|
|
79
|
+
## Standalone pages
|
|
80
|
+
|
|
81
|
+
An Excalidraw drawing stored as Markdown is also served as a standalone page
|
|
82
|
+
instead of a plain Markdown page. The plugin registers a page type
|
|
83
|
+
(`excalidraw-document`) that claims the entry's permalink with a trailing
|
|
84
|
+
`.excalidraw` removed.
|
|
85
|
+
|
|
86
|
+
- Detection: the slug ends in `.excalidraw` (for `*.excalidraw.md`), or the
|
|
87
|
+
frontmatter contains `excalidraw-plugin: parsed`
|
|
88
|
+
- `drawing.excalidraw.md` (permalink `/drawing.excalidraw`) is served at
|
|
89
|
+
`/drawing`
|
|
90
|
+
- The page body provides its own `<main>` and `<h1>`, and reuses the same
|
|
91
|
+
placeholder figure and `initExcalidraw` client as embedded wikilinks
|
|
92
|
+
|
|
93
|
+
The page type resolves before normal content routing, so no extra app route is
|
|
94
|
+
required. Only public entries are listed.
|
|
95
|
+
|
|
75
96
|
## Options
|
|
76
97
|
|
|
77
98
|
| Option | Type | Default | Description |
|
|
@@ -85,6 +106,6 @@ fenced code block is extracted and supports both `json` and lz-string
|
|
|
85
106
|
|
|
86
107
|
## See also
|
|
87
108
|
|
|
88
|
-
- [Plugin guide](
|
|
109
|
+
- [Plugin guide](https://github.com/Rerurate514/riebeckite/blob/main/docs/docs/reference/plugin-api.md)
|
|
89
110
|
- [`@riebeckite/plugin-obsidian-markdown`](../obsidian-markdown/README.md)
|
|
90
111
|
- [`@riebeckite/plugin-attachment`](../attachment/README.md)
|
package/README_ja.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @riebeckite/plugin-excalidraw
|
|
2
2
|
|
|
3
|
+
<!-- Generated from docs/docs/plugins/excalidraw.ja.md. Edit the canonical documentation in docs/docs/plugins and run `pnpm docs:sync`. -->
|
|
4
|
+
|
|
3
5
|
Obsidian の Excalidraw 埋め込みを SVG として表示するプラグインです。
|
|
4
6
|
|
|
5
7
|
[English](./README.md)
|
|
@@ -28,6 +30,16 @@ export default defineConfig({
|
|
|
28
30
|
|
|
29
31
|
`![[drawing.excalidraw|800]]` なら幅を、`![[drawing.excalidraw|800x600]]` なら幅と高さを指定できます。ファイルがない、内容が不正、またはコンテンツディレクトリ外を指す場合は、エラー用プレースホルダーを表示します。
|
|
30
32
|
|
|
33
|
+
## 実行例
|
|
34
|
+
|
|
35
|
+
以下のソースは、このページ上で SVG の図として実際に描画されます。
|
|
36
|
+
|
|
37
|
+
````md
|
|
38
|
+
![[HW]]
|
|
39
|
+
````
|
|
40
|
+
|
|
41
|
+
![[HW]]
|
|
42
|
+
|
|
31
43
|
## 描画のタイミング
|
|
32
44
|
|
|
33
45
|
既定では、図が表示領域の近くに入ってから描画します。`IntersectionObserver` が使えない環境を含め、描画に失敗した要素は `data-excalidraw="error"` になり、エラー表示へ切り替わります。
|
|
@@ -36,6 +48,16 @@ export default defineConfig({
|
|
|
36
48
|
| --- | --- | --- | --- |
|
|
37
49
|
| `lazy` | `boolean` | `true` | 表示領域に近づいてから SVG を描画するか |
|
|
38
50
|
|
|
51
|
+
## 単体ページとしての表示
|
|
52
|
+
|
|
53
|
+
Excalidraw の描画である Markdown エントリは、通常の Markdown ページではなく専用ページとして表示されます。プラグインはページ型(`excalidraw-document`)を登録し、パーマリンク末尾の `.excalidraw` を除いたパスを引き受けます。
|
|
54
|
+
|
|
55
|
+
- 判定: slug が `.excalidraw` で終わる(`*.excalidraw.md`)、または frontmatter に `excalidraw-plugin: parsed` がある
|
|
56
|
+
- `drawing.excalidraw.md`(パーマリンク `/drawing.excalidraw`)は `/drawing` で表示される
|
|
57
|
+
- ページ本文は自前の `<main>` と `<h1>` を持ち、埋め込みウィキリンクと同じプレースホルダーと `initExcalidraw` を再利用する
|
|
58
|
+
|
|
59
|
+
ページ型は通常のコンテンツルーティングより先に解決されるため、アプリ側のルート追加は不要です。対象は公開エントリのみです。
|
|
60
|
+
|
|
39
61
|
## 公開 API
|
|
40
62
|
|
|
41
63
|
- `excalidraw(options?)` / `excalidrawPlugin` — プラグインファクトリ
|
|
@@ -43,7 +65,6 @@ export default defineConfig({
|
|
|
43
65
|
|
|
44
66
|
## 関連資料
|
|
45
67
|
|
|
46
|
-
- [プラグインシステム](
|
|
68
|
+
- [プラグインシステム](https://github.com/Rerurate514/riebeckite/blob/main/docs/docs/reference/plugin-api.ja.md)
|
|
47
69
|
- [`@riebeckite/plugin-obsidian-markdown`](../obsidian-markdown/README_ja.md)
|
|
48
70
|
- [`@riebeckite/plugin-attachment`](../attachment/README_ja.md)
|
|
49
|
-
|
package/dist/index.js
CHANGED
|
@@ -105,6 +105,17 @@ import {
|
|
|
105
105
|
escapeHtmlAttribute,
|
|
106
106
|
escapeScriptJson
|
|
107
107
|
} from "@riebeckite/core";
|
|
108
|
+
function renderExcalidrawPage(input) {
|
|
109
|
+
const heading = input.title ? `<h1 class="rr-excalidraw-page__title">${escapeHtml(input.title)}</h1>` : "";
|
|
110
|
+
return `<main class="rr-excalidraw-page">${heading}${renderExcalidrawPlaceholder(
|
|
111
|
+
{
|
|
112
|
+
title: input.title,
|
|
113
|
+
scene: input.scene,
|
|
114
|
+
lazy: input.lazy,
|
|
115
|
+
error: input.error
|
|
116
|
+
}
|
|
117
|
+
)}</main>`;
|
|
118
|
+
}
|
|
108
119
|
function renderExcalidrawPlaceholder(input) {
|
|
109
120
|
const style = renderSizeStyle(input.size);
|
|
110
121
|
const error = input.error ? `<p class="rr-excalidraw__error">${escapeHtml(input.error)}</p>` : "";
|
|
@@ -158,8 +169,24 @@ function excalidraw(options = {}) {
|
|
|
158
169
|
render: async (context) => renderAttachment(context, options)
|
|
159
170
|
}
|
|
160
171
|
],
|
|
161
|
-
|
|
162
|
-
|
|
172
|
+
pageTypes: [
|
|
173
|
+
{
|
|
174
|
+
id: "excalidraw-document",
|
|
175
|
+
paths: ({ manifest }) => listExcalidrawDocumentPaths(manifest),
|
|
176
|
+
outputDependencies: [{ type: "global" }],
|
|
177
|
+
resolve: (context) => resolveExcalidrawDocument(context, options)
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
assets: [
|
|
181
|
+
createStyleAsset(PLUGIN_NAME, "@riebeckite/plugin-excalidraw/style.css")
|
|
182
|
+
],
|
|
183
|
+
clientEntries: [
|
|
184
|
+
createClientEntry(
|
|
185
|
+
PLUGIN_NAME,
|
|
186
|
+
"@riebeckite/plugin-excalidraw/client",
|
|
187
|
+
"initExcalidraw"
|
|
188
|
+
)
|
|
189
|
+
]
|
|
163
190
|
});
|
|
164
191
|
}
|
|
165
192
|
var excalidrawPlugin = excalidraw;
|
|
@@ -211,6 +238,95 @@ function isExcalidrawDocument(filePath, raw) {
|
|
|
211
238
|
if (lowerPath.endsWith(".excalidraw.md")) return true;
|
|
212
239
|
return lowerPath.endsWith(".md") && isObsidianExcalidrawMarkdown(raw);
|
|
213
240
|
}
|
|
241
|
+
function listExcalidrawDocumentPaths(manifest) {
|
|
242
|
+
const paths = /* @__PURE__ */ new Set();
|
|
243
|
+
for (const entry of manifest.publicEntries) {
|
|
244
|
+
const path = getExcalidrawPagePath(entry);
|
|
245
|
+
if (path) paths.add(path);
|
|
246
|
+
}
|
|
247
|
+
return [...paths];
|
|
248
|
+
}
|
|
249
|
+
function isExcalidrawEntry(entry) {
|
|
250
|
+
if (entry.slug.toLowerCase().endsWith(".excalidraw")) return true;
|
|
251
|
+
return entry.frontmatter["excalidraw-plugin"] === "parsed";
|
|
252
|
+
}
|
|
253
|
+
function getExcalidrawPagePath(entry) {
|
|
254
|
+
if (!isExcalidrawEntry(entry)) return null;
|
|
255
|
+
const path = normalizePath(entry.permalink).replace(/\.excalidraw$/i, "");
|
|
256
|
+
if (!path || path === "/") return null;
|
|
257
|
+
if (/\.[a-zA-Z0-9]+$/.test(path.slice(1))) return null;
|
|
258
|
+
return path;
|
|
259
|
+
}
|
|
260
|
+
function findExcalidrawEntry(manifest, pathname) {
|
|
261
|
+
for (const entry of manifest.publicEntries) {
|
|
262
|
+
if (getExcalidrawPagePath(entry) === pathname) return entry;
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
async function resolveExcalidrawDocument(context, options) {
|
|
267
|
+
const entry = findExcalidrawEntry(context.manifest, context.pathname);
|
|
268
|
+
if (!entry) return null;
|
|
269
|
+
const title = resolveEntryTitle(entry);
|
|
270
|
+
const description = typeof entry.frontmatter.description === "string" ? entry.frontmatter.description : void 0;
|
|
271
|
+
const lazy = options.lazy !== false;
|
|
272
|
+
if (!context.contentSource) {
|
|
273
|
+
return {
|
|
274
|
+
type: "excalidraw-document",
|
|
275
|
+
pathname: context.pathname,
|
|
276
|
+
title,
|
|
277
|
+
description,
|
|
278
|
+
body: renderExcalidrawPage({
|
|
279
|
+
title,
|
|
280
|
+
lazy,
|
|
281
|
+
error: "Content source is not available."
|
|
282
|
+
})
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const sourcePath = `${entry.slug}.md`;
|
|
286
|
+
try {
|
|
287
|
+
const raw = await readText(context.contentSource, sourcePath);
|
|
288
|
+
if (raw === null) return null;
|
|
289
|
+
const parsed = parseExcalidrawScene(raw, sourcePath);
|
|
290
|
+
const scene = await mergeEmbeddedFiles({
|
|
291
|
+
scene: parsed.scene,
|
|
292
|
+
embeddedFiles: parsed.embeddedFiles,
|
|
293
|
+
contentSource: context.contentSource,
|
|
294
|
+
contentIndex: context.contentIndex
|
|
295
|
+
});
|
|
296
|
+
return {
|
|
297
|
+
type: "excalidraw-document",
|
|
298
|
+
pathname: context.pathname,
|
|
299
|
+
title,
|
|
300
|
+
description,
|
|
301
|
+
body: renderExcalidrawPage({ title, scene, lazy })
|
|
302
|
+
};
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error("[plugin-excalidraw] Failed to render Excalidraw page", {
|
|
305
|
+
path: sourcePath,
|
|
306
|
+
error
|
|
307
|
+
});
|
|
308
|
+
return {
|
|
309
|
+
type: "excalidraw-document",
|
|
310
|
+
pathname: context.pathname,
|
|
311
|
+
title,
|
|
312
|
+
description,
|
|
313
|
+
body: renderExcalidrawPage({
|
|
314
|
+
title,
|
|
315
|
+
lazy,
|
|
316
|
+
error: "Excalidraw could not be rendered."
|
|
317
|
+
})
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function resolveEntryTitle(entry) {
|
|
322
|
+
const trimmed = entry.title.trim().replace(/\.excalidraw$/i, "");
|
|
323
|
+
if (trimmed) return trimmed;
|
|
324
|
+
return getFileName(entry.slug).replace(/\.excalidraw$/i, "");
|
|
325
|
+
}
|
|
326
|
+
function normalizePath(pathname) {
|
|
327
|
+
const collapsed = `/${pathname.split("/").filter(Boolean).join("/")}`;
|
|
328
|
+
return collapsed === "/" ? "/" : collapsed.replace(/\/+$/, "");
|
|
329
|
+
}
|
|
214
330
|
function parseSize(label) {
|
|
215
331
|
const value = label.trim();
|
|
216
332
|
if (/^\d{1,5}$/.test(value)) return { width: Number(value) };
|
package/dist/src/render.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { ExcalidrawScene } from "./types.js";
|
|
2
|
+
export declare function renderExcalidrawPage(input: {
|
|
3
|
+
title: string;
|
|
4
|
+
scene?: ExcalidrawScene;
|
|
5
|
+
lazy?: boolean;
|
|
6
|
+
error?: string;
|
|
7
|
+
}): string;
|
|
2
8
|
export declare function renderExcalidrawPlaceholder(input: {
|
|
3
9
|
title: string;
|
|
4
10
|
scene?: ExcalidrawScene;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-excalidraw",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,10 +24,11 @@
|
|
|
24
24
|
"@excalidraw/excalidraw": "^0.18.0",
|
|
25
25
|
"lz-string": "^1.5.0",
|
|
26
26
|
"unist-util-visit": "^5.0.0",
|
|
27
|
-
"@riebeckite/core": "0.0.
|
|
27
|
+
"@riebeckite/core": "0.0.23"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@types/mdast": "^4.0.4"
|
|
30
|
+
"@types/mdast": "^4.0.4",
|
|
31
|
+
"tsx": "^4.22.4"
|
|
31
32
|
},
|
|
32
33
|
"publishConfig": {
|
|
33
34
|
"access": "public"
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
"./style.css"
|
|
63
64
|
],
|
|
64
65
|
"scripts": {
|
|
65
|
-
"build": "node ../../../scripts/build_package.mjs"
|
|
66
|
+
"build": "node ../../../scripts/build_package.mjs",
|
|
67
|
+
"test": "node --import tsx --test \"test/*.test.ts\""
|
|
66
68
|
}
|
|
67
69
|
}
|
package/style.css
CHANGED
|
@@ -54,3 +54,22 @@
|
|
|
54
54
|
--rr-excalidraw-ink: var(--rb-color-paper, #ffffff);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
.rr-excalidraw-page {
|
|
59
|
+
width: min(100%, 96rem);
|
|
60
|
+
margin-inline: auto;
|
|
61
|
+
padding-inline: var(--rb-space-2, 1rem);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.rr-excalidraw-page__title {
|
|
65
|
+
margin: 0 0 var(--rb-space-2, 1rem);
|
|
66
|
+
font-family: var(--rb-font-heading, inherit);
|
|
67
|
+
font-size: clamp(1.75rem, 4vw, 2.75rem);
|
|
68
|
+
line-height: 1.05;
|
|
69
|
+
letter-spacing: -0.02em;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.rr-excalidraw-page .rr-excalidraw {
|
|
73
|
+
width: 100%;
|
|
74
|
+
margin: 0;
|
|
75
|
+
}
|