Packaging: offer GridCraft for .xlsb and .ods files on macOS

GridCraft imports XLSB (#101) and ODS (#100) files, and #321 added both
to the Linux and Windows "Open with" lists, but the macOS Info.plist
still declared only .xlsx, .xlsm, .csv and .tsv, so Finder's Open With
didn't offer GridCraft for them. Add two CFBundleDocumentTypes entries
(Editor, rank Alternate) with the UTIs macOS reports for these files,
plus an imported UTI for .xlsb like the .xlsm one. A test now checks
that the macOS and Windows packaging offer the same file types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
neilcawseandClaude Opus 5.5 committed 2026-10-12 20:52:03 -04:00
1 parent fc45b7f6d8
commit 0485e7f4ba
4 files changed
+86 -5

No files matched your search

+29
View File
@@ -369,3 +369,32 @@ fn main() -> eframe::Result<()> {
}
result
}
#[cfg(test)]
mod packaging_tests {
use gridcraft_engine::io::FileKind;
/// The text between `start` and `end` that follows each `marker` in `text`, sorted (e.g. the
/// `xlsb` in `Classes\.xlsb\OpenWithProgids`).
fn extensions(text: &str, marker: &str, start: &str, end: char) -> Vec<String> {
let mut v: Vec<String> = text.split(marker).skip(1).filter_map(|s| s.split_once(start)?.1.split(end).next()).map(str::to_string).collect();
v.sort();
v
}
/// macOS Open With (`CFBundleDocumentTypes`) offers GridCraft for the same files as Windows
/// Open with, and GridCraft opens each of them. #321 added `.xlsb` and `.ods` on Windows and
/// Linux only.
#[test]
fn macos_and_windows_offer_the_same_file_types() {
let plist = include_str!("../../../packaging/macos/Info.plist.in");
let wxs = include_str!("../../../packaging/windows/gridcraft.wxs");
let mac = extensions(plist, "<key>CFBundleTypeExtensions</key>", "<string>", '<');
let win = extensions(wxs, "Key=\"Software\\Classes\\.", "", '\\');
assert!(mac.iter().any(|e| e == "xlsx"), "Info.plist.in extensions not found: {mac:?}");
assert_eq!(mac, win);
for ext in &mac {
assert!(FileKind::from_path(&format!("book.{ext}")).is_some(), "GridCraft doesn't open .{ext}");
}
}
}
+2 -1
View File
@@ -1,6 +1,6 @@
# File-format parity
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-12 · **Change:** minor (CSV save keeps the separator it was opened with) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-12 · **Change:** minor (macOS Open With offers GridCraft for `.xlsb` and `.ods`) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
Every format Excel opens or saves, with GridCraft's read and write support, fidelity and tests.
Part of [target-app-parity.md](target-app-parity.md). **File formats: ~50% ready, 80–120 h**
@@ -78,6 +78,7 @@ refresh. Counted under features (Get & Transform, ~10% ready), not here.
| Date | Change | Summary |
|---|---|---|
| 2026-10-12 | minor | macOS: the app bundle declares `.xlsb` and `.ods`, so Finder's Open With offers GridCraft for them, as Linux and Windows already did (#321) |
| 2026-10-12 | minor | CSV: saving a file opened as CSV keeps its detected separator (`;` files were rewritten with `,`) |
| 2026-10-11 | minor | XLSX: cell/shared/array formulas read through the file→model conversion (`_xlpm.` LET/LAMBDA parameter prefixes stripped); table column names keep line breaks; table names may start with `\` |
| 2026-10-11 | minor | CSV/TSV export writes displayed values (number formats applied, no `#####`); `.xltx`, `.xltm` and `.xlsm` saved with their own workbook content types |
+4 -4
View File
@@ -15,7 +15,7 @@ This is GridCraft's implementation of the shared [release playbook](release-play
| Display name `GridCraft`, binaries `gridcraft` + `gridcraft-cli` | every script and workflow |
| Bundle / app id `ai.storyteller.gridcraft` | `packaging/macos/Info.plist.in`, `packaging/linux/*`, flatpak manifest |
| WiX UpgradeCode `5511327B-F305-41BE-92D0-EB84944E7C4D` (fixed forever) | `packaging/windows/gridcraft.wxs` |
| File types `.xlsx` `.xlsm` `.csv` `.tsv` | Info.plist (`CFBundleDocumentTypes`), WiX (ProgIds/OpenWithProgids), `.desktop` `MimeType=`, `ai.storyteller.gridcraft.mime.xml`, metainfo |
| File types `.xlsx` `.xlsm` `.xlsb` `.ods` `.csv` `.tsv` | Info.plist (`CFBundleDocumentTypes`), WiX (ProgIds/OpenWithProgids), `.desktop` `MimeType=`, `ai.storyteller.gridcraft.mime.xml`, metainfo |
| Web app dir `apps/gridcraft-web` | `packaging/web/package.sh` |
| App colour `#1f9d55` (ink `#147a40`), **proposed** | `assets/app-icon/README.md`, metainfo `<branding>` |
@@ -94,9 +94,9 @@ and upload their artifacts, the environment refuses macOS and Windows, and no re
- `Info.plist` comes from `Info.plist.in`: bundle id `ai.storyteller.gridcraft`,
`LSMinimumSystemVersion` 11.0, `NSHighResolutionCapable`, category productivity, and
`CFBundleDocumentTypes` for `.xlsx`, `.xlsm`, `.csv` and `.tsv` with rank **Alternate**, so
GridCraft is offered in *Open With* without taking over Numbers' (or another app's)
defaults. `.xlsm` gets an imported UTI in case macOS doesn't know it.
`CFBundleDocumentTypes` for `.xlsx`, `.xlsm`, `.xlsb`, `.ods`, `.csv` and `.tsv` with rank
**Alternate**, so GridCraft is offered in *Open With* without taking over Numbers' (or another
app's) defaults. `.xlsm` and `.xlsb` get imported UTIs in case macOS doesn't know them.
- **Signing** goes inside-out with the hardened runtime and a secure timestamp; no `--deep` on
the final signature; `entitlements.plist` is deliberately empty.
- **Notarization:** the app is zipped and sent with `xcrun notarytool submit --wait`, then
+51
View File
@@ -111,6 +111,39 @@
<key>CFBundleTypeMIMETypes</key>
<array><string>text/tab-separated-values</string></array>
</dict>
<!-- .xlsb and .ods are imported (data only) into a new workbook that saves as .xlsx. -->
<dict>
<key>CFBundleTypeName</key>
<string>Excel Binary Workbook</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>com.microsoft.excel.sheet.binary.macroenabled</string>
</array>
<key>CFBundleTypeExtensions</key>
<array><string>xlsb</string></array>
<key>CFBundleTypeMIMETypes</key>
<array><string>application/vnd.ms-excel.sheet.binary.macroEnabled.12</string></array>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>OpenDocument Spreadsheet</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>org.oasis-open.opendocument.spreadsheet</string>
</array>
<key>CFBundleTypeExtensions</key>
<array><string>ods</string></array>
<key>CFBundleTypeMIMETypes</key>
<array><string>application/vnd.oasis.opendocument.spreadsheet</string></array>
</dict>
</array>
<!-- Types macOS may not know; the system's own declaration wins when it has one. -->
@@ -134,6 +167,24 @@
<array><string>application/vnd.ms-excel.sheet.macroEnabled.12</string></array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.microsoft.excel.sheet.binary.macroenabled</string>
<key>UTTypeDescription</key>
<string>Excel Binary Workbook</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.zip-archive</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array><string>xlsb</string></array>
<key>public.mime-type</key>
<array><string>application/vnd.ms-excel.sheet.binary.macroEnabled.12</string></array>
</dict>
</dict>
</array>
</dict>