# CCS PDF Editor — Changelog 1.5.25

**Product version:** `1.5.40`  
**Runtime cache-bust:** `config.js` → `1.5.40-redact-select-ux`  
**Date:** 2026-08-15  
**Owner:** CCS Information Technology  

Internal product development release. Builds on **1.5.3** (tabs, forms KISS export,
session restore) with **catalog-embedded project state**, **layered seals**, and
**form-signature attestation** UX. Patch line **1.5.26–1.5.40** below.

Prior notes: [CHANGELOG-1.5.3.md](CHANGELOG-1.5.3.md).

---

## 1.5.40 (patch) — 2026-08-15

- **Staff how-to:** `docs/HOW-TO.md` renders to **`docs/index.html`** (docs-folder landing). In-app **?** modal links to it. `Open Documentation.bat` opens the how-to.
- **`docs/marketing/index.html` is still the hand-maintained IT/compliance landing** and is **not** regenerated by `render-docs.ps1`.
- **Renderer encoding:** generated HTML no longer mojibakes `·` / em dashes (`Â·`, `â€"`). Markdown is read as UTF-8; template punctuation is HTML entities.
- Cache-bust: `1.5.40-howto-docs`.

## 1.5.39 (patch) — 2026-08-07

- **Tab close focus:** closing the active tab focuses the neighbor to the **right**, or if none the neighbor to the **left** (browser/IDE style). Previously activated `Map` first entry (often a hidden spare), which looked blank until the user clicked another tab.
- Cache-bust: `1.5.39-tab-close-focus`.

## 1.5.38 (patch) — 2026-08-07

- **Spare empty shells:** bootstrap/close-last-tab keeps an internal empty pane for `APP.DOM.viewer` but **hides it from the tab bar** while real documents exist. Only shown when it's the sole doc. `+` still creates a visible Untitled tab. No more flash of blank `document.pdf` on restore.
- Cache-bust: `1.5.38-spare-shell-hidden`.

## 1.5.37 (patch) — 2026-08-07

- Hard-refresh blank tab cleanup + don't persist empty shells (refined further in 1.5.38).
- Cache-bust: `1.5.37-empty-shell-cleanup`.

## 1.5.36 (patch) — 2026-08-07

- **Session restore:** uses soft toast + doc gate (no full-screen dim). Multi-page docs stream pages; chip shows `Restoring k/n: name` and page progress; save/print still blocked until done.
- Cache-bust: `1.5.36-restore-soft-status`.

## 1.5.35 (patch) — 2026-08-07

- **Soft doc gate for large single-PDF load:** no full-screen dim — pages stream in as they render; non-blocking chip shows progress; **save / export / print** stay blocked until done.
- **Stuck-lock hardening:** refcounted `beginDocGate`/`endDocGate`, always released in `finally`, **30‑minute safety timeout** force-clear, plus `forceClearAppBusy()` emergency unlock. Full dim overlay (`showLoading`) still used for multi-file combine and export.
- Cache-bust: `1.5.35-soft-doc-gate`.

## 1.5.34 (patch) — 2026-08-07

- **Single multi-page PDF load:** progress during page render (refined in 1.5.35 to soft-gate without dim).
- Cache-bust: `1.5.34-page-load-status`.

## 1.5.33 (patch) — 2026-08-07

- **Service worker:** network-only pass-through (install/activate, `skipWaiting` + `clients.claim()`, wipe prior Cache API stores). Removes network-then-cache fallback that could serve stale assets after deploys. PWA installability retained; asset freshness relies on origin + `config.js` `?v=` cache-bust.
- Cache-bust: `1.5.33-sw-network-only`.

## 1.5.32 (patch) — 2026-08-07

- **Restore de-dupe:** if you re-open the same PDF you just had open, no “Restore 1 previous…” bar (and restore skips tabs that already match open docs by name / file handle). Multi-tab sessions still offer restore for the *other* files.
- Cache-bust: `1.5.32-restore-dedupe`.

## 1.5.31 (patch) — 2026-08-07

- **PWA multi-file drag/drop:** Chromium invalidates `DataTransferItemList` after the first `await getAsFileSystemHandle()` in a loop — only one PDF was kept, so the combine/tabs prompt never appeared. Collect all handle promises synchronously, prefer `FileList` when it has more files, and treat `.pdf` by extension as well as MIME.
- Cache-bust: `1.5.31-multidrop-fix`.

## 1.5.30 (patch) — 2026-08-07

- **Restore bar after startup open:** freeze the prior multi-tab session *before* the newly opened file can `persistSession()`. Clicking **Restore** no longer re-reads an overwritten IDB entry and re-opens a duplicate of the file you just launched.
- Hold session writes until **Restore** or **Dismiss**; restore uses the frozen payload + original blob keys.
- Cache-bust: `1.5.30-restore-snapshot`.

## 1.5.29 (patch) — 2026-08-07

- **Multi-PDF load busy UI:** dim + spinner while combining / appending / opening multiple PDFs (`showLoading`), with progress text (`Appending PDF k of N…`). Blocks save/export and nested imports while busy so users cannot save a partial combine.
- **Startup tab race fix:** opening a PDF after closing multi-tab sessions no longer leaves a blank black left tab with content + stale title on the next slot.
  - Pin `renderPDF` / `renderPage` to a target viewer pane (tab-switch safe).
  - Serialize open vs session restore; host waits for “accepting opens”.
  - If launched by opening a file, skip silent session restore (offer restore bar instead).
  - Remap restored tab ids for correct active tab; title updates target a specific doc bag.
- **Shell:** WebView2 open-files flush waits for page ready (longer fallback) to avoid racing restore.
- Cache-bust: `1.5.29-load-busy-tab-race`.

---

## Highlights (1.5.3 → 1.5.25)

| Area | Change |
|------|--------|
| **Project storage** | Primary state in PDF catalog `/CCSProject` (GZipHex + integrity `/Hash`); dual trailer **off** by default |
| **Save vs export** | **Save Project (`.ccspdf`)** = re-editable; **Export PDF (`.pdf`)** = flatten annotations, keep fillable AcroForms, still embeds `/CCSProject` for audit/forms |
| **Layered seal** | `originHash` / `contentHash` / `schemaHash` (+ optional device WebCrypto ECDSA) — no hash loops; see `project-state.js` / `project-crypto.js` |
| **Form signature** | Typed name line + fixed attestation text + client meta + timestamp; `attestationHash` in schema → `schemaHash` |
| **Caption + hover** | Compact mid-hash caption under signed field; **body-level popover** with full hashes and **Copy** (not native `title`) |
| **Provenance** | Heuristics via Producer/Keywords; origin hash logged on open |
| **UX polish** | Form/Editor fill hit targets; keyboard `e.key` guards; zoom / horizontal scroll; clone vs free-move hotkeys |

---

## 1.5.28 (patch)

- **Export signed signatures:** burn ink+caption, **omit fillable field** from AcroForm and from embedded schema (`burnedSignatures[]` audit only) so reopen does not stack a form shell on the ink.
- **Verify fix:** stamp-only exports no longer false-fail “wrote 1, verified 0”.
- **Save Project:** still keeps signed fields re-editable (does not burn).
- Cache-bust: `1.5.28-sig-burn-no-reimport`.

## 1.5.27 (patch)

- **Signed form signature export:** Export PDF burns **ink + caption** (name, time, mid hashes) into the page via pdf-lib (no longer a plain text name field when signed). Unsigned signature widgets remain fillable name lines.
- **Print:** signed form signatures included (ink + caption); designer chrome still omitted.
- **Project schema:** persists `signatureImage` so ink restores on Save Project / reopen.
- Cache-bust: `1.5.27-sig-export-print`.

## 1.5.26 (patch)

- **Audit extract tool:** `scripts/extract-ccspdf-audit.ps1` (and Windows package copy) reads **catalog `/CCSProject`** (GZipHex) as well as legacy trailers; summarizes **`.seal`**, exports form **signature attestations**, optional `-PreferCatalog`.
- **Popover a11y:** blur focused Copy before hide; `role="region"` + `inert` when hidden (fixes `aria-hidden` on focused descendant console warning).
- Cache-bust: `1.5.26-extract-catalog-a11y`.

## 1.5.25

- **Signature hash hover:** truncated `doc` / `sig` lines remain on the field caption; full `originHash` and `attestationHash` open in a fixed popover on hover (caption or ink), with one-click **Copy**.
- Popover dismisses on Escape, scroll, or resize; click caption still re-opens the sign flow.
- Documentation and `VERSION` aligned to **1.5.25**.

### Hotfixes carried in the 1.5.x line (summary)

- Form signature fill no longer collapses to a 1px line under Editor flex.
- Caption does not steal ink area; min-heights for unsigned / signed shells.
- Keyboard safety when `e.key` is undefined.
- Export MediaBox / Rotate handling; snip z-order; multi-line + fontSize form fields.

---

## Compatibility

- **`.ccspdf` / catalog:** schema **4.0+** `/CCSProject`; legacy trailer still **read** (and optional dual-write).
- **Integrity salt:** unchanged (`CCS-SAVE-HASH-CHECK-S4lT!!`) for trailer/hash recipe compatibility.
- **Deploy layout:** flat `js/`, `lib/`, `index.html`, `style.css`, `fonts.css`, `config.js`.
- **Not a certified e-sign product** — device-local attestation + project seal only; no TSA / identity proofing.

---

## Operator notes

1. After deploy, hard-refresh (or rely on `?v=` from `config.js` version).  
2. Smoke: design **Form: Signature** → Editor fill → attest & sign → hover caption for full hashes + Copy → **Save Project** and **Export PDF** → reopen.  
3. IT audit extract still: `scripts/extract-ccspdf-audit.ps1` / Windows `tools\Extract Project Audit.bat`.  
4. Rebuild Windows packages after browser smoke-test.

---

## Files of interest

| Path | Role |
|------|------|
| `config.js` | Feature flags + cache-bust version |
| `js/core/project-state.js` | `/CCSProject` embed + layered seal |
| `js/core/project-crypto.js` | Device ECDSA helpers |
| `js/core/project-audit.js` | Rolling `audit[]` (incl. `FIELD_SIGNED`) |
| `js/features/signature.js` | Attestation + caption + hash popover |
| `js/overlays/form-fields.js` | Designer shells + signature fill |
| `style.css` | Signature fill layout + `.sig-att-popover` |

---

## Version stamp checklist

| Artifact | Value |
|----------|--------|
| [VERSION](../VERSION) | `1.5.40` |
| `config.js` → `version` | `1.5.40-redact-select-ux` |
| Doc headers / SYSTEM-PROFILE | `1.5.40` |
| Marketing HTML | Regenerate via `docs/marketing/render-docs.ps1` |
