@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.
Files changed (36) hide show
  1. package/README.md +39 -13
  2. package/dist/cli.js +80 -52
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client/assets/{HomePage-DoPLARHL.js → HomePage-DfwGX3I1.js} +47 -47
  5. package/dist/client/assets/HomePage-DfwGX3I1.js.map +1 -0
  6. package/dist/client/assets/{JsonEditorPage-uVaNPE8q.js → JsonEditorPage-Ba40pGIp.js} +3 -3
  7. package/dist/client/assets/{JsonEditorPage-uVaNPE8q.js.map → JsonEditorPage-Ba40pGIp.js.map} +1 -1
  8. package/dist/client/assets/{MonacoPluginProvider-BTaxJhgr.js → MonacoPluginProvider-y0S1WRcO.js} +3 -3
  9. package/dist/client/assets/{MonacoPluginProvider-BTaxJhgr.js.map → MonacoPluginProvider-y0S1WRcO.js.map} +1 -1
  10. package/dist/client/assets/{button-C9-hWwtZ.js → button-Cxhj_Vqc.js} +2 -2
  11. package/dist/client/assets/{button-C9-hWwtZ.js.map → button-Cxhj_Vqc.js.map} +1 -1
  12. package/dist/client/assets/{editor-WAlwAQUI.js → editor-C5jlPSdE.js} +2 -2
  13. package/dist/client/assets/{editor-WAlwAQUI.js.map → editor-C5jlPSdE.js.map} +1 -1
  14. package/dist/client/assets/{editor-monaco-json-BLhhoYVD.js → editor-monaco-json-DyDeK1fB.js} +2 -2
  15. package/dist/client/assets/{editor-monaco-json-BLhhoYVD.js.map → editor-monaco-json-DyDeK1fB.js.map} +1 -1
  16. package/dist/client/assets/{index-Bu7pVmbf.js → index-B2Ju-X_M.js} +3 -3
  17. package/dist/client/assets/{index-Bu7pVmbf.js.map → index-B2Ju-X_M.js.map} +1 -1
  18. package/dist/client/assets/{preview-Dpw5ePME.js → preview-sfoszPPX.js} +2 -2
  19. package/dist/client/assets/{preview-Dpw5ePME.js.map → preview-sfoszPPX.js.map} +1 -1
  20. package/dist/client/index.html +1 -1
  21. package/dist/client/templates/Lumina Analytics.pptx.json +1913 -0
  22. package/dist/client/templates/themes/lumina.pptx.theme.json +42 -0
  23. package/dist/client/templates/themes/meridian.pptx.theme.json +42 -0
  24. package/dist/index.js +3 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/prompts/instructions-edit-document-pptx-slides.md +23 -2
  27. package/dist/prompts/instructions-edit-document-pptx-templates.md +16 -1
  28. package/dist/prompts/instructions-edit-document-pptx.md +59 -5
  29. package/dist/prompts/instructions-generate-pptx.md +3 -1
  30. package/package.json +3 -3
  31. package/dist/client/assets/HomePage-DoPLARHL.js.map +0 -1
  32. package/dist/client/templates/Charts Demo.pptx.json +0 -174
  33. package/dist/client/templates/Company Branding.pptx.json +0 -143
  34. package/dist/client/templates/Dashboard.pptx.json +0 -91
  35. package/dist/client/templates/Product Launch.pptx.json +0 -87
  36. 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). Edit document definitions in a Monaco editor with autocomplete and validation, preview rendered output live, and generate `.docx` / `.pptx` files from the command line.
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
  [![npm](https://img.shields.io/npm/v/@json-to-office/jto.svg)](https://www.npmjs.com/package/@json-to-office/jto)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/Wiseair-srl/json-to-office/blob/main/LICENSE)
7
7
 
8
- ## Install
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
- ## Usage
18
+ ![Visual Playground](https://raw.githubusercontent.com/Wiseair-srl/json-to-office/main/docs/playground-screenshot.png)
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
- # Generate files directly
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
- ## Visual playground
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
- The dev server opens a browser-based IDE with:
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
- - **Monaco editor** Full JSON editing with autocomplete, inline validation, and syntax highlighting
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) Wiseair srl
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(binary, args, { timeout: timeoutMs, maxBuffer: MAX_EXEC_BUFFER_BYTES, windowsHide: true }, (error, stdout, stderr) => {
1379
- if (error) {
1380
- const execError = error;
1381
- execError.stdout = stdout;
1382
- execError.stderr = stderr;
1383
- reject(execError);
1384
- return;
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
- resolve16({ stdout: stdout ?? "", stderr: stderr ?? "" });
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("BINARY_NOT_FOUND", "LibreOffice binary not found. Install LibreOffice locally or set LIBREOFFICE_PATH.");
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("CONVERSION_TIMEOUT", `LibreOffice conversion timed out after ${timeoutMs}ms`);
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("OUTPUT_NOT_FOUND", "LibreOffice conversion completed but PDF output was not produced");
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(path13.join(os.tmpdir(), "jto-libreoffice-"));
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") throw new LibreOfficeTimeoutError(this.timeoutMs);
1536
- if (execError.code === "ENOENT") throw new LibreOfficeBinaryNotFoundError(this.getBinaryCandidates());
1537
- const details = [toErrorText(execError.stderr), toErrorText(execError.stdout)].filter(Boolean).join("\n").trim();
1538
- throw new LibreOfficeConversionError("LibreOffice failed to convert to PDF", details || execError.message);
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
- honoApp.route("/api/ai", createAiRouter());
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", { path: clientPath });
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", { path: clientPath });
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", { path: p });
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", { path: clientPath });
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("/assets/*", serveStatic({
3287
- root: clientPath,
3288
- rewriteRequestPath: (path14) => path14.replace(/^\/assets/, "/assets")
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") return c.notFound();
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 { serveStatic } = await import("@hono/node-server/serve-static");
3323
- const format = this.adapter.name.replace(/[^a-zA-Z0-9]/g, "");
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.3.6" : "dev-mode";
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));