@json-to-office/jto 0.3.6 → 0.5.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/README.md +39 -13
- package/dist/cli.js +80 -52
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/{HomePage-DoPLARHL.js → HomePage-DfwGX3I1.js} +47 -47
- package/dist/client/assets/HomePage-DfwGX3I1.js.map +1 -0
- package/dist/client/assets/{JsonEditorPage-uVaNPE8q.js → JsonEditorPage-Ba40pGIp.js} +3 -3
- package/dist/client/assets/{JsonEditorPage-uVaNPE8q.js.map → JsonEditorPage-Ba40pGIp.js.map} +1 -1
- package/dist/client/assets/{MonacoPluginProvider-BTaxJhgr.js → MonacoPluginProvider-y0S1WRcO.js} +3 -3
- package/dist/client/assets/{MonacoPluginProvider-BTaxJhgr.js.map → MonacoPluginProvider-y0S1WRcO.js.map} +1 -1
- package/dist/client/assets/{button-C9-hWwtZ.js → button-Cxhj_Vqc.js} +2 -2
- package/dist/client/assets/{button-C9-hWwtZ.js.map → button-Cxhj_Vqc.js.map} +1 -1
- package/dist/client/assets/{editor-WAlwAQUI.js → editor-C5jlPSdE.js} +2 -2
- package/dist/client/assets/{editor-WAlwAQUI.js.map → editor-C5jlPSdE.js.map} +1 -1
- package/dist/client/assets/{editor-monaco-json-BLhhoYVD.js → editor-monaco-json-DyDeK1fB.js} +2 -2
- package/dist/client/assets/{editor-monaco-json-BLhhoYVD.js.map → editor-monaco-json-DyDeK1fB.js.map} +1 -1
- package/dist/client/assets/{index-Bu7pVmbf.js → index-B2Ju-X_M.js} +3 -3
- package/dist/client/assets/{index-Bu7pVmbf.js.map → index-B2Ju-X_M.js.map} +1 -1
- package/dist/client/assets/{preview-Dpw5ePME.js → preview-sfoszPPX.js} +2 -2
- package/dist/client/assets/{preview-Dpw5ePME.js.map → preview-sfoszPPX.js.map} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/templates/Lumina Analytics.pptx.json +1913 -0
- package/dist/client/templates/themes/lumina.pptx.theme.json +42 -0
- package/dist/client/templates/themes/meridian.pptx.theme.json +42 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/prompts/instructions-edit-document-pptx-slides.md +23 -2
- package/dist/prompts/instructions-edit-document-pptx-templates.md +16 -1
- package/dist/prompts/instructions-edit-document-pptx.md +59 -5
- package/dist/prompts/instructions-generate-pptx.md +3 -1
- package/package.json +3 -3
- package/dist/client/assets/HomePage-DoPLARHL.js.map +0 -1
- package/dist/client/templates/Charts Demo.pptx.json +0 -174
- package/dist/client/templates/Company Branding.pptx.json +0 -143
- package/dist/client/templates/Dashboard.pptx.json +0 -91
- package/dist/client/templates/Product Launch.pptx.json +0 -87
- package/dist/client/templates/Sales Deck.pptx.json +0 -80
package/README.md
CHANGED
|
@@ -1,38 +1,64 @@
|
|
|
1
1
|
# @json-to-office/jto
|
|
2
2
|
|
|
3
|
-
CLI and visual playground for [json-to-office](https://github.com/Wiseair-srl/json-to-office).
|
|
3
|
+
CLI and visual playground for [json-to-office](https://github.com/Wiseair-srl/json-to-office). Describe `.docx` and `.pptx` files as JSON, preview them live, generate them from the command line.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@json-to-office/jto)
|
|
6
6
|
[](https://github.com/Wiseair-srl/json-to-office/blob/main/LICENSE)
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Quick start
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
npm install -g @json-to-office/jto
|
|
12
|
+
|
|
13
|
+
# Open the visual playground with live preview
|
|
14
|
+
jto docx dev
|
|
15
|
+
jto pptx dev
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
## CLI commands
|
|
21
|
+
|
|
22
|
+
### `dev`: visual playground
|
|
15
23
|
|
|
16
24
|
```bash
|
|
17
|
-
# Start the dev server with visual playground
|
|
18
25
|
jto docx dev --input ./template.json
|
|
19
26
|
jto pptx dev --input ./template.json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Opens a browser-based IDE at `localhost:3000` with:
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
| Feature | Description |
|
|
32
|
+
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
33
|
+
| Monaco editor | JSON editing with autocomplete, inline validation, syntax highlighting |
|
|
34
|
+
| Live preview | Rendered document updates as you type |
|
|
35
|
+
| Built-in templates | Start from example documents (dashboards, sales decks, reports) |
|
|
36
|
+
| Theme switching | Toggle between built-in themes or load your own |
|
|
37
|
+
| Schema validation | Real-time error reporting against TypeBox schemas |
|
|
38
|
+
| AI assistant | Describe a document in plain English, get schema-validated JSON back (requires Claude API key) |
|
|
39
|
+
| LibreOffice preview | **Optional.** If LibreOffice (headless) is installed, enables high-fidelity PDF rendering for pixel-accurate output. Not required for live previews. |
|
|
40
|
+
|
|
41
|
+
### `generate`: render files
|
|
42
|
+
|
|
43
|
+
```bash
|
|
22
44
|
jto docx generate --input ./template.json --output ./report.docx
|
|
23
45
|
jto pptx generate --input ./template.json --output ./deck.pptx
|
|
24
46
|
```
|
|
25
47
|
|
|
26
|
-
|
|
48
|
+
Reads a JSON document definition and writes a `.docx` or `.pptx` file. Works in CI/CD pipelines, cron jobs, and scripts.
|
|
49
|
+
|
|
50
|
+
## Part of json-to-office
|
|
51
|
+
|
|
52
|
+
`jto` is the CLI companion to the programmatic libraries:
|
|
27
53
|
|
|
28
|
-
|
|
54
|
+
| Package | Use case |
|
|
55
|
+
| -------------------------------------------------------------------------------------------- | -------------------------- |
|
|
56
|
+
| [`@json-to-office/json-to-docx`](https://www.npmjs.com/package/@json-to-office/json-to-docx) | Generate `.docx` from code |
|
|
57
|
+
| [`@json-to-office/json-to-pptx`](https://www.npmjs.com/package/@json-to-office/json-to-pptx) | Generate `.pptx` from code |
|
|
58
|
+
| **`@json-to-office/jto`** | CLI + visual playground |
|
|
29
59
|
|
|
30
|
-
|
|
31
|
-
- **Live preview** — See rendered document output update as you type
|
|
32
|
-
- **Built-in templates** — Start from example documents (dashboards, sales decks, reports)
|
|
33
|
-
- **Theme switching** — Toggle between built-in themes or load your own
|
|
34
|
-
- **Schema validation** — Real-time error reporting against TypeBox schemas
|
|
60
|
+
See the [monorepo](https://github.com/Wiseair-srl/json-to-office) for full docs, examples, and the schema reference.
|
|
35
61
|
|
|
36
62
|
## License
|
|
37
63
|
|
|
38
|
-
[MIT](https://github.com/Wiseair-srl/json-to-office/blob/main/LICENSE)
|
|
64
|
+
[MIT](https://github.com/Wiseair-srl/json-to-office/blob/main/LICENSE), Wiseair srl
|
package/dist/cli.js
CHANGED
|
@@ -1375,16 +1375,25 @@ import os from "os";
|
|
|
1375
1375
|
import path13 from "path";
|
|
1376
1376
|
function executeFile(binary, args, timeoutMs) {
|
|
1377
1377
|
return new Promise((resolve16, reject) => {
|
|
1378
|
-
execFile(
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1378
|
+
execFile(
|
|
1379
|
+
binary,
|
|
1380
|
+
args,
|
|
1381
|
+
{
|
|
1382
|
+
timeout: timeoutMs,
|
|
1383
|
+
maxBuffer: MAX_EXEC_BUFFER_BYTES,
|
|
1384
|
+
windowsHide: true
|
|
1385
|
+
},
|
|
1386
|
+
(error, stdout, stderr) => {
|
|
1387
|
+
if (error) {
|
|
1388
|
+
const execError = error;
|
|
1389
|
+
execError.stdout = stdout;
|
|
1390
|
+
execError.stderr = stderr;
|
|
1391
|
+
reject(execError);
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
resolve16({ stdout: stdout ?? "", stderr: stderr ?? "" });
|
|
1385
1395
|
}
|
|
1386
|
-
|
|
1387
|
-
});
|
|
1396
|
+
);
|
|
1388
1397
|
});
|
|
1389
1398
|
}
|
|
1390
1399
|
function toErrorText(value) {
|
|
@@ -1417,7 +1426,10 @@ var init_libreoffice_converter = __esm({
|
|
|
1417
1426
|
LibreOfficeBinaryNotFoundError = class extends LibreOfficeError {
|
|
1418
1427
|
candidates;
|
|
1419
1428
|
constructor(candidates) {
|
|
1420
|
-
super(
|
|
1429
|
+
super(
|
|
1430
|
+
"BINARY_NOT_FOUND",
|
|
1431
|
+
"LibreOffice binary not found. Install LibreOffice locally or set LIBREOFFICE_PATH."
|
|
1432
|
+
);
|
|
1421
1433
|
this.name = "LibreOfficeBinaryNotFoundError";
|
|
1422
1434
|
this.candidates = candidates;
|
|
1423
1435
|
}
|
|
@@ -1425,7 +1437,10 @@ var init_libreoffice_converter = __esm({
|
|
|
1425
1437
|
LibreOfficeTimeoutError = class extends LibreOfficeError {
|
|
1426
1438
|
timeoutMs;
|
|
1427
1439
|
constructor(timeoutMs) {
|
|
1428
|
-
super(
|
|
1440
|
+
super(
|
|
1441
|
+
"CONVERSION_TIMEOUT",
|
|
1442
|
+
`LibreOffice conversion timed out after ${timeoutMs}ms`
|
|
1443
|
+
);
|
|
1429
1444
|
this.name = "LibreOfficeTimeoutError";
|
|
1430
1445
|
this.timeoutMs = timeoutMs;
|
|
1431
1446
|
}
|
|
@@ -1441,7 +1456,10 @@ var init_libreoffice_converter = __esm({
|
|
|
1441
1456
|
LibreOfficeOutputNotFoundError = class extends LibreOfficeError {
|
|
1442
1457
|
outputPath;
|
|
1443
1458
|
constructor(outputPath) {
|
|
1444
|
-
super(
|
|
1459
|
+
super(
|
|
1460
|
+
"OUTPUT_NOT_FOUND",
|
|
1461
|
+
"LibreOffice conversion completed but PDF output was not produced"
|
|
1462
|
+
);
|
|
1445
1463
|
this.name = "LibreOfficeOutputNotFoundError";
|
|
1446
1464
|
this.outputPath = outputPath;
|
|
1447
1465
|
}
|
|
@@ -1458,7 +1476,9 @@ var init_libreoffice_converter = __esm({
|
|
|
1458
1476
|
throw new LibreOfficeConversionError("Input file is empty");
|
|
1459
1477
|
}
|
|
1460
1478
|
const binaryPath = await this.resolveBinaryPath();
|
|
1461
|
-
const tempDir = await fs10.mkdtemp(
|
|
1479
|
+
const tempDir = await fs10.mkdtemp(
|
|
1480
|
+
path13.join(os.tmpdir(), "jto-libreoffice-")
|
|
1481
|
+
);
|
|
1462
1482
|
const outputBaseName = sanitizeBaseName(originalName);
|
|
1463
1483
|
const ext = this.format === "pptx" ? ".pptx" : ".docx";
|
|
1464
1484
|
const inputPath = path13.join(tempDir, `${outputBaseName}${ext}`);
|
|
@@ -1486,6 +1506,11 @@ var init_libreoffice_converter = __esm({
|
|
|
1486
1506
|
if (configured) candidates.push(configured);
|
|
1487
1507
|
if (process.platform === "darwin") {
|
|
1488
1508
|
candidates.push("/Applications/LibreOffice.app/Contents/MacOS/soffice");
|
|
1509
|
+
} else if (process.platform === "win32") {
|
|
1510
|
+
candidates.push("C:\\Program Files\\LibreOffice\\program\\soffice.exe");
|
|
1511
|
+
candidates.push(
|
|
1512
|
+
"C:\\Program Files (x86)\\LibreOffice\\program\\soffice.exe"
|
|
1513
|
+
);
|
|
1489
1514
|
}
|
|
1490
1515
|
candidates.push("soffice", "libreoffice");
|
|
1491
1516
|
return [...new Set(candidates)];
|
|
@@ -1532,10 +1557,18 @@ var init_libreoffice_converter = __esm({
|
|
|
1532
1557
|
await executeFile(binaryPath, args, this.timeoutMs);
|
|
1533
1558
|
} catch (error) {
|
|
1534
1559
|
const execError = error;
|
|
1535
|
-
if (execError.code === "ETIMEDOUT")
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1560
|
+
if (execError.code === "ETIMEDOUT")
|
|
1561
|
+
throw new LibreOfficeTimeoutError(this.timeoutMs);
|
|
1562
|
+
if (execError.code === "ENOENT")
|
|
1563
|
+
throw new LibreOfficeBinaryNotFoundError(this.getBinaryCandidates());
|
|
1564
|
+
const details = [
|
|
1565
|
+
toErrorText(execError.stderr),
|
|
1566
|
+
toErrorText(execError.stdout)
|
|
1567
|
+
].filter(Boolean).join("\n").trim();
|
|
1568
|
+
throw new LibreOfficeConversionError(
|
|
1569
|
+
"LibreOffice failed to convert to PDF",
|
|
1570
|
+
details || execError.message
|
|
1571
|
+
);
|
|
1539
1572
|
}
|
|
1540
1573
|
}
|
|
1541
1574
|
};
|
|
@@ -3063,7 +3096,9 @@ function createAPIApp(adapter) {
|
|
|
3063
3096
|
const legacyPath = adapter.name === "docx" ? "/api/documents" : "/api/presentations";
|
|
3064
3097
|
honoApp.route(legacyPath, formatRouter);
|
|
3065
3098
|
honoApp.route("/api/discovery", discoveryRouter);
|
|
3066
|
-
|
|
3099
|
+
if (process.env.AI_ENABLED !== "false") {
|
|
3100
|
+
honoApp.route("/api/ai", createAiRouter());
|
|
3101
|
+
}
|
|
3067
3102
|
honoApp.get("/", async (c) => {
|
|
3068
3103
|
const apiInfo = await getApiInfo();
|
|
3069
3104
|
return c.json(apiInfo);
|
|
@@ -3199,13 +3234,17 @@ var init_unified_server = __esm({
|
|
|
3199
3234
|
const envClientPath = process.env.JTO_CLIENT_PATH;
|
|
3200
3235
|
if (existsSync7(envClientPath) && existsSync7(resolve15(envClientPath, "index.html"))) {
|
|
3201
3236
|
clientPath = envClientPath;
|
|
3202
|
-
logger.debug("[Dev Server] Using client from JTO_CLIENT_PATH", {
|
|
3237
|
+
logger.debug("[Dev Server] Using client from JTO_CLIENT_PATH", {
|
|
3238
|
+
path: clientPath
|
|
3239
|
+
});
|
|
3203
3240
|
}
|
|
3204
3241
|
} else if (isBundled) {
|
|
3205
3242
|
const bundledClientPath = resolve15(__dirname3, "client");
|
|
3206
3243
|
if (existsSync7(bundledClientPath) && existsSync7(resolve15(bundledClientPath, "index.html"))) {
|
|
3207
3244
|
clientPath = bundledClientPath;
|
|
3208
|
-
logger.debug("[Dev Server] Using bundled client at", {
|
|
3245
|
+
logger.debug("[Dev Server] Using bundled client at", {
|
|
3246
|
+
path: clientPath
|
|
3247
|
+
});
|
|
3209
3248
|
}
|
|
3210
3249
|
} else {
|
|
3211
3250
|
const possiblePaths = [
|
|
@@ -3215,7 +3254,9 @@ var init_unified_server = __esm({
|
|
|
3215
3254
|
for (const p of possiblePaths) {
|
|
3216
3255
|
if (existsSync7(p)) {
|
|
3217
3256
|
if (p.replace(/\\/g, "/").includes("/src/client")) {
|
|
3218
|
-
logger.debug("[Dev Server] Using Vite dev server for", {
|
|
3257
|
+
logger.debug("[Dev Server] Using Vite dev server for", {
|
|
3258
|
+
path: p
|
|
3259
|
+
});
|
|
3219
3260
|
try {
|
|
3220
3261
|
const { createServer: createViteServer } = await import("vite");
|
|
3221
3262
|
this.viteServer = await createViteServer({
|
|
@@ -3232,7 +3273,9 @@ var init_unified_server = __esm({
|
|
|
3232
3273
|
break;
|
|
3233
3274
|
} else if (existsSync7(resolve15(p, "index.html"))) {
|
|
3234
3275
|
clientPath = p;
|
|
3235
|
-
logger.debug("[Dev Server] Using pre-built client at", {
|
|
3276
|
+
logger.debug("[Dev Server] Using pre-built client at", {
|
|
3277
|
+
path: clientPath
|
|
3278
|
+
});
|
|
3236
3279
|
break;
|
|
3237
3280
|
}
|
|
3238
3281
|
}
|
|
@@ -3283,10 +3326,13 @@ var init_unified_server = __esm({
|
|
|
3283
3326
|
const { extname: extname2 } = await import("path");
|
|
3284
3327
|
const mime = await import("mime-types");
|
|
3285
3328
|
const format = this.adapter.name.replace(/[^a-zA-Z0-9]/g, "");
|
|
3286
|
-
this.app.use(
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3329
|
+
this.app.use(
|
|
3330
|
+
"/assets/*",
|
|
3331
|
+
serveStatic({
|
|
3332
|
+
root: clientPath,
|
|
3333
|
+
rewriteRequestPath: (path14) => path14.replace(/^\/assets/, "/assets")
|
|
3334
|
+
})
|
|
3335
|
+
);
|
|
3290
3336
|
this.app.use("/*", async (c, next) => {
|
|
3291
3337
|
const reqPath = c.req.path;
|
|
3292
3338
|
if (reqPath.startsWith("/api") || reqPath === "/health") return next();
|
|
@@ -3304,7 +3350,8 @@ var init_unified_server = __esm({
|
|
|
3304
3350
|
});
|
|
3305
3351
|
this.app.get("*", async (c) => {
|
|
3306
3352
|
const reqPath = c.req.path;
|
|
3307
|
-
if (reqPath.startsWith("/api") || reqPath === "/health")
|
|
3353
|
+
if (reqPath.startsWith("/api") || reqPath === "/health")
|
|
3354
|
+
return c.notFound();
|
|
3308
3355
|
const indexPath = resolve15(clientPath, "index.html");
|
|
3309
3356
|
if (fs11.existsSync(indexPath)) {
|
|
3310
3357
|
let html = fs11.readFileSync(indexPath, "utf-8");
|
|
@@ -3319,30 +3366,8 @@ var init_unified_server = __esm({
|
|
|
3319
3366
|
});
|
|
3320
3367
|
}
|
|
3321
3368
|
async setupProdClient() {
|
|
3322
|
-
const
|
|
3323
|
-
|
|
3324
|
-
this.app.use("/*", async (c, next) => {
|
|
3325
|
-
const path14 = c.req.path;
|
|
3326
|
-
if (path14.startsWith("/api") || path14 === "/health") return next();
|
|
3327
|
-
return serveStatic({ root: "./dist/client" })(c, next);
|
|
3328
|
-
});
|
|
3329
|
-
this.app.get("*", (c) => {
|
|
3330
|
-
const path14 = c.req.path;
|
|
3331
|
-
if (path14.startsWith("/api") || path14 === "/health") return c.notFound();
|
|
3332
|
-
return c.html(`<!DOCTYPE html>
|
|
3333
|
-
<html>
|
|
3334
|
-
<head>
|
|
3335
|
-
<meta charset="UTF-8">
|
|
3336
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3337
|
-
<title>JSON to Office</title>
|
|
3338
|
-
<script>window.__JTO_FORMAT__ = '${format}';</script>
|
|
3339
|
-
<script>window.location.href = '/';</script>
|
|
3340
|
-
</head>
|
|
3341
|
-
<body>
|
|
3342
|
-
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
3343
|
-
</body>
|
|
3344
|
-
</html>`);
|
|
3345
|
-
});
|
|
3369
|
+
const clientPath = resolve15(__dirname3, "client");
|
|
3370
|
+
return this.setupBuiltClient(clientPath);
|
|
3346
3371
|
}
|
|
3347
3372
|
async start() {
|
|
3348
3373
|
await this.initialize();
|
|
@@ -5715,6 +5740,9 @@ async function loadConfig(configPath) {
|
|
|
5715
5740
|
}
|
|
5716
5741
|
}
|
|
5717
5742
|
const config2 = deepMerge(defaultConfig, userConfig);
|
|
5743
|
+
if (process.env.NODE_ENV === "production") {
|
|
5744
|
+
config2.mode = "production";
|
|
5745
|
+
}
|
|
5718
5746
|
if (!Value.Check(ConfigSchema, config2)) {
|
|
5719
5747
|
const errors = [...Value.Errors(ConfigSchema, config2)];
|
|
5720
5748
|
console.warn("Warning: Invalid configuration detected:", errors);
|
|
@@ -5826,7 +5854,7 @@ ${chalk8.cyan("Health:")} ${url}/health
|
|
|
5826
5854
|
}
|
|
5827
5855
|
|
|
5828
5856
|
// src/cli.ts
|
|
5829
|
-
var PACKAGE_VERSION = true ? "0.
|
|
5857
|
+
var PACKAGE_VERSION = true ? "0.5.0" : "dev-mode";
|
|
5830
5858
|
function registerFormatCommands(parent, adapter) {
|
|
5831
5859
|
parent.addCommand(createGenerateCommand(adapter));
|
|
5832
5860
|
parent.addCommand(createValidateCommand(adapter));
|