@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 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 and emits
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](../../../docs/docs/reference/plugin-api.md)
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
- - [プラグインシステム](../../../docs/docs/reference/plugin-api.ja.md)
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
- assets: [createStyleAsset(PLUGIN_NAME)],
162
- clientEntries: [createClientEntry(PLUGIN_NAME, "initExcalidraw")]
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) };
@@ -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.21",
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.21"
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
+ }