@christophervr/pptx-viewer 1.4.0 → 1.4.2

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 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.4.1](https://github.com/ChristopherVR/pptx-viewer/releases/tag/@christophervr/pptx-viewer@1.4.1) - 2026-07-05
8
+
9
+ ## [1.4.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/@christophervr/pptx-viewer@1.4.0) - 2026-07-05
10
+
11
+ ### Features
12
+
13
+ - **core,cli:** Add react, angular, vue to npm keywords (by @ChristopherVR) ([528ec61](https://github.com/ChristopherVR/pptx-viewer/commit/528ec6182bb77c07444dd0e93560b65e604b9524))
14
+
7
15
  ## [1.3.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/@christophervr/pptx-viewer@1.3.0) - 2026-07-04
8
16
 
9
17
  ## [1.2.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/@christophervr/pptx-viewer@1.2.0) - 2026-07-04
package/dist/index.mjs CHANGED
@@ -412,6 +412,7 @@ var REACT_APP_TSX = `import { useCallback, useState } from 'react';
412
412
  import { PptxHandler } from 'pptx-viewer-core';
413
413
  import { PowerPointViewer } from 'pptx-react-viewer';
414
414
  import 'pptx-react-viewer/styles.css';
415
+ import './i18n';
415
416
 
416
417
  export default function App() {
417
418
  const [content, setContent] = useState<Uint8Array | null>(null);
@@ -439,20 +440,58 @@ export default function App() {
439
440
  }
440
441
 
441
442
  return (
442
- <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, height: '100vh' }}>
443
- <input
444
- type="file"
445
- accept=".pptx"
446
- onChange={(e) => {
447
- const file = e.target.files?.[0];
448
- if (file) loadFile(file);
449
- }}
450
- />
451
- <button onClick={() => void newPresentation()}>or create a New Presentation</button>
443
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 24, height: '100vh', fontFamily: 'system-ui, sans-serif' }}>
444
+ <h1 style={{ margin: 0, fontSize: 24, fontWeight: 500, color: '#e5e7eb' }}>Open a Presentation</h1>
445
+ <label style={{ display: 'inline-flex', alignItems: 'center', gap: 8, padding: '10px 20px', borderRadius: 8, border: '1px solid #4b5563', background: '#1f2937', color: '#f3f4f6', cursor: 'pointer', fontSize: 14, transition: 'background 0.15s' }}>
446
+ Choose .pptx file
447
+ <input
448
+ type="file"
449
+ accept=".pptx"
450
+ style={{ display: 'none' }}
451
+ onChange={(e) => {
452
+ const file = e.target.files?.[0];
453
+ if (file) loadFile(file);
454
+ }}
455
+ />
456
+ </label>
457
+ <span style={{ color: '#6b7280', fontSize: 13 }}>or</span>
458
+ <button
459
+ onClick={() => void newPresentation()}
460
+ style={{ padding: '10px 20px', borderRadius: 8, border: 'none', background: '#2563eb', color: '#fff', cursor: 'pointer', fontSize: 14, fontWeight: 500 }}
461
+ >
462
+ New Presentation
463
+ </button>
452
464
  </div>
453
465
  );
454
466
  }
455
467
  `;
468
+ var REACT_I18N_TS = `import { createInstance } from 'i18next';
469
+ import { translationsEn, keyToLabel } from 'pptx-react-viewer/i18n';
470
+ import { initReactI18next } from 'react-i18next';
471
+
472
+ const i18n = createInstance();
473
+
474
+ i18n.use(initReactI18next).init({
475
+ resources: {
476
+ en: { translation: translationsEn },
477
+ },
478
+ lng: 'en',
479
+ fallbackLng: 'en',
480
+ interpolation: { escapeValue: false },
481
+ parseMissingKeyHandler: (key: string) => keyToLabel(key),
482
+ missingKeyHandler: false,
483
+ });
484
+
485
+ export default i18n;
486
+ `;
487
+ var REACT_INDEX_CSS = `:root {
488
+ color-scheme: light dark;
489
+ }
490
+
491
+ body {
492
+ margin: 0;
493
+ }
494
+ `;
456
495
  var VUE_APP_VUE = `<script setup lang="ts">
457
496
  import { ref } from 'vue';
458
497
  import { PptxHandler } from 'pptx-viewer-core';
@@ -485,12 +524,34 @@ async function newPresentation() {
485
524
  <div v-if="content" style="height: 100vh">
486
525
  <PowerPointViewer :content="content" can-edit style="height: 100%" />
487
526
  </div>
488
- <div v-else style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; height: 100vh">
489
- <input type="file" accept=".pptx" @change="onPick" />
490
- <button @click="newPresentation">or create a New Presentation</button>
527
+ <div v-else style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 24px; height: 100vh; font-family: system-ui, sans-serif">
528
+ <h1 style="margin: 0; font-size: 24px; font-weight: 500; color: #e5e7eb">Open a Presentation</h1>
529
+ <label style="display: inline-flex; align-items: center; gap: 8px; padding: 10px 20px; border-radius: 8px; border: 1px solid #4b5563; background: #1f2937; color: #f3f4f6; cursor: pointer; font-size: 14px">
530
+ Choose .pptx file
531
+ <input type="file" accept=".pptx" style="display: none" @change="onPick" />
532
+ </label>
533
+ <span style="color: #6b7280; font-size: 13px">or</span>
534
+ <button style="padding: 10px 20px; border-radius: 8px; border: none; background: #2563eb; color: #fff; cursor: pointer; font-size: 14px; font-weight: 500" @click="newPresentation">New Presentation</button>
491
535
  </div>
492
536
  </template>
493
537
  `;
538
+ var VUE_MAIN_TS = `import { createApp } from 'vue';
539
+ import { createI18n } from 'vue-i18n';
540
+ import { translationsEn, keyToLabel } from 'pptx-vue-viewer/i18n';
541
+ import App from './App.vue';
542
+
543
+ const i18n = createI18n({
544
+ legacy: false,
545
+ locale: 'en',
546
+ fallbackLocale: 'en',
547
+ messages: { en: translationsEn },
548
+ missing: (_locale, key) => keyToLabel(key),
549
+ missingWarn: false,
550
+ fallbackWarn: false,
551
+ });
552
+
553
+ createApp(App).use(i18n).mount('#app');
554
+ `;
494
555
  var ANGULAR_APP_TS = `import { Component, signal } from '@angular/core';
495
556
  import { PptxHandler } from 'pptx-viewer-core';
496
557
  import { PowerPointViewerComponent } from 'pptx-angular-viewer';
@@ -505,9 +566,14 @@ import { PowerPointViewerComponent } from 'pptx-angular-viewer';
505
566
  <pptx-power-point-viewer [content]="c" [canEdit]="true" style="height: 100%" />
506
567
  </div>
507
568
  } @else {
508
- <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; height: 100vh">
509
- <input type="file" accept=".pptx" (change)="onPick($event)" />
510
- <button (click)="newPresentation()">or create a New Presentation</button>
569
+ <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 24px; height: 100vh; font-family: system-ui, sans-serif">
570
+ <h1 style="margin: 0; font-size: 24px; font-weight: 500; color: #e5e7eb">Open a Presentation</h1>
571
+ <label style="display: inline-flex; align-items: center; gap: 8px; padding: 10px 20px; border-radius: 8px; border: 1px solid #4b5563; background: #1f2937; color: #f3f4f6; cursor: pointer; font-size: 14px">
572
+ Choose .pptx file
573
+ <input type="file" accept=".pptx" style="display: none" (change)="onPick($event)" />
574
+ </label>
575
+ <span style="color: #6b7280; font-size: 13px">or</span>
576
+ <button style="padding: 10px 20px; border-radius: 8px; border: none; background: #2563eb; color: #fff; cursor: pointer; font-size: 14px; font-weight: 500" (click)="newPresentation()">New Presentation</button>
511
577
  </div>
512
578
  }
513
579
  \`,
@@ -531,6 +597,36 @@ export class App {
531
597
  }
532
598
  }
533
599
  `;
600
+ var ANGULAR_MAIN_TS = `import 'zone.js';
601
+ import '@angular/compiler';
602
+ import { bootstrapApplication } from '@angular/platform-browser';
603
+ import { Injectable } from '@angular/core';
604
+ import type { MissingTranslationHandlerParams } from '@ngx-translate/core';
605
+ import { MissingTranslationHandler, provideTranslateService } from '@ngx-translate/core';
606
+ import { keyToLabel } from 'pptx-angular-viewer';
607
+
608
+ import { App } from './app/app.ts';
609
+
610
+ @Injectable()
611
+ class LabelFallbackHandler implements MissingTranslationHandler {
612
+ handle(params: MissingTranslationHandlerParams): string {
613
+ return keyToLabel(params.key);
614
+ }
615
+ }
616
+
617
+ bootstrapApplication(App, {
618
+ providers: [
619
+ provideTranslateService({
620
+ lang: 'en',
621
+ fallbackLang: 'en',
622
+ missingTranslationHandler: {
623
+ provide: MissingTranslationHandler,
624
+ useClass: LabelFallbackHandler,
625
+ },
626
+ }),
627
+ ],
628
+ }).catch((err) => console.error(err));
629
+ `;
534
630
  var TARGETS = [
535
631
  {
536
632
  id: "react",
@@ -578,7 +674,11 @@ Docs: https://www.npmjs.com/package/pptx-react-viewer`,
578
674
  "react-i18next"
579
675
  ],
580
676
  entryCandidates: ["src/App.tsx"],
581
- entryContent: REACT_APP_TSX
677
+ entryContent: REACT_APP_TSX,
678
+ extraFiles: {
679
+ "src/i18n.ts": REACT_I18N_TS,
680
+ "src/index.css": REACT_INDEX_CSS
681
+ }
582
682
  }
583
683
  },
584
684
  {
@@ -602,9 +702,16 @@ Docs: https://www.npmjs.com/package/pptx-vue-viewer`,
602
702
  scaffold: {
603
703
  command: "create-vite@latest",
604
704
  args: (dir) => [dir, "--template", "vue-ts", "--no-interactive", "--no-immediate"],
605
- extraPackages: ["pptx-vue-viewer", "pptx-viewer-core", "jszip", "fast-xml-parser"],
705
+ extraPackages: [
706
+ "pptx-vue-viewer",
707
+ "pptx-viewer-core",
708
+ "vue-i18n",
709
+ "jszip",
710
+ "fast-xml-parser"
711
+ ],
606
712
  entryCandidates: ["src/App.vue"],
607
- entryContent: VUE_APP_VUE
713
+ entryContent: VUE_APP_VUE,
714
+ extraFiles: { "src/main.ts": VUE_MAIN_TS }
608
715
  }
609
716
  },
610
717
  {
@@ -636,10 +743,11 @@ Docs: https://www.npmjs.com/package/pptx-angular-viewer`,
636
743
  "--skip-install",
637
744
  "--no-interactive"
638
745
  ],
639
- extraPackages: ["pptx-angular-viewer", "pptx-viewer-core"],
746
+ extraPackages: ["pptx-angular-viewer", "pptx-viewer-core", "@ngx-translate/core"],
640
747
  // Angular v20+ generates `app.ts`; older schematics generate `app.component.ts`.
641
748
  entryCandidates: ["src/app/app.ts", "src/app/app.component.ts"],
642
- entryContent: ANGULAR_APP_TS
749
+ entryContent: ANGULAR_APP_TS,
750
+ extraFiles: { "src/main.ts": ANGULAR_MAIN_TS }
643
751
  }
644
752
  },
645
753
  {
@@ -735,18 +843,19 @@ function mergePackages(targets) {
735
843
 
736
844
  // src/run-command.ts
737
845
  import { spawn } from "child_process";
738
- function runCommand(command, args, cwd) {
846
+ function runCommand(command, args, cwd, options) {
739
847
  return new Promise((resolve, reject) => {
740
848
  const isWindows = process.platform === "win32";
741
- const child = isWindows ? spawn([command, ...args].join(" "), { cwd, stdio: "inherit", shell: true }) : spawn(command, args, { cwd, stdio: "inherit" });
849
+ const stdio = options?.silent ? "ignore" : "inherit";
850
+ const child = isWindows ? spawn([command, ...args].join(" "), { cwd, stdio, shell: true }) : spawn(command, args, { cwd, stdio });
742
851
  child.on("error", reject);
743
852
  child.on("close", (code) => resolve(code ?? 1));
744
853
  });
745
854
  }
746
855
 
747
856
  // src/scaffold.ts
748
- import { existsSync as existsSync3, writeFileSync } from "fs";
749
- import { join as join3 } from "path";
857
+ import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
858
+ import { dirname, join as join3 } from "path";
750
859
  function sanitizeProjectName(name) {
751
860
  const cleaned = name.trim().replace(/[^a-zA-Z0-9._-]+/gu, "-").replace(/-{2,}/gu, "-").replace(/^-+|-+$/gu, "");
752
861
  return cleaned || "pptx-viewer-app";
@@ -763,7 +872,8 @@ async function scaffoldProject(recipe, projectName, pm, cwd) {
763
872
  const scaffoldExit = await runCommand(
764
873
  "npx",
765
874
  ["--yes", recipe.command, ...recipe.args(projectName)],
766
- cwd
875
+ cwd,
876
+ { silent: true }
767
877
  );
768
878
  if (scaffoldExit !== 0) {
769
879
  throw new Error(`${recipe.command} exited with code ${scaffoldExit}`);
@@ -773,6 +883,13 @@ async function scaffoldProject(recipe, projectName, pm, cwd) {
773
883
  if (patchedFile) {
774
884
  writeFileSync(join3(projectDir, patchedFile), recipe.entryContent);
775
885
  }
886
+ if (recipe.extraFiles) {
887
+ for (const [relativePath, content] of Object.entries(recipe.extraFiles)) {
888
+ const fullPath = join3(projectDir, relativePath);
889
+ mkdirSync(dirname(fullPath), { recursive: true });
890
+ writeFileSync(fullPath, content);
891
+ }
892
+ }
776
893
  if (recipe.extraPackages.length > 0) {
777
894
  const [command, args] = installCommand(pm, recipe.extraPackages);
778
895
  const installExit = await runCommand(command, args, projectDir);
@@ -891,18 +1008,20 @@ ${dim("Skipped.")}`);
891
1008
  ${yellow("Scaffolded the project, but could not find an entry file to wire up automatically.")} See the quick-start snippet below and add it yourself.`
892
1009
  );
893
1010
  }
894
- console.log(
895
- `
896
- ${green("\u2714")} ${bold("Done.")} Next steps:
897
-
1011
+ for (const configTarget of configTargets) {
1012
+ console.log(`
1013
+ ${configTarget.nextSteps}
1014
+ `);
1015
+ }
1016
+ console.log(`
1017
+ ${green("\u2714")} ${bold("Done!")} Starting dev server...
1018
+ `);
1019
+ const projectDir = result.projectDir;
1020
+ const devExit = await runCommand(pm, ["run", "dev"], projectDir);
1021
+ if (devExit !== 0) {
1022
+ console.log(`
898
1023
  ${cyan(`cd ${projectName}`)}
899
1024
  ${cyan(`${pm} run dev`)}
900
-
901
- ${target.nextSteps}
902
- `
903
- );
904
- for (const configTarget of configTargets) {
905
- console.log(`${configTarget.nextSteps}
906
1025
  `);
907
1026
  }
908
1027
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@christophervr/pptx-viewer",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
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
  "angular",