Merge pull request #457 from neilcawse:fix/macos-open-with-xlsb-ods

This commit is contained in:
Brandon Thomas 2026-10-12 23:35:17 -04:00
commit 81b35afe08
4 changed files with 86 additions and 5 deletions

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}");
}
}
}

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 |

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

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>