09 — Minimal bundled template (KeynoteKit resource) - #30
Merged
Conversation
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>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
This was referenced Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #21. Part of #12, see also #7.
Ships the hand-authored blank-theme
blank.key(98,240 bytes) as a SwiftPM resource on theKeynoteKittarget, and establishes the write entry point with itsbasedOn:parameter..copy, not.process— verified empiricallyThe ticket and PLAN both say
.copy"because a.keyis 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:
.processContents/Resources/blank.key.copyContents/Resources/blank.keyChose
.copy..processpassing 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.keyextension — a toolchain or platform that adds one would silently corrupt the archive..copystates 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 inPackage.swiftrecords the real one.write(to:basedOn:)basedOn:defaults toKeynoteTemplate.bundled. It exists from day one so the primary API does not change shape once slide content lands. With no content modelled yet,writeemits the template verbatim.KeynoteTemplateis a value type over either the bundled resource or a caller-supplied URL;TemplateError.bundledResourceMissingcoversBundle.modulefailing 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 threewritepaths.The byte-identity test was checked for false confidence. My first tamper attempt passed — because
Bundle.modulein a test context resolves to the copy nested insideKeynoteKitTests.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 againstshasumand Python'shashlib.Redistribution caveat (#7)
The template derives from Keynote's
21_basicwhite, so a small amount of Apple-authored theme content ships inside this package:Data/st-*.jpgDocumentStylesheet.iwaof theme stylingdocumentResourceLocatorentries resolving against Keynote's installed theme bundle at open timeReducing 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 theKeynoteTemplatetype and at the resource declaration inPackage.swift.Scope
Deliberately minimal per the lane note — resource plus the default
basedOn:path only. No authoring DSL, no slide/text-item supply, noSlideContentsurface; those stay with #20/#22/#23.Deckis an empty value type whose only job today is to own the write entry point.Touched only
Sources/KeynoteKit/**,Tests/KeynoteKitTests/**, and theKeynoteKittarget stanza inPackage.swift. No changes toSnappy,IWAFraming,KeynoteKitProtobuf,KeynoteKitScripting,research/, orPLAN.md.Verification
swift build/swift testgreen under Swift 6.4 — 7 tests passLINT_MODE=STRICT ./Scripts/lint.shexits 0, 0 violationsTwo strict-mode violations came up and were fixed by renaming and reordering, not by disabling rules:
identifier_name(SHA-256'ss0/s1→sigma0/sigma1) andtype_contents_order(static helpers moved above the instance@Testmethods). New types went into separate files to satisfyone_declaration_per_file.🤖 Generated with Claude Code