@deckflow/deckuse 1.1.0 → 1.2.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/dist/.tsbuildinfo +1 -1
- package/dist/bin.js +364 -81
- package/dist/bin.js.map +1 -1
- package/dist/cli-text.d.ts +12 -0
- package/dist/cli-text.d.ts.map +1 -0
- package/dist/cli-text.js +49 -0
- package/dist/cli-text.js.map +1 -0
- package/dist/edition.d.ts +3 -10
- package/dist/edition.d.ts.map +1 -1
- package/dist/edition.js +1 -8
- package/dist/edition.js.map +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +164 -50
- package/dist/help.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/monitor-daemon.d.ts +38 -0
- package/dist/monitor-daemon.d.ts.map +1 -0
- package/dist/monitor-daemon.js +182 -0
- package/dist/monitor-daemon.js.map +1 -0
- package/dist/monitor.d.ts.map +1 -1
- package/dist/monitor.js +11 -1
- package/dist/monitor.js.map +1 -1
- package/dist/office2html-exec.d.ts +3 -0
- package/dist/office2html-exec.d.ts.map +1 -0
- package/dist/office2html-exec.js +22 -0
- package/dist/office2html-exec.js.map +1 -0
- package/dist/render.d.ts +4 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +14 -4
- package/dist/render.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -9
package/dist/bin.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { access, readFile } from 'node:fs/promises';
|
|
3
3
|
import { dirname, extname, join, resolve } from 'node:path';
|
|
4
|
-
import { PROTOCOL_VERSION, revisionAsNumber, } from '@deckflow/deckuse-core';
|
|
4
|
+
import { PROTOCOL_VERSION, commandJsonSchema, measureText, parseLength, revisionAsNumber, } from '@deckflow/deckuse-core';
|
|
5
|
+
import { resolveCliText } from './cli-text.js';
|
|
5
6
|
import { helpTopicFromArgs, resolveHelp } from './help.js';
|
|
6
7
|
import { runCommand } from './index.js';
|
|
7
|
-
import {
|
|
8
|
+
import { monitorStart, monitorStatus, monitorStop, runMonitorForeground, } from './monitor-daemon.js';
|
|
8
9
|
import { renderPage } from './render.js';
|
|
9
10
|
import { EDITION } from './edition.js';
|
|
10
11
|
import { version } from './version.js';
|
|
@@ -84,6 +85,11 @@ const toEnvelope = (commandLabel, result, extras = {}) => {
|
|
|
84
85
|
message: result.error.message,
|
|
85
86
|
...(result.error.target ? { target: result.error.target } : {}),
|
|
86
87
|
...(result.error.hint ? { hint: result.error.hint } : {}),
|
|
88
|
+
...(result.error.diagnostics?.length
|
|
89
|
+
? { diagnostics: result.error.diagnostics }
|
|
90
|
+
: result.diagnostics.length
|
|
91
|
+
? { diagnostics: result.diagnostics }
|
|
92
|
+
: {}),
|
|
87
93
|
},
|
|
88
94
|
...extras,
|
|
89
95
|
};
|
|
@@ -124,6 +130,13 @@ const outputEnvelope = (envelope) => {
|
|
|
124
130
|
process.stderr.write(`deckuse: ${envelope.error?.code ?? 'ERROR'}: ${envelope.error?.message ?? 'failed'}\n`);
|
|
125
131
|
if (envelope.error?.hint)
|
|
126
132
|
process.stderr.write(`hint: ${envelope.error.hint}\n`);
|
|
133
|
+
const diagnostics = envelope.error?.diagnostics;
|
|
134
|
+
if (Array.isArray(diagnostics)) {
|
|
135
|
+
for (const d of diagnostics) {
|
|
136
|
+
const path = Array.isArray(d.path) && d.path.length > 0 ? `${d.path.map(String).join('.')}: ` : '';
|
|
137
|
+
process.stderr.write(` - ${path}${d.message}\n`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
127
140
|
return;
|
|
128
141
|
}
|
|
129
142
|
process.stdout.write(`${JSON.stringify(envelope, null, 2)}\n`);
|
|
@@ -199,6 +212,63 @@ const optionFrom = (list, name) => {
|
|
|
199
212
|
const i = list.indexOf(name);
|
|
200
213
|
return i >= 0 ? list[i + 1] : undefined;
|
|
201
214
|
};
|
|
215
|
+
/** Pass through bare numbers as number; keep unit strings for protocol parseLength. */
|
|
216
|
+
const lengthArg = (raw) => {
|
|
217
|
+
if (raw === undefined)
|
|
218
|
+
return undefined;
|
|
219
|
+
if (raw === 'auto')
|
|
220
|
+
return 'auto';
|
|
221
|
+
if (/^-?\d+(\.\d+)?$/.test(raw))
|
|
222
|
+
return Number(raw);
|
|
223
|
+
return raw;
|
|
224
|
+
};
|
|
225
|
+
const APPLY_WRITE_TYPES = new Set([
|
|
226
|
+
'setText',
|
|
227
|
+
'replaceText',
|
|
228
|
+
'setTransform',
|
|
229
|
+
'setProperties',
|
|
230
|
+
'set',
|
|
231
|
+
'xfrmSet',
|
|
232
|
+
'zMove',
|
|
233
|
+
'add',
|
|
234
|
+
'addSlide',
|
|
235
|
+
'addShape',
|
|
236
|
+
'remove',
|
|
237
|
+
'replacePicture',
|
|
238
|
+
'duplicate',
|
|
239
|
+
'alignElements',
|
|
240
|
+
'batch',
|
|
241
|
+
]);
|
|
242
|
+
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
243
|
+
const isTransactionOp = (value) => isRecord(value) && 'op' in value;
|
|
244
|
+
const isHighLevelWrite = (value) => isRecord(value) && APPLY_WRITE_TYPES.has(String(value['type']));
|
|
245
|
+
/** Classify apply payload items: high-level batch vs low-level transaction. */
|
|
246
|
+
const classifyApplyOps = (ops) => {
|
|
247
|
+
if (ops.length === 0)
|
|
248
|
+
return { kind: 'invalid', message: 'apply input operations array is empty' };
|
|
249
|
+
if (ops.every(isTransactionOp))
|
|
250
|
+
return { kind: 'transaction' };
|
|
251
|
+
if (ops.every(isHighLevelWrite))
|
|
252
|
+
return { kind: 'batch' };
|
|
253
|
+
if (ops.some(isHighLevelWrite) && ops.some(isTransactionOp)) {
|
|
254
|
+
return {
|
|
255
|
+
kind: 'invalid',
|
|
256
|
+
message: 'apply input mixes high-level write commands (type) with transaction ops (op); use one format',
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
if (ops.some(isHighLevelWrite))
|
|
260
|
+
return { kind: 'batch' };
|
|
261
|
+
if (ops.some(isTransactionOp)) {
|
|
262
|
+
return {
|
|
263
|
+
kind: 'invalid',
|
|
264
|
+
message: 'apply transaction operations must all include "op"; high-level writes must use "type"',
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
kind: 'invalid',
|
|
269
|
+
message: 'apply input must be high-level write commands ({ "type": "addShape"|... }) or transaction ops ({ "op": ... })',
|
|
270
|
+
};
|
|
271
|
+
};
|
|
202
272
|
const main = async () => {
|
|
203
273
|
try {
|
|
204
274
|
if (clean.length === 0) {
|
|
@@ -219,6 +289,89 @@ const main = async () => {
|
|
|
219
289
|
}
|
|
220
290
|
const action = clean[0];
|
|
221
291
|
let ok = true;
|
|
292
|
+
if (action === 'schema') {
|
|
293
|
+
const typeOpt = optionFrom(clean, '--type') ??
|
|
294
|
+
(clean[1] && !clean[1].startsWith('--') ? clean[1] : undefined);
|
|
295
|
+
const full = commandJsonSchema;
|
|
296
|
+
const defs = full.$defs ?? {};
|
|
297
|
+
const resolveRef = (entry) => {
|
|
298
|
+
if (!entry || typeof entry !== 'object')
|
|
299
|
+
return undefined;
|
|
300
|
+
const record = entry;
|
|
301
|
+
if (typeof record['$ref'] === 'string') {
|
|
302
|
+
const ref = record['$ref'];
|
|
303
|
+
const name = ref.replace(/^#\/\$defs\//, '');
|
|
304
|
+
const resolved = defs[name];
|
|
305
|
+
return resolved && typeof resolved === 'object'
|
|
306
|
+
? resolved
|
|
307
|
+
: undefined;
|
|
308
|
+
}
|
|
309
|
+
return record;
|
|
310
|
+
};
|
|
311
|
+
const typeOf = (entry) => {
|
|
312
|
+
const resolved = resolveRef(entry);
|
|
313
|
+
const typeProp = resolved?.['properties']
|
|
314
|
+
? resolved['properties']['type']
|
|
315
|
+
: undefined;
|
|
316
|
+
if (!typeProp || typeof typeProp !== 'object')
|
|
317
|
+
return undefined;
|
|
318
|
+
const typeRecord = typeProp;
|
|
319
|
+
if (typeof typeRecord['const'] === 'string')
|
|
320
|
+
return typeRecord['const'];
|
|
321
|
+
const nested = resolveRef(typeProp);
|
|
322
|
+
return typeof nested?.['const'] === 'string' ? nested['const'] : undefined;
|
|
323
|
+
};
|
|
324
|
+
let schema = full;
|
|
325
|
+
if (typeOpt) {
|
|
326
|
+
const variants = full.oneOf ?? full.anyOf ?? [];
|
|
327
|
+
const match = variants.find((entry) => typeOf(entry) === typeOpt);
|
|
328
|
+
if (!match) {
|
|
329
|
+
const known = variants.map(typeOf).filter(Boolean).join(', ');
|
|
330
|
+
throw new Error(`Unknown command type for schema: ${typeOpt}. Known: ${known || '(none)'}`);
|
|
331
|
+
}
|
|
332
|
+
schema = { ...(resolveRef(match) ?? match), $defs: defs };
|
|
333
|
+
}
|
|
334
|
+
const payload = {
|
|
335
|
+
ok: true,
|
|
336
|
+
command: 'deckuse schema',
|
|
337
|
+
data: {
|
|
338
|
+
cliVersion: version,
|
|
339
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
340
|
+
edition: EDITION,
|
|
341
|
+
...(typeOpt ? { type: typeOpt } : {}),
|
|
342
|
+
schema,
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
process.stdout.write(`${JSON.stringify(payload, null, json ? undefined : 2)}\n`);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (action === 'measure') {
|
|
349
|
+
const text = optionFrom(clean, '--text');
|
|
350
|
+
if (text === undefined)
|
|
351
|
+
throw new Error('Usage: deckuse measure --text <string> --font-size <pt>');
|
|
352
|
+
const fontSize = Number(optionFrom(clean, '--font-size') ?? optionFrom(clean, '--fontSize') ?? 12);
|
|
353
|
+
if (!(fontSize > 0))
|
|
354
|
+
throw new Error('--font-size must be a positive number (pt)');
|
|
355
|
+
const bold = clean.includes('--bold');
|
|
356
|
+
const fontFamily = optionFrom(clean, '--font-family') ?? optionFrom(clean, '--fontFamily');
|
|
357
|
+
const maxWidthRaw = optionFrom(clean, '--max-width') ?? optionFrom(clean, '--maxWidth');
|
|
358
|
+
const maxWidthEmu = maxWidthRaw !== undefined
|
|
359
|
+
? parseLength(lengthArg(maxWidthRaw) ?? maxWidthRaw, { axis: 'x' })
|
|
360
|
+
: undefined;
|
|
361
|
+
const measured = measureText({
|
|
362
|
+
text,
|
|
363
|
+
fontSize,
|
|
364
|
+
...(bold ? { bold: true } : {}),
|
|
365
|
+
...(fontFamily ? { fontFamily } : {}),
|
|
366
|
+
...(maxWidthEmu !== undefined ? { maxWidthEmu } : {}),
|
|
367
|
+
});
|
|
368
|
+
outputEnvelope({
|
|
369
|
+
ok: true,
|
|
370
|
+
command: 'deckuse measure',
|
|
371
|
+
data: measured,
|
|
372
|
+
});
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
222
375
|
if (action === 'init') {
|
|
223
376
|
const source = clean[1];
|
|
224
377
|
const workspace = workspaceOpt ?? clean[2];
|
|
@@ -316,7 +469,7 @@ const main = async () => {
|
|
|
316
469
|
const slide = optionFrom(clean, '--slide');
|
|
317
470
|
const shapeType = optionFrom(clean, '--type');
|
|
318
471
|
if (!slide || !shapeType)
|
|
319
|
-
throw new Error('Usage: deckuse add shape --slide <n> --type <text|rect|rounded-rect|ellipse|line|image|group|table|chart
|
|
472
|
+
throw new Error('Usage: deckuse add shape --slide <n> --type <text|rect|rounded-rect|ellipse|line|connector|elbow|arrow|chevron|circular-arrow|image|group|table|chart|…>');
|
|
320
473
|
const rowsRaw = optionFrom(clean, '--rows');
|
|
321
474
|
let rows;
|
|
322
475
|
if (rowsRaw !== undefined) {
|
|
@@ -343,6 +496,25 @@ const main = async () => {
|
|
|
343
496
|
}
|
|
344
497
|
}
|
|
345
498
|
const chartType = optionFrom(clean, '--chart-type');
|
|
499
|
+
const textRaw = takeFlag(clean, '--text-raw');
|
|
500
|
+
const textFile = optionFrom(clean, '--text-file');
|
|
501
|
+
const textOpt = optionFrom(clean, '--text');
|
|
502
|
+
const initialText = await resolveCliText({
|
|
503
|
+
...(textOpt !== undefined ? { value: textOpt } : {}),
|
|
504
|
+
...(textFile !== undefined ? { textFile } : {}),
|
|
505
|
+
raw: textRaw,
|
|
506
|
+
});
|
|
507
|
+
const theme = optionFrom(clean, '--theme');
|
|
508
|
+
const alignColumnsRaw = optionFrom(clean, '--align-columns');
|
|
509
|
+
let alignColumns;
|
|
510
|
+
if (alignColumnsRaw) {
|
|
511
|
+
try {
|
|
512
|
+
alignColumns = JSON.parse(alignColumnsRaw);
|
|
513
|
+
}
|
|
514
|
+
catch {
|
|
515
|
+
alignColumns = alignColumnsRaw.split(',').map((s) => s.trim());
|
|
516
|
+
}
|
|
517
|
+
}
|
|
346
518
|
ok = await execute('deckuse add shape', {
|
|
347
519
|
...base,
|
|
348
520
|
type: 'addShape',
|
|
@@ -350,19 +522,26 @@ const main = async () => {
|
|
|
350
522
|
shapeType,
|
|
351
523
|
...(optionFrom(clean, '--name') ? { name: optionFrom(clean, '--name') } : {}),
|
|
352
524
|
...(optionFrom(clean, '--role') ? { role: optionFrom(clean, '--role') } : {}),
|
|
353
|
-
...(
|
|
354
|
-
|
|
355
|
-
...(optionFrom(clean, '--width') ? { width: Number(optionFrom(clean, '--width')) } : {}),
|
|
356
|
-
...(optionFrom(clean, '--height')
|
|
357
|
-
? { height: Number(optionFrom(clean, '--height')) }
|
|
525
|
+
...(lengthArg(optionFrom(clean, '--x')) !== undefined
|
|
526
|
+
? { x: lengthArg(optionFrom(clean, '--x')) }
|
|
358
527
|
: {}),
|
|
359
|
-
...(
|
|
360
|
-
|
|
361
|
-
|
|
528
|
+
...(lengthArg(optionFrom(clean, '--y')) !== undefined
|
|
529
|
+
? { y: lengthArg(optionFrom(clean, '--y')) }
|
|
530
|
+
: {}),
|
|
531
|
+
...(lengthArg(optionFrom(clean, '--width')) !== undefined
|
|
532
|
+
? { width: lengthArg(optionFrom(clean, '--width')) }
|
|
362
533
|
: {}),
|
|
534
|
+
...(lengthArg(optionFrom(clean, '--height')) !== undefined
|
|
535
|
+
? { height: lengthArg(optionFrom(clean, '--height')) }
|
|
536
|
+
: {}),
|
|
537
|
+
...(optionFrom(clean, '--file') ? { file: optionFrom(clean, '--file') } : {}),
|
|
538
|
+
...(initialText !== undefined ? { text: initialText } : {}),
|
|
363
539
|
...(rows !== undefined ? { rows } : {}),
|
|
540
|
+
...(theme ? { theme } : {}),
|
|
541
|
+
...(alignColumns ? { alignColumns } : {}),
|
|
364
542
|
...(chartType ? { chartType } : {}),
|
|
365
543
|
...(data !== undefined ? { data } : {}),
|
|
544
|
+
...(takeFlag(clean, '--show-data-labels') ? { showDataLabels: true } : {}),
|
|
366
545
|
});
|
|
367
546
|
}
|
|
368
547
|
else
|
|
@@ -384,15 +563,42 @@ const main = async () => {
|
|
|
384
563
|
const base = await mutationExtras(workspace);
|
|
385
564
|
if (clean[1] === 'text') {
|
|
386
565
|
const target = clean[2];
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
566
|
+
const textRaw = takeFlag(clean, '--text-raw');
|
|
567
|
+
const textFile = optionFrom(clean, '--text-file');
|
|
568
|
+
const valueOpt = optionFrom(clean, '--value');
|
|
569
|
+
const blocksRaw = optionFrom(clean, '--blocks');
|
|
570
|
+
if (!target)
|
|
571
|
+
throw new Error('Usage: deckuse set text <target> --value <text> | --text-file <path> | --blocks <json>');
|
|
572
|
+
if (blocksRaw !== undefined) {
|
|
573
|
+
let blocks;
|
|
574
|
+
try {
|
|
575
|
+
blocks = JSON.parse(blocksRaw);
|
|
576
|
+
}
|
|
577
|
+
catch {
|
|
578
|
+
throw new Error('--blocks must be JSON array of {text, fontSize?, textColor?, ...}');
|
|
579
|
+
}
|
|
580
|
+
ok = await execute('deckuse set text', {
|
|
581
|
+
...base,
|
|
582
|
+
type: 'setText',
|
|
583
|
+
target,
|
|
584
|
+
blocks,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
const value = await resolveCliText({
|
|
589
|
+
...(valueOpt !== undefined ? { value: valueOpt } : {}),
|
|
590
|
+
...(textFile !== undefined ? { textFile } : {}),
|
|
591
|
+
raw: textRaw,
|
|
592
|
+
});
|
|
593
|
+
if (value === undefined)
|
|
594
|
+
throw new Error('Usage: deckuse set text <target> --value <text> | --text-file <path> | --blocks <json>');
|
|
595
|
+
ok = await execute('deckuse set text', {
|
|
596
|
+
...base,
|
|
597
|
+
type: 'setText',
|
|
598
|
+
target,
|
|
599
|
+
value,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
396
602
|
}
|
|
397
603
|
else {
|
|
398
604
|
const target = clean[1];
|
|
@@ -451,19 +657,42 @@ const main = async () => {
|
|
|
451
657
|
...(await mutationExtras(workspace)),
|
|
452
658
|
type: 'xfrmSet',
|
|
453
659
|
target,
|
|
454
|
-
...(
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
660
|
+
...(lengthArg(optionFrom(clean, '--x')) !== undefined
|
|
661
|
+
? { x: lengthArg(optionFrom(clean, '--x')) }
|
|
662
|
+
: {}),
|
|
663
|
+
...(lengthArg(optionFrom(clean, '--y')) !== undefined
|
|
664
|
+
? { y: lengthArg(optionFrom(clean, '--y')) }
|
|
665
|
+
: {}),
|
|
666
|
+
...(lengthArg(optionFrom(clean, '--width') ?? optionFrom(clean, '--cx')) !== undefined
|
|
667
|
+
? { width: lengthArg(optionFrom(clean, '--width') ?? optionFrom(clean, '--cx')) }
|
|
458
668
|
: {}),
|
|
459
|
-
...(optionFrom(clean, '--height')
|
|
460
|
-
? { height:
|
|
669
|
+
...(lengthArg(optionFrom(clean, '--height') ?? optionFrom(clean, '--cy')) !== undefined
|
|
670
|
+
? { height: lengthArg(optionFrom(clean, '--height') ?? optionFrom(clean, '--cy')) }
|
|
461
671
|
: {}),
|
|
462
672
|
...(optionFrom(clean, '--rotation')
|
|
463
673
|
? { rotation: Number(optionFrom(clean, '--rotation')) }
|
|
464
674
|
: {}),
|
|
465
675
|
});
|
|
466
676
|
}
|
|
677
|
+
else if (action === 'align') {
|
|
678
|
+
const workspace = await findWorkspace(workspaceOpt);
|
|
679
|
+
const slide = optionFrom(clean, '--slide');
|
|
680
|
+
const targetsRaw = optionFrom(clean, '--targets');
|
|
681
|
+
const mode = optionFrom(clean, '--mode');
|
|
682
|
+
if (!slide || !targetsRaw || !mode)
|
|
683
|
+
throw new Error('Usage: deckuse align --slide <n> --targets <t1,t2,...> --mode <left|right|top|bottom|center-h|center-v|distribute-h|distribute-v> [--gap <length>]');
|
|
684
|
+
const targets = targetsRaw.split(',').map((t) => t.trim()).filter(Boolean);
|
|
685
|
+
ok = await execute('deckuse align', {
|
|
686
|
+
...(await mutationExtras(workspace)),
|
|
687
|
+
type: 'alignElements',
|
|
688
|
+
slide: Number(slide),
|
|
689
|
+
targets,
|
|
690
|
+
mode,
|
|
691
|
+
...(lengthArg(optionFrom(clean, '--gap')) !== undefined
|
|
692
|
+
? { gap: lengthArg(optionFrom(clean, '--gap')) }
|
|
693
|
+
: {}),
|
|
694
|
+
});
|
|
695
|
+
}
|
|
467
696
|
else if (action === 'z') {
|
|
468
697
|
if (clean[1] !== 'move')
|
|
469
698
|
throw new Error('Usage: deckuse z move <target> --above <target>');
|
|
@@ -486,13 +715,13 @@ const main = async () => {
|
|
|
486
715
|
const workspace = await findWorkspace(workspaceOpt ?? clean[1]);
|
|
487
716
|
const raw = input === '-' ? await readStdin() : await readFile(resolve(input), 'utf8');
|
|
488
717
|
let values;
|
|
489
|
-
let
|
|
718
|
+
let wrappedOperations;
|
|
490
719
|
try {
|
|
491
720
|
const parsed = JSON.parse(raw);
|
|
492
721
|
if (typeof parsed === 'object' &&
|
|
493
722
|
parsed !== null &&
|
|
494
723
|
Array.isArray(parsed.operations)) {
|
|
495
|
-
|
|
724
|
+
wrappedOperations = parsed.operations;
|
|
496
725
|
}
|
|
497
726
|
else {
|
|
498
727
|
values = Array.isArray(parsed) ? parsed : [parsed];
|
|
@@ -504,36 +733,50 @@ const main = async () => {
|
|
|
504
733
|
.filter(Boolean)
|
|
505
734
|
.map((line) => JSON.parse(line));
|
|
506
735
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
736
|
+
const extras = await mutationExtras(workspace);
|
|
737
|
+
const runHighLevelBatch = async (list) => {
|
|
738
|
+
const commands = list.map((value) => {
|
|
739
|
+
if (!isRecord(value))
|
|
740
|
+
throw new Error('apply input must contain command objects');
|
|
741
|
+
if (value['type'] === 'batch')
|
|
742
|
+
return value;
|
|
743
|
+
if (!APPLY_WRITE_TYPES.has(String(value['type'])))
|
|
744
|
+
throw new Error(`apply input contains non-write command: ${String(value['type'])}`);
|
|
745
|
+
return { ...extras, ...value };
|
|
512
746
|
});
|
|
747
|
+
const payload = commands.length === 1 && commands[0]?.['type'] !== 'batch'
|
|
748
|
+
? commands[0]
|
|
749
|
+
: {
|
|
750
|
+
...extras,
|
|
751
|
+
type: 'batch',
|
|
752
|
+
atomic: true,
|
|
753
|
+
commands: commands.flatMap((command) => command['type'] === 'batch'
|
|
754
|
+
? (command.commands ?? [])
|
|
755
|
+
: [command]),
|
|
756
|
+
};
|
|
757
|
+
return execute('deckuse apply', payload);
|
|
758
|
+
};
|
|
759
|
+
if (wrappedOperations) {
|
|
760
|
+
const classified = classifyApplyOps(wrappedOperations);
|
|
761
|
+
if (classified.kind === 'invalid')
|
|
762
|
+
throw new Error(classified.message);
|
|
763
|
+
if (classified.kind === 'transaction') {
|
|
764
|
+
ok = await execute('deckuse apply', {
|
|
765
|
+
...extras,
|
|
766
|
+
type: 'applyTransaction',
|
|
767
|
+
operations: wrappedOperations,
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
ok = await runHighLevelBatch(wrappedOperations);
|
|
772
|
+
}
|
|
513
773
|
}
|
|
514
774
|
else {
|
|
515
|
-
const extras = await mutationExtras(workspace);
|
|
516
|
-
const WRITE = new Set([
|
|
517
|
-
'setText',
|
|
518
|
-
'replaceText',
|
|
519
|
-
'setTransform',
|
|
520
|
-
'setProperties',
|
|
521
|
-
'set',
|
|
522
|
-
'xfrmSet',
|
|
523
|
-
'zMove',
|
|
524
|
-
'add',
|
|
525
|
-
'addSlide',
|
|
526
|
-
'addShape',
|
|
527
|
-
'remove',
|
|
528
|
-
'replacePicture',
|
|
529
|
-
'duplicate',
|
|
530
|
-
'batch',
|
|
531
|
-
]);
|
|
532
775
|
const list = values ?? [];
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
776
|
+
const classified = classifyApplyOps(list);
|
|
777
|
+
if (classified.kind === 'invalid')
|
|
778
|
+
throw new Error(classified.message);
|
|
779
|
+
if (classified.kind === 'transaction') {
|
|
537
780
|
ok = await execute('deckuse apply', {
|
|
538
781
|
...extras,
|
|
539
782
|
type: 'applyTransaction',
|
|
@@ -541,27 +784,7 @@ const main = async () => {
|
|
|
541
784
|
});
|
|
542
785
|
}
|
|
543
786
|
else {
|
|
544
|
-
|
|
545
|
-
if (typeof value !== 'object' || value === null)
|
|
546
|
-
throw new Error('apply input must contain command objects');
|
|
547
|
-
const record = value;
|
|
548
|
-
if (record['type'] === 'batch')
|
|
549
|
-
return record;
|
|
550
|
-
if (!WRITE.has(String(record['type'])))
|
|
551
|
-
throw new Error(`apply input contains non-write command: ${String(record['type'])}`);
|
|
552
|
-
return { ...extras, ...record };
|
|
553
|
-
});
|
|
554
|
-
const payload = commands.length === 1 && commands[0]?.['type'] !== 'batch'
|
|
555
|
-
? commands[0]
|
|
556
|
-
: {
|
|
557
|
-
...extras,
|
|
558
|
-
type: 'batch',
|
|
559
|
-
atomic: true,
|
|
560
|
-
commands: commands.flatMap((command) => command['type'] === 'batch'
|
|
561
|
-
? command.commands
|
|
562
|
-
: [command]),
|
|
563
|
-
};
|
|
564
|
-
ok = await execute('deckuse apply', payload);
|
|
787
|
+
ok = await runHighLevelBatch(list);
|
|
565
788
|
}
|
|
566
789
|
}
|
|
567
790
|
}
|
|
@@ -602,24 +825,78 @@ const main = async () => {
|
|
|
602
825
|
if (!output)
|
|
603
826
|
throw new Error('Usage: deckuse export <output.pptx>');
|
|
604
827
|
const workspace = await findWorkspace(workspaceOpt);
|
|
828
|
+
const fromPackage = takeFlag(clean, '--from-package');
|
|
605
829
|
ok = await execute('deckuse export', {
|
|
606
830
|
version: PROTOCOL_VERSION,
|
|
607
831
|
type: 'export',
|
|
608
832
|
workspaceId: workspace,
|
|
609
833
|
output: resolve(output),
|
|
610
834
|
...(revisionOpt ? { revision: revisionOpt } : {}),
|
|
835
|
+
...(fromPackage ? { fromPackage: true } : {}),
|
|
611
836
|
});
|
|
612
837
|
}
|
|
613
838
|
else if (action === 'monitor') {
|
|
614
|
-
const
|
|
839
|
+
const sub = clean[1] === 'start' || clean[1] === 'stop' || clean[1] === 'status' ? clean[1] : undefined;
|
|
840
|
+
const daemonWorker = takeFlag(clean, '--daemon-worker');
|
|
841
|
+
const positionalWorkspace = sub
|
|
842
|
+
? clean[2] && !clean[2].startsWith('--')
|
|
843
|
+
? clean[2]
|
|
844
|
+
: undefined
|
|
845
|
+
: clean[1] && !clean[1].startsWith('--')
|
|
846
|
+
? clean[1]
|
|
847
|
+
: undefined;
|
|
848
|
+
const workspace = await findWorkspace(workspaceOpt ?? positionalWorkspace);
|
|
615
849
|
const port = Number(optionFrom(clean, '--port') ?? 4173);
|
|
616
850
|
if (!Number.isInteger(port) || port < 0 || port > 65535)
|
|
617
851
|
throw new Error('--port must be an integer between 0 and 65535');
|
|
618
|
-
const
|
|
619
|
-
|
|
852
|
+
const host = optionFrom(clean, '--host') ?? '0.0.0.0';
|
|
853
|
+
if (sub === 'start') {
|
|
854
|
+
try {
|
|
855
|
+
const meta = await monitorStart(workspace, { host, port });
|
|
856
|
+
outputEnvelope({
|
|
857
|
+
ok: true,
|
|
858
|
+
command: 'deckuse monitor start',
|
|
859
|
+
data: meta,
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
catch (error) {
|
|
863
|
+
outputEnvelope({
|
|
864
|
+
ok: false,
|
|
865
|
+
command: 'deckuse monitor start',
|
|
866
|
+
error: {
|
|
867
|
+
code: 'IO_ERROR',
|
|
868
|
+
message: error instanceof Error ? error.message : 'Monitor start failed',
|
|
869
|
+
},
|
|
870
|
+
});
|
|
871
|
+
process.exitCode = 1;
|
|
872
|
+
}
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
if (sub === 'stop') {
|
|
876
|
+
const result = await monitorStop(workspace);
|
|
877
|
+
outputEnvelope({
|
|
878
|
+
ok: true,
|
|
879
|
+
command: 'deckuse monitor stop',
|
|
880
|
+
data: result,
|
|
881
|
+
});
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
if (sub === 'status') {
|
|
885
|
+
const result = await monitorStatus(workspace);
|
|
886
|
+
outputEnvelope({
|
|
887
|
+
ok: true,
|
|
888
|
+
command: 'deckuse monitor status',
|
|
889
|
+
data: result,
|
|
890
|
+
});
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
const monitor = await runMonitorForeground(workspace, {
|
|
894
|
+
host,
|
|
620
895
|
port,
|
|
896
|
+
asDaemonWorker: daemonWorker,
|
|
621
897
|
});
|
|
622
|
-
|
|
898
|
+
if (!daemonWorker)
|
|
899
|
+
process.stdout.write(`Deckuse monitor: ${monitor.url}\n`);
|
|
623
900
|
const shutdown = async (signal) => {
|
|
624
901
|
process.stderr.write(`deckuse monitor: received ${signal}, shutting down\n`);
|
|
625
902
|
await monitor.close();
|
|
@@ -632,23 +909,29 @@ const main = async () => {
|
|
|
632
909
|
else if (action === 'render') {
|
|
633
910
|
const pageRaw = optionFrom(clean, '--page');
|
|
634
911
|
if (!pageRaw)
|
|
635
|
-
throw new Error('Usage: deckuse render --page <n> [--output <file.png>]');
|
|
912
|
+
throw new Error('Usage: deckuse render --page <n> [--output <file.png>] [--scale <n>]');
|
|
636
913
|
if (/\s|,|-/.test(pageRaw))
|
|
637
914
|
throw new Error('--page accepts exactly one positive integer (not a range or list)');
|
|
638
915
|
const page = Number(pageRaw);
|
|
639
916
|
if (!Number.isInteger(page) || page < 1)
|
|
640
917
|
throw new Error('--page must be a positive integer');
|
|
918
|
+
const scaleRaw = optionFrom(clean, '--scale');
|
|
919
|
+
const scale = scaleRaw !== undefined ? Number(scaleRaw) : undefined;
|
|
920
|
+
if (scale !== undefined && !(scale > 0))
|
|
921
|
+
throw new Error('--scale must be a positive number');
|
|
641
922
|
const workspace = await findWorkspace(workspaceOpt ?? clean[1]);
|
|
642
923
|
const outputOpt = optionFrom(clean, '--output');
|
|
643
924
|
try {
|
|
644
925
|
const rendered = await renderPage(workspace, {
|
|
645
926
|
page,
|
|
646
927
|
...(outputOpt ? { output: resolve(outputOpt) } : {}),
|
|
928
|
+
...(scale !== undefined ? { scale } : {}),
|
|
647
929
|
});
|
|
648
930
|
outputEnvelope({
|
|
649
931
|
ok: true,
|
|
650
932
|
command: 'deckuse render',
|
|
651
|
-
|
|
933
|
+
warnings: [...rendered.warnings],
|
|
934
|
+
data: { page: rendered.page, output: rendered.output, warnings: rendered.warnings },
|
|
652
935
|
});
|
|
653
936
|
}
|
|
654
937
|
catch (error) {
|