mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 02:27:08 -06:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b61a5d20e |
+3
-3
@@ -2,7 +2,7 @@
|
||||
|
||||
**Stage: alpha** · next: beta, ~20 points and ~120–210 h away
|
||||
|
||||
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-18 · **Change:** minor (native chart payloads stored in bounded ZIP members; previous: major, community PR wave 2026-10-14: ~60 landed items in the progress log, among them the Font, Paragraph, Photo Album, theme colour and font and Date and Time dialogs, presenter and show controls, the SmartArt Text Pane and PPTX corpus slices; breadth 84% after `cargo xtask parity`, ready stays ~55%, stage stays alpha; duplicated rows and headers folded; previous: minor, hours to ~95% per audience added) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-21 · **Change:** minor (macOS Finder document opening; native chart payloads stored in bounded ZIP members; previous: major, community PR wave 2026-10-14: ~60 landed items in the progress log, among them the Font, Paragraph, Photo Album, theme colour and font and Date and Time dialogs, presenter and show controls, the SmartArt Text Pane and PPTX corpus slices; breadth 84% after `cargo xtask parity`, ready stays ~55%, stage stays alpha; duplicated rows and headers folded; previous: minor, hours to ~95% per audience added) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
|
||||
|
||||
DeckCraft aims at full PowerPoint parity, and to be better: faster, open (a documented zip+JSON
|
||||
format plus PPTX), scriptable by agents (CLI, JSON control channel, MCP), and available everywhere
|
||||
@@ -207,7 +207,7 @@ app's JSON control channel (`deckcraft --control PORT`).
|
||||
| 2026-10-14 | Draw: pen pressure scales ink width when the pointer reports it (a mouse or a missing reading stays at the pen width; a stroke saved as a flat 0.5 still draws at the pen width). Tilt is not read |
|
||||
| 2026-10-13 | Saving a presentation keeps the embedded workbook behind an unchanged chart, and the chart part's relationships (#335). Editing the chart drops that workbook instead of writing numbers that no longer match it |
|
||||
| 2026-10-13 | A presentation whose object ids are used up refuses to grow instead of handing out duplicate ids (#407) |
|
||||
| 2026-10-13 | macOS: a Finder double-click, Open With, a drop on the Dock icon and `open -a DeckCraft deck.pptx` open the file (#138, #347). `Info.plist` now declares the documents DeckCraft opens (`.deckcraft`, `.pptx`, `.potx`, `.ppsx`, the same set as the Windows installer and the Linux desktop file, kept equal by a test), and the `kAEOpenDocuments` Apple event is handled through the pinned `fmv-macos-events` crate as in GridCraft; the `-psn_` argument older macOS adds on a Finder launch is ignored. PowerPoint stays the default until the user picks DeckCraft |
|
||||
| 2026-10-21 | macOS: a Finder double-click, Open With, a drop on the Dock icon and `open -a DeckCraft deck.pptx` open the file (#138, #347). `Info.plist` now declares the documents DeckCraft opens (`.deckcraft`, `.pptx`, `.potx`, `.ppsx`, the same set as the Windows installer and the Linux desktop file, kept equal by a test), and the `kAEOpenDocuments` Apple event is handled through the pinned `fmv-macos-events` crate as in GridCraft; the `-psn_` argument older macOS adds on a Finder launch is ignored. The current default app is preserved until the user chooses DeckCraft; system-opened `.ppsx` files start their slide show |
|
||||
| 2026-10-13 | Slide transitions like PowerPoint: every transition and option measured frame by frame against PowerPoint's video export and retuned (curves, directions, soft edges, 3-D perspective, contents moving over backgrounds); PowerPoint's own XML elements and defaults read and written (diagonal Wipe, Zoom, Shape In/Out, Glitter Hexagons, Shred In/Out, Page Curl Single); `deckcraft-cli render-transition` renders frames headless |
|
||||
| 2026-10-13 | Wild text cases measured against PowerPoint (102 synthetic slides): bidi by run language, East Asian vertical text upright, mongolianVert/wordArtVert/wordArtVertRtl, underline styles and strikethrough at the font's metrics, centre/right/decimal tabs, East Asian, Thai, Hindi, Arabic and Hebrew numbering, small caps, all-caps "ß", transparent text, soft hyphens, kinsoku, rtlCol and just/dist anchoring, emoji presentation, fallback faces that no longer move the baseline |
|
||||
| 2026-10-13 | Render matches PowerPoint more closely on wild shape, picture and table cases: even-odd custom paths, dashes and caps, arrowheads, radial and path gradients, tile rects, rotWithShape, gradient lines, shadows cast by what is drawn (group and perspective too), group fill, picture borders, tiles, reflection, recolour (biLevel, alphaInv, brightness/contrast), table borders and grid default, 20+ preset shapes by their ECMA formulas, default miter joins, and approximate 3-D bevels, cameras and face lighting |
|
||||
@@ -298,7 +298,7 @@ app's JSON control channel (`deckcraft --control PORT`).
|
||||
| 2026-10-13 | trivial | Progress log: Grid Options dialog |
|
||||
| 2026-10-14 | trivial | Progress log: pen pressure scales ink width |
|
||||
| 2026-10-13 | trivial | Progress log: chart workbook round-trip (#335) |
|
||||
| 2026-10-13 | trivial | Progress log: Finder opens presentations on macOS (#138) |
|
||||
| 2026-10-21 | minor | Progress log: Finder opens presentations on macOS (#138) |
|
||||
| 2026-10-13 | trivial | Progress log: wild text cases |
|
||||
| 2026-10-13 | trivial | Progress log: slide show for interactive decks |
|
||||
| 2026-10-12 | minor | Progress log: table cell editing and border resizing |
|
||||
|
||||
@@ -36,17 +36,78 @@ impl AppleEvents {
|
||||
|
||||
/// Open the documents and act on the quit requests that arrived since the last frame.
|
||||
pub fn poll(inbox: &Inbox, app: &mut SlideApp, ctx: &egui::Context) {
|
||||
for e in inbox.drain() {
|
||||
match e {
|
||||
Event::Open(paths) => {
|
||||
for p in paths.iter().filter_map(|p| p.to_str()) {
|
||||
if let Err(e) = app.open_path(p) {
|
||||
log::error!("{p}: {e}");
|
||||
app.set_status(format!("Couldn't open {p}: {e}"));
|
||||
}
|
||||
}
|
||||
for event in inbox.drain() {
|
||||
handle(event, app, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle(event: Event, app: &mut SlideApp, ctx: &egui::Context) {
|
||||
match event {
|
||||
Event::Open(paths) => {
|
||||
let paths: Vec<String> = paths.iter().filter_map(|p| p.to_str().map(str::to_string)).collect();
|
||||
for (path, error) in app.open_from_system(&paths) {
|
||||
log::error!("{path}: {error}");
|
||||
app.set_status(format!("Couldn't open {path}: {error}"));
|
||||
}
|
||||
Event::Quit => ctx.send_viewport_cmd(egui::ViewportCommand::Close),
|
||||
}
|
||||
Event::Quit => ctx.send_viewport_cmd(egui::ViewportCommand::Close),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use deckcraft_engine::Session;
|
||||
use deckcraft_ui_egui::Services;
|
||||
use serde_json::json;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn presentation(name: &str) -> PathBuf {
|
||||
let dir = std::env::temp_dir().join(format!("deckcraft-apple-events-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join(name);
|
||||
let mut session = Session::with_new();
|
||||
session.execute("file.saveAs", &json!({"path": path})).unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn open_events_append_presentations_at_launch_and_while_running() {
|
||||
let first = presentation("Finder launch ü.pptx");
|
||||
let second = presentation("Finder second.deckcraft");
|
||||
let mut app = SlideApp::new(Session::new(), Services::default());
|
||||
let ctx = egui::Context::default();
|
||||
handle(Event::Open(vec![first.clone()]), &mut app, &ctx);
|
||||
assert_eq!(app.session.documents().len(), 1);
|
||||
handle(Event::Open(vec![second.clone()]), &mut app, &ctx);
|
||||
assert_eq!(app.session.documents().len(), 2);
|
||||
assert_eq!(app.ui.recent.first(), second.to_str().map(str::to_string).as_ref());
|
||||
std::fs::remove_file(first).unwrap();
|
||||
std::fs::remove_file(second).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn open_event_starts_only_the_last_successful_powerpoint_show() {
|
||||
let show = presentation("Finder show.PPSX");
|
||||
let edit = presentation("Finder edit.pptx");
|
||||
let ctx = egui::Context::default();
|
||||
let mut app = SlideApp::new(Session::new(), Services::default());
|
||||
handle(Event::Open(vec![show.clone(), edit.clone()]), &mut app, &ctx);
|
||||
assert!(app.show.is_none(), "the last file opens for editing");
|
||||
handle(Event::Open(vec![show.clone()]), &mut app, &ctx);
|
||||
assert!(app.show.is_some(), "a system-opened PPSX starts its show");
|
||||
std::fs::remove_file(show).unwrap();
|
||||
std::fs::remove_file(edit).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_open_event_reports_the_error_and_keeps_the_document() {
|
||||
let mut app = SlideApp::new(Session::with_new(), Services::default());
|
||||
let ctx = egui::Context::default();
|
||||
handle(Event::Open(vec![PathBuf::from("/nonexistent/deckcraft-finder-missing.pptx")]), &mut app, &ctx);
|
||||
assert_eq!(app.session.documents().len(), 1);
|
||||
// Status is visible through the public UI control inspection.
|
||||
let result = deckcraft_ui_egui::control::inspect(&app, &ctx);
|
||||
assert!(result.to_string().contains("Couldn't open"), "{result}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user