Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets).

We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md).
11 changes: 11 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
5 changes: 5 additions & 0 deletions .changeset/initial-1-0-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@textcortex/slidewise": major
---

First versioned release. Marks the public API surface (`SlidewiseEditor`, `SlidewiseFileEditor`, `parsePptx`, `serializeDeck`, `Deck`, `migrate`) as the v1 contract that hosts can pin against. Earlier 0.x builds were unpublished.
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
pull_request:
push:
branches:
- main

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-test:
name: Typecheck, test, build
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Typecheck
run: pnpm exec tsc -b

- name: Test
run: pnpm test

- name: Build library bundle
run: pnpm run build:lib
71 changes: 71 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Release

# On every push to main: if there are pending changesets, open (or update) a
# "Version Packages" PR that consumes them and bumps package.json + writes
# CHANGELOG. When that PR is merged there are no pending changesets, so the
# action publishes to the registry instead.
#
# Required secrets:
# NPM_TOKEN — automation token with publish access for @textcortex scope.
# If absent, the publish step is skipped (the Version Packages
# PR still gets opened).
on:
push:
branches:
- main

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: write
pull-requests: write
id-token: write

jobs:
release:
name: Version or publish
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
# changesets needs full history to compute changelog deltas.
fetch-depth: 0

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
registry-url: https://registry.npmjs.org

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Typecheck
run: pnpm exec tsc -b

- name: Test
run: pnpm test

- name: Build library bundle
run: pnpm run build:lib

- name: Create release PR or publish
uses: changesets/action@v1
with:
# Bump versions and update CHANGELOG when a Version Packages PR is
# opened/updated; publish to the registry when it merges.
version: pnpm run version-packages
publish: pnpm run release
commit: "chore(release): version packages"
title: "chore(release): version packages"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
114 changes: 112 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,112 @@
# SlideWise
PPTX + React = ❤️
# Slidewise

Embeddable React PPTX editor. PPTX import + canvas editor + PPTX export, in
one component.

```bash
pnpm add @textcortex/slidewise
```

Peer dependencies: `react >=19`, `react-dom >=19`.

## Quick start

`SlidewiseFileEditor` wraps the editor with PPTX load/save plumbing — give it
async `loadBlob` and `saveBlob` callbacks and it handles parsing, dirty
tracking, and serialisation.

```tsx
import {
SlidewiseFileEditor,
type SlidewiseFileEditorApi,
} from "@textcortex/slidewise";
import "@textcortex/slidewise/style.css";
import { useRef } from "react";

export function PresentationsRoute({ fileId }: { fileId: string }) {
const apiRef = useRef<SlidewiseFileEditorApi | null>(null);

return (
<SlidewiseFileEditor
onEditorApiChange={(api) => (apiRef.current = api)}
loadBlob={async () => {
const res = await fetch(`/api/files/${fileId}`);
return res.blob();
}}
saveBlob={async (pptx) => {
await fetch(`/api/files/${fileId}`, { method: "PUT", body: pptx });
}}
/>
);
}
```

The host owns transport and conflict detection; Slidewise owns parsing,
editing, and serialisation. Call `apiRef.current.save()` to trigger a save
from outside the editor's top bar; call `apiRef.current.isDirty()` to gate
"unsaved changes" UI.

## Lower-level entry point

If your host already has a `Deck` in memory (e.g. you're storing the JSON
shape in your own database rather than `.pptx` blobs), mount
`SlidewiseEditor` directly:

```tsx
import { SlidewiseEditor, type Deck } from "@textcortex/slidewise";
import "@textcortex/slidewise/style.css";

<SlidewiseEditor
deck={deck}
onChange={(next) => setDeck(next)}
onSave={(next) => persist(next)}
/>;
```

## Working with decks programmatically

Slidewise persists slides as a versioned JSON `Deck`. The schema is the
canonical contract — undo/redo, exports, AI features, and persistence all
key off it.

```ts
import {
parsePptx,
serializeDeck,
migrate,
CURRENT_DECK_VERSION,
type Deck,
} from "@textcortex/slidewise";

const deck: Deck = await parsePptx(blob); // import
const pptx: Blob = await serializeDeck(deck); // export
const safe: Deck = migrate(unknownDeckJson); // normalise an external deck
```

`migrate()` runs every external deck (PPTX import, JSON import, localStorage
hydration, host props) through the schema migration chain so the rest of the
editor only sees current-shape decks. It throws if the input was written by a
newer Slidewise than the host has installed — pin the version range you can
support.

## Releasing

Versioning and publishing run through
[changesets](https://github.com/changesets/changesets).

```bash
pnpm changeset # describe the impact of your change
pnpm version-packages # bump versions + update CHANGELOG (CI usually does this)
pnpm release # build + publish (CI does this on merge)
```

CI (`.github/workflows/release.yml`) opens a "Version Packages" PR whenever
there are pending changesets and publishes to npm when that PR merges.

## Repo layout

- `src/SlidewiseEditor.tsx` / `src/SlidewiseFileEditor.tsx` — public entry components
- `src/components/editor/` — top bar, slide rail, canvas, panels
- `src/lib/pptx/` — PPTX import (`pptxToDeck`) and export (`deckToPptx`)
- `src/lib/schema/` — `Deck` schema versioning + migrator
- `src/lib/types.ts` — `Deck` / `Slide` / `SlideElement` shapes (the contract)
27 changes: 23 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"name": "slidewise",
"name": "@textcortex/slidewise",
"version": "0.2.0",
"description": "Embeddable React PPTX editor.",
"license": "UNLICENSED",
"private": false,
"type": "module",
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
Expand All @@ -15,14 +18,29 @@
"./fonts.css": "./src/fonts.css"
},
"sideEffects": ["**/*.css"],
"repository": {
"type": "git",
"url": "git+https://github.com/textcortex/SlideWise.git"
},
"homepage": "https://github.com/textcortex/SlideWise#readme",
"bugs": {
"url": "https://github.com/textcortex/SlideWise/issues"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"build:lib": "tsc -p tsconfig.lib.json && vite build --mode lib",
"preview": "vite preview",
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint ."
"lint": "eslint .",
"changeset": "changeset",
"version-packages": "changeset version",
"release": "pnpm run build:lib && changeset publish"
},
"peerDependencies": {
"react": ">=19",
Expand All @@ -37,11 +55,10 @@
"lucide-react": "^1.11.0",
"nanoid": "^5.1.9",
"pptxgenjs": "^4.0.1",
"react": "19.2.4",
"react-dom": "19.2.4",
"zustand": "^5.0.12"
},
"devDependencies": {
"@changesets/cli": "^2.27.10",
"@tailwindcss/vite": "^4",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
Expand All @@ -53,6 +70,8 @@
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.2.0",
"jsdom": "^29.1.1",
"react": "19.2.4",
"react-dom": "19.2.4",
"tailwindcss": "^4",
"typescript": "^5",
"typescript-eslint": "^8.42.0",
Expand Down
Loading
Loading