Document TLP classification enforcement, marking, and export metadata #10
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).
|
(no file is written when blocked, and the export dialog explains why).
|
||||||
Classifying a deck stays optional — the ceiling only stops decks that exceed
|
Classifying a deck stays optional — the ceiling only stops decks that exceed
|
||||||
it, and it is off by default.
|
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
|
- **Dual-screen presenter** — on a second display the beamer shows the slide
|
||||||
while the laptop shows the presenter view (current/next slide, notes, timer),
|
while the laptop shows the presenter view (current/next slide, notes, timer),
|
||||||
kept in sync over method channels.
|
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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **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/
|
lib/
|
||||||
models/ # Deck, Slide, Settings/ThemeProfile, Chart, Annotation
|
models/ # Deck, Slide, Settings/ThemeProfile, Chart, Annotation
|
||||||
services/ # markdown, markdown_validator, file, export, classification_policy,
|
services/ # markdown, markdown_validator, file, export,
|
||||||
# image, caption,
|
# classification_policy, classification_enforcement_policy,
|
||||||
|
# export_metadata, image, caption,
|
||||||
# description, image_dedup (md5 duplicates),
|
# description, image_dedup (md5 duplicates),
|
||||||
# image_reference (.md rewrites), recovery, rasterizer,
|
# image_reference (.md rewrites), recovery, rasterizer,
|
||||||
# marp_html, annotation_codec, rehearsal_controller
|
# marp_html, annotation_codec, rehearsal_controller
|
||||||
|
|
@ -75,13 +76,48 @@ the key thing to understand before touching rendering:
|
||||||
renderer by design.
|
renderer by design.
|
||||||
|
|
||||||
Both worlds converge at one chokepoint: `services/export_service.dart`
|
Both worlds converge at one chokepoint: `services/export_service.dart`
|
||||||
(`ExportService.export()`) is the only place that writes an export, so the
|
(`ExportService.export()`) is the only place that writes an export.
|
||||||
**classification gate** lives there rather than in the export dialog. A
|
|
||||||
`ClassificationPolicy` enforces an optional *release ceiling* and refuses,
|
### Classification enforcement
|
||||||
**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);
|
Export blocking is decided by `ClassificationEnforcementPolicy`
|
||||||
the dialog also runs the same check up front so a blocked export is explained
|
(`services/classification_enforcement_policy.dart`), evaluated inside
|
||||||
before any work starts.
|
`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
|
## Presenter
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,18 @@ Opgeslagen onder de sleutel `tlp` met deze stabiele waarden:
|
||||||
| `amber+strict` | `TLP:AMBER+STRICT` |
|
| `amber+strict` | `TLP:AMBER+STRICT` |
|
||||||
| `red` | `TLP:RED` |
|
| `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)
|
### 3.2 `ocideck_style_profile` (stijlprofiel)
|
||||||
|
|
||||||
Het complete visuele profiel wordt als JSON geserialiseerd, ge-UTF-8'd en
|
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:
|
Implementatie: `lib/services/markdown_validator.dart`; tests:
|
||||||
`test/markdown_validator_test.dart`. Zie ook [`USER_GUIDE.md`](USER_GUIDE.md) (§
|
`test/markdown_validator_test.dart`. Zie ook [`USER_GUIDE.md`](USER_GUIDE.md) (§
|
||||||
Markdown mode).
|
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)
|
## Traffic Light Protocol (TLP)
|
||||||
|
|
||||||
A deck has an overall TLP level (shown as a marking on the slides). Each slide can
|
A deck has an overall TLP level (set from the **TLP** chip in the title bar, or
|
||||||
*also* carry its own level. When you present or export, slides whose level is
|
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
|
**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
|
deck can be shown safely to audiences with different clearances. Order, least to
|
||||||
most restrictive: none < CLEAR < GREEN < AMBER < AMBER+STRICT < RED.
|
most restrictive: none < CLEAR < GREEN < AMBER < AMBER+STRICT < RED.
|
||||||
|
|
||||||
Classifying a deck is **optional**. As an extra guardrail, an organisation can
|
Classifying a deck is **optional** by default. An organisation can tighten that
|
||||||
set a **release ceiling** — a maximum level that may leave the machine; see
|
with the **classification enforcement** settings under *Settings → General →
|
||||||
*Exporting* below.
|
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
|
## Presenting
|
||||||
|
|
||||||
|
|
@ -174,10 +197,37 @@ Export to:
|
||||||
- **Portable package** (`.ocideck`) — a single zip with the Markdown and all
|
- **Portable package** (`.ocideck`) — a single zip with the Markdown and all
|
||||||
assets, to hand the whole deck to someone else.
|
assets, to hand the whole deck to someone else.
|
||||||
|
|
||||||
**Release ceiling (optional).** When a maximum TLP level is configured, exporting
|
**Classification enforcement (optional).** Under *Settings → General →
|
||||||
a deck classified *above* it is blocked for every format, and the export dialog
|
Accessibility → Classification enforcement* an organisation can configure up to
|
||||||
explains why. The ceiling is off by default and classifying a deck stays
|
four independent rules. All are off by default; together they form a single
|
||||||
optional — it only stops decks that exceed the configured level.
|
**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
|
## Accessibility
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue