@christophervr/pptx-viewer 1.1.46 → 1.3.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/CHANGELOG.md +8 -0
- package/dist/index.mjs +102 -33
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
This file is generated from [Conventional Commits](https://www.conventionalcommits.org)
|
|
5
5
|
by [git-cliff](https://git-cliff.org); do not edit it by hand.
|
|
6
6
|
|
|
7
|
+
## [1.2.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/@christophervr/pptx-viewer@1.2.0) - 2026-07-04
|
|
8
|
+
|
|
9
|
+
## [1.1.46](https://github.com/ChristopherVR/pptx-viewer/releases/tag/@christophervr/pptx-viewer@1.1.46) - 2026-07-03
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- **cli:** Arrow-key colour prompts and PowerPoint-ready scaffolds (by @ChristopherVR) ([8de03c9](https://github.com/ChristopherVR/pptx-viewer/commit/8de03c9da8c8d20e28cca253ff6d7083de65a0d8))
|
|
14
|
+
|
|
7
15
|
## [1.1.45](https://github.com/ChristopherVR/pptx-viewer/releases/tag/@christophervr/pptx-viewer@1.1.45) - 2026-07-02
|
|
8
16
|
|
|
9
17
|
### Features
|
package/dist/index.mjs
CHANGED
|
@@ -66,14 +66,13 @@ var blue = wrap(34, 39);
|
|
|
66
66
|
var magenta = wrap(35, 39);
|
|
67
67
|
var cyan = wrap(36, 39);
|
|
68
68
|
var gray = wrap(90, 39);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
69
|
+
function isUnicodeSupported() {
|
|
70
|
+
if (process.platform !== "win32") {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
return Boolean(process.env.CI) || Boolean(process.env.WT_SESSION) || Boolean(process.env.ConEmuTask) || process.env.TERM_PROGRAM === "vscode" || process.env.TERM === "xterm-256color";
|
|
74
|
+
}
|
|
75
|
+
var symbols = isUnicodeSupported() ? { pointer: "\u276F", check: "\u2714", cross: "\u2718", radioOn: "\u25C9", radioOff: "\u25EF", bullet: "\xB7" } : { pointer: ">", check: "\u221A", cross: "\xD7", radioOn: "(*)", radioOff: "( )", bullet: "*" };
|
|
77
76
|
|
|
78
77
|
// src/project-deps.ts
|
|
79
78
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -179,15 +178,26 @@ import { emitKeypressEvents } from "readline";
|
|
|
179
178
|
var HIDE_CURSOR = "\x1B[?25l";
|
|
180
179
|
var SHOW_CURSOR = "\x1B[?25h";
|
|
181
180
|
var CLEAR_LINE = "\x1B[2K";
|
|
181
|
+
var ERASE_DOWN = "\x1B[0J";
|
|
182
182
|
function moveUp(lines) {
|
|
183
183
|
return lines > 0 ? `\x1B[${lines}A` : "";
|
|
184
184
|
}
|
|
185
|
+
function isEnterKey(str, key) {
|
|
186
|
+
return key?.name === "return" || key?.name === "enter" || str === "\r" || str === "\n";
|
|
187
|
+
}
|
|
185
188
|
function renderChoice(choice, isCursor, checked) {
|
|
186
189
|
const pointer = isCursor ? cyan(symbols.pointer) : " ";
|
|
187
190
|
const box = checked === null ? "" : `${checked ? green(symbols.radioOn) : gray(symbols.radioOff)} `;
|
|
188
191
|
const label = isCursor ? bold(choice.label) : choice.label;
|
|
189
192
|
return `${pointer} ${box}${label} ${dim(`- ${choice.description}`)}`;
|
|
190
193
|
}
|
|
194
|
+
function groupMatesOf(choices, index) {
|
|
195
|
+
const group = choices[index].group;
|
|
196
|
+
if (!group) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
return choices.flatMap((c, i) => i !== index && c.group === group ? [i] : []);
|
|
200
|
+
}
|
|
191
201
|
function runMenu(choices, multi) {
|
|
192
202
|
return new Promise((resolve) => {
|
|
193
203
|
if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== "function") {
|
|
@@ -196,24 +206,34 @@ function runMenu(choices, multi) {
|
|
|
196
206
|
}
|
|
197
207
|
let cursor = 0;
|
|
198
208
|
const checked = /* @__PURE__ */ new Set();
|
|
209
|
+
let statusMessage = "";
|
|
199
210
|
let settled = false;
|
|
200
211
|
const hint = multi ? dim("(\u2191/\u2193 move, space toggle, a select all, enter confirm)") : dim("(\u2191/\u2193 move, enter confirm)");
|
|
201
|
-
|
|
212
|
+
const totalLines = choices.length + 2;
|
|
202
213
|
process.stdout.write(HIDE_CURSOR);
|
|
203
214
|
function draw(first) {
|
|
204
215
|
if (!first) {
|
|
205
|
-
process.stdout.write(moveUp(
|
|
216
|
+
process.stdout.write(moveUp(totalLines));
|
|
206
217
|
}
|
|
218
|
+
process.stdout.write(`${CLEAR_LINE}${hint}
|
|
219
|
+
`);
|
|
207
220
|
for (const [i, choice] of choices.entries()) {
|
|
208
221
|
const checkedState = multi ? checked.has(i) : null;
|
|
209
222
|
process.stdout.write(`${CLEAR_LINE}${renderChoice(choice, i === cursor, checkedState)}
|
|
210
223
|
`);
|
|
211
224
|
}
|
|
225
|
+
process.stdout.write(`${CLEAR_LINE}${statusMessage}
|
|
226
|
+
`);
|
|
227
|
+
}
|
|
228
|
+
function eraseWidget() {
|
|
229
|
+
process.stdout.write(moveUp(totalLines));
|
|
230
|
+
process.stdout.write(ERASE_DOWN);
|
|
212
231
|
}
|
|
213
232
|
function cleanup() {
|
|
214
233
|
process.stdin.setRawMode?.(false);
|
|
215
234
|
process.stdin.removeListener("keypress", onKeypress);
|
|
216
235
|
process.stdin.pause();
|
|
236
|
+
eraseWidget();
|
|
217
237
|
process.stdout.write(SHOW_CURSOR);
|
|
218
238
|
}
|
|
219
239
|
function finish(result) {
|
|
@@ -224,12 +244,30 @@ function runMenu(choices, multi) {
|
|
|
224
244
|
cleanup();
|
|
225
245
|
resolve(result);
|
|
226
246
|
}
|
|
227
|
-
function
|
|
247
|
+
function check(index) {
|
|
248
|
+
for (const mate of groupMatesOf(choices, index)) {
|
|
249
|
+
checked.delete(mate);
|
|
250
|
+
}
|
|
251
|
+
checked.add(index);
|
|
252
|
+
}
|
|
253
|
+
function toggleSelectAll() {
|
|
254
|
+
const selectable = choices.flatMap((c, i) => c.group ? [] : [i]);
|
|
255
|
+
const allSelected = selectable.every((i) => checked.has(i));
|
|
256
|
+
for (const i of selectable) {
|
|
257
|
+
if (allSelected) {
|
|
258
|
+
checked.delete(i);
|
|
259
|
+
} else {
|
|
260
|
+
checked.add(i);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function onKeypress(str, key) {
|
|
228
265
|
if (key?.ctrl && key.name === "c") {
|
|
229
266
|
finish(null);
|
|
230
267
|
process.exit(130);
|
|
231
268
|
return;
|
|
232
269
|
}
|
|
270
|
+
statusMessage = "";
|
|
233
271
|
if (key?.name === "up") {
|
|
234
272
|
cursor = (cursor - 1 + choices.length) % choices.length;
|
|
235
273
|
draw(false);
|
|
@@ -240,20 +278,19 @@ function runMenu(choices, multi) {
|
|
|
240
278
|
if (checked.has(cursor)) {
|
|
241
279
|
checked.delete(cursor);
|
|
242
280
|
} else {
|
|
243
|
-
|
|
281
|
+
check(cursor);
|
|
244
282
|
}
|
|
245
283
|
draw(false);
|
|
246
284
|
} else if (multi && key?.name === "a") {
|
|
247
|
-
|
|
248
|
-
checked.clear();
|
|
249
|
-
} else {
|
|
250
|
-
choices.forEach((_, i) => checked.add(i));
|
|
251
|
-
}
|
|
285
|
+
toggleSelectAll();
|
|
252
286
|
draw(false);
|
|
253
|
-
} else if (key
|
|
287
|
+
} else if (isEnterKey(str, key)) {
|
|
254
288
|
if (multi) {
|
|
255
289
|
if (checked.size > 0) {
|
|
256
290
|
finish([...checked].sort((a, b) => a - b));
|
|
291
|
+
} else {
|
|
292
|
+
statusMessage = dim("Select at least one option with space, then press enter.");
|
|
293
|
+
draw(false);
|
|
257
294
|
}
|
|
258
295
|
} else {
|
|
259
296
|
finish([cursor]);
|
|
@@ -374,7 +411,7 @@ async function input(question, defaultValue) {
|
|
|
374
411
|
var REACT_APP_TSX = `import { useCallback, useState } from 'react';
|
|
375
412
|
import { PptxHandler } from 'pptx-viewer-core';
|
|
376
413
|
import { PowerPointViewer } from 'pptx-react-viewer';
|
|
377
|
-
import 'pptx-react-viewer/styles';
|
|
414
|
+
import 'pptx-react-viewer/styles.css';
|
|
378
415
|
|
|
379
416
|
export default function App() {
|
|
380
417
|
const [content, setContent] = useState<Uint8Array | null>(null);
|
|
@@ -420,7 +457,7 @@ var VUE_APP_VUE = `<script setup lang="ts">
|
|
|
420
457
|
import { ref } from 'vue';
|
|
421
458
|
import { PptxHandler } from 'pptx-viewer-core';
|
|
422
459
|
import { PowerPointViewer } from 'pptx-vue-viewer';
|
|
423
|
-
import 'pptx-vue-viewer/styles';
|
|
460
|
+
import 'pptx-vue-viewer/styles.css';
|
|
424
461
|
|
|
425
462
|
const content = ref<Uint8Array>();
|
|
426
463
|
|
|
@@ -500,6 +537,7 @@ var TARGETS = [
|
|
|
500
537
|
label: "React",
|
|
501
538
|
description: "pptx-react-viewer - viewer/editor component for a React 19 app",
|
|
502
539
|
mode: "install",
|
|
540
|
+
group: "framework",
|
|
503
541
|
packages: [
|
|
504
542
|
"pptx-react-viewer",
|
|
505
543
|
"react",
|
|
@@ -514,7 +552,7 @@ var TARGETS = [
|
|
|
514
552
|
"react-i18next"
|
|
515
553
|
],
|
|
516
554
|
nextSteps: `import { PowerPointViewer } from 'pptx-react-viewer';
|
|
517
|
-
import 'pptx-react-viewer/styles';
|
|
555
|
+
import 'pptx-react-viewer/styles.css';
|
|
518
556
|
|
|
519
557
|
<PowerPointViewer content={arrayBuffer} canEdit />
|
|
520
558
|
|
|
@@ -522,7 +560,11 @@ Docs: https://www.npmjs.com/package/pptx-react-viewer`,
|
|
|
522
560
|
compat: { peerPackage: "react", requiredMajor: 19 },
|
|
523
561
|
scaffold: {
|
|
524
562
|
command: "create-vite@latest",
|
|
525
|
-
|
|
563
|
+
// --no-interactive/--no-immediate stop create-vite from prompting for a linter
|
|
564
|
+
// choice and then auto-installing + auto-starting its own dev server; if it did,
|
|
565
|
+
// that dev server would block forever and our own entry-file patch + extra
|
|
566
|
+
// package install below would never run, leaving the default Vite template in place.
|
|
567
|
+
args: (dir) => [dir, "--template", "react-ts", "--no-interactive", "--no-immediate"],
|
|
526
568
|
extraPackages: [
|
|
527
569
|
"pptx-react-viewer",
|
|
528
570
|
"pptx-viewer-core",
|
|
@@ -544,10 +586,11 @@ Docs: https://www.npmjs.com/package/pptx-react-viewer`,
|
|
|
544
586
|
label: "Vue",
|
|
545
587
|
description: "pptx-vue-viewer - viewer/editor component for a Vue 3.5+ app",
|
|
546
588
|
mode: "install",
|
|
589
|
+
group: "framework",
|
|
547
590
|
packages: ["pptx-vue-viewer", "vue", "jszip", "fast-xml-parser"],
|
|
548
591
|
nextSteps: `<script setup lang="ts">
|
|
549
592
|
import { PowerPointViewer } from 'pptx-vue-viewer';
|
|
550
|
-
import 'pptx-vue-viewer/styles';
|
|
593
|
+
import 'pptx-vue-viewer/styles.css';
|
|
551
594
|
</script>
|
|
552
595
|
|
|
553
596
|
<template>
|
|
@@ -558,7 +601,7 @@ Docs: https://www.npmjs.com/package/pptx-vue-viewer`,
|
|
|
558
601
|
compat: { peerPackage: "vue", requiredMajor: 3 },
|
|
559
602
|
scaffold: {
|
|
560
603
|
command: "create-vite@latest",
|
|
561
|
-
args: (dir) => [dir, "--template", "vue-ts"],
|
|
604
|
+
args: (dir) => [dir, "--template", "vue-ts", "--no-interactive", "--no-immediate"],
|
|
562
605
|
extraPackages: ["pptx-vue-viewer", "pptx-viewer-core", "jszip", "fast-xml-parser"],
|
|
563
606
|
entryCandidates: ["src/App.vue"],
|
|
564
607
|
entryContent: VUE_APP_VUE
|
|
@@ -569,9 +612,10 @@ Docs: https://www.npmjs.com/package/pptx-vue-viewer`,
|
|
|
569
612
|
label: "Angular",
|
|
570
613
|
description: "pptx-angular-viewer - viewer/editor component for an Angular 22+ app",
|
|
571
614
|
mode: "install",
|
|
615
|
+
group: "framework",
|
|
572
616
|
packages: ["pptx-angular-viewer", "@angular/core", "@angular/common", "rxjs"],
|
|
573
617
|
nextSteps: `import { PowerPointViewerComponent } from 'pptx-angular-viewer';
|
|
574
|
-
import 'pptx-angular-viewer/styles';
|
|
618
|
+
import 'pptx-angular-viewer/styles.css';
|
|
575
619
|
|
|
576
620
|
<pptx-power-point-viewer [content]="content" />
|
|
577
621
|
|
|
@@ -579,7 +623,19 @@ Docs: https://www.npmjs.com/package/pptx-angular-viewer`,
|
|
|
579
623
|
compat: { peerPackage: "@angular/core", requiredMajor: 22 },
|
|
580
624
|
scaffold: {
|
|
581
625
|
command: "@angular/cli@latest",
|
|
582
|
-
|
|
626
|
+
// --no-interactive matters even with the flags above supplied: the
|
|
627
|
+
// `application` schematic's `ssr` option has an `x-prompt`, and `ng new`
|
|
628
|
+
// prompts for it (plus anything else not already given a value) whenever
|
|
629
|
+
// stdin is a TTY, which ours is (we inherit the real user's terminal).
|
|
630
|
+
args: (dir) => [
|
|
631
|
+
"new",
|
|
632
|
+
dir,
|
|
633
|
+
"--standalone",
|
|
634
|
+
"--skip-git",
|
|
635
|
+
"--style=css",
|
|
636
|
+
"--skip-install",
|
|
637
|
+
"--no-interactive"
|
|
638
|
+
],
|
|
583
639
|
extraPackages: ["pptx-angular-viewer", "pptx-viewer-core"],
|
|
584
640
|
// Angular v20+ generates `app.ts`; older schematics generate `app.component.ts`.
|
|
585
641
|
entryCandidates: ["src/app/app.ts", "src/app/app.component.ts"],
|
|
@@ -645,6 +701,24 @@ function findTargetsByIds(ids) {
|
|
|
645
701
|
return match;
|
|
646
702
|
});
|
|
647
703
|
}
|
|
704
|
+
function assertSingleFramework(targets) {
|
|
705
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
706
|
+
for (const target of targets) {
|
|
707
|
+
if (!target.group) {
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
const mates = grouped.get(target.group) ?? [];
|
|
711
|
+
mates.push(target);
|
|
712
|
+
grouped.set(target.group, mates);
|
|
713
|
+
}
|
|
714
|
+
for (const mates of grouped.values()) {
|
|
715
|
+
if (mates.length > 1) {
|
|
716
|
+
throw new Error(
|
|
717
|
+
`${mates.map((t) => t.label).join(", ")} can't be selected together; pick a single UI framework.`
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
648
722
|
function mergePackages(targets) {
|
|
649
723
|
const seen = /* @__PURE__ */ new Set();
|
|
650
724
|
const merged = [];
|
|
@@ -721,6 +795,7 @@ ${bold("Usage:")} npx @christophervr/pptx-viewer [options]
|
|
|
721
795
|
|
|
722
796
|
${bold("Options:")}
|
|
723
797
|
${cyan("--target <ids>")} Skip the picker; comma-separated, any of: ${TARGETS.map((t) => t.id).join(", ")}
|
|
798
|
+
(react, vue, and angular are mutually exclusive; pick at most one)
|
|
724
799
|
${cyan("--scaffold")} Bootstrap a brand-new starter project instead of installing here
|
|
725
800
|
${cyan("--dir <name>")} Project directory name for --scaffold
|
|
726
801
|
${cyan("--pm <manager>")} Package manager to use: bun, pnpm, yarn, npm (default: auto-detected)
|
|
@@ -772,13 +847,6 @@ async function resolveScaffoldChoice(installTargets, args) {
|
|
|
772
847
|
}
|
|
773
848
|
return { useScaffold: true, scaffoldTarget: scaffoldable[0] };
|
|
774
849
|
}
|
|
775
|
-
if (scaffoldable.length > 1) {
|
|
776
|
-
console.log(
|
|
777
|
-
`
|
|
778
|
-
${dim("Scaffolding a new project only supports one UI framework at a time; installing instead.")}`
|
|
779
|
-
);
|
|
780
|
-
return { useScaffold: false };
|
|
781
|
-
}
|
|
782
850
|
if (scaffoldable.length === 1 && process.stdin.isTTY) {
|
|
783
851
|
const choice = await selectOption("Install into the current project, or scaffold a new one?", [
|
|
784
852
|
{ label: "Install here", description: "Add the package(s) to the project in this directory" },
|
|
@@ -895,6 +963,7 @@ async function main() {
|
|
|
895
963
|
}
|
|
896
964
|
printBanner();
|
|
897
965
|
const targets = await resolveTargets(args.target);
|
|
966
|
+
assertSingleFramework(targets);
|
|
898
967
|
const installTargets = targets.filter((t) => t.mode === "install");
|
|
899
968
|
const configTargets = targets.filter((t) => t.mode === "print-config");
|
|
900
969
|
const cwd = process.cwd();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@christophervr/pptx-viewer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Interactive installer for the pptx-viewer family of packages: React, Vue, and Angular viewer components, the framework-agnostic core engine, and the MCP server.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|