Commit Graph
1836 Commits
Author SHA1 Message Date
aiongg 88c2ceff5f Merge pull request #1461 from Silvertree2010/perf/export-speed
Speed up PDF export by reusing equal slide backdrops
2026-10-27 13:42:02 -04:00
NetterSilverandClaude Opus 5.5 2f4a6d906a Speed up PDF export by reusing equal slide backdrops
PDF export rasterises every slide as a backdrop and a content layer and
hands both to the writer. Profiling (gdb stack sampling) showed most of
the main thread outside rendering going to per-pixel work on those
rasters: compositing the backdrop onto white (not vectorised), the
writer hashing every backdrop to deduplicate it, and the straight-alpha
conversion scanning every content pixel.

- Composite the backdrop with saturating adds and skip 16-pixel blocks
  that are already opaque; skip blocks with no partly transparent pixel
  in the straight-alpha conversion. Same formulas, same bytes.
- Keep the last opaque backdrops (up to 32 MiB of pixels) and hand the
  writer the same image again when a slide renders an identical
  backdrop. The writer stored equal backdrops once already; now it also
  skips hashing and compressing the copies. Output is byte-identical.

deckcraft-cli bench, export .pdf step, 15 interleaved runs each on a
16-thread Ryzen (A/A noise +0.9 %):
- sample x4 (32 slides): median 531 -> 322 ms (-39 %), min 529 -> 317 ms
- same deck, every slide a different background: 557 -> 441 ms (-21 %)
- sample (8 slides): 149 -> 103 ms (-31 %)
Exported PDFs are byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 17:39:31 +01:00
NetterSilverandClaude Opus 5.5 53e183495b Time PDF export over all bench runs
`deckcraft-cli bench` capped the "export .pdf" step at three runs while
its help promises the median of N runs for every step. Use --runs for
it too, so a PDF export benchmark can take enough samples to beat noise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:43:29 +01:00
Jeremy 8445d98e24 fix(installer): resolve the wizard scope and folder correctly
Real msiexec runs (per-user and all-users, silent and wizard) found two defects
that the metadata fixtures cannot see:

- "Only for me" published ALLUSERS=2, so FindRelatedProducts treated the run as
  per-machine and skipped the existing per-user copy. An interactive upgrade
  left two registered products and uninstall left files behind. The page now
  leaves ALLUSERS unset for a per-user choice.
- Windows Installer resolves Program Files once, for the context it started in,
  so choosing "Everyone" in the wizard installed into the user profile with a
  machine-wide registration. The wizard now sets the new-install folder itself
  after the choice (Program Files, Program Files (x86) for 32-bit, or the
  per-user Programs folder). Silent installs are unchanged.

check-install-policy.ps1 now fails if either regresses.
2026-10-27 15:32:16 +00:00
aiongg 2c9edcc394 refactor(ui): use craft-ui's ComboBox for every dropdown (#1457)
* ui: use craft_ui::ComboBox so dropdown menus grow back (egui#5225); repin craft-libs

* chore(deps): craft-libs 60d60d48 (dropdown fixes)

* chore(deps): craft-libs 6af63481 (dropdown fixes)

* chore(deps): craft-libs 4d7b82f7 (dropdown fixes)

* chore(deps): craft-libs 92d392cf (#387 merged)

* chore(xtask): pdf-deps expects craft-libs 92d392cf
2026-10-27 10:26:51 -04:00
NaseqandClaude Opus 5.5 feecee0a30 Slide show tests: check the pending preview and the one-render gate directly (#964)
A harness runs frames until no repaint is asked for, so the preview had
already landed when its first assertion ran. One raw frame now shows the
preview pending and one render started, and the paging test pins a pending
job and checks a page turn starts no other render.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 18:13:44 +04:30
NaseqandClaude Opus 5.5 356195ba33 Slide show: render the Presenter View's next-slide preview off the UI thread (#964)
Turning the page with Presenter View on re-rendered the next-slide preview
synchronously, single-threaded, on the UI thread. The preview now renders
on a worker thread and arrives on a later frame; the web build (no
threads), or a worker that dies, renders it inline. At most one render runs:
paging faster than it asks for the newest preview once it is done, and a
finished one no longer wanted is dropped. Job and texture are keyed by the
presentation as well as the slide, so a preview rendered before an edit is
rendered again. While a preview is on its way the box stays empty rather
than saying "End of slide show".

Rebased onto the show module split: show/presenter.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 17:53:57 +04:30
NaseqandClaude Opus 5.5 b1645b006e Render: skip shapes whose outline is not finite or far larger than any screen (#1165)
An ellipse at the EMU limit, compounding group scales or custom-geometry
guides could reach vello's flattener with an outline billions of pixels
long; it sizes its buffer by that length and the allocation aborts the
process. geometry::drawable() rejects a shape with a non-finite path
point, then measures the outline's control box (which always holds the
curve, unlike the tight bounding box when its extremum overflows) unioned
with the frame; past 1e7 device pixels the shape is skipped with a warning.

Rebased onto the render module split: the guard now lives in geometry.rs
and renderer.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 17:12:07 +04:30
Jeremy 428c8809ea fix(installer): harden install-policy checks and packaging-lint workflow 2026-10-27 12:00:56 +00:00
Jeremy abee4d7522 fix(installer): address scope and ARM64 review feedback 2026-10-27 12:00:56 +00:00
Jeremy 4d9b325d35 feat(installer): default to a per-user ArtCraft folder 2026-10-27 12:00:56 +00:00
FaabyyandClaude b9842d0b82 Slide show: Set Up Slide Show can keep the mouse cursor visible (#1445)
A full-screen show hides a still pointer after 2.5 s, which makes decks
with clickable, branching choices hard to use. Set Up Slide Show now has
Keep mouse cursor visible (`show.keepCursor {on?}`): the show then keeps
the normal pointer, and the pointing hand over links and actions, while
links still follow. It is a user preference kept in ui.json, since PPTX
has no place for it; off by default, Reading View unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-27 10:50:26 +00:00
NetterSilverandClaude Haiku 5.5 cd2e780432 Reject '+' sign in slide range numbers
Found while testing crates/engine/src/cmd/file.rs:390-400 (parse_slide_range)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 09:19:49 +01:00
neoandClaude Opus 5.5 20553865cc Fonts: a variable font's named instances answer to their Windows family names
Documents name non-RIBBI weights by their Windows family ("Montserrat ExtraBold" / Regular).
The bundled Montserrat is variable, so its instances had no Windows names; the installed static
files that do carry them were skipped as already present, and the lookup fell back to Inter
Regular without reporting the font missing. Each named instance other than Regular, Italic,
Bold and Bold Italic now also answers to "<family> <weight>" with a Regular or Italic style,
as Windows lists them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 00:10:58 -07:00
Dominic Anaeto d8961ebc1e P0.2a: finish file output after stdout closes 2026-10-27 02:24:42 -04:00
Harrison Booth 9ad147b310 Format Shape Effects: drawn Shadow, Glow and Soft Edges preset tiles, and a shadow Size slider 2026-10-26 21:51:54 -05:00
Harrison Booth 6fa873dcfc feat(ui-egui): Scale Height/Width and a From origin in Format Shape's Size & Properties
Scale is measured from the size the tab first showed, like the Size and Position
dialog, and runs shape.sizePosition (honouring Lock aspect ratio). From switches
Horizontal/Vertical between the top-left corner and the centre. The lock checkbox
used a fresh id every frame so it never held; it now keeps its state.
2026-10-26 21:18:44 -05:00
aiongg ee875abffa Merge pull request #1433 from aiongg/layout
refactor: shared module layout (craft-contrib#20)
2026-10-26 21:30:28 -04:00
skylar taylor-barrick 020e53e7a0 Merge pull request #1429 from storytold/fix/nextest-0.9.150
ci: require nextest 0.9.150
2026-10-26 17:35:27 -06:00
Harrison Booth 65826295cc fix(text): draw Unicode symbol-font bullets with the fallback chain, not a dot (#1347)
A buFont named like Noto Sans Symbols was treated as a legacy symbol font, so its
bullet characters mapped to a generic dot. Only Wingdings and Symbol map by code now;
the bullet's own face is chosen by the usual cascade instead of a fixed Inter fallback.
2026-10-26 18:20:23 -05:00
Harrison Booth 8c8ab9561b fix(render): animation state never applies to layout and master shapes sharing a slide shape id (fixes #1362) 2026-10-26 18:20:19 -05:00
62ea604394 Animations: the Add Animation menu scrolls when it is taller than the window (fixes #1370)
Every effect in one column is taller than a 1440p screen, and the popup
did not scroll, so the last entries could not be reached. Cap the menu at
the window height minus the title bar and ribbon, and scroll inside it.

Co-Authored-By: Grok 4.7 <noreply@x.ai>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 23:48:25 +01:00
Harrison Booth fa0a3e9dee Duplicate: count the names inside groups when numbering copies 2026-10-26 17:40:21 -05:00
Harrison Booth 1b2e5ea9e0 fix(format): keep explicit slider edits, skip only the rounding 2026-10-26 17:36:59 -05:00
Harrison Booth c5994e16db fix(pptx): read Auto-reverse from animation behaviour nodes (#1363)
PowerPoint writes autoRev on p:cBhvr/p:cTn, not the effect node, so Grow/Shrink with auto-reverse grew and held. Color Pulse writes it there as part of the effect and is left out. Tests patch autoRev onto the behaviour node of a saved deck and read it back. Not checked against real PowerPoint: no PowerPoint COM here.
2026-10-26 17:34:28 -05:00
Harrison Booth 5a28760b1e fix(format): Reflection None clears after another preset (#1356)
The effect sliders rounded 55.000000000000007 to 55 and read that as a change,
so each frame re-added the reflection the None button had just cleared.
2026-10-26 17:29:31 -05:00
Harrison Booth 8ada601630 Duplicate gives the copy the next automatic name and keeps a chosen name, as PowerPoint does 2026-10-26 17:17:53 -05:00
aiongg 12001d14e7 refactor(engine): split cmd/shape.rs into cmd/shape/{size,appearance,picture,freeform}.rs 2026-10-26 22:10:40 +00:00
aiongg bbf7a8b8c7 docs: update source paths after the module layout moves 2026-10-26 22:08:44 +00:00
aiongg 96f0ac204e test(engine): read past #[cfg(test)] imports in the file-system scan 2026-10-26 22:08:44 +00:00
aiongg 6b57773373 refactor(geom): split preset.rs into preset/{catalog,builder,build,shapes}.rs 2026-10-26 22:08:44 +00:00
aiongg 838d51ed59 refactor(text): split lib.rs into {run,style,autonum,shape,symbol,breaking,layout}.rs 2026-10-26 22:08:44 +00:00
aiongg 5b02b1744c refactor(render): split lib.rs into {raster,opts,slide,renderer,geometry,stroke,text_draw}.rs 2026-10-26 22:08:44 +00:00
aiongg 8b456dce41 refactor(engine): split tools.rs into tools/{hit,pointer,drag,click,geometry}.rs 2026-10-26 22:08:44 +00:00
aiongg 15bf821535 refactor(engine): split cmd/text.rs into cmd/text/{ime,layout,editing,autocorrect,caret,formatting}.rs 2026-10-26 22:08:44 +00:00
aiongg d7c06b53eb refactor(engine): split cmd/smartart.rs into cmd/smartart/{nodes,paint,layout,diagrams,edit}.rs 2026-10-26 22:08:44 +00:00
aiongg 97b98767f1 refactor(engine): split cmd/insert.rs into cmd/insert/{picture,album,chart,text_fields,links}.rs 2026-10-26 22:08:44 +00:00
aiongg d5180fbb70 refactor(engine): split cmd/format.rs into cmd/format/{font,text_effects,paragraph,painter,ruler}.rs 2026-10-26 22:08:44 +00:00
aiongg 9c222be711 refactor(engine): split cmd/design.rs into cmd/design/{colors,slide_background,slide_setup,master_view}.rs 2026-10-26 22:08:44 +00:00
aiongg 28371e27d7 refactor(ui-egui): split backstage.rs into backstage/{files,info,print}.rs 2026-10-26 22:08:44 +00:00
aiongg 4bdd5b5bee refactor(ui-egui): split icons.rs into icons/{geometry,draw}.rs 2026-10-26 22:08:44 +00:00
aiongg 99688fe45a refactor(ui-egui): split show.rs into show/{render,keys,ui,media,presenter,zoom}.rs 2026-10-26 22:08:44 +00:00
aiongg ada66524a2 refactor(ui-egui): split panels.rs into one file per pane under panels/ 2026-10-26 22:08:44 +00:00
aiongg e84ba9def1 refactor(ui-egui): split ribbon.rs into one file per tab under ribbon/ 2026-10-26 22:08:44 +00:00
aiongg 9545ff4d61 refactor(ui-egui): move the zoom, trim, about and alt-text test modules to their dialog files 2026-10-26 22:08:44 +00:00
aiongg c8a6363a0f refactor(ui-egui): split dialogs.rs into one file per dialog under dialogs/ 2026-10-26 22:08:44 +00:00
aiongg 462310faed refactor(ui-egui): split canvas.rs into canvas/{input,scroll,ime,media,rulers,guides,overlays,context_menu}.rs 2026-10-26 22:08:44 +00:00
aiongg d428b4804a refactor(ui-egui): move the backstage test files beside backstage.rs 2026-10-26 22:08:44 +00:00
aiongg 9ab4ed9581 refactor(ui-egui): move the sorter test files beside sorter.rs 2026-10-26 22:08:44 +00:00
aiongg a094f37ed9 refactor(ui-egui): move the ribbon test files beside ribbon.rs 2026-10-26 22:08:44 +00:00