wgpu's default DX12 compiler (Auto) loads the first dxcompiler.dll on the
DLL search path. GridCraft ships none, so another program's copy is used;
without a matching dxil.dll its 'DXIL signing library not found' warning
fails wgpu's internal compute pipelines, the device is lost and the app
exits before the first frame. Pin FXC (part of Windows) unless
WGPU_DX12_COMPILER asks for another compiler.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clean-room import of Excel 97-2003 workbooks from the public [MS-CFB] and
[MS-XLS] specifications: compound-file streams (FAT, mini FAT, directory
tree), the BIFF8 Workbook stream with Continue records, shared strings,
all cell records, formulas decoded from Ptg tokens (shared and array
formulas, 3-D refs, names, add-in/newer functions by name) with cached
results, number formats, the 1904 date system, merges, column widths, row
heights, defined names and print areas/titles. Encrypted (FILEPASS) and
BIFF5 files are refused with a clear message; everything else is skipped
with a warning. .xls opens as a new unsaved workbook (Save As .xlsx).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The theme could only be chosen in the View tab's Display theme box, and
the app always started light. Now:
- a title-bar button switches light/dark in one click (view.darkMode);
right-click offers System / Light / Dark;
- File › Display theme offers the same three (Excel keeps it under
File › Account › Office Theme);
- the desktop app follows the system appearance until a theme is chosen
and saved in ui.json (UiState's default stays light for tests).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AccessKit's macOS adapter subclasses the window's content view and
restores its class when the window is torn down; AppKit's Touch Bar
support observes that view via KVO, so the next display cycle throws in
-[_NSTouchBarFinderObservation invalidate] and the quit ends in a crash
report. On macOS, on_exit now exits right after saving the preferences,
before eframe drops the window (what eframe itself does without
run_and_return). Same fix as storytold/vectorcraft#670.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Enumerating Vulkan adapters crashes inside Intel's igvk64.dll on some UHD
Graphics drivers, before any Rust error handling runs. Since #22 the
Windows instance is DirectX 12 only; this keeps Vulkan out of every
default path and adds two fallbacks so DX12-only can't leave a PC without
a window:
- A DirectX 12 probe before eframe starts: no hardware adapter that meets
eframe's limits -> OpenGL (plus DX12/WARP as last resort when WARP is
all DX12 offers).
- If run_native fails with a wgpu error before the app was created, start
once more with the other of DX12/GL (eframe reuses its event loop).
WGPU_BACKEND still overrides everything, and nothing else is tried then.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spreadsheets put an HTML table next to the tab-separated text; Excel
(x:num) and LibreOffice Calc (sdval) record each cell's number there.
Paste now reads the HTML flavour (new paste_html host service, arboard on
desktop) and edit.paste takes an optional html param: a field that would
paste as text becomes the recorded number when the HTML cell shows the
same text, with an inferred format ("Rp"#,##0.00) that renders it back
identically.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The web build had no way to actually print — Ctrl+P (Cmd+P, declared on
file.print's shortcut) did nothing at all, and the ribbon's "Print…" menu
item ran file.print and silently discarded the result. file.print already
builds a PDF and returns it as base64 specifically "for the UI to hand to
the system print dialog" per its own doc comment — this wires that up.
- ui-egui: new Services::print hook (web: hand PDF bytes to the browser's
print flow directly, no download); SheetApp::print_now runs file.print,
decodes the base64 result, and calls the hook.
- ui-egui: Key::P now dispatches to print_now (was unbound); the ribbon's
"Print…" menu item calls print_now instead of discarding file.print's
result.
- gridcraft-web: implements the hook (Blob -> object URL -> hidden iframe
-> iframe.contentWindow.print() on load).
Desktop builds are unaffected (no Services::print set there; print_now
falls back to the existing alert-on-error behaviour with no UI action on
success, same as before this change).
Add static pictures inside cells with XLSX roundtrip
Rebased onto main (conflicts with #83/#98 and main resolved by maintainer); the contributor's branch does not allow maintainer edits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge the fixed#31 branch (which carries #38's font fallback) and resolve:
- theme.rs: build on #38's fallback_faces/load_fallback instead of the old
load_cjk. The interface language only reorders the two Han entries
(sys-cjk-han-ja / sys-cjk-han-zh; Japanese first for ja, Chinese first
otherwise); Hangul, Thai and Hebrew keep their place. The region-merged
Noto Sans CJK .ttc is loaded once, with the leading language's face index.
- Fonts are rebuilt on a language switch only when the Han order changes
(HanOrder), not on every switch. font_definitions()/setup_context() use
the default order and never read the host locale.
- Tests stay locale-independent (UiState defaults to English; the desktop
app applies the system language). The web build follows the browser
language except CJK, which would render as tofu without system fonts.
- app.language.set error/params list all five codes; engine.commands lists
it. zh/ko/ru gain the keys #31 added (Page Layout|Orientation, display
theme). The Page Layout screen checkbox keeps English "View" via a
"Sheet Options|View" key (was shown as "Screen").
- Coverage test checks zh/ja/ko/Thai/Hebrew/Cyrillic in both the UI and
cell families by comparing glyphs with the tofu glyph.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- UiState::default() is English: tests, the kittest harness and the
snapshot example no longer depend on the host's locale. The desktop app
alone starts from the system language, when ui.json names no supported
language (first run, an older file, an unknown value).
- An unknown or malformed saved language reads as English instead of
failing the whole UiState and resetting every other preference.
- app.language.set takes {"language": "ja"} (with `code` as an alias, the
name the PR description used); the View combo now uses it via
Language::code(). The app.language.* commands are listed by
engine.commands with the other UI commands and documented in
docs/control-protocol.md.
- Home's text "Orientation" (方向) and Page Layout's print "Orientation"
(印刷の向き) get separate keys through a `context|label` key form.
- Japanese: Hide Rows → 行を表示しない, Trace Dependents → 参照先のトレース;
translate the display-theme combo merged in from main.
- system_theme test: the View tab now has two combo boxes; pick the theme one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blank cell and row repeats that run past 16384 columns / 1048576 rows
(padding from writers with larger sheets) are clipped instead of
rejecting the file; any data or merge past the edge is still an error.
convert() builds its Cell with ..Cell::value(value) so new Cell fields
take their defaults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the shared integration points for both formats: combined sniff,
file.open spec, file filters, MCP description, docs. open_bytes now lets
sniffed content (ODS or XLSB) win over the extension.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PhRun is 10 bytes per [MS-XLSB] 2.5.103 (four u16 fields plus a 16-bit
phType/alcH/unused1 word), so the documented size is kept; since the
RichStr ends its record and phonetic data is not imported, bytes after
the documented runs are now ignored instead of rejecting the file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The logger (apps/gridcraft/src/logging.rs, merged from main) reads SystemTime::now; the desktop
app never runs on wasm, so allow the lint at that call.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Double-clicking a workbook, Open With, dropping on the Dock icon and
`open -a GridCraft book.xlsx` send the app a kAEOpenDocuments Apple event
instead of command-line paths. winit 0.30 doesn't handle it, so AppKit
answered "GridCraft cannot open files in the Excel Workbook format".
Register the handler with the audited fmv-macos-events crate that PdfCraft
and PhotoCraft already use: it does the Objective-C interop (unsafe, which
this workspace forbids) behind a safe main-thread API and leaves winit's
delegate alone. Opened paths go through SheetApp::open_path; a quit event
closes the window. The registration lives in main() until the event loop
returns, so the event that launched the app is caught too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eframe's default wgpu instance enables PRIMARY | GL on Windows. Creating
the OpenGL backend can crash inside AMD's atio6axx.dll before the window
appears, so the app exits silently at launch. When WGPU_BACKEND is not
set, the Windows instance now enables only DirectX 12; an explicit
WGPU_BACKEND is still honoured. macOS, Linux and web are unchanged.
Same fix as storytold/designcraft#168 and storytold/filmcraft#179.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LyVbT6Vv4BwWZ4hY8973qD
eprintln! panics when standard error is a broken pipe (a GUI launch on
Windows), and a panic inside the panic hook aborts. The "no log file"
message goes through log::warn! (standard error only by then); the panic
hook's fallback writes to standard error with write_fmt and ignores the
error. Same fix as storytold/vectorcraft#465.
The desktop app installed no logger, so every log::warn!/log::error! in
the workspace was discarded (a sample that failed to build, wgpu and naga
warnings), and panics a command's guard recovered from only reached
standard error. A launch from a desktop menu has no terminal either, so
there was nothing to attach to a bug report.
apps/gridcraft/src/logging.rs (no new dependency):
- standard error plus logs/gridcraft.log next to ui.json and prefs.json
(~/.config/gridcraft/logs, or under $XDG_CONFIG_HOME); each launch
rotates the previous log to gridcraft.1.log and .2.log, so the log of a
run that crashed survives the next start; the file is capped at 16 MiB
- records logged before the settings directory is known are buffered
(bounded) and written once the file is attached
- default levels: info for gridcraft*, warn for everything else;
RUST_LOG replaces them with env_logger-style directives
- never panics: an unusable directory or a failed write leaves standard
error as the only sink
- runs with GRIDCRAFT_NO_PREFS (agents' test runs) log to standard error
only, so they don't rotate away the user's own logs
The logger is installed first in main, followed by a panic hook that
reports every panic, caught or not, through log::error! (with a backtrace
when RUST_BACKTRACE is set). The file is opened after argument parsing
(--version leaves no file). The start-up messages in main and the
control channel's go through the logger. Documented in README.md (Logs).