mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-28 04:17:06 -06:00
Task panes: drop a pane outside or inside a docked pane to order a side (#4386)
* Task panes: a pane dropped over a docked pane's outer or inner third docks outside or inside it, and the order is kept (craft-libs#357) * Pin craft-libs at b74f1b6 (pane order on an edge, craft-libs#357) * Pin craft-libs at b926b5c (craft-libs main)
This commit is contained in:
Generated
+19
-6
@@ -1011,10 +1011,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "craft-fonts-build"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
|
||||
[[package]]
|
||||
name = "craft-i18n"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1e6f127844ec61b3b8afdcc2bb76e7cfe470153f#1e6f127844ec61b3b8afdcc2bb76e7cfe470153f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
dependencies = [
|
||||
"fixed_decimal",
|
||||
"icu_decimal",
|
||||
@@ -1037,7 +1042,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-pdf"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1e6f127844ec61b3b8afdcc2bb76e7cfe470153f#1e6f127844ec61b3b8afdcc2bb76e7cfe470153f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
dependencies = [
|
||||
"krilla",
|
||||
"thiserror 2.0.21",
|
||||
@@ -1046,7 +1051,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-print"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1e6f127844ec61b3b8afdcc2bb76e7cfe470153f#1e6f127844ec61b3b8afdcc2bb76e7cfe470153f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
dependencies = [
|
||||
"craft-winprint-gdi",
|
||||
"miniz_oxide 0.9.1",
|
||||
@@ -1059,19 +1064,27 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-ui"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1e6f127844ec61b3b8afdcc2bb76e7cfe470153f#1e6f127844ec61b3b8afdcc2bb76e7cfe470153f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
dependencies = [
|
||||
"craft-fonts-build",
|
||||
"craft-i18n",
|
||||
"egui",
|
||||
"epaint",
|
||||
"js-sys",
|
||||
"log",
|
||||
"objc2 0.6.5",
|
||||
"objc2-app-kit 0.3.2",
|
||||
"objc2-foundation 0.3.2",
|
||||
"skrifa 0.44.0",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "craft-update"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1e6f127844ec61b3b8afdcc2bb76e7cfe470153f#1e6f127844ec61b3b8afdcc2bb76e7cfe470153f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dunce",
|
||||
@@ -2830,7 +2843,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "krilla"
|
||||
version = "0.8.2"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=1e6f127844ec61b3b8afdcc2bb76e7cfe470153f#1e6f127844ec61b3b8afdcc2bb76e7cfe470153f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=b926b5c03e256c86ca10934f58d03f628810b5b7#b926b5c03e256c86ca10934f58d03f628810b5b7"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bumpalo",
|
||||
|
||||
+3
-3
@@ -58,8 +58,8 @@ wuff = "0.2.9"
|
||||
brotli-decompressor = "5"
|
||||
vello_cpu = "0.2"
|
||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp", "gif", "bmp", "tiff"] }
|
||||
craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "1e6f127844ec61b3b8afdcc2bb76e7cfe470153f", version = "=0.1.0", default-features = false, features = ["write"] }
|
||||
craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "1e6f127844ec61b3b8afdcc2bb76e7cfe470153f", features = ["egui"] }
|
||||
craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "b926b5c03e256c86ca10934f58d03f628810b5b7", version = "=0.1.0", default-features = false, features = ["write"] }
|
||||
craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "b926b5c03e256c86ca10934f58d03f628810b5b7", features = ["egui"] }
|
||||
quick-xml = "0.41"
|
||||
zip = { version = "3", default-features = false, features = ["deflate"] }
|
||||
# zlib streams (compressed metafile pictures in `.doc` drawings); already in the lock as a dependency of other crates.
|
||||
@@ -120,7 +120,7 @@ debug = 0
|
||||
|
||||
# Required by craft-pdf: the backend rejects writers that failed during unwinding.
|
||||
[patch.crates-io]
|
||||
krilla = { git = "https://github.com/storytold/craft-libs", rev = "1e6f127844ec61b3b8afdcc2bb76e7cfe470153f" }
|
||||
krilla = { git = "https://github.com/storytold/craft-libs", rev = "b926b5c03e256c86ca10934f58d03f628810b5b7" }
|
||||
ecolor = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
||||
emath = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
||||
epaint = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
||||
|
||||
@@ -101,7 +101,7 @@ list (author, source, license) is in ATTRIBUTION.md.
|
||||
Rust dependencies (egui, eframe, vello_cpu, skrifa, harfrust, krilla, quick-xml, zip, image, …)
|
||||
are permissively licensed (MIT/Apache-2.0/BSD/ISC/Zlib) and fetched through crates.io or pinned Git sources.
|
||||
The craft-pdf writer and Krilla 0.8.2 backend are pinned to storytold/craft-libs revision
|
||||
1e6f127844ec61b3b8afdcc2bb76e7cfe470153f. The backend retains its MIT OR Apache-2.0 licensing
|
||||
b926b5c03e256c86ca10934f58d03f628810b5b7. The backend retains its MIT OR Apache-2.0 licensing
|
||||
and upstream bundled material; see that revision's licenses/pdf-writer/ and ATTRIBUTION.md.
|
||||
The backend patch prevents finalization during an unwind and rejects a writer after failed
|
||||
cleanup. WordCraft enables only writing; PDF import and its image decoders are not enabled.
|
||||
|
||||
@@ -73,7 +73,7 @@ windows = { version = "0.62", features = ["UI_ViewManagement", "System_UserProfi
|
||||
clipboard-win = { version = "5.4", features = ["std"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
craft-print = { git = "https://github.com/storytold/craft-libs", rev = "1e6f127844ec61b3b8afdcc2bb76e7cfe470153f" }
|
||||
craft-print = { git = "https://github.com/storytold/craft-libs", rev = "b926b5c03e256c86ca10934f58d03f628810b5b7" }
|
||||
rfd = { workspace = true }
|
||||
# Waits for an `rfd::AsyncFileDialog` on a background thread (the dialogs don't block the UI, #94);
|
||||
# rfd itself already depends on it.
|
||||
|
||||
@@ -9,12 +9,12 @@ description = "WordCraft egui front end (Word-style ribbon UI); swappable: nothi
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
craft-ui = { git = "https://github.com/storytold/craft-libs", rev = "1e6f127844ec61b3b8afdcc2bb76e7cfe470153f" }
|
||||
craft-ui = { git = "https://github.com/storytold/craft-libs", rev = "b926b5c03e256c86ca10934f58d03f628810b5b7" }
|
||||
# Interface translations: lookup, fallback, plurals and system-locale matching over the craft-i18n
|
||||
# catalogs that `build.rs` embeds from `CRAFT_I18N_DIR` (storytold/craft-i18n, pinned in
|
||||
# `craft-i18n.rev`). Same craft-libs rev as craft-ui; it brings lazy per-locale parsing
|
||||
# (storytold/craft-libs#247), ICU4X plural rules and bidi isolates around inserted values.
|
||||
craft-i18n = { git = "https://github.com/storytold/craft-libs", rev = "1e6f127844ec61b3b8afdcc2bb76e7cfe470153f" }
|
||||
craft-i18n = { git = "https://github.com/storytold/craft-libs", rev = "b926b5c03e256c86ca10934f58d03f628810b5b7" }
|
||||
wordcraft-geom = { workspace = true, features = ["image"] }
|
||||
wordcraft-doc = { workspace = true }
|
||||
wordcraft-fonts = { workspace = true }
|
||||
|
||||
@@ -304,6 +304,10 @@ pub struct UiState {
|
||||
/// Where each of the other task panes sits, by [`panes::Pane::key`] (#1756).
|
||||
#[serde(deserialize_with = "panes::lenient_places")]
|
||||
pub pane_places: std::collections::BTreeMap<String, panes::SavedPane>,
|
||||
/// The task panes in the order the user put them, by [`panes::Pane::key`]: each edge shows its
|
||||
/// panes in this order, the first outermost. Empty: the default order.
|
||||
#[serde(deserialize_with = "panes::lenient_order", skip_serializing_if = "Vec::is_empty")]
|
||||
pub pane_order: Vec<String>,
|
||||
/// Headings whose sub-headings the Navigation pane hides (#1997); per session, not saved.
|
||||
#[serde(skip)]
|
||||
pub nav_collapsed: std::collections::HashSet<wordcraft_doc::Path>,
|
||||
@@ -425,6 +429,7 @@ impl Default for UiState {
|
||||
styles_pane_float: None,
|
||||
styles_pane_width: None,
|
||||
pane_places: Default::default(),
|
||||
pane_order: Vec::new(),
|
||||
nav_collapsed: Default::default(),
|
||||
show_discord: true,
|
||||
author: String::new(),
|
||||
|
||||
+121
-8
@@ -17,7 +17,7 @@ use wordcraft_engine::StylesPaneDock;
|
||||
use crate::WordApp;
|
||||
use crate::theme::{Tokens, regular, semibold};
|
||||
|
||||
/// The task panes, in the order they open: each edge's first pane is outermost.
|
||||
/// The task panes, in their default order: each edge's first pane is outermost.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Pane {
|
||||
/// SmartArt Design › Text Pane, while a SmartArt graphic WordCraft can edit is selected.
|
||||
@@ -94,6 +94,10 @@ impl Pane {
|
||||
Self::Comments,
|
||||
];
|
||||
|
||||
fn from_key(key: &str) -> Option<Self> {
|
||||
Self::ALL.into_iter().find(|pane| pane.key() == key)
|
||||
}
|
||||
|
||||
/// The command that closes the pane; Restrict Editing has none and closes in [`close`].
|
||||
fn close_command(self) -> Option<&'static str> {
|
||||
match self {
|
||||
@@ -111,11 +115,45 @@ impl Pane {
|
||||
}
|
||||
}
|
||||
|
||||
/// The panes open now, from the session's view flags.
|
||||
/// Every pane in the order the user left them (`paneOrder` in `ui.json`), then the panes that
|
||||
/// list lacks in their default order. Each edge shows its panes in this order, the first outermost.
|
||||
fn order(app: &WordApp) -> Vec<Pane> {
|
||||
let mut order: Vec<Pane> = app.ui.pane_order.iter().filter_map(|key| Pane::from_key(key)).collect();
|
||||
for pane in Pane::ALL {
|
||||
if !order.contains(&pane) {
|
||||
order.push(pane);
|
||||
}
|
||||
}
|
||||
order
|
||||
}
|
||||
|
||||
/// Put `pane` directly before `before` in the order, outside it on their edge (`None`: after
|
||||
/// every pane).
|
||||
fn move_before(app: &mut WordApp, pane: Pane, before: Option<Pane>) {
|
||||
let mut order = order(app);
|
||||
order.retain(|p| *p != pane);
|
||||
let at = before.and_then(|b| order.iter().position(|p| *p == b)).unwrap_or(order.len());
|
||||
order.insert(at, pane);
|
||||
app.ui.pane_order = order.iter().map(|p| p.key().to_string()).collect();
|
||||
}
|
||||
|
||||
/// `paneOrder` as saved: unknown and repeated keys are dropped, not the whole file.
|
||||
pub(crate) fn lenient_order<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Vec<String>, D::Error> {
|
||||
let v = <Value as serde::Deserialize>::deserialize(d)?;
|
||||
let mut keys: Vec<String> = Vec::new();
|
||||
for key in v.as_array().map(Vec::as_slice).unwrap_or_default().iter().take(Pane::ALL.len() * 4).filter_map(Value::as_str) {
|
||||
if Pane::from_key(key).is_some() && !keys.iter().any(|k| k == key) {
|
||||
keys.push(key.to_string());
|
||||
}
|
||||
}
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
/// The panes open now, from the session's view flags, in the user's order.
|
||||
pub fn open(app: &WordApp) -> Vec<Pane> {
|
||||
let v = &app.session.view;
|
||||
let smart_art = v.smart_art_pane && wordcraft_engine::cmd::smart_art::selected_smart_art(&app.session).is_some();
|
||||
[
|
||||
let mut open = [
|
||||
(Pane::SmartArtText, smart_art),
|
||||
(Pane::Navigation, v.nav_pane),
|
||||
(Pane::Clipboard, v.clipboard_pane),
|
||||
@@ -129,7 +167,10 @@ pub fn open(app: &WordApp) -> Vec<Pane> {
|
||||
]
|
||||
.into_iter()
|
||||
.filter_map(|(pane, shown)| shown.then_some(pane))
|
||||
.collect()
|
||||
.collect::<Vec<_>>();
|
||||
let order = order(app);
|
||||
open.sort_by_key(|pane| order.iter().position(|p| p == pane).unwrap_or(usize::MAX));
|
||||
open
|
||||
}
|
||||
|
||||
/// Where a task pane other than Styles sits, kept in `ui.json` (#1756). Styles keeps its place in
|
||||
@@ -345,16 +386,21 @@ pub(crate) fn apply(app: &mut WordApp, request: PaneRequest<Pane>) {
|
||||
}
|
||||
PaneRequest::Resize(pane, width) => app.ui.pane_places.entry(pane.key().into()).or_default().width = Some(width),
|
||||
PaneRequest::FloatRect(pane, rect) => app.ui.pane_places.entry(pane.key().into()).or_default().float = Some(rect),
|
||||
PaneRequest::Before(pane, before) => {
|
||||
let _ = app.run("ui.paneDock", json!({"pane": pane.key(), "before": before.map(Pane::key)}));
|
||||
}
|
||||
PaneRequest::Focus(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// `ui.paneDock {"pane": key, "dock"?: "left" | "right" | "float"}` (#1756): docks or floats a
|
||||
/// task pane, as its menu's Dock Left, Dock Right and Float do; without `dock` it reads the
|
||||
/// pane's place. `pane` is the pane's `panePlaces` key; Styles goes through `view.stylesPaneDock`.
|
||||
/// `ui.paneDock {"pane": key, "dock"?: "left" | "right" | "float", "before"?: key | null}` (#1756):
|
||||
/// docks or floats a task pane, as its menu's Dock Left, Dock Right and Float do; without `dock`
|
||||
/// it reads the pane's place. `pane` is the pane's `panePlaces` key; Styles goes through
|
||||
/// `view.stylesPaneDock`. `before` puts the pane directly before that pane in the order, outside
|
||||
/// it on their edge, as a drop over a docked pane does; `null` puts it after every pane.
|
||||
pub(crate) fn dock_command(app: &mut WordApp, p: &Value) -> Result<Value, String> {
|
||||
let key = p.get("pane").and_then(Value::as_str).ok_or("`pane` must be a task pane's key, such as \"navigation\"")?;
|
||||
let pane = Pane::ALL.into_iter().find(|pane| pane.key() == key).ok_or_else(|| format!("unknown task pane `{key}`"))?;
|
||||
let pane = Pane::from_key(key).ok_or_else(|| format!("unknown task pane `{key}`"))?;
|
||||
let dock = match p.get("dock") {
|
||||
None => None,
|
||||
Some(v) => Some(match v.as_str() {
|
||||
@@ -364,6 +410,17 @@ pub(crate) fn dock_command(app: &mut WordApp, p: &Value) -> Result<Value, String
|
||||
_ => return Err("`dock` must be left, right or float".into()),
|
||||
}),
|
||||
};
|
||||
let before = match p.get("before") {
|
||||
None => None,
|
||||
Some(Value::Null) => Some(None),
|
||||
Some(v) => match v.as_str().and_then(Pane::from_key) {
|
||||
Some(before) if before != pane => Some(Some(before)),
|
||||
_ => return Err("`before` must be another task pane's key or null".into()),
|
||||
},
|
||||
};
|
||||
if let Some(before) = before {
|
||||
move_before(app, pane, before);
|
||||
}
|
||||
if pane == Pane::Styles {
|
||||
let params = p.get("dock").map_or_else(|| json!({}), |d| json!({"dock": d}));
|
||||
let result = app.run("view.stylesPaneDock", params)?;
|
||||
@@ -1336,6 +1393,62 @@ mod tests {
|
||||
assert_eq!(super::pane_state(h.state(), Pane::Navigation).place, docked, "the drag was cancelled");
|
||||
}
|
||||
|
||||
/// With Navigation docked left, Comments dropped over Navigation's outer third docks left of
|
||||
/// it, where the preview showed, and the order survives a restart; over Navigation's inner
|
||||
/// third it goes back inside.
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn comments_dropped_over_navigations_outer_third_docks_left_of_it() {
|
||||
use egui_kittest::kittest::Queryable;
|
||||
|
||||
let mut app = crate::WordApp::new(wordcraft_engine::Session::new(wordcraft_doc::Document::from_text("")), crate::Services::default());
|
||||
app.run("view.navigationPane", serde_json::json!({"value": true})).unwrap();
|
||||
app.run("view.commentsPane", serde_json::json!({"value": true})).unwrap();
|
||||
let mut h = pane_harness(app);
|
||||
// A docked pane's title bar starts at its panel's left edge.
|
||||
let panel = |h: &egui_kittest::Harness<'static, crate::WordApp>, pane: Pane| h.get_by_label(&format!("Move Pane: {}", pane.title())).rect();
|
||||
let button =
|
||||
|pos, pressed| egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: egui::Modifiers::NONE };
|
||||
let drag = |h: &mut egui_kittest::Harness<'static, crate::WordApp>, to: egui::Pos2| {
|
||||
let from = h.get_by_label("Move Pane: Comments").rect().center();
|
||||
h.input_mut().events.extend([egui::Event::PointerMoved(from), button(from, true)]);
|
||||
h.run_steps(1);
|
||||
h.input_mut().events.push(egui::Event::PointerMoved(from + egui::vec2(-8.0, 6.0)));
|
||||
h.run_steps(1);
|
||||
h.input_mut().events.push(egui::Event::PointerMoved(to));
|
||||
h.run_steps(1);
|
||||
h.input_mut().events.push(button(to, false));
|
||||
h.run_steps(3);
|
||||
};
|
||||
let nav = panel(&h, Pane::Navigation);
|
||||
drag(&mut h, egui::pos2(nav.left() + 60.0, nav.center().y));
|
||||
let (comments, nav_after) = (panel(&h, Pane::Comments), panel(&h, Pane::Navigation));
|
||||
assert_eq!(comments.left(), nav.left(), "Comments is outermost");
|
||||
assert!(nav_after.left() > comments.right(), "Navigation sits inside it");
|
||||
assert_eq!(super::open(h.state()), vec![Pane::Comments, Pane::Navigation]);
|
||||
|
||||
let saved = serde_json::to_vec(&h.state().prefs()).unwrap();
|
||||
let mut next = crate::WordApp::new(wordcraft_engine::Session::new(wordcraft_doc::Document::new()), crate::Services::default());
|
||||
next.apply_prefs(serde_json::from_slice(&saved).unwrap());
|
||||
next.session.view.comments_pane = true;
|
||||
assert_eq!(super::open(&next), vec![Pane::Comments, Pane::Navigation], "the order is kept");
|
||||
|
||||
let nav = panel(&h, Pane::Navigation);
|
||||
let width = Pane::Navigation.default_state().size.unwrap();
|
||||
drag(&mut h, egui::pos2(nav.left() + width * 0.85, nav.center().y));
|
||||
assert_eq!(super::open(h.state()), vec![Pane::Navigation, Pane::Comments]);
|
||||
assert!(panel(&h, Pane::Comments).left() > panel(&h, Pane::Navigation).right(), "back inside Navigation");
|
||||
|
||||
let mut junk = crate::WordApp::new(wordcraft_engine::Session::new(wordcraft_doc::Document::new()), crate::Services::default());
|
||||
junk.apply_prefs(serde_json::from_str(r#"{"paneOrder": ["comments", 3, "nope", "comments", "navigation"], "navPane": true}"#).unwrap());
|
||||
assert_eq!(junk.ui.pane_order, vec!["comments", "navigation"], "unknown and repeated keys are dropped");
|
||||
for bad in [serde_json::json!({"pane": "comments", "before": "comments"}), serde_json::json!({"pane": "comments", "before": 7})] {
|
||||
assert!(junk.run("ui.paneDock", bad.clone()).is_err(), "{bad}");
|
||||
}
|
||||
junk.run("ui.paneDock", serde_json::json!({"pane": "navigation", "before": null})).unwrap();
|
||||
assert_eq!(junk.ui.pane_order.last().map(String::as_str), Some("navigation"));
|
||||
}
|
||||
|
||||
/// An F6 the user bound runs its command; plain F6 still moves to the panes when only
|
||||
/// Shift+F6 is bound.
|
||||
#[test]
|
||||
|
||||
@@ -54,7 +54,7 @@ and set `WORDCRAFT_CONTROL_KEY` to that app's key.
|
||||
|
||||
| Method | Params | Result |
|
||||
|---|---|---|
|
||||
| `engine.execute` | `command`, `params` | the command's result (any id from `engine.commands`, including UI commands `ui.tab`, `ui.dialog` (`{"name": dialog, "part"?: table style part}`; `part` opens New / Modify Table Style (`newTableStyle` / `modifyTableStyle`) on that part: `wholeTable`, `headerRow`, `firstColumn`, `bandedRows`, `nwCell`… as `table.modifyStyle` names them), `ui.backstage`, `ui.zotero.*` (see `docs/zotero.md`), `ui.language` (`{"value": "auto"\|"en"\|"zh-hans"\|"zh-hant"\|"ja"\|"pt-br"\|"es"\|"uk"\|"sr"\|"sr-latn"\|"et"\|"tr"\|"ru"\|"pl"\|"de"\|"it"\|"cs"\|"hu"\|"fr"}`), `ui.theme` (`{"value": "system"\|"light"\|"dark"}`; `system` follows the OS appearance, light when it reports none), `ui.interfaceSize` (`{"value": "auto"}` or `{"percent": 50–300}`, clamped; no value reads it; `auto` keeps the system display scale, a percentage is absolute, so 100 is one screen pixel per point (#822); the result's `percent` is the size drawn at and `auto` whether it follows the system; document zoom stays `view.zoom`), `ui.animations` (`{"value": "system"\|"on"\|"off"}`; no value reads it; `system` follows the system's animation setting, which the desktop app reads on Windows, macOS, Linux and the BSDs and the web app from `prefers-reduced-motion`, and means on where the system doesn't say; the result's `enabled` is what the setting comes to and `system` what the system reports, `null` when it doesn't say), `ui.fileBrowser` (desktop: `{"purpose": "document"\|"picture"\|"recipients"\|"save"}` opens WordCraft's own file browser, for when the system file dialog doesn't show up, #576), `ui.saveAsTemplate` (File › Save As › Save as Word Template: the Save dialog for a `.dotx`, starting in the user templates folder; the result's `dir` is that folder, `null` on the web, #2852), `ui.caretAnimation` (`{"value": true\|false}`; no value reads it; off, the caret jumps and blinks on and off instead of gliding and fading), `ui.startScreen` (`{"value": true\|false}`; no value reads it; on, the default, the desktop and web apps start on File › Home when launched with nothing to open, #766), `ui.alignmentGuides` (`{"value": true\|false}`; no value reads it; on, the default, moving a picture or shape snaps it to the page, the margins and other objects, with guide lines while dragging; Alt moves freely), `ui.quickAccessToolbar` (`{"value": true\|false}`; no value toggles it; false takes the Quick Access Toolbar out of the title bar, as its menu's Hide Quick Access Toolbar does, and true brings it back, #1945), `ui.focusRibbon` (`{"value": true\|false}`; no value toggles it; in Focus, true drops the ribbon down over the page, as the bar's ⋯ does, and false puts it away, as a click on the page does; outside Focus it stays put away, #1678), `ui.contextMenu` (opens the page's context menu below the caret, as Shift+F10 does), `ui.paneDock` (`{"pane": "navigation"\|"clipboard"\|"styles"\|"styleInspector"\|"accessibility"\|"altText"\|"formatShape"\|"restrictEditing"\|"comments"\|"smartArtText", "dock"?: "left"\|"right"\|"float"}`; no `dock` reads it; docks or floats a task pane as its menu does, kept between runs; Styles is `view.stylesPaneDock`, #1756)…) |
|
||||
| `engine.execute` | `command`, `params` | the command's result (any id from `engine.commands`, including UI commands `ui.tab`, `ui.dialog` (`{"name": dialog, "part"?: table style part}`; `part` opens New / Modify Table Style (`newTableStyle` / `modifyTableStyle`) on that part: `wholeTable`, `headerRow`, `firstColumn`, `bandedRows`, `nwCell`… as `table.modifyStyle` names them), `ui.backstage`, `ui.zotero.*` (see `docs/zotero.md`), `ui.language` (`{"value": "auto"\|"en"\|"zh-hans"\|"zh-hant"\|"ja"\|"pt-br"\|"es"\|"uk"\|"sr"\|"sr-latn"\|"et"\|"tr"\|"ru"\|"pl"\|"de"\|"it"\|"cs"\|"hu"\|"fr"}`), `ui.theme` (`{"value": "system"\|"light"\|"dark"}`; `system` follows the OS appearance, light when it reports none), `ui.interfaceSize` (`{"value": "auto"}` or `{"percent": 50–300}`, clamped; no value reads it; `auto` keeps the system display scale, a percentage is absolute, so 100 is one screen pixel per point (#822); the result's `percent` is the size drawn at and `auto` whether it follows the system; document zoom stays `view.zoom`), `ui.animations` (`{"value": "system"\|"on"\|"off"}`; no value reads it; `system` follows the system's animation setting, which the desktop app reads on Windows, macOS, Linux and the BSDs and the web app from `prefers-reduced-motion`, and means on where the system doesn't say; the result's `enabled` is what the setting comes to and `system` what the system reports, `null` when it doesn't say), `ui.fileBrowser` (desktop: `{"purpose": "document"\|"picture"\|"recipients"\|"save"}` opens WordCraft's own file browser, for when the system file dialog doesn't show up, #576), `ui.saveAsTemplate` (File › Save As › Save as Word Template: the Save dialog for a `.dotx`, starting in the user templates folder; the result's `dir` is that folder, `null` on the web, #2852), `ui.caretAnimation` (`{"value": true\|false}`; no value reads it; off, the caret jumps and blinks on and off instead of gliding and fading), `ui.startScreen` (`{"value": true\|false}`; no value reads it; on, the default, the desktop and web apps start on File › Home when launched with nothing to open, #766), `ui.alignmentGuides` (`{"value": true\|false}`; no value reads it; on, the default, moving a picture or shape snaps it to the page, the margins and other objects, with guide lines while dragging; Alt moves freely), `ui.quickAccessToolbar` (`{"value": true\|false}`; no value toggles it; false takes the Quick Access Toolbar out of the title bar, as its menu's Hide Quick Access Toolbar does, and true brings it back, #1945), `ui.focusRibbon` (`{"value": true\|false}`; no value toggles it; in Focus, true drops the ribbon down over the page, as the bar's ⋯ does, and false puts it away, as a click on the page does; outside Focus it stays put away, #1678), `ui.contextMenu` (opens the page's context menu below the caret, as Shift+F10 does), `ui.paneDock` (`{"pane": "navigation"\|"clipboard"\|"styles"\|"styleInspector"\|"accessibility"\|"altText"\|"formatShape"\|"restrictEditing"\|"comments"\|"smartArtText", "dock"?: "left"\|"right"\|"float", "before"?: pane\|null}`; no `dock` reads it; docks or floats a task pane as its menu does, kept between runs; Styles is `view.stylesPaneDock`, #1756; `before` puts the pane directly before that pane in the order, outside it on their edge, as a drop over a docked pane's outer third does, and `null` after every pane; the order is kept between runs)…) |
|
||||
| `engine.commands` | — | every command: id, label, location, shortcut, params, enabled |
|
||||
| `document.inspect` | `text?` | blocks (text, style, runs, lists, tables), parts, sections, selection, pages |
|
||||
| `ui.inspect` | — | UI state, view, dialog, window size, page rects on screen, caret, comment balloons (`balloons`: id + screen rect; `balloon`: the selected one), perf |
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
WordCraft uses `craft-pdf` with default features disabled and `write` enabled. Both that dependency
|
||||
and the required root Krilla patch use the immutable `storytold/craft-libs` revision
|
||||
`1e6f127844ec61b3b8afdcc2bb76e7cfe470153f`. Rust 1.95 or newer is required. The revision must be
|
||||
`b926b5c03e256c86ca10934f58d03f628810b5b7`. Rust 1.95 or newer is required. The revision must be
|
||||
available from the remote before a fresh checkout can resolve it. The shared writer checks a
|
||||
backend marker at compile time so an unpatched Krilla cannot replace the required backend.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user