Skip to content

09 — Minimal bundled template (KeynoteKit resource) - #30

Merged
leogdion merged 1 commit into
v0.1.xfrom
21-template
Jul 29, 2026
Merged

09 — Minimal bundled template (KeynoteKit resource)#30
leogdion merged 1 commit into
v0.1.xfrom
21-template

Conversation

@leogdion

Copy link
Copy Markdown
Member

Closes #21. Part of #12, see also #7.

Ships the hand-authored blank-theme blank.key (98,240 bytes) as a SwiftPM resource on the KeynoteKit target, and establishes the write entry point with its basedOn: parameter.

.copy, not .process — verified empirically

The ticket and PLAN both say .copy "because a .key is a directory-shaped bundle." That rationale does not hold for this file — it is a flat single-file zip (16 stored entries), not a directory-shaped bundle. So I tested both rules rather than inheriting the reasoning.

Both build, and both preserve the bytes on macOS today:

Rule Result Bundle layout
.process builds, SHA-256 unchanged Contents/Resources/blank.key
.copy builds, SHA-256 unchanged Contents/Resources/blank.key

Chose .copy. .process passing today is incidental, not a guarantee: it licenses SwiftPM to transform or rename resources, and the no-op here is just the absence of a known rule for the .key extension — a toolchain or platform that adds one would silently corrupt the archive. .copy states the actual requirement (bytes survive verbatim) and keeps the path stable across the Darwin/Linux/Windows/Android legs. The conclusion matches the ticket; the stated reason is different, and the comment in Package.swift records the real one.

write(to:basedOn:)

try Deck().write(to: url)                                          // bundled template
try Deck().write(to: url, basedOn: KeynoteTemplate(contentsOf: myTheme))

basedOn: defaults to KeynoteTemplate.bundled. It exists from day one so the primary API does not change shape once slide content lands. With no content modelled yet, write emits the template verbatim.

KeynoteTemplate is a value type over either the bundled resource or a caller-supplied URL; TemplateError.bundledResourceMissing covers Bundle.module failing at runtime (stripped or relocated resource bundle).

Tests — verified load-bearing

7 tests: resource resolves from Bundle.module, SHA-256 + byte count pinned, zip local-header signature intact, and the three write paths.

The byte-identity test was checked for false confidence. My first tamper attempt passed — because Bundle.module in a test context resolves to the copy nested inside KeynoteKitTests.xctest, not the top-level product bundle, and I had corrupted the latter. After tampering the copy the test actually reads, a single flipped byte fails the suite. The assertion is real.

SHA-256 is implemented in plain Swift (Tests/KeynoteKitTests/SHA256Digest.swift) rather than CryptoKit, which is Darwin-only, so the Linux/Windows/Android legs keep running. Its output was cross-checked against shasum and Python's hashlib.

Redistribution caveat (#7)

The template derives from Keynote's 21_basicwhite, so a small amount of Apple-authored theme content ships inside this package:

  • a 2.5 KB Data/st-*.jpg
  • a 50 KB DocumentStylesheet.iwa of theme styling
  • three documentResourceLocator entries resolving against Keynote's installed theme bundle at open time

Reducing the template from 458 KB (by removing the theme's slide layouts through Keynote's UI) minimized this exposure but did not eliminate it, exactly as #7 anticipated. Callers who must ship no Apple-authored theme content can supply their own base via KeynoteTemplate(contentsOf:). Documented on the KeynoteTemplate type and at the resource declaration in Package.swift.

Scope

Deliberately minimal per the lane note — resource plus the default basedOn: path only. No authoring DSL, no slide/text-item supply, no SlideContent surface; those stay with #20/#22/#23. Deck is an empty value type whose only job today is to own the write entry point.

Touched only Sources/KeynoteKit/**, Tests/KeynoteKitTests/**, and the KeynoteKit target stanza in Package.swift. No changes to Snappy, IWAFraming, KeynoteKitProtobuf, KeynoteKitScripting, research/, or PLAN.md.

Verification

  • swift build / swift test green under Swift 6.4 — 7 tests pass
  • LINT_MODE=STRICT ./Scripts/lint.sh exits 0, 0 violations

Two strict-mode violations came up and were fixed by renaming and reordering, not by disabling rules: identifier_name (SHA-256's s0/s1sigma0/sigma1) and type_contents_order (static helpers moved above the instance @Test methods). New types went into separate files to satisfy one_declaration_per_file.

🤖 Generated with Claude Code

Adds the hand-authored blank-theme `blank.key` (98,240 bytes) as a SwiftPM
resource on the `KeynoteKit` target, and establishes the write entry point
with its `basedOn:` parameter.

Resource rule: `.copy`, not `.process`. Both were tested and both preserve
the bytes on macOS today, but `.process` licenses SwiftPM to transform or
rename resources per platform — today's no-op for an unknown extension is
incidental, not a guarantee. A `.key` is a zip archive whose bytes must
survive verbatim, so `.copy` is the rule that actually states the
requirement. It also keeps the path stable across the CI legs.

`Deck.write(to:basedOn:)` defaults to `KeynoteTemplate.bundled` and accepts
a caller-supplied base via `KeynoteTemplate(contentsOf:)`. The parameter
exists from day one so the primary API does not change shape when slide
content lands (#20/#22/#23). No authoring DSL here.

Tests pin the resource's SHA-256 and byte count and assert the zip local
header signature. Verified load-bearing: flipping one byte in the bundle
`Bundle.module` actually resolves to fails the suite. SHA-256 is implemented
in plain Swift rather than CryptoKit to keep the Linux/Windows/Android legs
running.

Redistribution caveat (#7): the template derives from `21_basicwhite`, so a
small amount of Apple-authored theme content ships in the package — a 2.5 KB
`Data/st-*.jpg`, a 50 KB `DocumentStylesheet.iwa`, and three theme-bundle
resource locators. Reducing from 458 KB minimized this; it did not eliminate
it. Documented on `KeynoteTemplate` and at the resource declaration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e7e1f5c1-d9b7-45e7-88f4-b398c562c1da

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@leogdion
leogdion merged commit 3ec7c77 into v0.1.x Jul 29, 2026
17 checks passed
@leogdion
leogdion deleted the 21-template branch July 29, 2026 18:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant