Replace the Account page with About (#1697)

* UI: replace Account backstage page with About

* UI: stack About columns in narrow windows; keep PowerPoint's Account in parity doc

* UI: About review fixes: stack columns below 860pt, Help icon, Credits task; doc headers
This commit is contained in:
Dylan M. Taylor
2026-11-02 19:02:14 -05:00
committed by GitHub
parent 0d45a64db2
commit f68150ee10
6 changed files with 87 additions and 46 deletions
+3
View File
@@ -2,6 +2,7 @@
**Stage: alpha** · next: beta, ~20 points and ~120–210 h away
> **Last reviewed:** 2026-11-02 · **Last updated:** 2026-11-02 · **Change:** trivial (File › About replaces Account; product links, credits and appearance retained; parity scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-11-01 · **Change:** trivial (locked No Fill and no-outline click-through regression coverage added; No Fill hit-testing follows resolved fill instead of the presence of a style (#1621); scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-30 · **Change:** trivial (progress log: read-only `slide.actions` lists shape and table text actions; parity scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-27 · **Change:** trivial (progress log: Set Up Slide Show ▸ Keep mouse cursor visible, #1445; stage and scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
@@ -148,6 +149,7 @@ app's JSON control channel (`deckcraft --control PORT`).
| Date | What landed |
|---|---|
| 2026-11-02 | File › About replaces the unused Account page, with version, project links, contributor/model credits and appearance preferences |
| 2026-10-31 | No Fill shape interiors allow selection and editing of underlying objects, even when a quick style remains; hit-testing uses the renderer’s fill resolution. Outlines, rotation, text targets and actual fills keep their hit behaviour (#1621) |
| 2026-10-30 | Print-preview tests use the URL emitted by egui and its exact file path; previews are explicitly removed before the macOS process exits |
| 2026-10-30 | `slide.actions` lists shape click and hover actions and text-run hyperlinks (including table cells and grouped shapes) for audit; listing never runs an action. The UI pane is a follow-up |
@@ -319,6 +321,7 @@ app's JSON control channel (`deckcraft --control PORT`).
| Date | Change | Summary |
|---|---|---|
| 2026-11-02 | trivial | File › About replaces Account; product links, credits and appearance retained |
| 2026-10-31 | trivial | No Fill hit-testing follows resolved fill instead of the presence of a style (#1621); scores unchanged |
| 2026-10-30 | trivial | Progress log: read-only slide action inspection; parity scores unchanged |
| 2026-10-29 | trivial | Progress log: going back in the slide show reverses the left slide's transition (#1531). Scores and milestone names unchanged |
+13 -9
View File
@@ -2,7 +2,7 @@
//! bar until Esc or the back arrow returns to the presentation. With nothing open it is the start
//! screen: PowerPoint's start screen and File › Home are the same page.
//!
//! A side menu on the left picks a page (Home, New, Open, Info, Save As, Print, Export, Account);
//! A side menu on the left picks a page (Home, New, Open, Info, Save As, Print, Export, About);
//! Save, Close and Options act at once. Every action runs an existing command through
//! [`SlideApp::run`] or one of the app's file helpers ([`SlideApp::export_as`],
//! [`SlideApp::print_pdf`], [`SlideApp::save_as_dialog`]); the state here is UI-only and never
@@ -24,7 +24,7 @@ mod print;
pub use files::split_path;
use files::{home, new_page, open_page};
use info::{PROPERTIES, account, commit_props, info, save_as};
use info::{PROPERTIES, about, commit_props, info, save_as};
pub use info::{SAVE_TYPES, format_bytes};
use print::{export, print};
pub use print::{print_page_size, print_params, print_slides};
@@ -47,11 +47,11 @@ pub enum Page {
SaveAs,
Print,
Export,
Account,
About,
}
impl Page {
pub const ALL: [Page; 8] = [Page::Home, Page::New, Page::Open, Page::Info, Page::SaveAs, Page::Print, Page::Export, Page::Account];
pub const ALL: [Page; 8] = [Page::Home, Page::New, Page::Open, Page::Info, Page::SaveAs, Page::Print, Page::Export, Page::About];
/// The id `app.backstage {page}` takes.
pub fn id(self) -> &'static str {
@@ -63,12 +63,16 @@ impl Page {
Page::SaveAs => "saveAs",
Page::Print => "print",
Page::Export => "export",
Page::Account => "account",
Page::About => "about",
}
}
pub fn parse(id: &str) -> Option<Page> {
let id = id.trim();
// Keep existing control-channel callers working after the visible page rename.
if id.eq_ignore_ascii_case("account") {
return Some(Page::About);
}
Page::ALL.into_iter().find(|p| p.id().eq_ignore_ascii_case(id))
}
@@ -82,7 +86,7 @@ impl Page {
Page::SaveAs => "Save As",
Page::Print => "Print",
Page::Export => "Export",
Page::Account => "Account",
Page::About => "About",
}
}
@@ -95,7 +99,7 @@ impl Page {
Page::SaveAs => Icon::SaveAs,
Page::Print => Icon::Print,
Page::Export => Icon::Export,
Page::Account => Icon::Account,
Page::About => Icon::Help,
}
}
@@ -140,7 +144,7 @@ pub fn entries(has_doc: bool) -> (Vec<Nav>, Vec<Nav>) {
if has_doc {
top.extend([Nav::Page(Page::Info), Nav::Save, Nav::Page(Page::SaveAs), Nav::Page(Page::Print), Nav::Page(Page::Export), Nav::Close]);
}
(top, vec![Nav::Page(Page::Account), Nav::Options])
(top, vec![Nav::Page(Page::About), Nav::Options])
}
/// Print page: which slides.
@@ -412,7 +416,7 @@ pub fn show(app: &mut SlideApp, ui: &mut Ui) {
Page::SaveAs => save_as(app, ui, &mut bs, &mut out),
Page::Print => print(app, ui, &mut bs),
Page::Export => export(app, ui, &mut bs),
Page::Account => account(app, ui),
Page::About => about(app, ui),
}
});
});
+23 -31
View File
@@ -189,11 +189,30 @@ pub(super) fn save_as(app: &mut SlideApp, ui: &mut Ui, bs: &mut Backstage, out:
}
}
/// Account: the interface's appearance and what DeckCraft is. DeckCraft has no accounts, so
/// there is nothing to sign in to.
pub(super) fn account(app: &mut SlideApp, ui: &mut Ui) {
/// Product information, project links, credits and appearance preferences.
pub(super) fn about(app: &mut SlideApp, ui: &mut Ui) {
let t = Tokens::get(ui.ctx());
ui.horizontal_top(|ui| {
// Side by side needs 480 + 40 + 340 points; stack the columns when the page is narrower.
let layout = if ui.available_width() >= 860.0 { egui::Layout::left_to_right(egui::Align::Min) } else { egui::Layout::top_down(egui::Align::Min) };
ui.with_layout(layout, |ui| {
ui.vertical(|ui| {
ui.set_width(480.0);
heading(ui, "Product Information");
ui.horizontal(|ui| {
let (r, _) = ui.allocate_exact_size(vec2(40.0, 40.0), Sense::hover());
crate::dialogs::paint_logo(ui.painter(), r);
ui.label(RichText::new("DeckCraft").font(theme::bold(24.0)).color(t.text));
});
dim(ui, &crate::dialogs::version_line());
dim(ui, "Open source under MIT OR Apache-2.0. Not affiliated with Microsoft.");
ui.add_space(14.0);
if task(ui, Icon::Help, "Credits", "Credits", "The people and models who built DeckCraft.", true) {
let _ = app.run("app.about", json!({}));
}
ui.hyperlink_to("Join our community on Discord", deckcraft_engine::links::DISCORD);
ui.hyperlink_to("Source on GitHub", deckcraft_engine::links::GITHUB);
});
ui.add_space(40.0);
ui.vertical(|ui| {
ui.set_width(340.0);
heading(ui, "Appearance");
@@ -210,33 +229,6 @@ pub(super) fn account(app: &mut SlideApp, ui: &mut Ui) {
})
.response
.widget_info(|| egui::WidgetInfo::labeled(egui::Role::ComboBox, true, "Appearance"));
ui.add_space(16.0);
heading(ui, "User Information");
dim(ui, "DeckCraft has no accounts: there is nothing to sign in to, and your presentations stay on your device.");
});
ui.add_space(40.0);
ui.vertical(|ui| {
heading(ui, "Product Information");
ui.horizontal(|ui| {
let (r, _) = ui.allocate_exact_size(vec2(40.0, 40.0), Sense::hover());
crate::dialogs::paint_logo(ui.painter(), r);
ui.label(RichText::new("DeckCraft").font(theme::bold(24.0)).color(t.text));
});
dim(ui, &crate::dialogs::version_line());
dim(ui, "Open source under MIT OR Apache-2.0. Not affiliated with Microsoft.");
ui.add_space(14.0);
if task(
ui,
Icon::Help,
"About DeckCraft",
"About DeckCraft",
"The version, the people and models who built it, and where to find us.",
true,
) {
let _ = app.run("app.about", json!({}));
}
ui.hyperlink_to("Join our community on Discord", deckcraft_engine::links::DISCORD);
ui.hyperlink_to("Source on GitHub", deckcraft_engine::links::GITHUB);
});
});
}
+41 -2
View File
@@ -85,11 +85,13 @@ fn page_ids_round_trip_and_unknown_ids_are_refused() {
assert_eq!(Page::parse(p.id()), Some(p));
}
assert_eq!(Page::parse(" SAVEAS "), Some(Page::SaveAs));
assert_eq!(Page::parse("account"), Some(Page::About));
assert_eq!(Page::parse("about"), Some(Page::About));
assert_eq!(Page::parse("share"), None);
assert_eq!(Page::parse(""), None);
let (top, bottom) = entries(false);
assert_eq!(top, vec![Nav::Page(Page::Home), Nav::Page(Page::New), Nav::Page(Page::Open)], "the start screen has no presentation pages");
assert_eq!(bottom, vec![Nav::Page(Page::Account), Nav::Options]);
assert_eq!(bottom, vec![Nav::Page(Page::About), Nav::Options]);
assert_eq!(entries(true).0.len(), 9);
assert_eq!(split_path("/home/me/Talk.pptx"), ("Talk.pptx".into(), "/home/me".into()));
assert_eq!(format_bytes(512), "512 bytes");
@@ -166,7 +168,7 @@ fn every_page_renders() {
(Page::SaveAs, "Not saved yet"),
(Page::Print, "Print Hidden Slides"),
(Page::Export, "Create PDF Document"),
(Page::Account, "About DeckCraft"),
(Page::About, "Credits"),
];
for (p, marker) in checks {
h.state_mut().run("app.backstage", json!({"page": p.id()})).unwrap();
@@ -704,3 +706,40 @@ fn options_in_backstage_opens_preferences_and_cancel_closes_it() {
assert!(h.state().dialog.is_none(), "Cancel closes Preferences dialog");
assert!(h.state().backstage_open(), "Backstage remains open");
}
/// About replaces the account entry and still opens the credits dialog.
#[test]
fn about_has_product_links_and_credits_without_account_controls() {
let mut h = harness(new_app());
h.get_by_label("File").click();
h.run_steps(3);
assert!(h.query_by_label("Account").is_none());
click_nav(&h, "About");
h.run_steps(3);
assert_eq!(page(&h), Some(Page::About));
assert!(h.query_by_label("User Information").is_none());
assert!(h.query_by_label("Source on GitHub").is_some());
assert!(h.query_by_label("Join our community on Discord").is_some());
assert!(h.query_all_by_label("Appearance").next().is_some());
click_on_page(&h, "Credits");
h.run_steps(3);
assert!(h.state().dialog.is_some());
assert!(h.query_by_label("Contributors").is_some());
assert!(h.query_by_label("Models").is_some());
}
/// At the 900-point minimum window width the About columns stack and Appearance stays on the page.
#[test]
fn about_appearance_stays_on_the_page_at_minimum_width() {
let mut h = Harness::builder().with_size(egui::vec2(900.0, 900.0)).build_ui_state(
|ui, app: &mut SlideApp| {
app.logic(ui.ctx());
app.ui(ui);
},
new_app(),
);
h.state_mut().run("app.backstage", json!({"page": "about"})).unwrap();
h.run_steps(3);
let r = h.query_all_by_role_and_label(Role::ComboBox, "Appearance").next().expect("Appearance selector").rect();
assert!(r.right() <= 900.0, "Appearance selector ends at {}, inside the window", r.right());
}
+1 -1
View File
@@ -129,7 +129,7 @@ pub const UI_COMMANDS: &[(&str, &str, Option<&str>, &str)] = &[
("show.end", "End Show", None, "{}"),
("animation.preview", "Preview Animations", None, "{on?: bool, from?: effect index}"),
("app.home", "Home", None, "{on?}"),
("app.backstage", "File", Some("Alt+F"), "{page?: home|new|open|info|saveAs|print|export|account, on?: bool}"),
("app.backstage", "File", Some("Alt+F"), "{page?: home|new|open|info|saveAs|print|export|about, on?: bool}"),
("app.closePresentation", "Close", Some("Cmd+W"), "{}"),
(
"app.print",
+6 -3
View File
@@ -1,5 +1,6 @@
# UI parity with PowerPoint
> **Last reviewed:** 2026-11-02 · **Last updated:** 2026-11-02 · **Change:** trivial (File › About replaces Account; product links, credits and appearance retained; parity scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-11-01 · **Change:** trivial (locked No Fill and no-outline click-through regression coverage added; No Fill hit-testing follows resolved fill instead of the presence of a style (#1621); scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-26 · **Change:** trivial (dragging several selected slides moves them all; previous: trivial, Edit Points: Shift, Ctrl/Cmd or Alt/Option on a handle drag sets the point type; previous: trivial, scroll bars on the zoomed slide pane, #1157; previous: major, community PR wave 2026-10-14: 14 new dialogs (41 in all), Eyedropper, Export button, New Window, Grid Options, WordArt gallery and handles, Slide Sorter sections, Quick Access Toolbar, Format Background and Format Shape depth, rich-text clipboard; duplicated rows and headers folded; previous: minor, File tab / Backstage, #76) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-22 · **Change:** trivial (group fill and outline edits reach members, #1079; scores unchanged) · **Target:** Microsoft PowerPoint for Mac 16.113 (Microsoft 365)
@@ -28,7 +29,7 @@ ends and are omitted from thumbnails, slide shows and exports.
| Colour menus: theme and standard colours, hex field, Eyedropper | yes (plus a More Colors dialog) | yes: theme and standard colours, hex field and an Eyedropper that takes the colour drawn under the pointer on the slide (#367) | More Colors dialog; Eyedropper only takes colours from the slide, not the rest of the screen |
| Contextual tabs: Shape Format, Picture Format, Table Design, Table Layout, Chart Design, Chart Format, Video/Audio Format and Playback | yes | most; checklist P0 row partial | Chart Format, media tabs thinner |
| File tab (Backstage): Home, New, Open, Info, Save, Save As, Print, Share, Export, Close, Account, Options | yes | yes (#76), see [File tab](#file-tab-backstage); no Share (cloud) | Video/GIF/CD exports, templates beyond the built-in themes |
| Start screen: new, themes, templates, recent, open | yes, the same page as File › Home | yes: File › Home with Home, New, Open, Account and Options (#76) | |
| Start screen: new, themes, templates, recent, open | yes, the same page as File › Home | yes: File › Home with Home, New, Open, About and Options (#76) | |
| Status bar: slide counter, language, notes, comments, views, zoom slider | yes | yes | |
| Command search (Tell Me) | yes | command palette | |
| Light / dark appearance | follows the system | light or dark; doesn't follow the system (#41) | 1–2 h |
@@ -45,7 +46,8 @@ Close and, at the bottom under a rule, Account and Options. Entries are 40 point
outline icon; the current page is drawn in the accent colour and outlined. Esc goes back. Alt+F opens
on Home with the keyboard in the side menu; Up and Down move through it and a page shows as soon as
its entry has the focus. PowerPoint's start screen is the same Home page, with only Home, New and
Open at the top. DeckCraft follows all of this (`crates/ui-egui/src/backstage.rs`), with the page and
Open at the top. DeckCraft follows all of this (`crates/ui-egui/src/backstage.rs`), except that
Account is About, with the page and
menu colours taken from the theme tokens (menu `chrome`, page `card`).
| Page | PowerPoint | DeckCraft |
@@ -60,7 +62,7 @@ menu colours taken from the theme tokens (menu `chrome`, page `card`).
| Share | Cloud sharing | Not offered |
| Export | Create PDF/XPS Document, Create a Video, Create an Animated GIF, Package Presentation for CD, Create Handouts, Change File Type | Create PDF Document, Create Handouts (a PDF), Change File Type (PNG with an optional transparent background, #784). No video, GIF or CD package |
| Close | Closes; asks Save / Don't Save / Cancel about unsaved changes | Same (`file.close` after the prompt); Ctrl+W asks too |
| Account | User information, sign-in, Office Background and Office Theme, product information, About | Appearance (light/dark, `view.dark`), product information, About DeckCraft. No accounts to sign in to |
| Account | User information, sign-in, Office Background and Office Theme, product information, About | About: product information, project links, contributor/model credits and appearance (light/dark, `view.dark`). No Account entry |
| Options | Opens the PowerPoint Options dialog | Opens Preferences |
## Panes and dialogs
@@ -171,6 +173,7 @@ consistency is open (#73, #56).
| Date | Change | Summary |
|---|---|---|
| 2026-11-02 | trivial | File › About replaces Account; product links, credits and appearance retained |
| 2026-10-31 | trivial | No Fill hit-testing follows resolved fill instead of the presence of a style (#1621); scores unchanged |
| 2026-10-29 | trivial | Picture Format: the Picture Styles row shows as many styles as fit beside the groups after it (at least two) and a More arrow with all 24, as the Transitions and Animations galleries do; Crop and Size are in view in the default window (#1232) |
| 2026-10-26 | trivial | Thumbnail pane and Slide Sorter: dragging one of several selected slides moves the whole selection |