---
name: superprez-packaging
description: >-
  Package interactive HTML presentations as Superprez.io drop-code zips and
  Superengine-compatible static ports. Use when building, repackaging, fixing,
  or updating decks for Superprez.io upload, GitHub-linked repo push, zip drop-code,
  or html/ docroot. Enforces hosting compatibility; UX (slides, scroll, layout)
  follows user choices — ask before imposing patterns.
---

# Superprez.io packaging

Package presentations so **Superprez.io** can `npm ci` → `npm run build` → `npm start` and serve the deck at `/`.

Default stack: **static HTML under `html/`**, Node preview server, nginx in production via Superengine compose. Do **not** use Vite/React/Next unless the user explicitly requires it.

| Resource | Purpose |
|----------|---------|
| [reference.md](reference.md) | Superengine boilerplate templates |
| [deck-html-patterns.md](deck-html-patterns.md) | Hosting rules + optional UX patterns + **style layer** |
| [github-workflow.md](github-workflow.md) | Git-linked repo: bump, commit, push |
| [failures.md](failures.md) | Known failure modes |
| [../presentation-web-app/style-layer.md](../presentation-web-app/style-layer.md) | **Canonical Phase 3d schema** (palette/fonts/roles, ColorValues) — required |

Public references only (never assume access to a private repo):

- [Superengine ports catalog](https://software.superengine.tech/superengine/)
- [Superengine docs](https://www.superengine.tech/docs)
- [Port license](https://www.superengine.tech/legal/software-license-agreement-for-ports)

---

---

## What Superprez.io requires vs what is yours to design

**Superprez.io only cares that the hosted app works** — it does not mandate a specific look, slide chrome, or navigation style.

| Superprez.io requires (non-negotiable) | User's creative choice (ask first) |
|-------------------------------------|-------------------------------------|
| Entry at `html/index.html`, assets under `html/` | Visual design, colors, typography, layout |
| `GET /` returns real content (not a redirect stub) | Slide deck vs scroll story vs single-page app vs dashboard |
| Paths resolve from docroot; build + start pass | How users move through content (keys, scroll, clicks, none) |
| Superengine boilerplate (manifest, compose, etc.) | Fixed canvas vs fluid responsive layout |
| Self-hosted fonts (reliability, not aesthetics) | Visible controls vs minimal/hidden UI |
| Version bump + cache-bust on redeploy | Canvas size, animations, interactions |

**Do not impose a default slide-deck UX** unless the user wants one or their existing HTML already uses slides. Preserve their creative intent; only fix what breaks hosting or what they ask you to fix.

When past issues appear (no way to advance, text piling up), **propose** fixes — do not silently replace their UX with the template deck.

---

## Before you start — ask the user

Ask when the answer is not obvious. **UX questions matter** — do not skip them for greenfield decks.

### Packaging (ask if unclear)

| Question | Why |
|----------|-----|
| **Delivery:** zip upload or GitHub repo linked to Superprez.io? | Git → bump + commit + push; zip → rebuild archive |
| **Deck title + slug** | manifest, cache-bust strings, package name |
| **Starting material:** single HTML, folder of slides, or scratch? | Step 0 vs merge workflow |

### Presentation model (ask for new decks or broken UX)

| Question | Options (examples) |
|----------|-------------------|
| **How should viewers experience it?** | Classic slides · vertical scroll · one immersive page · free-form interactive app |
| **How do they move forward/back?** | Arrow keys · on-screen buttons · scroll/swipe · click hotspots · custom (describe) |
| **Layout on different screen sizes?** | Fixed canvas scaled as one unit · fluid responsive · full-bleed (describe) |
| **Chrome / controls?** | Visible prev/next + counter · minimal · none (keyboard only) · fully custom |

**Defaults when user says "just package it"** and files already exist: **preserve their UX**; only fix hosting paths and boilerplate. Note assumptions in deliverables.

**Defaults for a brand-new deck with no preference:** ask the presentation-model questions above, or offer: *"Classic arrow-key slides, 1280×720 scaled canvas — or describe something different?"*

| Other | Default |
|-------|---------|
| **Fonts** | Self-host under `html/fonts/` |
| **Interactive features** | Ask if charts, maps, forms, video — may need resize hooks |

---

## Workflow

```
Packaging progress:
- [ ] Pre-flight — delivery method, title/slug (ask if unclear)
- [ ] Step 0 — Normalize HTML + assets into html/
- [ ] Step 1 — Add Superengine boilerplate
- [ ] Step 2 — Deck content: preserve user UX; fix only hosting breakers
- [ ] Step 2b — Slide analytics beacon (classic / multi-view) — **required for Insights**
      · notifySlideChange: postMessage + CustomEvent `superprez:slide_change`
      · payload `{ source: "superprez-deck", type: "slide_change", slideId, slideIndex }` (0-based index)
      · every section: `data-slide` + `data-slide-id` (multi-view: `data-view`)
      · call on every nav **and** initial paint; validate-build.js must PASS
- [ ] Step 2c — Content layer (studio text edit) — **required for Superprez studio copy editing**
      · `<html lang="{iso2}">` matches deck language (never assume English)
      · `html/content/{iso2}.json` with all static copy keys
      · `html/content-bind.js` + `data-sp-text` on every static visible string
      · include content-bind.js before shell.js; cache-bust
- [ ] Step 2d — Style layer (studio style edit) — **required for Superprez studio visual editing**
      · **Follow** [../presentation-web-app/style-layer.md](../presentation-web-app/style-layer.md) completely (do not improvise)
      · `html/styles/theme.json` with non-empty **`palette`**, **`fonts`**, **`roles`**
      · Palette = ColorValues (solid hex **or** `{ type:"gradient", angle, stops }`); brand colors via `palette.*`
      · Font slots from the design (`heading`/`body`, plus `primary`/`secondary`/`tertiary` when multiple faces)
      · Roles use `"font": "<slot>"` and `"color": "palette.<name>"`
      · `slides.json` entry per `data-slide-id` (`solid` / `gradient` / `palette` / `image`) — **no** inline slide backgrounds
      · `elements.json` (may start `{}`); overrides prefer tokens over one-off hex/families
      · `html/style-bind.js` + `data-sp-style` (**same key** as `data-sp-text`) + `data-sp-role`
      · include style-bind.js before shell.js; self-hosted fonts only (no Google Fonts CDN)
- [ ] Step 3 — npm ci && npm run icon && npm run build && npm start
- [ ] Step 4 — Zip (if upload path) or prepare git commit (if GitHub path)
- [ ] Step 5 — PASS/FAIL deliverables
- [ ] Step 6 — Bump version; ask user about commit + push (GitHub path)
```

### Step 0 — User gave HTML + assets (most common)

Do this **before** Superengine boilerplate:

1. Create **`html/`** at package root (sibling of `package.json`).
2. Put entry at **`html/index.html`** (rename `deck.html`, `My Slides.html`, etc.).
3. Move **every** referenced asset under **`html/`**, preserving relative paths.
4. Fix all paths so they resolve from `/` when served (no `../` escaping docroot).
5. **Never** leave `index.html` at zip root next to `package.json`.
6. Add cache-bust query strings: `styles.css?v=DECK_SLUG-1.0.0`.
7. **Self-host fonts** — replace any Google Fonts / CDN `<link>` with local `.woff2` under `html/fonts/` (see [deck-html-patterns.md](deck-html-patterns.md) § Fonts).

Anti-patterns (see [failures.md](failures.md)): meta-refresh to `slides/`, multi-page nav, `../public/` paths.

### Step 1 — Superengine boilerplate

Create every file in [reference.md](reference.md). Replace `DECK_*` placeholders.

**Golden rules:**

1. **One docroot:** `html/` everywhere.
2. **Entitlements:** exactly **`port-number` → `host` → `os`**.
3. **Compose ports:** unquoted `      - {{entitlements.port-number}}:80`
4. **Zip root:** flat or one top-level folder — never double-nested.
5. **`package-lock.json` required.**
6. **`npm start`:** `0.0.0.0`, honor `PORT`, serve `html/`.
7. **LF line endings** in JSON/YAML.
8. **No secrets** in repo or zip.
9. **Exclude:** `node_modules`, `dist`, `build`, `.next`, `__MACOSX`, `.DS_Store`.
10. **Zip size:** keep under **100 MB** (compress large assets; exclude junk).

### Step 2 — Deck content (hosting + user's UX intent)

`GET /` must return **meaningful content** the user intended — not a redirect stub or broken shell.

**Always enforce (hosting):**

- One servable `html/index.html` (or one app entry the user designed)
- No meta-refresh / multi-file page navigation to `slides/*.html`
- Asset paths work from `/`
- If merging per-slide sources, combine into one entry file — **how** they navigate inside is the user's choice

**Only enforce UX the user asked for** (or agree to when fixing a reported bug):

- Slide deck → some way to advance (see [deck-html-patterns.md](deck-html-patterns.md) Pattern A)
- Scaled canvas → uniform scale, not broken reflow (Pattern A)
- Studio iframe compatibility → stage/root container must pin top-left and fill 100% (`position: absolute; top: 0; left: 0; width: 100%; height: 100%;`). Avoid `100vw`/`100vh` or body-level `margin: auto` viewport-centering which clashes with iframe embedding.
- Scroll story → vertical scroll works; test on narrow viewport
- Custom app → verify **their** stated interactions work

If the user wants full creative freedom, package as-is after hosting fixes and verify **their** acceptance criteria — not a generic slide template.

If sources are `html/slides/*.html`, merge into one entry at build time — do not ship separate-page navigation as the hosted experience.

### Step 2b — Slide analytics beacon (classic / multi-view) — **required for Superprez Insights**

When the deck uses **classic slides** (`.deck-slide`) or **multi-view** (`.deck-view` / `data-view`), Superprez **Live / History / funnel** only work if the deck emits the freeze-v1 slide events below. Treat this as packaging infrastructure (same class as `html/` docroot) — not optional UX.

Scroll / single-canvas decks may omit slide events; deck-level open + time still work from the Superprez shell.

#### Protocol (freeze v1 — do not invent another shape)

On **every** slide/view change **and** on **initial paint**, emit:

```js
{
  source: "superprez-deck",
  type: "slide_change",
  slideId: String,      // stable slug, e.g. "intro" — NOT the 1-based counter text
  slideIndex: Number,   // 0-based (first slide = 0). Deck UI may show "1 / N"; Insights maps +1 for display
  totalSlides: Number   // optional but preferred
}
```

Copy-paste helper (put in `html/shell.js` or equivalent):

```js
function notifySlideChange(slideId, slideIndex, totalSlides) {
  var payload = {
    source: "superprez-deck",
    type: "slide_change",
    slideId: String(slideId),
    slideIndex: Number(slideIndex),
    totalSlides: totalSlides != null ? Number(totalSlides) : undefined,
  };
  try {
    if (window.parent && window.parent !== window) {
      window.parent.postMessage(payload, "*");
    }
  } catch (_) { /* ignore when not embedded */ }
  /* Required for top-level Open live (Superprez inject listens for this). */
  try {
    window.dispatchEvent(new CustomEvent("superprez:slide_change", { detail: payload }));
  } catch (_) { /* ignore */ }
}
```

Wire from `updateUI` / `showView` / router **after** toggling `.is-active` (include first paint):

```js
var active = slides[current]; // or views[current]
var id =
  (active && (active.getAttribute("data-slide-id") || active.getAttribute("data-slide"))) ||
  String(current);
notifySlideChange(id, current, total);
```

### Host → deck: `goto_slide` (studio filmstrip)

The Superprez studio posts:

```js
{ source: "superprez-chrome", type: "goto_slide", slideIndex: Number /* 0-based */, slideId?: String }
```

Listen and jump (Pattern A):

```js
window.addEventListener("message", function (ev) {
  var d = ev && ev.data;
  if (!d || d.source !== "superprez-chrome" || d.type !== "goto_slide") return;
  window.__SP_GOTO_SLIDE_HANDLED__ = 1; // skip proxy fallback inject
  current = Math.max(0, Math.min(total - 1, Number(d.slideIndex) || 0));
  updateUI();
});
```

Superprez also injects a fallback activator for `.deck-slide[data-slide]` decks.

#### HTML conventions (non-negotiable for classic slides)

```html
<section class="deck-slide is-active" data-slide="0" data-slide-id="intro">…</section>
<section class="deck-slide" data-slide="1" data-slide-id="problem">…</section>
```

| Attribute | Rule |
|-----------|------|
| `data-slide` | Explicit **0-based** index matching shell `current` |
| `data-slide-id` | Stable slug for Insights labels (`intro`, `ask`, …) — prefer over numeric-only ids |
| Multi-view | Use `data-view="pricing"` (etc.) as `slideId`; still pass 0-based `slideIndex` |

#### Must / must-not

- **Must** call `notifySlideChange` on initial load (not only after the first Next).
- **Must** keep `slideIndex` in sync with the active section’s `data-slide` (no off-by-one vs the counter).
- **Must** ship both `postMessage` (iframe) and `CustomEvent` (Open live).
- **Must not** invent a different `source` / `type`, or put PII / secrets in the payload.
- **Must** pass `npm run build` analytics checks in `scripts/validate-build.js` before zip/push.

Full patterns: [deck-html-patterns.md](deck-html-patterns.md) § Superprez slide analytics beacon. Failure modes: [failures.md](failures.md).

### Step 3 — Verify locally

```bash
npm ci && npm run icon && npm run build && npm start
```

Then at `http://127.0.0.1:8080/`:

- HTTP 200, intended content visible
- **User's chosen navigation** works (e.g. arrows if slide deck, scroll if scroll story)
- No broken layout at a narrow width **for the layout model they chose**
- Network tab: no 404s

Do **not** claim PASS without evidence.

### Step 4 — Deliver

**Zip upload path:**

```bash
cd /path/to/package-root
zip -r ../DECK_SLUG-DECK_VERSION.zip . \
  -x "*.DS_Store" -x "*__MACOSX*" -x "node_modules/*"
```

**GitHub path:** skip zip — follow [github-workflow.md](github-workflow.md) after Step 5.

### Step 5 — Deliverables

Print file tree, changed files, and PASS/FAIL for:

**Packaging** (FAIL blocks delivery)

- `html/index.html` exists; assets under `html/`
- Zip topology / manifest / compose / legal files
- manifest app-spec 1.2; 3 entitlements; templates order
- `npm run build` exits 0
- Cache-bust query params present
- Fonts self-hosted (no `fonts.googleapis.com` in index)
- Classic slides / multi-view: deck posts `{ source: "superprez-deck", type: "slide_change", slideId, slideIndex }` (0-based) on nav + initial load via `postMessage` **and** `CustomEvent("superprez:slide_change")` (`validate-build.js` enforces)
- **Style layer:** `theme.json` has non-empty `palette` + `fonts` + `roles`; every `data-sp-text` has `data-sp-style`; every `data-slide-id` in `slides.json`; `style-bind.js` present — see [../presentation-web-app/style-layer.md](../presentation-web-app/style-layer.md)

**Deck UX** (FAIL only if it contradicts **user's stated intent** or they reported it broken)

- Index serves real content (not redirect)
- Agreed navigation/interaction works
- Agreed layout holds on narrow viewport (no unintended overlap)
- Document what was tested and what the user chose

Fix packaging FAILs before delivery. For UX FAILs, **ask** whether to fix or accept before overriding their design.

### Step 6 — After modifications (GitHub-linked decks)

On **every** edit to a Git-linked deck:

1. Bump `manifest.json` `subject.version` + `package.json` `version`
2. Bump all `?v=` cache-bust strings in `html/index.html`
3. Run `npm run build`
4. **Ask the user:** "Commit and push to GitHub to update Superprez.io live?"

Only commit/push when the user agrees. See [github-workflow.md](github-workflow.md).

For zip-only decks: bump version, rebuild zip, tell user to re-upload.

---

## After upload / redeploy

1. Bump version + cache-bust strings.
2. Push to GitHub **or** re-upload zip.
3. Hard-refresh live URL (private window if cached).

If deck missing from My presentations, see [failures.md](failures.md).

---

## Repackaging an existing project

1. Read `manifest.json`, `compose.yaml.hbs`, `html/index.html`, check for git remote.
2. Fix Step 0 first (docroot, paths, entry file). Fix UX only per user intent or reported bugs.
3. Align boilerplate with [reference.md](reference.md).
4. Re-run Steps 3–6.

Do not reference local-only folders the assistant cannot access.
