Merge #311: fix(pages): inherit the current page background when adding pages (M1) (@tylerjenningsw)

# Conflicts:
#	ROADMAP.md
This commit is contained in:
bflatastic
2026-10-15 15:08:00 +07:00
7 changed files with 80 additions and 16 deletions
+4
View File
@@ -16,3 +16,7 @@
#
# To remove your names, delete your entry (or set fields empty) in a PR. The next
# contributors.py run, and the next build, drop them.
[people.tylerjenningsw]
real_name = "Tyler Jennings"
display_name = "tylerjenningsw"
+6 -1
View File
@@ -26,7 +26,12 @@ fn settle(s: &mut Session, cmd: &str) -> crate::Result<()> {
pub fn specs() -> Vec<CommandSpec> {
vec![
spec!("page.add", "Add page", "{name?} (a name starting with a hyphen adds a divider)", true, |s, p| {
let background = s.new_page_background();
// An added page takes the current page's colour; the theme only decides new files.
let current = s.page().ok().and_then(|p| match s.doc().ok()?.node(p)?.kind {
NodeKind::Page { background, .. } => Some(background),
_ => None,
});
let background = current.unwrap_or_else(|| s.new_page_background());
let doc = s.doc_mut()?;
let name = string(p, "name").unwrap_or_else(|| format!("Page {}", doc.pages.len() + 1));
let id = doc.add_page(&name);
+3 -3
View File
@@ -663,8 +663,8 @@ pub struct Session {
pub prefs: Prefs,
pub tool: tools::ToolState,
pub ui_requests: Vec<UiRequest>,
/// The interface's theme once the UI knows it ([`Session::set_ui_dark`]): new files and pages
/// get the dark page colour while it is dark. Headless sessions (CLI, MCP, exports) have no
/// The interface's theme once the UI knows it ([`Session::set_ui_dark`]): new files get the
/// dark page colour while it is dark (added pages take the current page's). Headless sessions (CLI, MCP, exports) have no
/// theme, so what scripts make is the same everywhere.
pub ui_dark: Option<bool>,
/// Command log (id + params) for actions/automation.
@@ -721,7 +721,7 @@ impl Session {
}
}
/// The background new pages get: the dark page colour while the interface is dark.
/// The background a new file's page gets: the dark page colour while the interface is dark.
pub fn new_page_background(&self) -> figcraft_color::Color {
if self.ui_dark == Some(true) { figcraft_color::Color::DARK_PAGE } else { figcraft_color::Color::DEFAULT_PAGE }
}
+60 -7
View File
@@ -1,7 +1,7 @@
//! The page colour of new files and pages follows the interface's theme when they are made (dark:
//! #1E1E1E, light: #F5F5F5) and is the page's own from then on; a session without an interface
//! (CLI, MCP, exports) has no theme and keeps the light default. Text drawn with the Text tool is
//! white on a dark page and black on a light one.
//! The page colour of a new file follows the interface's theme when it is made (dark: #1E1E1E,
//! light: #F5F5F5) and is the page's own from then on; an added page takes the colour of the page
//! it was added from. A session without an interface (CLI, MCP, exports) has no theme and keeps the
//! light default. Text drawn with the Text tool is white on a dark page and black on a light one.
use figcraft_color::{Color, Paint};
use figcraft_doc::{NodeId, NodeKind};
@@ -38,8 +38,8 @@ fn draw_text(s: &mut Session, x: f64, y: f64) -> NodeId {
}
#[test]
fn new_files_and_pages_take_the_theme_they_are_made_in() {
// (theme the interface reported, colour of a new file's page and of an added page)
fn new_files_take_the_theme_they_are_made_in_and_added_pages_follow() {
// (theme the interface reported, colour of a new file's page and of a page added to it)
for (dark, want) in [(None, "#F5F5F5"), (Some(false), "#F5F5F5"), (Some(true), "#1E1E1E")] {
let mut s = Session::empty();
if let Some(d) = dark {
@@ -62,8 +62,10 @@ fn a_change_of_theme_leaves_existing_pages_alone() {
ex(&mut s, "file.new", json!({}));
s.set_ui_dark(false);
ex(&mut s, "page.add", json!({}));
assert_eq!(backgrounds(&s), ["#1E1E1E", "#1E1E1E"], "an added page follows its page, not the theme");
ex(&mut s, "page.setBackground", json!({"color": "#F5F5F5"}));
s.set_ui_dark(true);
assert_eq!(backgrounds(&s), ["#1E1E1E", "#F5F5F5"], "each page keeps the colour it was made with");
assert_eq!(backgrounds(&s), ["#1E1E1E", "#F5F5F5"], "each page keeps its colour");
// A copy of a page is that page's colour, whatever the theme.
let first = s.doc().unwrap().pages[0];
s.set_ui_dark(false);
@@ -71,6 +73,57 @@ fn a_change_of_theme_leaves_existing_pages_alone() {
assert_eq!(background(&s, copy), Color::DARK_PAGE);
}
#[test]
fn an_added_page_takes_the_current_pages_colour() {
// Whatever the theme, and headless too: the page it was added from decides.
for dark in [None, Some(false), Some(true)] {
let mut s = Session::empty();
if let Some(d) = dark {
s.set_ui_dark(d);
}
ex(&mut s, "file.new", json!({}));
ex(&mut s, "page.setBackground", json!({"color": "#336699"}));
ex(&mut s, "page.add", json!({}));
assert_eq!(backgrounds(&s), ["#336699", "#336699"], "theme {dark:?}");
// From the second page, now current, after giving it its own colour.
ex(&mut s, "page.setBackground", json!({"color": "#FFE08A"}));
ex(&mut s, "page.add", json!({"name": "Third"}));
assert_eq!(backgrounds(&s), ["#336699", "#FFE08A", "#FFE08A"], "theme {dark:?}");
// One undo step, the colour with it.
ex(&mut s, "edit.undo", json!({}));
assert_eq!(backgrounds(&s), ["#336699", "#FFE08A"], "theme {dark:?} after undo");
ex(&mut s, "edit.redo", json!({}));
assert_eq!(backgrounds(&s), ["#336699", "#FFE08A", "#FFE08A"], "theme {dark:?} after redo");
}
// From a page other than the first: go there, then add.
let mut s = Session::new();
s.set_ui_dark(true);
ex(&mut s, "page.add", json!({}));
ex(&mut s, "page.setBackground", json!({"color": "#0B1F3A"}));
ex(&mut s, "page.select", json!({"index": 0}));
ex(&mut s, "page.add", json!({}));
assert_eq!(backgrounds(&s), ["#1E1E1E", "#0B1F3A", "#1E1E1E"], "the current page decides, not the last");
}
#[test]
fn added_page_colours_survive_save_and_reopen() {
let dir = std::env::temp_dir().join(format!("figcraft-added-pages-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("pages.figcraft");
let mut s = Session::new();
ex(&mut s, "page.setBackground", json!({"color": "#336699"}));
ex(&mut s, "page.add", json!({}));
ex(&mut s, "file.saveAs", json!({"path": path.to_string_lossy()}));
let mut s = Session::empty();
s.set_ui_dark(true);
ex(&mut s, "file.open", json!({"path": path.to_string_lossy()}));
assert_eq!(backgrounds(&s), ["#336699", "#336699"]);
// And a page added to the reopened file in the dark theme follows the file, not the theme.
ex(&mut s, "page.add", json!({}));
assert_eq!(backgrounds(&s), ["#336699", "#336699", "#336699"]);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn the_file_the_app_opened_with_takes_the_theme_once_it_is_known() {
// Made before the interface knew its theme, nothing done to it yet.
+1 -1
View File
@@ -892,7 +892,7 @@ impl FigApp {
_ => false,
};
self.ui.dark = dark;
// New files and pages take the theme's page colour (existing pages keep theirs).
// New files take the theme's page colour (existing pages keep theirs).
self.session.set_ui_dark(dark);
theme::apply_with(ctx, if dark { theme::Tokens::dark() } else { theme::Tokens::light() }, self.reduce_motion());
if self.worker.is_none() {
+3 -1
View File
@@ -75,5 +75,7 @@ fn a_new_file_takes_the_page_colour_of_the_theme_and_keeps_it() {
u.app.run("view.themeDark", json!({}));
u.frame(vec![]);
u.app.run("page.add", json!({}));
assert_eq!(page_colour(&u), Color::DARK_PAGE, "a page added in the dark theme");
assert_eq!(page_colour(&u), Color::DEFAULT_PAGE, "a page added in the dark theme follows its light page");
u.app.run("file.new", json!({}));
assert_eq!(page_colour(&u), Color::DARK_PAGE, "a file made in the dark theme");
}
+3 -3
View File
@@ -301,9 +301,9 @@ on; `key` resolves keys the way the canvas does. Commands behind keys:
given, are or are in; default the selection) out of the library the file publishes, or let them
back in (`on: false`; without `on` it toggles). The sign of it is the name: hiding puts a `.`
before it, showing takes the leading `.` and `_` away. Returns `{ids, hidden}`; one undo step.
- New files and pages: `file.new` and `page.add` give a page the colour `#F5F5F5`. Only the app
with its dark theme showing makes them `#1E1E1E`; headless sessions (the CLI, this server) have
no theme. Text made with `text.create` or `node.create` is black whatever the page here; in the
- New files and pages: `file.new` gives its page the colour `#F5F5F5`. Only the app with its dark
theme showing makes it `#1E1E1E`; headless sessions (the CLI, this server) have no theme.
`page.add` gives the new page the current page's colour, wherever it runs. Text made with `text.create` or `node.create` is black whatever the page here; in the
app (the control channel, `--attach`) it is white on a dark page unless `props` name a `fill`,
as the Text tool (`pointer` with `tool.text`) makes it everywhere.
- `transform.rotationOrigin {on?}` (`Alt+R`): show or hide the selection's rotation origin handle