chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:08:41 +08:00
commit bcc7968ff6
9988 changed files with 1016778 additions and 0 deletions
+192
View File
@@ -0,0 +1,192 @@
# Amazon Bedrock provider
Presenton can use **Amazon Bedrock** as the text LLM provider for presentation generation, editing, and chat. Configure Bedrock in the app UI (**Settings → Text provider**) or with environment variables / `userConfig.json` (Docker and desktop).
The backend sends your configured **Model** value to the Bedrock **Converse** API as `modelId`. That means you can use:
- Standard on-demand **model IDs** (for example `us.anthropic.claude-3-5-haiku-20241022-v1:0`)
- **Inference profile ARNs** (required for some newer models such as Claude Sonnet 4.6)
Region and credentials must match the model or profile you choose.
---
## Required configuration
| Field (UI) | Environment variable | Description |
| --- | --- | --- |
| **Provider** | `LLM=bedrock` | Select **Amazon Bedrock**. |
| **Region** | `BEDROCK_REGION` | AWS region for Bedrock (default: `us-east-1`). Must match where the model or inference profile is available. |
| **Model** | `BEDROCK_MODEL` | Model ID or inference profile ARN (see below). |
| **AWS Access Key ID** | `BEDROCK_AWS_ACCESS_KEY_ID` | IAM access key with Bedrock invoke permissions. |
| **AWS Secret Access Key** | `BEDROCK_AWS_SECRET_ACCESS_KEY` | Secret for the access key above. |
### Authentication
Use **one** of these patterns:
1. **AWS access key pair** (most common): Access Key ID + Secret Access Key (required fields above).
2. **Bedrock API key** (optional UI field): `BEDROCK_API_KEY` — if set, you do not need the access key pair.
Do not mix Bedrock API key auth with explicit AWS access key fields; the client accepts one method only.
---
## Optional (advanced) fields
Under **Advanced settings** in the Bedrock form (or via env):
| Field (UI) | Environment variable | When to use |
| --- | --- | --- |
| **AWS Session Token** | `BEDROCK_AWS_SESSION_TOKEN` | Temporary credentials (STS, assumed role, SSO session). |
| **AWS Profile Name** | `BEDROCK_PROFILE_NAME` | Use a named profile from `~/.aws/credentials` instead of inline keys (desktop/local dev). |
These are optional. Most Docker and server deployments only need region, model, and access key + secret.
---
## Model ID vs inference profile ARN
Many Bedrock models support **on-demand** invocation with a model ID like:
```text
us.anthropic.claude-3-5-haiku-20241022-v1:0
```
Some **newer** models (for example **Claude Sonnet 4.6**) do **not** support on-demand throughput with a plain model ID. For those you must use an **inference profile** ARN in the **Model** field.
In the AWS console: **Bedrock → Inference profiles** (or cross-region inference profiles), copy the full ARN, and paste it into Presentons **Model** field unchanged.
Presenton passes that string directly to Converse as `modelId`, so both formats work as long as AWS accepts them for your account and region.
---
## Common error: on-demand throughput not supported
You may see an error similar to:
```text
Invocation of model ID anthropic.claude-sonnet-4-6 with on-demand throughput isn't supported.
A model ID is currently required in model invocation requests for on-demand throughput.
A foundation model ARN or inference profile ARN is currently required in model invocation requests.
```
**What it means**
- The model ID you entered is valid in Bedrock, but that model **only** supports invocation through a **foundation model ARN** or **inference profile ARN**, not classic on-demand model IDs.
- This often applies to newer Anthropic models and **cross-region inference** setups.
**What to do**
1. Open **Amazon Bedrock** in the same **region** as `BEDROCK_REGION`.
2. Find the **inference profile** for the model (for example `us.anthropic.claude-sonnet-4-6`).
3. Copy the full **inference profile ARN**.
4. Paste the ARN into Presentons **Model** field (not the short model ID).
5. Ensure IAM allows `bedrock:InvokeModel` / Converse on that profile in that region.
---
## Example: working configuration (Claude Sonnet 4.6)
Use an inference profile ARN in **Model**, with region **us-east-1**:
| Setting | Value |
| --- | --- |
| Provider | `bedrock` |
| Region | `us-east-1` |
| Model | `arn:aws:bedrock:us-east-1:471112542209:inference-profile/us.anthropic.claude-sonnet-4-6` |
| AWS Access Key ID | Your IAM user or role access key |
| AWS Secret Access Key | Matching secret |
Replace `471112542209` with **your** AWS account ID. The ARN must come from **your** Bedrock console for the profile you enabled.
### Docker
```bash
docker run -it --name presenton -p 5001:80 \
-e LLM="bedrock" \
-e BEDROCK_REGION="us-east-1" \
-e BEDROCK_AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY_ID" \
-e BEDROCK_AWS_SECRET_ACCESS_KEY="YOUR_SECRET_ACCESS_KEY" \
-e BEDROCK_MODEL="arn:aws:bedrock:us-east-1:471112542209:inference-profile/us.anthropic.claude-sonnet-4-6" \
-e IMAGE_PROVIDER="pexels" \
-e PEXELS_API_KEY="YOUR_PEXELS_KEY" \
-e CAN_CHANGE_KEYS="false" \
-v "./app_data:/app_data" \
ghcr.io/presenton/presenton:latest
```
### On-demand model example (Haiku)
For models that support on-demand IDs:
```bash
-e BEDROCK_MODEL="us.anthropic.claude-3-5-haiku-20241022-v1:0"
```
---
## IAM permissions
The IAM principal (user or role) behind your keys needs permission to call Bedrock in the configured region, for example:
- `bedrock:InvokeModel`
- `bedrock:InvokeModelWithResponseStream`
Scope policies to the model IDs or inference profile ARNs you use. If you use inference profiles, include the profile ARN (or a wildcard your org allows) in the resource list.
Enable model access in the Bedrock console (**Model access**) for the foundation models tied to your profile.
---
## Troubleshooting
### Invalid model identifier
- **Symptom:** `ValidationException`, unknown model, or model not found.
- **Checks:**
- **Model** matches exactly what Bedrock shows (ID or full ARN, no extra spaces).
- For Sonnet 4.6class models, use an **inference profile ARN**, not only `anthropic.claude-…` without the profile path.
- Model access is **enabled** for your account in that region.
### Missing Bedrock permissions
- **Symptom:** `AccessDeniedException`, not authorized to perform `bedrock:InvokeModel`.
- **Checks:**
- IAM policy allows invoke on the model or inference profile.
- Keys belong to the intended account; no typo in access key or secret.
- If using `BEDROCK_PROFILE_NAME`, the profiles role has Bedrock permissions.
### Region mismatch
- **Symptom:** Model not found, or profile ARN rejected.
- **Checks:**
- `BEDROCK_REGION` is the region where the model/profile was created (for example `us-east-1` in the ARN path must match `BEDROCK_REGION`).
- Cross-region inference profiles still use a home region in the ARN; configure Presentons region to match AWS guidance for that profile.
### Unsupported on-demand invocation
- **Symptom:** Error text mentions *on-demand throughput isnt supported* or requires *inference profile ARN*.
- **Fix:** Switch **Model** from a plain model ID to the **inference profile ARN** (see [Common error](#common-error-on-demand-throughput-not-supported) above).
### Auth configuration errors at startup
- **Symptom:** “Bedrock auth is incomplete” or “Provide either api_key or AWS credentials”.
- **Fix:** Set `BEDROCK_API_KEY`, **or** both `BEDROCK_AWS_ACCESS_KEY_ID` and `BEDROCK_AWS_SECRET_ACCESS_KEY`. Do not leave all three empty.
---
## Environment variable reference
| Variable | Required | Notes |
| --- | --- | --- |
| `LLM` | Yes | Must be `bedrock`. |
| `BEDROCK_REGION` | Recommended | Default `us-east-1` if unset. |
| `BEDROCK_MODEL` | Yes | Model ID or inference profile ARN. |
| `BEDROCK_AWS_ACCESS_KEY_ID` | If not using API key | Pair with secret. |
| `BEDROCK_AWS_SECRET_ACCESS_KEY` | If not using API key | Pair with access key ID. |
| `BEDROCK_API_KEY` | Optional | Alternative to access key pair. |
| `BEDROCK_AWS_SESSION_TOKEN` | Optional | Temporary credentials. |
| `BEDROCK_PROFILE_NAME` | Optional | Named AWS profile. |
See also the [Deployment configurations](../README.md#-deployment-configurations) section in the main README.
+110
View File
@@ -0,0 +1,110 @@
# Electron Dependency Strategy
This is the working strategy for reducing first-run dependency installers in
the Electron app while keeping Presenton Apache-2.0.
## Recommendation
- Bundle Chrome for Testing with the Electron package (including Microsoft APPX)
for export rendering.
- Bundle ImageMagick under `resources/imagemagick/` for each platform build; the
packaged app validates that bundle during `afterPack`.
- Bundle the presentation export runtime for PPTX-to-HTML conversion and use
Chromium to render custom template previews.
- Extract modern OOXML/OpenDocument text directly without an office engine.
## Licensing Notes
This is engineering guidance, not legal advice.
ImageMagick is practical to bundle. The official license permits personal,
internal, and commercial use, and its terms are close to Apache-2.0. Keep the
ImageMagick license and notices in the distributed app.
Source: https://imagemagick.org/license/
Chromium/Chrome for Testing can be bundled, but the notices matter. Puppeteer
now targets Chrome for Testing for supported automation, and Chromium source is
BSD-style plus third-party licenses. Keep generated browser credits/notices with
the shipped runtime.
Sources:
- https://pptr.dev/supported-browsers
- https://chromium.googlesource.com/chromium/src/+/main/LICENSE
- https://www.chromium.org/chromium-os/licensing/
## Runtime Layout
Bundled Chromium:
```text
electron/resources/chromium/
presenton-runtime.json
chrome/<platform-build-id>/...
```
Populate it with:
```bash
cd electron
npm run prepare:export-chromium
```
Set `SKIP_BUNDLED_CHROMIUM=1` to keep the old first-run download behavior.
Bundled ImageMagick:
```text
electron/resources/imagemagick/<platform>-<arch>/
presenton-runtime.json
...
```
Examples:
```text
electron/resources/imagemagick/win32-x64/magick.exe
electron/resources/imagemagick/darwin-arm64/bin/magick
electron/resources/imagemagick/linux-x64/bin/magick
```
Populate it with:
```bash
cd electron
npm run prepare:imagemagick
```
Platform behavior:
- Windows downloads and validates the official portable `.7z` runtime.
- Linux downloads and validates the official AppImage, then writes a `bin/magick`
wrapper with `APPIMAGE_EXTRACT_AND_RUN=1` so it works without a host FUSE setup.
- macOS vendors a build-host ImageMagick prefix (`magick` on PATH, or
`IMAGEMAGICK_VENDOR_DIR`) and rewrites non-system dylib references into the
packaged runtime with `otool` and `install_name_tool`.
The app checks the manifest-backed bundle before PATH, Homebrew, MacPorts, or
other system installs.
## Current Behavior
- FastAPI receives `IMAGEMAGICK_BINARY`, `MAGICK_HOME`, and
`MAGICK_CONFIGURE_PATH` when the bundled or system ImageMagick runtime is
detected at startup.
- PPTX previews use the bundled PPTX-to-HTML converter and Chromium renderer.
- Modern OOXML/OpenDocument text extraction uses the bundled Python parser.
- Export Chromium and ImageMagick resolution check manifest-backed bundled app
runtimes before user or system locations.
## APPX / Store builds
Before `npm run build:electron`:
1. Run `npm run prepare:export-chromium` so Chromium is under `resources/chromium/`.
2. Run `npm run prepare:imagemagick` so ImageMagick is under
`resources/imagemagick/<platform>-<arch>/`.
3. The bundled export runtime and Chromium handle Template Studio previews.
Microsoft Store (MSIX/APPX) packages install under `Program Files\WindowsApps`.
Bundled Chrome cannot be launched in place from that folder; on first export the app
copies the browser tree to `%LOCALAPPDATA%\…\Cache\msix-export-chromium\` (same pattern
as the MSIX export runtime for Sharp). The portable EXE install does not need this copy.
@@ -0,0 +1,54 @@
# Custom Template Import Getting Started
This document explains the current PPTX import behavior from the custom
template page.
## What This Flow Does
The `Select a PPTX file` action on:
```txt
/custom-template
```
uses the original Template Studio UX. The slide-editor import backend is always
used.
The flow is:
1. Select a PPTX file.
2. Click `Check Fonts`.
3. Review available and missing fonts in the inline font management step.
4. Upload any missing fonts you want to preserve.
5. Continue to generate the old `Slide Preview` screen.
6. Click `Generate Template` to create the reusable template.
Template Studio still uses the same old UX: inline font management, slide
preview, progress, and generated preview cards. Only the Generate backend
changes. It sends the preview data to the Templates V2 API:
```txt
POST /api/v2/templates
```
That v2 request creates and saves the template, while the page stays on the
old `/custom-template` preview experience. The generated preview cards render
the raw Templates V2 `layouts.layouts[*]` JSON directly; they do not stage or
open a slide-editor deck.
Template Studio does not open the `Prepare fonts` dialog, create a slide-editor
deck, stage anything in IndexedDB, or redirect to `/slide-editor`.
## Verify The Flow
1. Start the app normally.
2. Open `/custom-template`.
3. Click `Select a PPTX file`.
4. Choose a `.pptx` file under 100 MB.
5. Confirm the selected file appears in the upload card.
6. Click `Check Fonts`.
7. Confirm the inline font management step appears.
8. Continue to preview.
9. Click `Generate Template`.
10. Confirm the page stays on `/custom-template`.
11. Confirm Generate creates the template with `POST /api/v2/templates`.
+56
View File
@@ -0,0 +1,56 @@
# macOS Development & Distribution
Guides for building and signing the Presenton Electron app on macOS.
| Guide | Description |
|-------|-------------|
| [Direct distribution](./direct-distribution.md) | Developer ID signing, notarization, DMG verification, and release commands for distribution outside the Mac App Store |
| [Mac App Store setup](./mac-app-store-setup.md) | Certificates, provisioning profiles, MAS build commands, and submission notes |
## Quick reference
All commands below run from the `electron/` directory on a Mac.
**First-time setup**
```bash
cd electron
npm run setup:env
```
**Run locally (development)**
```bash
npm run dev
```
**Build a DMG (local distribution, not App Store)**
```bash
npm run build:all
```
Output is written to `electron/dist/`. The default macOS target is a DMG built via `electron/build.js`.
For public releases outside the Mac App Store, use the signed and notarized direct distribution flow:
```bash
export APPLE_KEYCHAIN_PROFILE="presenton-notary"
npm run build:all:mac:signed
```
That assumes the release Mac already has a **Developer ID Application** certificate and a stored `notarytool` profile named `presenton-notary`. See [Direct distribution](./direct-distribution.md) for the exact one-time setup and verification commands.
**Build for the Mac App Store**
See [Mac App Store setup](./mac-app-store-setup.md) for certificates, provisioning profiles, and signing. Summary:
```bash
npm run build:all:mas-dev # development / TestFlight-style testing
npm run build:all:mas # distribution / App Store submission
```
## Related docs
- [Electron dependency strategy](../../electron-dependency-strategy.md) — bundled Chromium, ImageMagick, and export runtime
- [Project README — Electron section](../../../README.md) — prerequisites and high-level build steps
+300
View File
@@ -0,0 +1,300 @@
# macOS Direct Distribution
Use this guide to ship Presenton as a signed and notarized macOS app outside the Mac App Store. This is the correct path for a downloadable DMG from GitHub Releases or presenton.ai.
This is not a Mac App Store build. Do not use MAS provisioning profiles, App Store Connect upload, or App Review for this flow.
If you are building from a Mac that is already registered in Apple Developer, that is fine, but the registration is not what makes the public DMG trusted. Registered devices are for development provisioning and MAS-style testing. Public direct distribution requires a Developer ID Application certificate and Apple notarization.
## What This Produces
The signed release build creates:
```text
electron/dist/
Presenton-<version>.dmg
```
Users should be able to open the DMG and launch Presenton without macOS warning that the app is from an unidentified developer.
## How This Differs From MAS
| Area | Direct distribution | Mac App Store |
|------|---------------------|---------------|
| Certificate | Developer ID Application | Apple Distribution / 3rd Party Mac Developer |
| Provisioning profile | Not used | Required |
| App Sandbox | Not required by this build | Required |
| Notarization | Required | App Store processing handles distribution review |
| Output | DMG for download | PKG for App Store Connect |
The MAS guide in the referenced gist is still useful for the Apple Developer account and signing concepts, but this repo's direct distribution flow deliberately stops before MAS provisioning and App Store submission.
## Repository Configuration
Direct macOS distribution is configured in `electron/build.js`:
- `mac.hardenedRuntime` is enabled for non-MAS macOS builds.
- `mac.entitlements` uses `electron/build/entitlements.mac.plist`.
- `mac.entitlementsInherit` uses `electron/build/entitlements.mac.inherit.plist`.
- `mac.notarize` is enabled unless `PRESENTON_SKIP_NOTARIZATION=1`.
- `dmg.sign` is disabled because the app bundle is signed and notarized; signing the DMG itself is not required.
Use the release scripts in `electron/package.json`:
```bash
npm run build:all:mac:signed
npm run build:electron:mac:signed
npm run dist:mac:signed
```
The `:mac:signed` scripts set `PRESENTON_REQUIRE_MAC_SIGNING=1`, so they fail before packaging if a Developer ID certificate or notarization credentials are missing.
## Exact Setup For A Release Mac
Run this once on the Mac that will build releases.
### 1. Install Apple Command Line Tools
```bash
xcode-select --install
xcrun notarytool --version
```
`notarytool` must be available. If it is missing, install or update Xcode.
### 2. Install The Developer ID Certificate
In Xcode:
1. Open **Xcode** -> **Settings** -> **Accounts**.
2. Select the Apple Developer team.
3. Click **Manage Certificates**.
4. Click **+**.
5. Select **Developer ID Application**.
Do not choose **Apple Development** for public distribution. Do not choose **Apple Distribution** unless you are building for the Mac App Store. For a downloadable DMG, the certificate must be **Developer ID Application**.
Confirm the certificate is visible to `codesign`:
```bash
security find-identity -v -p codesigning | grep "Developer ID Application"
```
Expected shape:
```text
Developer ID Application: Your Company Name (TEAMID)
```
Most release Macs only have one Developer ID Application certificate, so you usually do not need to export a signing identity. If multiple Developer ID certificates are installed, set the exact identity before building:
```bash
export PRESENTON_MAC_SIGN_IDENTITY="Developer ID Application: Your Company Name (TEAMID)"
```
### 3. Store Notarization Credentials Once
Create an app-specific password for the Apple ID, then store notarization credentials in the local Keychain:
```bash
xcrun notarytool store-credentials "presenton-notary" \
--apple-id "apple-id@example.com" \
--team-id "TEAMID" \
--password "app-specific-password"
```
After that, the only notarization environment variable needed for normal local release builds is:
```bash
export APPLE_KEYCHAIN_PROFILE="presenton-notary"
```
To avoid exporting it manually every shell session, add it to your shell profile:
```bash
echo 'export APPLE_KEYCHAIN_PROFILE="presenton-notary"' >> ~/.zshrc
source ~/.zshrc
```
If you store the profile in a non-default keychain, also set:
```bash
export APPLE_KEYCHAIN="/path/to/keychain"
```
Do not commit notarization credentials, app-specific passwords, `.p8` keys, or real certificates to the repo.
## Build A Signed DMG
After the one-time setup, a normal release build is:
```bash
cd electron
npm run build:all:mac:signed
```
If `APPLE_KEYCHAIN_PROFILE` is not in your shell profile, run it inline:
```bash
cd electron
APPLE_KEYCHAIN_PROFILE="presenton-notary" npm run build:all:mac:signed
```
For the very first build on a fresh checkout, run setup first:
```bash
cd electron
npm run setup:env
npm run build:all:mac:signed
```
If the app resources are already built and you only need to re-run Electron packaging:
```bash
cd electron
npm run dist:mac:signed
```
The signed DMG is written to `electron/dist/`.
## What You Do Not Need
For public direct distribution, you do not need:
- A registered test device.
- A `.provisionprofile` file.
- `PRESENTON_MAS_DEV_IDENTITY`.
- `PRESENTON_MAS_DISTRIBUTION_IDENTITY`.
- `PRESENTON_APP_STORE_VERSION`.
- App Store Connect upload.
- App Review approval.
Those are MAS or development-provisioning concerns. The public DMG path is Developer ID signing plus notarization.
## Other Credential Options
The local Keychain profile above is the recommended flow for a human-operated release Mac. CI can use App Store Connect API keys instead:
```bash
export APPLE_API_KEY="/secure/path/AuthKey_XXXXXXXXXX.p8"
export APPLE_API_KEY_ID="XXXXXXXXXX"
export APPLE_API_ISSUER="00000000-0000-0000-0000-000000000000"
npm run build:all:mac:signed
```
You can also pass Apple ID credentials directly, but this is less convenient than a stored Keychain profile:
```bash
export APPLE_ID="apple-id@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="app-specific-password"
export APPLE_TEAM_ID="TEAMID"
npm run build:all:mac:signed
```
## Verify The Release
Run these checks before publishing the DMG.
### 1. Check Code Signature
Replace the app path if the architecture-specific output folder differs.
```bash
codesign --verify --deep --strict --verbose=2 "dist/mac/Presenton.app"
codesign -dv --verbose=4 "dist/mac/Presenton.app" 2>&1 | grep -E "Authority|TeamIdentifier|Runtime"
```
Expected:
- `Authority=Developer ID Application: ...`
- `TeamIdentifier=S6W5C54KL6`
- Hardened Runtime is present.
### 2. Check Notarization Stapling
```bash
xcrun stapler validate "dist/mac/Presenton.app"
```
Expected:
```text
The validate action worked!
```
### 3. Check Gatekeeper
```bash
spctl --assess --type execute --verbose=4 "dist/mac/Presenton.app"
spctl --assess --type open --verbose=4 "dist/Presenton-0.8.8-beta.dmg"
```
Expected shape:
```text
accepted
source=Notarized Developer ID
```
### 4. Test On Another Mac
Download the DMG on a Mac that did not build it, mount it, drag Presenton to `/Applications`, and launch it normally. This catches quarantine and Gatekeeper behavior that local build machines can hide.
## Troubleshooting
**The signed release build says the Developer ID identity is missing**
Install a Developer ID Application certificate in Keychain Access, or set:
```bash
export PRESENTON_MAC_SIGN_IDENTITY="Developer ID Application: Your Company Name (TEAMID)"
```
**The build says notarization credentials are missing**
Set one complete credential group:
```bash
export APPLE_KEYCHAIN_PROFILE="presenton-notary"
```
or:
```bash
export APPLE_ID="apple-id@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="app-specific-password"
export APPLE_TEAM_ID="TEAMID"
```
or:
```bash
export APPLE_API_KEY="/secure/path/AuthKey_XXXXXXXXXX.p8"
export APPLE_API_KEY_ID="XXXXXXXXXX"
export APPLE_API_ISSUER="00000000-0000-0000-0000-000000000000"
```
**macOS still says the app is damaged or cannot be opened**
Run the verification commands above. If stapling fails, rebuild with valid notarization credentials and do not publish the DMG until `spctl` reports `source=Notarized Developer ID`.
**You need a local unsigned build**
Use the generic build script instead of the signed release script:
```bash
npm run build:all
```
For release artifacts, always use:
```bash
npm run build:all:mac:signed
```
## References
- Steve Crickmore's Electron MAS release gist: https://gist.github.com/steve981cr/def310670dfd9ed1439bf31cc734f941
- Electron signing and notarization docs: https://www.electronjs.org/docs/latest/tutorial/code-signing
- electron-builder notarization docs: https://www.electron.build/code-signing-mac.html#notarize
- Apple notarization docs: https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution
+201
View File
@@ -0,0 +1,201 @@
# Mac App Store Setup
Presentons Electron app is configured for Mac App Store (MAS) distribution with App Sandbox. MAS builds must be produced on macOS because Apples signing tools are required.
This guide covers Apple Developer setup, local provisioning files, and the npm scripts in `electron/package.json` on `main`.
## Overview
| Target | Purpose | npm script |
|--------|---------|------------|
| `dmg` | Direct download outside the App Store | `npm run build:all:mac:signed` |
| `mas-dev` | Sandbox testing on registered Macs | `npm run build:all:mas-dev` |
| `mas` | App Store submission | `npm run build:all:mas` |
Build configuration lives in `electron/build.js`. Key identifiers:
- **Bundle ID:** `com.presenton.presenton`
- **Team ID:** `S6W5C54KL6` (set in `extendInfo.ElectronTeamID`)
## MAS files in the repo
These are checked in under `electron/build/`:
| File | Role |
|------|------|
| `entitlements.mas.plist` | Main app entitlements (sandbox, network, JIT, user-selected files) |
| `entitlements.mas.inherit.plist` | Inherited entitlements for helper processes and bundled binaries |
| `MacAppStore.provisionprofile.replace_me` | Placeholder — replace with your real App Store profile (see below) |
`build.js` validates provisioning profiles with `security cms -D` before building. Invalid or placeholder files will fail the build with a clear error.
## What you need from Apple
### 1. Apple Development certificate
Xcode → **Settings****Accounts** → your team → **Manage Certificates****+** → **Apple Development**.
Used for `mas-dev` builds and local sandbox testing.
### 2. Apple Distribution certificate
Same path as above, but choose **Apple Distribution**.
Used for `mas` (App Store submission) builds.
### 3. App ID (Bundle ID)
[Apple Developer Portal](https://developer.apple.com/account) → **Certificates, Identifiers & Profiles****Identifiers****App IDs**.
Create a macOS App ID that matches `com.presenton.presenton` (or update `APP_ID` in `electron/build.js` if you use a different ID).
### 4. Provisioning profiles
Create **two** macOS profiles and download each `.provisionprofile` file into `electron/build/`:
#### AppleDevelopment (MAS development)
- Portal → **Profiles****+**
- Type: **macOS App Development**
- App ID: your Presenton app ID
- Certificate: **Apple Development**
- Save as `electron/build/AppleDevelopment.provisionprofile`
`build.js` also accepts `AppleDev.provisionprofile` or `AppDev.provisionprofile` as fallbacks.
#### MacAppStore (MAS distribution)
- Portal → **Profiles****+**
- Type: **Mac App Store**
- App ID: your Presenton app ID
- Certificate: **Apple Distribution**
- Save as `electron/build/MacAppStore.provisionprofile`
Verify profiles decode correctly:
```bash
cd electron
security cms -D -i build/AppleDevelopment.provisionprofile
security cms -D -i build/MacAppStore.provisionprofile
```
Provisioning profiles expire (typically yearly). Regenerate and replace the local files when they do.
## Environment variables
| Variable | Used for | Description |
|----------|----------|-------------|
| `PRESENTON_MAC_TARGET` | Set by npm scripts | `mas-dev` or `mas` (do not set manually when using the scripts below) |
| `PRESENTON_MAS_DEV_IDENTITY` | `mas-dev` | Signing identity, e.g. `Apple Development: Your Name (TEAMID)` |
| `PRESENTON_MAS_DISTRIBUTION_IDENTITY` | `mas` | Distribution identity, e.g. `Apple Distribution: Your Org (TEAMID)` |
| `PRESENTON_MAS_IDENTITY` | `mas` | Alias for distribution identity |
| `CSC_NAME` | Either target | Fallback if the target-specific identity vars are unset |
| `PRESENTON_APP_STORE_VERSION` | `mas` | App Store **version** (`x.y.z`). Required when `package.json` version contains a suffix like `-beta` |
| `PRESENTON_APP_STORE_BUILD` | `mas` | App Store **build** number. Defaults to the short version if unset |
Example for a beta package version:
```bash
export PRESENTON_APP_STORE_VERSION=1.0.0
export PRESENTON_APP_STORE_BUILD=42
```
## Build commands
Run from `electron/` after `npm run setup:env` (first time only).
### Full build (recommended)
Rebuilds Next.js, FastAPI, export runtime, and packages the app:
```bash
# MAS development — runs on Macs in your dev provisioning profile
PRESENTON_MAS_DEV_IDENTITY="Apple Development: Your Name (TEAMID)" \
npm run build:all:mas-dev
# MAS distribution — for App Store Connect upload
PRESENTON_MAS_DISTRIBUTION_IDENTITY="Apple Distribution: Your Org (TEAMID)" \
PRESENTON_APP_STORE_VERSION=1.0.0 \
npm run build:all:mas
```
### Package only (resources already built)
If you already ran `build:all` and only need to re-run the Electron packager:
```bash
PRESENTON_MAS_DEV_IDENTITY="Apple Development: Your Name (TEAMID)" \
npm run dist:mac:mas-dev
PRESENTON_MAS_DISTRIBUTION_IDENTITY="Apple Distribution: Your Org (TEAMID)" \
PRESENTON_APP_STORE_VERSION=1.0.0 \
npm run dist:mac:mas
```
### Direct DMG (not App Store)
For a signed and notarized macOS disk image that does not go through App Store approval:
```bash
npm run build:all:mac:signed
```
See [Direct distribution](./direct-distribution.md) for the Developer ID certificate, notarization, and verification flow.
Artifacts land in `electron/dist/`.
## Output locations
Exact paths depend on architecture (`arm64` vs `x64`). Typical layouts:
```text
electron/dist/
mas-dev-arm64/ # unsigned or dev-signed .app (mas-dev)
mas-arm64/ # distribution-signed .app (mas)
Presenton-<version>.pkg # MAS installer for upload
Presenton-<version>.dmg # signed and notarized direct distribution DMG
```
Upload the `.pkg` from the `mas` build to [App Store Connect](https://appstoreconnect.apple.com).
## Icons
The default macOS icon is `electron/resources/ui/assets/images/presenton_short_filled.png`.
For App Store packaging you can provide a proper `.icns` at `electron/build/icon.icns` and update the `mac.icon` field in `electron/build.js` if you want a custom store icon.
Source PNGs for generating an icon set are in `electron/build/icon.iconset/`.
## Testing notes
- **MAS-signed apps** require the MAS Electron build (`mas` or `mas-dev` targets). A normal DMG build is not sandbox-compatible in the same way.
- **`mas-dev` builds** run only on Macs registered in your development provisioning profile.
- **`mas` (distribution) builds** usually do not launch locally; they are meant for App Store Connect processing and release.
- **`mas-dev` signing** uses `--timestamp=none` in `build.js` so local signing works without a network timestamp server.
## Troubleshooting
**“Missing MAS development/distribution provisioning profile”**
Place the correct `.provisionprofile` in `electron/build/` (see [Provisioning profiles](#4-provisioning-profiles)).
**“macOS could not decode” a profile**
Re-download the profile from the Developer Portal. Do not commit real profiles to git.
**“Cannot derive an App Store version from package version”**
Set `PRESENTON_APP_STORE_VERSION` to three integers, e.g. `1.0.0`.
**“MAS builds must be run on macOS”**
Run the build on a Mac with Xcode command-line tools installed (`xcode-select --install`).
**FastAPI or export binary not executable inside the .app**
`build.js` runs an `afterPack` hook that sets executable permissions on bundled binaries. If signing fails afterward, check that entitlements allow the sandboxed app to execute helpers in `Contents/Resources/app/resources/`.
## See also
- [macOS dev README](./README.md) — quick reference for all macOS build targets
- [Electron dependency strategy](../../electron-dependency-strategy.md) — what gets bundled in the app
+12
View File
@@ -0,0 +1,12 @@
## 2. Remove the runtime adapter path
From the Template V2 presentation flow, remove calls to:
- adaptTemplateV2LayoutToSlide
- normalizeTemplateV2Slide
- serializeTemplateV2LayoutFromSlide
- serializeTemplateV2ContentFromSlide
- applyGeneratedSlideContentToLayout
- old normalized slide layout resolver
Changes in editor, to adpot the slide.ui /components/elements.
@@ -0,0 +1,295 @@
# Template V2 direct component rendering
This document describes the Template V2 presentation runtime on branch
`dipesh/pre-159-move-new-editor-code-to-main-codebase-for-proper-integration`.
The governing rule is:
> Select a layout → write the complete layout to `slide.ui` → render `slide.ui`
> directly → edit `slide.ui` → persist `slide.ui`.
`slide.ui` is the only frontend source of truth for Template V2 layout and
content. The presentation renderer does not read Template V2 content from
`slide.content` and does not maintain a second canonical `Slide` copy.
## Active flow
```text
selected Template V2 layout
|
v
slide.ui = raw 1280 x 720 layout JSON
|
v
V1ContentRender
|
v
TemplateV2KonvaSlide
|
+-- render raw ui.components with react-konva
+-- edit an immutable, structurally shared raw UI tree
+-- dispatch updateSlideUi({ index, ui })
|
v
Redux presentationData.slides[index].ui
|
v
debounced presentation autosave
```
The presentation runtime no longer performs this conversion loop:
```text
raw slide.ui
-> canonical editor Slide
-> layout resolver / SlideSurface
-> reverse serialization
-> slide.ui plus hidden slide.content state
```
This removes redundant object construction, schema translation, Jotai editor
state, layout resolution, and reverse serialization from the render/edit path.
## Primary implementation files
| File | Responsibility |
| --- | --- |
| [`TemplateV2KonvaSlide.tsx`](../servers/nextjs/components/slide-editor/surface/TemplateV2KonvaSlide.tsx) | The preserved Dipesh component, now responsible for raw UI rendering, selection, transforms, existing element toolbars, inline editing, insertions, chart/image integration, history, and UI commits. |
| [`V1ContentRender.tsx`](../servers/nextjs/app/(presentation-generator)/components/V1ContentRender.tsx) | Routes Template V2 presentation slides directly to the raw renderer. |
| [`events.ts`](../servers/nextjs/components/slide-editor/events/events.ts) | Shared insert, chart, and active-surface event contracts. |
| [`presentationGeneration.ts`](../servers/nextjs/store/slices/presentationGeneration.ts) | Owns `updateSlideUi`, which updates only the selected slide's `ui`. |
| [`NewSlide.tsx`](../servers/nextjs/app/(presentation-generator)/presentation/components/NewSlide.tsx) | Writes a selected Template V2 layout directly into a new slide's `ui`. |
| [`blank-slide.ts`](../servers/nextjs/app/(presentation-generator)/_shared/blank-slide.ts) | Supplies an empty raw UI with `components: []`. |
| [`useAutoSave.tsx`](../servers/nextjs/app/(presentation-generator)/presentation/hooks/useAutoSave.tsx) | Persists the Redux presentation after its debounce. |
There are two `NewSlide.tsx` entry points in the presentation-generator tree.
Both now use the same `slide.ui` behavior.
## Raw UI contract
The renderer consumes the backend Template V2 shape without converting units:
```json
{
"id": "layout_id",
"background": "#FFFFFF",
"components": [
{
"id": "hero",
"position": { "x": 80, "y": 80 },
"size": { "width": 1120, "height": 560 },
"elements": [
{
"type": "text",
"name": "title",
"position": { "x": 0, "y": 0 },
"size": { "width": 900, "height": 140 },
"runs": [{ "text": "Title" }]
}
]
}
]
}
```
Geometry is interpreted in the native `1280 x 720` stage coordinate system:
- component positions are slide-relative;
- top-level element positions are component-relative;
- nested element positions are parent-relative;
- positions, sizes, font sizes, gaps, padding, strokes, and radii remain raw
UI values.
The loose raw boundary is intentional. Alias readers handle known snake-case
and camel-case variants without rewriting the entire tree.
## Rendering and editing
`TemplateV2KonvaSlide` creates one fixed-size Konva Stage and renders
each raw component as a clipped Group. Raw elements are traversed recursively
through `children`, `elements`, `child`, or repeated `item` structures.
Supported visuals include:
- text and text lists;
- images and SVG;
- rectangles, ellipses, lines, groups, containers, flex, and grid;
- tables;
- bar, line, area, and pie-style charts;
- infographic/progress primitives.
Interaction behavior:
- components can be selected, dragged, resized, and rotated;
- a selected element can be dragged, resized, and rotated;
- the first pointer interaction on an unselected child continues to select or
move its component; a selected child receives its own drag;
- layout-managed children receive `__presenton_manual_position` after a manual
transform so the layout engine does not immediately overwrite the edit;
- text, text-list, table, and SVG values use inline overlays;
- images use the upload API and save the returned source in the raw element;
- charts use the shared chart-panel event bridge;
- the existing `ElementToolbar` controls are retained as a selected-element
editor boundary; only that selected element is projected into editor units,
while rendering and persistence remain raw `slide.ui` operations;
- component-level design-variable controls are retained through the same
editor boundary and write their changes back into the raw component tree;
- undo and redo store raw UI snapshots, not canonical editor Slides.
## Backend-shape compatibility
The renderer reads and preserves the backend's current data shapes:
- TextList items may be arrays of text runs, direct text records, or legacy
strings.
- Table cells may contain `runs`, a direct `text`, and either `color` or `fill`.
Editing preserves the existing cell and first-run style metadata.
- Chart series read `values`, with `data` accepted as a compatibility alias.
This matters because a direct renderer must not silently depend on the canonical
editor adapter to normalize these values first.
## Commit and persistence behavior
Every edit ends at one mutation boundary:
```ts
updateSlideUi({
index: slideIndex,
ui: nextUi,
})
```
The reducer changes only:
```text
presentationData.slides[slideIndex].ui
```
It does not replace the whole slide and does not write an editor snapshot into
`slide.content`. The existing autosave observes the Redux presentation object
and persists the updated `ui` after its debounce.
New Template V2 slides are initialized as follows:
```text
layout selection -> newSlide.ui = selected raw layout
empty selection -> newSlide.ui = { background: "#FFFFFF", components: [] }
```
`content: {}` is currently retained on newly created slides for the shared API
shape, but it is not a Template V2 rendering or editing source.
## Adapter cleanup status
The active Template V2 presentation runtime has no calls to:
- `adaptTemplateV2LayoutToSlide`;
- `normalizeTemplateV2Slide`;
- `serializeTemplateV2LayoutFromSlide`;
- `serializeTemplateV2ContentFromSlide`;
- `applyGeneratedSlideContentToLayout`;
- the old normalized slide layout resolver.
`TemplateV2KonvaSlide` was preserved and changed in place. Its old whole-slide
adapter, Jotai deck hydration, `SlideSurface` route, reverse serializers, and
hidden `__template_v2_konva_slide__` content fallback were removed.
Some similarly named utilities remain for separate consumers:
- `TemplateV2LayoutPreview` still uses `adaptTemplateV2LayoutToSlide` to feed
the shared preview surface.
- The reusable slide editor still uses `normalizeTemplateV2Slide` for canonical
`Slide` model conversion where needed.
- The Blocks/palette boundary can still convert a selected library component
into an insertion payload. This conversion occurs only on insertion, not on
every render or edit.
Deleting these shared utilities globally would break template previews and the
standalone canonical editor without improving presentation-render performance.
They are not mounted in the direct presentation path.
## Backend generation boundary
The FastAPI generator currently creates semantic `slide.content` and then calls
`_apply_template_v2_content_to_ui()` during generation/streaming. The result is
a hydrated `slide.ui`, which is what the frontend renders and persists.
That server operation is generation-time work, not a browser render adapter.
The stricter final architecture should generate named element values directly
into a copied UI tree and keep `slide.content` empty for Template V2. Removing
the server hydrator before replacing the generator output would produce blank
template values, so it is intentionally outside this frontend runtime cleanup.
## Performance analysis
The direct path is structurally cheaper than the old presentation path because
it removes full-tree model conversion and reverse conversion from mounts and
edits. It also avoids initializing the standalone slide editor's state graph for
each presentation surface.
The editor hot path is optimized as follows:
- the incoming immutable UI becomes the initial draft by reference; no
full-tree startup clone is performed;
- edits use structural sharing and copy only the UI/component/element path that
changed;
- unchanged components, nested elements, and visual nodes are memoized;
- undo/redo stores immutable UI references in a bounded 50-entry history
instead of deep-cloning the complete UI for every edit;
- inline typing keeps its draft inside the textarea component, so keystrokes do
not rerender the Stage;
- repeated `item` rendering reuses the immutable source object rather than
cloning it per occurrence and render;
- view-only Layers disable Konva hit testing;
- global deck history runs at the debounced autosave boundary and stores
immutable structurally shared snapshots instead of cloning/stringifying the
complete deck on every editor commit;
- autosave change detection uses Redux object identity rather than serializing
the complete presentation merely to compare it.
Costs that remain include redraw of the affected Konva Layer,
selected-element toolbar projection, and request serialization performed by the
API client when autosave executes.
The next performance work should be evidence-driven:
1. Profile initial render, drag-end commit, inline-edit commit, and autosave on a
large real deck.
2. Split unusually large slides across carefully measured Konva Layers if canvas
redraw is still a bottleneck after React subtree memoization.
3. Move autosave comparison/persistence to slide-level patches if full-deck
JSON serialization is measurable.
4. Add browser performance tests before claiming a specific speedup.
The architecture eliminates known redundant work, but “blazing fast” still
requires measurements on representative decks.
## Verification checklist
For a Template V2 slide:
1. Confirm selecting a normal or blank layout immediately creates `slide.ui`.
2. Confirm `V1ContentRender` passes only `slide.ui` plus slide identity to the
direct renderer.
3. Render text, TextList, table, image, chart, flex, grid, and nested elements.
4. Drag, resize, and rotate both a component and a selected element.
5. Edit text/list/table values and verify their raw source shape is preserved.
6. Insert a palette element and a reusable block.
7. Replace an image and update a chart.
8. Undo/redo, reload, and confirm the saved values remain in `slide.ui`.
9. Confirm the presentation runtime does not create
`__template_v2_konva_slide__` in `slide.content`.
10. Confirm template previews and the standalone slide editor still work,
because their adapters were deliberately retained.
## Rules to preserve
1. Treat `slide.ui` as the only Template V2 frontend source of truth.
2. Do not add a renderer-side merge from `slide.content`.
3. Do not convert the whole UI to a canonical `Slide` for presentation display.
4. Preserve unknown raw fields while editing known fields.
5. Keep raw geometry in one coordinate system.
6. Keep preview/editor compatibility code outside the presentation runtime.
7. Persist through `updateSlideUi` and the existing autosave boundary.
8. Benchmark before adding caching complexity.