@haklex/rich-compose 0.12.0 → 0.13.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haklex/rich-compose",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Compose primitives for haklex rich content renderers — Gundam-style assembly of Lexical nodes, sync/lazy renderers, and Provider stacks.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -246,36 +246,35 @@
246
246
  "types": "./dist/style-token/styles.d.ts"
247
247
  },
248
248
  "./style.css": "./dist/style.css",
249
- "./style/*": "./dist/style/*"
249
+ "./style/*": "./dist/style/*",
250
+ "./litexml-html-preview-client.js": "./dist/litexml-html-preview-client.js",
251
+ "./litexml-html-preview-client.css": "./dist/litexml-html-preview-client.css"
250
252
  },
251
253
  "main": "./dist/index.mjs",
252
- "bin": {
253
- "litexml-to-html": "./dist/litexml-to-html.mjs"
254
- },
255
254
  "files": [
256
255
  "dist"
257
256
  ],
258
257
  "dependencies": {
259
258
  "@lexical/headless": "^0.44.0",
260
- "@haklex/rich-ext-code-snippet": "0.12.0",
261
- "@haklex/rich-ext-embed": "0.12.0",
262
- "@haklex/rich-renderer-alert": "0.12.0",
263
- "@haklex/rich-renderer-banner": "0.12.0",
264
- "@haklex/rich-renderer-codeblock": "0.12.0",
265
- "@haklex/rich-renderer-image": "0.12.0",
266
- "@haklex/rich-litexml": "0.12.0",
267
- "@haklex/rich-renderer-linkcard": "0.12.0",
268
- "@haklex/rich-renderer-mention": "0.12.0",
269
- "@haklex/rich-renderer-mermaid": "0.12.0",
270
- "@haklex/rich-renderer-ruby": "0.12.0",
271
- "@haklex/rich-ext-chat": "0.12.0",
272
- "@haklex/rich-ext-gallery": "0.12.0",
273
- "@haklex/rich-renderer-video": "0.12.0",
274
- "@haklex/rich-ext-excalidraw": "0.12.0",
275
- "@haklex/rich-ext-nested-doc": "0.12.0",
276
- "@haklex/rich-ext-poll": "0.12.0",
277
- "@haklex/rich-renderer-katex": "0.12.0",
278
- "@haklex/rich-style-token": "0.12.0"
259
+ "@haklex/rich-ext-chat": "0.13.0",
260
+ "@haklex/rich-ext-code-snippet": "0.13.0",
261
+ "@haklex/rich-ext-excalidraw": "0.13.0",
262
+ "@haklex/rich-ext-nested-doc": "0.13.0",
263
+ "@haklex/rich-litexml": "0.13.0",
264
+ "@haklex/rich-ext-embed": "0.13.0",
265
+ "@haklex/rich-renderer-alert": "0.13.0",
266
+ "@haklex/rich-renderer-codeblock": "0.13.0",
267
+ "@haklex/rich-renderer-image": "0.13.0",
268
+ "@haklex/rich-renderer-linkcard": "0.13.0",
269
+ "@haklex/rich-renderer-katex": "0.13.0",
270
+ "@haklex/rich-renderer-mention": "0.13.0",
271
+ "@haklex/rich-renderer-mermaid": "0.13.0",
272
+ "@haklex/rich-ext-gallery": "0.13.0",
273
+ "@haklex/rich-renderer-ruby": "0.13.0",
274
+ "@haklex/rich-ext-poll": "0.13.0",
275
+ "@haklex/rich-renderer-banner": "0.13.0",
276
+ "@haklex/rich-renderer-video": "0.13.0",
277
+ "@haklex/rich-style-token": "0.13.0"
279
278
  },
280
279
  "devDependencies": {
281
280
  "@lexical/react": "^0.44.0",
@@ -291,7 +290,7 @@
291
290
  "vite": "^8.0.10",
292
291
  "vite-plugin-dts": "^4.5.4",
293
292
  "vitest": "^4.1.5",
294
- "@haklex/rich-editor": "0.12.0"
293
+ "@haklex/rich-editor": "0.13.0"
295
294
  },
296
295
  "peerDependencies": {
297
296
  "@lexical/react": "^0.44.0",
@@ -299,13 +298,13 @@
299
298
  "lucide-react": "^1.0.0",
300
299
  "react": ">=19",
301
300
  "react-dom": ">=19",
302
- "@haklex/rich-editor": "0.12.0"
301
+ "@haklex/rich-editor": "0.13.0"
303
302
  },
304
303
  "publishConfig": {
305
304
  "access": "public"
306
305
  },
307
306
  "scripts": {
308
- "build": "vite build && vite build -c vite.cli.config.ts --mode node-cli && vite build -c vite.cli.config.ts --mode browser-preview && tsx scripts/build-css.ts",
307
+ "build": "vite build && vite build -c vite.preview-client.config.ts && tsx scripts/build-css.ts",
309
308
  "dev:build": "vite build --watch",
310
309
  "test": "vitest run"
311
310
  },
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=litexml-to-html.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"litexml-to-html.d.ts","sourceRoot":"","sources":["../../src/cli/litexml-to-html.ts"],"names":[],"mappings":""}
@@ -1,237 +0,0 @@
1
- #!/usr/bin/env node
2
- import { spawnSync } from "node:child_process";
3
- import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import path from "node:path";
6
- import { fileURLToPath } from "node:url";
7
- //#region src/cli/litexml-to-html.ts
8
- var HELP = `Usage:
9
- litexml-to-html <file.xml>
10
- litexml-to-html '<p>Hello</p>' -o article.html
11
- litexml-to-html - < input.xml > article.html
12
- litexml-to-html input.xml --open
13
-
14
- Options:
15
- -o, --output <file> Write HTML to a file instead of stdout.
16
- --open Open the generated HTML in the system browser.
17
- --theme <light|dark> Preview color scheme. Default: light.
18
- --variant <variant> Render variant: article, note, or comment. Default: article.
19
- --title <title> HTML document title.
20
- --lang <lang> HTML document language. Default: en.
21
- -h, --help Show this help message.
22
- `;
23
- process.stdout.on("error", (error) => {
24
- if (error.code === "EPIPE") process.exit(0);
25
- throw error;
26
- });
27
- function parseArgs(args) {
28
- const options = {
29
- lang: "en",
30
- open: false,
31
- theme: "light",
32
- variant: "article"
33
- };
34
- for (let i = 0; i < args.length; i += 1) {
35
- const arg = args[i];
36
- switch (arg) {
37
- case "--open":
38
- options.open = true;
39
- break;
40
- case "--theme": {
41
- const theme = args[i + 1];
42
- if (theme !== "light" && theme !== "dark") throw new Error("Expected --theme to be \"light\" or \"dark\".");
43
- options.theme = theme;
44
- i += 1;
45
- break;
46
- }
47
- case "--variant": {
48
- const variant = args[i + 1];
49
- if (variant !== "article" && variant !== "note" && variant !== "comment") throw new Error("Expected --variant to be \"article\", \"note\", or \"comment\".");
50
- options.variant = variant;
51
- i += 1;
52
- break;
53
- }
54
- case "--title": {
55
- const title = args[i + 1];
56
- if (!title) throw new Error("Missing value for --title.");
57
- options.title = title;
58
- i += 1;
59
- break;
60
- }
61
- case "--lang": {
62
- const lang = args[i + 1];
63
- if (!lang) throw new Error("Missing value for --lang.");
64
- options.lang = lang;
65
- i += 1;
66
- break;
67
- }
68
- case "-o":
69
- case "--output": {
70
- const output = args[i + 1];
71
- if (!output) throw new Error(`Missing value for ${arg}.`);
72
- options.output = output;
73
- i += 1;
74
- break;
75
- }
76
- case "-h":
77
- case "--help":
78
- process.stdout.write(HELP);
79
- process.exit(0);
80
- break;
81
- default:
82
- if (arg.startsWith("-") && arg !== "-") throw new Error(`Unknown option: ${arg}.`);
83
- if (options.input) throw new Error("Expected a single LiteXML input argument.");
84
- options.input = arg;
85
- }
86
- }
87
- return options;
88
- }
89
- function hasElementTag(value) {
90
- return /<[!/]?[a-z][\w:-]*(?:\s|>|\/>)/i.test(value);
91
- }
92
- function readInput(input) {
93
- if (!input || input === "-") return readFileSync(0, "utf8");
94
- if (existsSync(input)) return readFileSync(input, "utf8");
95
- return input;
96
- }
97
- function defaultTitle(input) {
98
- if (input && input !== "-" && existsSync(input)) return path.basename(input);
99
- return "Haklex LiteXML Preview";
100
- }
101
- function escapeHtml(value) {
102
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
103
- }
104
- function escapeJsonScript(value) {
105
- return JSON.stringify(value).replaceAll("<", "\\u003c");
106
- }
107
- function readStaticRenderCss() {
108
- const candidates = [
109
- fileURLToPath(new URL("./style.css", import.meta.url)),
110
- fileURLToPath(new URL("../../dist/style.css", import.meta.url)),
111
- path.resolve(process.cwd(), "packages/rich-compose/dist/style.css"),
112
- path.resolve(process.cwd(), "dist/style.css")
113
- ];
114
- for (const candidate of candidates) if (existsSync(candidate)) return readFileSync(candidate, "utf8");
115
- throw new Error("Cannot find rich-compose static render CSS. Run `pnpm --filter @haklex/rich-compose build` first.");
116
- }
117
- function readPreviewClientJs() {
118
- const candidates = [
119
- fileURLToPath(new URL("./litexml-html-preview-client.js", import.meta.url)),
120
- path.resolve(process.cwd(), "packages/rich-compose/dist/litexml-html-preview-client.js"),
121
- path.resolve(process.cwd(), "dist/litexml-html-preview-client.js")
122
- ];
123
- for (const candidate of candidates) if (existsSync(candidate)) return readFileSync(candidate, "utf8");
124
- throw new Error("Cannot find LiteXML HTML preview client bundle. Run `pnpm --filter @haklex/rich-compose build` first.");
125
- }
126
- function readPreviewClientCss() {
127
- return [
128
- fileURLToPath(new URL("./litexml-html-preview-client.css", import.meta.url)),
129
- path.resolve(process.cwd(), "packages/rich-compose/dist/litexml-html-preview-client.css"),
130
- path.resolve(process.cwd(), "dist/litexml-html-preview-client.css")
131
- ].filter(existsSync).map((candidate) => readFileSync(candidate, "utf8")).join("\n");
132
- }
133
- async function convertLiteXmlToHtml(xml, options) {
134
- const trimmed = xml.trim();
135
- if (!trimmed) throw new Error("LiteXML input is empty.");
136
- if (!hasElementTag(trimmed)) throw new Error("Input does not look like LiteXML. Pass a file path, stdin, or an XML string.");
137
- const staticCss = readStaticRenderCss();
138
- const previewClientCss = readPreviewClientCss();
139
- const previewClientJs = readPreviewClientJs();
140
- const title = escapeHtml(options.title ?? defaultTitle(options.input));
141
- const lang = escapeHtml(options.lang);
142
- const payload = escapeJsonScript({
143
- theme: options.theme,
144
- variant: options.variant,
145
- xml: trimmed
146
- });
147
- return `<!doctype html>
148
- <html lang="${lang}">
149
- <head>
150
- <meta charset="utf-8">
151
- <meta name="viewport" content="width=device-width, initial-scale=1">
152
- <meta name="color-scheme" content="${options.theme}">
153
- <title>${title}</title>
154
- <style>
155
- ${staticCss}
156
- ${previewClientCss}
157
-
158
- html {
159
- color-scheme: ${options.theme};
160
- max-width: 100%;
161
- overflow-x: clip;
162
- }
163
-
164
- body {
165
- margin: 0;
166
- background: ${options.theme === "dark" ? "#0f1115" : "#ffffff"};
167
- max-width: 100%;
168
- overflow-x: clip;
169
- }
170
-
171
- .haklex-html-preview {
172
- box-sizing: border-box;
173
- width: 100%;
174
- max-width: 880px;
175
- margin: 0 auto;
176
- overflow-x: clip;
177
- padding: 48px 24px 64px;
178
- }
179
-
180
- .haklex-html-preview .rich-content {
181
- min-width: 0;
182
- max-width: 100%;
183
- }
184
-
185
- @media (max-width: 640px) {
186
- .haklex-html-preview {
187
- padding: 28px 16px 48px;
188
- }
189
- }
190
- </style>
191
- </head>
192
- <body>
193
- <main class="haklex-html-preview" id="haklex-litexml-root"></main>
194
- <script id="haklex-litexml-payload" type="application/json">${payload}<\/script>
195
- <script>
196
- ${previewClientJs}
197
- <\/script>
198
- </body>
199
- </html>`;
200
- }
201
- function openHtmlFile(filePath) {
202
- const resolved = path.resolve(filePath);
203
- const result = process.platform === "darwin" ? spawnSync("open", [resolved], { stdio: "ignore" }) : process.platform === "win32" ? spawnSync("cmd", [
204
- "/c",
205
- "start",
206
- "",
207
- resolved
208
- ], { stdio: "ignore" }) : spawnSync("xdg-open", [resolved], { stdio: "ignore" });
209
- if (result.error) throw result.error;
210
- if (typeof result.status === "number" && result.status !== 0) throw new Error(`Failed to open ${resolved}.`);
211
- }
212
- async function main() {
213
- try {
214
- const options = parseArgs(process.argv.slice(2));
215
- const html = `${await convertLiteXmlToHtml(readInput(options.input), options)}\n`;
216
- if (options.output) {
217
- writeFileSync(options.output, html);
218
- if (options.open) openHtmlFile(options.output);
219
- return;
220
- }
221
- if (options.open) {
222
- const dir = mkdtempSync(path.join(tmpdir(), "haklex-litexml-"));
223
- const output = path.join(dir, "preview.html");
224
- writeFileSync(output, html);
225
- openHtmlFile(output);
226
- return;
227
- }
228
- process.stdout.write(html);
229
- } catch (error) {
230
- const message = error instanceof Error ? error.message : String(error);
231
- process.stderr.write(`${path.basename(process.argv[1])}: ${message}\n\n${HELP}`);
232
- process.exit(1);
233
- }
234
- }
235
- main();
236
- //#endregion
237
- export {};