@textcortex/slidewise 1.0.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 (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/dist/__vite-browser-external-DYxpcVy9.js +5 -0
  4. package/dist/__vite-browser-external-DYxpcVy9.js.map +1 -0
  5. package/dist/file.svg +1 -0
  6. package/dist/globe.svg +1 -0
  7. package/dist/index.mjs +16697 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/dist/slidewise.css +1 -0
  10. package/dist/types/SlidewiseEditor.d.ts +47 -0
  11. package/dist/types/SlidewiseFileEditor.d.ts +54 -0
  12. package/dist/types/components/editor/BottomToolbar.d.ts +1 -0
  13. package/dist/types/components/editor/Canvas.d.ts +1 -0
  14. package/dist/types/components/editor/Editor.d.ts +8 -0
  15. package/dist/types/components/editor/ElementView.d.ts +6 -0
  16. package/dist/types/components/editor/FloatingToolbar.d.ts +6 -0
  17. package/dist/types/components/editor/GridView.d.ts +1 -0
  18. package/dist/types/components/editor/PlayMode.d.ts +1 -0
  19. package/dist/types/components/editor/SelectionFrame.d.ts +8 -0
  20. package/dist/types/components/editor/SlideRail.d.ts +1 -0
  21. package/dist/types/components/editor/SlideView.d.ts +5 -0
  22. package/dist/types/components/editor/TopBar.d.ts +7 -0
  23. package/dist/types/index.d.ts +7 -0
  24. package/dist/types/lib/StoreProvider.d.ts +8 -0
  25. package/dist/types/lib/fonts.d.ts +9 -0
  26. package/dist/types/lib/pptx/deckToPptx.d.ts +9 -0
  27. package/dist/types/lib/pptx/index.d.ts +3 -0
  28. package/dist/types/lib/pptx/pptxToDeck.d.ts +18 -0
  29. package/dist/types/lib/pptx/types.d.ts +15 -0
  30. package/dist/types/lib/pptx/units.d.ts +25 -0
  31. package/dist/types/lib/schema/migrate.d.ts +25 -0
  32. package/dist/types/lib/seed.d.ts +2 -0
  33. package/dist/types/lib/store.d.ts +55 -0
  34. package/dist/types/lib/types.d.ts +141 -0
  35. package/dist/window.svg +1 -0
  36. package/package.json +86 -0
  37. package/src/App.tsx +261 -0
  38. package/src/SlidewiseEditor.css +146 -0
  39. package/src/SlidewiseEditor.tsx +214 -0
  40. package/src/SlidewiseFileEditor.tsx +242 -0
  41. package/src/components/editor/BottomToolbar.tsx +216 -0
  42. package/src/components/editor/Canvas.tsx +467 -0
  43. package/src/components/editor/Editor.tsx +53 -0
  44. package/src/components/editor/ElementView.tsx +588 -0
  45. package/src/components/editor/FloatingToolbar.tsx +729 -0
  46. package/src/components/editor/GridView.tsx +232 -0
  47. package/src/components/editor/PlayMode.tsx +260 -0
  48. package/src/components/editor/SelectionFrame.tsx +241 -0
  49. package/src/components/editor/SlideRail.tsx +285 -0
  50. package/src/components/editor/SlideView.tsx +55 -0
  51. package/src/components/editor/TopBar.tsx +240 -0
  52. package/src/fonts.css +2 -0
  53. package/src/index.css +13 -0
  54. package/src/index.ts +36 -0
  55. package/src/lib/StoreProvider.tsx +43 -0
  56. package/src/lib/__tests__/css-scope.test.ts +133 -0
  57. package/src/lib/fonts.ts +104 -0
  58. package/src/lib/pptx/__tests__/roundtrip.test.ts +240 -0
  59. package/src/lib/pptx/deckToPptx.ts +300 -0
  60. package/src/lib/pptx/index.ts +3 -0
  61. package/src/lib/pptx/pptxToDeck.ts +1515 -0
  62. package/src/lib/pptx/types.ts +17 -0
  63. package/src/lib/pptx/units.ts +32 -0
  64. package/src/lib/schema/__tests__/migrate.test.ts +70 -0
  65. package/src/lib/schema/migrate.ts +102 -0
  66. package/src/lib/seed.ts +777 -0
  67. package/src/lib/store.ts +384 -0
  68. package/src/lib/types.ts +185 -0
  69. package/src/main.tsx +10 -0
  70. package/src/vite-env.d.ts +3 -0
@@ -0,0 +1,17 @@
1
+ import type { SlideElement } from "@/lib/types";
2
+
3
+ /**
4
+ * Result of parsing a PPTX archive. Surfaced to the caller so they can
5
+ * decide whether to warn about lossy fields (animations, transitions,
6
+ * unknown elements that fell into UnknownElement).
7
+ */
8
+ export interface ParseDiagnostics {
9
+ unknownElementCount: number;
10
+ droppedAnimations: number;
11
+ warnings: string[];
12
+ }
13
+
14
+ export interface ParseResult {
15
+ diagnostics: ParseDiagnostics;
16
+ elements: SlideElement[];
17
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Unit conversions between Slidewise pixels and PPTX EMU/inches/points.
3
+ *
4
+ * Slidewise authors at a fixed 1920×1080 px canvas. PPTX widescreen layout is
5
+ * 13.333 × 7.5 inches (12,192,000 × 6,858,000 EMU). The mapping is linear:
6
+ * 1920 px ↔ 12,192,000 EMU ↔ 13.333 in
7
+ * 1080 px ↔ 6,858,000 EMU ↔ 7.5 in
8
+ *
9
+ * That gives 6350 EMU per pixel (and 144 px per inch, 0.5 pt per px).
10
+ */
11
+
12
+ import { SLIDE_W, SLIDE_H } from "@/lib/types";
13
+
14
+ export const EMU_PER_INCH = 914400;
15
+ export const EMU_PER_POINT = 12700;
16
+ export const PX_PER_INCH = 144;
17
+ export const EMU_PER_PX = EMU_PER_INCH / PX_PER_INCH; // 6350
18
+ export const POINTS_PER_PX = 0.5;
19
+
20
+ export const PPTX_SLIDE_W_INCHES = SLIDE_W / PX_PER_INCH; // 13.333…
21
+ export const PPTX_SLIDE_H_INCHES = SLIDE_H / PX_PER_INCH; // 7.5
22
+ export const PPTX_SLIDE_W_EMU = SLIDE_W * EMU_PER_PX; // 12,192,000
23
+ export const PPTX_SLIDE_H_EMU = SLIDE_H * EMU_PER_PX; // 6,858,000
24
+
25
+ export const pxToEmu = (px: number): number => Math.round(px * EMU_PER_PX);
26
+ export const emuToPx = (emu: number): number => emu / EMU_PER_PX;
27
+
28
+ export const pxToInches = (px: number): number => px / PX_PER_INCH;
29
+ export const inchesToPx = (inches: number): number => inches * PX_PER_INCH;
30
+
31
+ export const pxToPoints = (px: number): number => px * POINTS_PER_PX;
32
+ export const pointsToPx = (pt: number): number => pt / POINTS_PER_PX;
@@ -0,0 +1,70 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { migrate, CURRENT_DECK_VERSION } from "../migrate";
3
+
4
+ describe("schema/migrate", () => {
5
+ it("stamps the current version on an unversioned (v0) deck", () => {
6
+ const v0 = {
7
+ title: "Pre-versioning deck",
8
+ slides: [{ id: "s1", background: "#FFFFFF", elements: [] }],
9
+ };
10
+ const out = migrate(v0);
11
+ expect(out.version).toBe(CURRENT_DECK_VERSION);
12
+ expect(out.title).toBe("Pre-versioning deck");
13
+ expect(out.slides).toHaveLength(1);
14
+ });
15
+
16
+ it("passes a current-version deck through unchanged in shape", () => {
17
+ const current = {
18
+ version: CURRENT_DECK_VERSION,
19
+ title: "Current",
20
+ slides: [{ id: "s1", background: "#FFFFFF", elements: [] }],
21
+ };
22
+ const out = migrate(current);
23
+ expect(out.version).toBe(CURRENT_DECK_VERSION);
24
+ expect(out.title).toBe("Current");
25
+ expect(out.slides[0].id).toBe("s1");
26
+ });
27
+
28
+ it("throws on a deck written by a newer Slidewise", () => {
29
+ const future = {
30
+ version: CURRENT_DECK_VERSION + 5,
31
+ title: "From the future",
32
+ slides: [],
33
+ };
34
+ expect(() => migrate(future)).toThrow(/newer than this build/);
35
+ });
36
+
37
+ it("rejects non-object input", () => {
38
+ expect(() => migrate(null)).toThrow();
39
+ expect(() => migrate("not a deck")).toThrow();
40
+ expect(() => migrate(42)).toThrow();
41
+ });
42
+
43
+ it("rejects a deck whose slides is not an array", () => {
44
+ expect(() => migrate({ title: "x", slides: "nope" })).toThrow(
45
+ /slides is not an array/
46
+ );
47
+ });
48
+
49
+ it("rejects a deck with an invalid version field", () => {
50
+ expect(() =>
51
+ migrate({ version: -1, title: "x", slides: [] })
52
+ ).toThrow(/non-negative integer/);
53
+ expect(() =>
54
+ migrate({ version: 1.5, title: "x", slides: [] })
55
+ ).toThrow(/non-negative integer/);
56
+ expect(() =>
57
+ migrate({ version: "1", title: "x", slides: [] })
58
+ ).toThrow(/non-negative integer/);
59
+ });
60
+
61
+ it("does not mutate the input deck", () => {
62
+ const v0 = {
63
+ title: "Original",
64
+ slides: [{ id: "s1", background: "#FFFFFF", elements: [] }],
65
+ };
66
+ const before = JSON.stringify(v0);
67
+ migrate(v0);
68
+ expect(JSON.stringify(v0)).toBe(before);
69
+ });
70
+ });
@@ -0,0 +1,102 @@
1
+ import type { Deck } from "@/lib/types";
2
+
3
+ /**
4
+ * Slidewise deck schema version. Bump this whenever the persisted shape of a
5
+ * Deck changes in a way old hosts cannot read transparently — adding a new
6
+ * required field, renaming a key, restructuring an element type, etc.
7
+ *
8
+ * Bumping always pairs with a migrator at `MIGRATIONS[oldVersion]` that takes
9
+ * a deck shaped at the previous version and returns one shaped at this one.
10
+ * Migrations are run forward in order, so a v0 deck becomes v1 then v2 etc.
11
+ *
12
+ * Never delete an old migrator — published decks in the wild may still be at
13
+ * any historical version, and they all need a path forward.
14
+ */
15
+ export const CURRENT_DECK_VERSION = 1;
16
+
17
+ /**
18
+ * One forward migration. Receives a deck shaped at version `from` and returns
19
+ * a deck shaped at version `from + 1`. Inputs are typed as `any` because the
20
+ * old shape is not the current `Deck` type — that is the whole point of the
21
+ * migration.
22
+ */
23
+ type Migrator = (deck: any) => any;
24
+
25
+ /**
26
+ * Map of migrations keyed by the version they migrate _from_. To migrate a
27
+ * v3 deck to current, we run MIGRATIONS[3] then MIGRATIONS[4] etc. until we
28
+ * reach CURRENT_DECK_VERSION.
29
+ */
30
+ const MIGRATIONS: Record<number, Migrator> = {
31
+ // v0 → v1: pre-versioning decks (no `version` field). Same structural
32
+ // shape as v1, so the migrator is identity — the version stamp happens
33
+ // at the end of `migrate()`.
34
+ 0: (deck) => deck,
35
+ // Future entries look like:
36
+ // 1: (deck) => ({ ...deck, slides: deck.slides.map(addNewField) }),
37
+ };
38
+
39
+ /**
40
+ * Normalise an external deck (from PPTX import, JSON import, localStorage,
41
+ * a host prop, etc.) to the current schema. Stamps the current version on
42
+ * its way out so downstream code can rely on `deck.version`.
43
+ *
44
+ * Throws if the input is missing the basic shape, or if its `version` is
45
+ * higher than `CURRENT_DECK_VERSION` — that means the deck was written by
46
+ * a newer Slidewise and the host should upgrade rather than silently render
47
+ * a degraded version.
48
+ */
49
+ export function migrate(input: unknown): Deck {
50
+ if (!isObject(input)) {
51
+ throw new Error("[slidewise] migrate: input is not an object");
52
+ }
53
+ if (!Array.isArray((input as { slides?: unknown }).slides)) {
54
+ throw new Error("[slidewise] migrate: deck.slides is not an array");
55
+ }
56
+
57
+ // Decks written before versioning existed have no `version` field; treat
58
+ // them as v0. Anything else must be a finite non-negative integer.
59
+ const rawVersion = (input as { version?: unknown }).version;
60
+ let version: number;
61
+ if (rawVersion === undefined) {
62
+ version = 0;
63
+ } else if (
64
+ typeof rawVersion === "number" &&
65
+ Number.isInteger(rawVersion) &&
66
+ rawVersion >= 0
67
+ ) {
68
+ version = rawVersion;
69
+ } else {
70
+ throw new Error(
71
+ `[slidewise] migrate: deck.version is not a non-negative integer (got ${String(
72
+ rawVersion
73
+ )})`
74
+ );
75
+ }
76
+
77
+ if (version > CURRENT_DECK_VERSION) {
78
+ throw new Error(
79
+ `[slidewise] migrate: deck version ${version} is newer than this build supports (max ${CURRENT_DECK_VERSION}). Upgrade Slidewise.`
80
+ );
81
+ }
82
+
83
+ let working: any = input;
84
+ while (version < CURRENT_DECK_VERSION) {
85
+ const step = MIGRATIONS[version];
86
+ if (!step) {
87
+ throw new Error(
88
+ `[slidewise] migrate: no migrator registered for version ${version} → ${
89
+ version + 1
90
+ }`
91
+ );
92
+ }
93
+ working = step(working);
94
+ version += 1;
95
+ }
96
+
97
+ return { ...working, version: CURRENT_DECK_VERSION } as Deck;
98
+ }
99
+
100
+ function isObject(v: unknown): v is Record<string, unknown> {
101
+ return typeof v === "object" && v !== null && !Array.isArray(v);
102
+ }