Document TLP classification enforcement, marking, and export metadata.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
173f1a3f26
commit
e601d7ee78
5 changed files with 150 additions and 19 deletions
10
CHANGELOG.md
10
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 `<meta
|
||||
name="classification">` / `<meta name="tlp">`, 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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 `<meta>` 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 (`<!-- 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` | `<title>` |
|
||||
| `author`, anders `organization` | `Author` / `dc:creator` | `<meta name="author">` |
|
||||
| `description` | — | `<meta name="description">` |
|
||||
| `keywords` + TLP + `OciDeck` | `Keywords` | `<meta name="keywords">` |
|
||||
| `tlp` (wanneer ≠ none) | `Subject`: `TLP:… — titel` | `<meta name="classification">`, `<meta name="tlp">`, vaste `.tlp-export-banner` bovenaan |
|
||||
|
||||
Visuele TLP-markering (banner, badge, optioneel watermerk) wordt **gerasterd**
|
||||
in PDF/PPTX-slides en staat los van deze documenteigenschappen. Zie
|
||||
[`USER_GUIDE.md`](USER_GUIDE.md) (§ Traffic Light Protocol, § Exporting) en
|
||||
[`ARCHITECTURE.md`](ARCHITECTURE.md) (§ Classification enforcement).
|
||||
|
|
|
|||
|
|
@ -108,15 +108,38 @@ Below each editor you can set:
|
|||
|
||||
## Traffic Light Protocol (TLP)
|
||||
|
||||
A deck has an overall TLP level (shown as a marking on the slides). Each slide can
|
||||
*also* carry its own level. When you present or export, slides whose level is
|
||||
A deck has an overall TLP level (set from the **TLP** chip in the title bar, or
|
||||
under *Presentation properties*). Each slide can *also* carry its own level
|
||||
(*Per-slide options*). When you present or export, slides whose level is
|
||||
**stricter** than the level chosen for the deck are **withheld** — so the same
|
||||
deck can be shown safely to audiences with different clearances. Order, least to
|
||||
most restrictive: none < CLEAR < GREEN < AMBER < AMBER+STRICT < RED.
|
||||
|
||||
Classifying a deck is **optional**. As an extra guardrail, an organisation can
|
||||
set a **release ceiling** — a maximum level that may leave the machine; see
|
||||
*Exporting* below.
|
||||
Classifying a deck is **optional** by default. An organisation can tighten that
|
||||
with the **classification enforcement** settings under *Settings → General →
|
||||
Accessibility* (see *Exporting* below).
|
||||
|
||||
### Visual marking (WYSIWYG)
|
||||
|
||||
When a slide is classified, OciDeck shows the official FIRST TLP 2.0 marking in
|
||||
the preview, presenter, audience window, thumbnails, and raster exports (PDF/PPTX).
|
||||
What you see on screen is what leaves the app — the marking is not a separate
|
||||
overlay added only at export time.
|
||||
|
||||
For each visible slide, the **effective** level is the **stricter** of the deck
|
||||
level and that slide's own level. On top of that:
|
||||
|
||||
- **Banner** — a full-width black bar at the top with the coloured TLP label
|
||||
(e.g. `TLP:AMBER`).
|
||||
- **Badge** — the same label in a compact box at the bottom-right (or bottom-left
|
||||
when the logo sits bottom-right), so the footer text can step aside.
|
||||
- **Watermark** (optional, off by default) — a faint diagonal repeat of the TLP
|
||||
label and the deck's **organisation** field across the slide. Enable it under
|
||||
*Settings → General → Accessibility → Classification watermark*.
|
||||
|
||||
Slides with no classification show none of the above. Per-slide TLP that is
|
||||
stricter than the deck still contributes to the effective marking on slides that
|
||||
are shown.
|
||||
|
||||
## Presenting
|
||||
|
||||
|
|
@ -174,10 +197,37 @@ Export to:
|
|||
- **Portable package** (`.ocideck`) — a single zip with the Markdown and all
|
||||
assets, to hand the whole deck to someone else.
|
||||
|
||||
**Release ceiling (optional).** When a maximum TLP level is configured, exporting
|
||||
a deck classified *above* it is blocked for every format, and the export dialog
|
||||
explains why. The ceiling is off by default and classifying a deck stays
|
||||
optional — it only stops decks that exceed the configured level.
|
||||
**Classification enforcement (optional).** Under *Settings → General →
|
||||
Accessibility → Classification enforcement* an organisation can configure up to
|
||||
four independent rules. All are off by default; together they form a single
|
||||
**export gate** that applies to PDF, PPTX, HTML, and the portable package.
|
||||
When a rule blocks export, **no file is written** (fail-closed) and the export
|
||||
dialog shows the reason. The status bar export button tooltip repeats that reason
|
||||
when the deck is saved and clean.
|
||||
|
||||
| Setting | Effect |
|
||||
| --- | --- |
|
||||
| **Release ceiling** | Blocks export when the deck's TLP is **higher** than the chosen maximum (same as before — a deck at RED cannot export when the ceiling is AMBER). |
|
||||
| **Required minimum level** | Blocks export when the deck's TLP is **lower** than the chosen minimum (e.g. minimum GREEN rejects CLEAR and unclassified decks). |
|
||||
| **Classification required** | Blocks export when the deck has **no** TLP level set, even if no minimum is configured. |
|
||||
| **Classification watermark** | Does not block export; adds the diagonal watermark described under *Traffic Light Protocol*. |
|
||||
|
||||
The gate evaluates the **deck-wide** TLP from front matter, not per-slide levels.
|
||||
Per-slide stricter levels still affect which slides appear in the export (they are
|
||||
withheld), but they do not satisfy "classification required" on their own — set
|
||||
the deck level explicitly.
|
||||
|
||||
When export is blocked because the deck is unclassified, the **TLP** chip in the
|
||||
title bar gets an orange border and its tooltip explains that a level is required.
|
||||
|
||||
**Export metadata.** PDF, PPTX, and HTML exports embed document properties derived
|
||||
from the deck: title, author (falling back to organisation), description, keywords,
|
||||
and TLP. When a TLP level is set, it is prefixed in the PDF/PPTX **Subject**
|
||||
(`TLP:GREEN — My deck`), added to **Keywords** (`TLP`, the label, and the stable
|
||||
key), and written to HTML `<meta name="classification">` and `<meta name="tlp">`.
|
||||
HTML exports also show a fixed top banner with the TLP label when classified.
|
||||
These properties are for discovery and handling downstream — they do not replace
|
||||
the visible banner, badge, and optional watermark on the slides themselves.
|
||||
|
||||
## Accessibility
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue