diff --git a/CHANGELOG.md b/CHANGELOG.md index 1498d86..5be2062 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,16 @@ and the project aims to follow [Semantic Versioning](https://semver.org/). (no file is written when blocked, and the export dialog explains why). Classifying a deck stays optional — the ceiling only stops decks that exceed it, and it is off by default. +- **Classification enforcement** — extends the export gate with an optional + **required minimum TLP**, a **classification required** flag (reject decks + with no TLP level), and a **classification watermark** on every slide + (diagonal `TLP · organisation`, WYSIWYG in preview and raster exports). + Settings live under *Settings → General → Accessibility → Classification + enforcement*. The title-bar TLP chip highlights in orange when export is + blocked because the deck is unclassified. +- **Export metadata** — PDF, PPTX, and HTML exports embed title, author, + description, keywords, and TLP (Subject prefix, Keywords, HTML `` / ``, plus a fixed HTML banner). - **Dual-screen presenter** — on a second display the beamer shows the slide while the laptop shows the presenter view (current/next slide, notes, timer), kept in sync over method channels. diff --git a/README.md b/README.md index 5a1b115..9cad5af 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Built with Flutter for macOS, Windows, and Linux. - **Source-code slides** — a "code sheet" with per-language syntax highlighting, stored as a fenced code block. Background, text colour and monospace font come from the style profile, with an optional syntax-colouring toggle (turn it off for a single-colour, CRT-terminal look). The code auto-sizes to fill the panel. - **Charts** — bar, line, pie, and spider/radar charts rendered natively (preview, presenter, PDF, PPTX) and as self-contained SVG in the HTML export. Data is entered in an in-app grid or imported from CSV; the spec is stored as JSON in the Markdown, with optional linking to a CSV kept in a tidy `data/` directory. Optional min/max draw reference lines (bar/line) or fix the scale (radar); legend hover highlights a series, and line tooltips pick the dot under the cursor. - **Live preview** — see each slide rendered as you edit, with inline Markdown, footers, and TLP (Traffic Light Protocol) marking. Free-Markdown slides render fenced code with syntax highlighting and `$…$` / `$$…$$` LaTeX math. -- **Traffic Light Protocol** — a deck-wide classification plus an optional **per-slide TLP level**; slides classified stricter than the level the deck is shown at are automatically withheld, both when presenting and exporting. An optional **export release ceiling** can block exporting any deck classified above a chosen level — enforced for every format, off by default. +- **Traffic Light Protocol** — a deck-wide classification plus an optional **per-slide TLP level**; slides classified stricter than the level the deck is shown at are automatically withheld, both when presenting and exporting. **Visual marking** (banner, badge, optional diagonal watermark) follows FIRST TLP 2.0 colours and is WYSIWYG through preview, presenter, and PDF/PPTX export. Optional **classification enforcement** (release ceiling, required minimum, mandatory classification, watermark toggle) blocks export fail-closed when policy fails; export metadata embeds TLP in PDF/PPTX/HTML. - **Fullscreen presenter** — keyboard-driven navigation, presenter view, blank screen, auto-advance, and a slide-grid overview. - **Presentation timer / rehearsal mode** — the presenter view doubles as a rehearsal clock: a countdown against a target time (set in Settings or live with `K`), the time spent on the current slide, and an end-of-run summary (total vs. target and per-slide times, copyable). It measures only — no pacing coaching — and is session-only, never written to disk. - **Dual-screen presenter** — when a second display is connected, the beamer shows the slide while the laptop shows the presenter view (current/next slide, notes, timer), kept in sync. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index a24f0a5..56178ca 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -15,8 +15,9 @@ are stored on disk, see [`FILE_FORMAT.md`](FILE_FORMAT.md). ``` lib/ models/ # Deck, Slide, Settings/ThemeProfile, Chart, Annotation - services/ # markdown, markdown_validator, file, export, classification_policy, - # image, caption, + services/ # markdown, markdown_validator, file, export, + # classification_policy, classification_enforcement_policy, + # export_metadata, image, caption, # description, image_dedup (md5 duplicates), # image_reference (.md rewrites), recovery, rasterizer, # marp_html, annotation_codec, rehearsal_controller @@ -75,13 +76,48 @@ the key thing to understand before touching rendering: renderer by design. Both worlds converge at one chokepoint: `services/export_service.dart` -(`ExportService.export()`) is the only place that writes an export, so the -**classification gate** lives there rather than in the export dialog. A -`ClassificationPolicy` enforces an optional *release ceiling* and refuses, -**fail-closed**, to export a deck classified above it — no format can bypass it. -The ceiling is stored in app settings (`maxReleaseExportTlpKey`, off by default); -the dialog also runs the same check up front so a blocked export is explained -before any work starts. +(`ExportService.export()`) is the only place that writes an export. + +### Classification enforcement + +Export blocking is decided by `ClassificationEnforcementPolicy` +(`services/classification_enforcement_policy.dart`), evaluated inside +`ExportService.export()` **before** any file bytes are built — fail-closed, so no +format (PDF/PPTX/HTML/package) can bypass the gate. The export dialog and status +bar run the same policy up front for UX (explain early, disable misleading work). + +The policy combines three optional rules from `AppSettings`: + +| Setting key | Rule | +| --- | --- | +| `maxReleaseExportTlpKey` | Release **ceiling** — deck TLP must not exceed this level. | +| `minRequiredExportTlpKey` | **Floor** — deck TLP must be at least this level. | +| `requireClassificationOnExport` | Deck must have a TLP level (`TlpLevel.none` is rejected). | + +`ClassificationPolicy` remains as a thin wrapper around the ceiling only +(backward compatible); new code should use `ClassificationEnforcementPolicy`. +The gate evaluates **deck-wide** `Deck.tlp`, not per-slide levels (those still +control visibility via `slideVisibleAtTlp`). + +`ExportDocumentMetadata` (`services/export_metadata.dart`) is built from deck +metadata and passed into PDF (`pw.Document` title/author/subject/keywords), +PPTX core properties, and HTML `` tags. HTML also gets a fixed +`.tlp-export-banner` when classified. + +### Visual TLP marking + +In-app slides (`SlidePreviewWidget`) compute `effectiveTlp(deckTlp, slideTlp)` — +the stricter of deck and slide — and render FIRST TLP 2.0 markings from +`widgets/slides/previews/overlays.dart`: + +- `_ClassificationBanner` — full-width top bar +- `_TlpOverlay` — bottom-right (or bottom-left) badge +- `_ClassificationWatermark` — optional diagonal watermark (`TLP · organisation`), + controlled by `AppSettings.classificationWatermarkEnabled` + +The same widget tree is rasterized for PDF/PPTX (`slide_rasterizer.dart`), so +markings are WYSIWYG. The watermark setting is threaded through preview, presenter, +audience window, thumbnails, and export dialog. ## Presenter diff --git a/docs/FILE_FORMAT.md b/docs/FILE_FORMAT.md index 1913b30..52b1a56 100644 --- a/docs/FILE_FORMAT.md +++ b/docs/FILE_FORMAT.md @@ -118,6 +118,18 @@ Opgeslagen onder de sleutel `tlp` met deze stabiele waarden: | `amber+strict` | `TLP:AMBER+STRICT` | | `red` | `TLP:RED` | +**Effectieve markering.** In de app wordt per slide het **strengste** niveau +getoond: het maximum van het deck-TLP (`tlp` in front matter) en het +per-slide TLP (``). Dat bepaalt banner, badge en optioneel +watermerk in preview, presenter en rasterexport — niet opgeslagen als extra +Markdown, maar berekend bij renderen (`effectiveTlp` in `lib/models/deck.dart`). + +**Zichtbaarheid vs. export-gate.** Per-slide TLP bepaalt welke slides worden +achtergehouden bij presenteren/exporteren (`slideVisibleAtTlp`). De +**export-handhaving** (plafond, minimum, verplichte classificatie) kijkt +alleen naar het deck-brede `tlp`-veld in front matter, niet naar per-slide +niveaus. + ### 3.2 `ocideck_style_profile` (stijlprofiel) Het complete visuele profiel wordt als JSON geserialiseerd, ge-UTF-8'd en @@ -552,3 +564,26 @@ niet modelleert wordt niet gerapporteerd. Implementatie: `lib/services/markdown_validator.dart`; tests: `test/markdown_validator_test.dart`. Zie ook [`USER_GUIDE.md`](USER_GUIDE.md) (§ Markdown mode). + +--- + +## 11. Exportmetadata (niet in `.md`) + +Bij PDF-, PPTX- en HTML-export schrijft OciDeck **documenteigenschappen** die +afgeleid zijn van front matter (`author`, `organization`, `description`, +`keywords`, `tlp`, titel). Deze metadata staat **niet** in het `.md`-bestand +en verandert het round-trip-formaat niet; ze worden alleen bij export gezet +(`ExportDocumentMetadata` in `lib/services/export_metadata.dart`). + +| Bron (front matter) | PDF / PPTX | HTML | +| --- | --- | --- | +| Titel | `Title` | `