mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-31 22:16:59 -06:00
Review: proofing language for the whole presentation
Review › Language gets "Apply to: Selected text / Whole presentation". The new format.languageAll command sets lang/noProof on every run in the deck (slides, groups, table cells, notes, masters, layouts) and on the master and default text styles, so new text inherits it. It needs no selection and is one undo step; format.language is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
38d2ade2b7
commit
2d3997d2e3
@@ -6,6 +6,7 @@ use deckcraft_model::resolve;
|
||||
use deckcraft_model::text::{Align, Anchor, AutoFit, Bullet, Caps, ParaProps, RunProps, Spacing, Strike, TabStop, TextDir};
|
||||
use deckcraft_model::{ColorRef, ColorTransform, Effects, Fill, Line, SchemeSlot, Shape, ShapeKind};
|
||||
use serde_json::{Value, json};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::text::{format_body, format_para, format_run};
|
||||
use super::*;
|
||||
@@ -60,6 +61,15 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
has_text_or_shapes,
|
||||
language
|
||||
),
|
||||
cmd!(
|
||||
"format.languageAll",
|
||||
"Language for All Text",
|
||||
["Review", "Language"],
|
||||
None,
|
||||
"{lang?, noProof?: bool} (proofing language of every text in the presentation: slides, tables, notes, masters and layouts, and the default for new text; one undo step)",
|
||||
has_doc,
|
||||
language_all
|
||||
),
|
||||
cmd!("format.highlight", "Text Highlight Color", ["Home", "Font"], None, "{color? (absent = none)}", has_text_or_shapes, highlight),
|
||||
cmd!(
|
||||
"format.case",
|
||||
@@ -398,16 +408,17 @@ fn text_transparency(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
r.fill = Some(f);
|
||||
})
|
||||
}
|
||||
fn language(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
/// The `lang` and `noProof` params of the language commands, checked: a run-props setter.
|
||||
fn language_edit(id: &str, p: &Value) -> Result<impl Fn(&mut RunProps) + Send + Sync + 'static> {
|
||||
let lang = str_param(p, "lang").map(str::to_string);
|
||||
let no_proof = bool_param(p, "noProof");
|
||||
if lang.is_none() && no_proof.is_none() {
|
||||
return Err(bad("format.language", "give `lang` or `noProof`"));
|
||||
return Err(bad(id, "give `lang` or `noProof`"));
|
||||
}
|
||||
if lang.as_deref().is_some_and(|l| l.is_empty() || l.len() > 35 || !l.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')) {
|
||||
return Err(bad("format.language", "`lang` is a language tag such as en-US"));
|
||||
return Err(bad(id, "`lang` is a language tag such as en-US"));
|
||||
}
|
||||
format_run(s, &move |r| {
|
||||
Ok(move |r: &mut RunProps| {
|
||||
if let Some(l) = &lang {
|
||||
r.lang = Some(l.clone());
|
||||
}
|
||||
@@ -416,6 +427,53 @@ fn language(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
})
|
||||
}
|
||||
fn language(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let f = language_edit("format.language", p)?;
|
||||
format_run(s, &f)
|
||||
}
|
||||
/// Every run in the deck, and the text styles new text inherits from, in one language.
|
||||
fn language_all(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let f = language_edit("format.languageAll", p)?;
|
||||
fn shapes(list: &mut [Shape], f: &dyn Fn(&mut RunProps), depth: usize) {
|
||||
if depth > 64 {
|
||||
return;
|
||||
}
|
||||
for sh in list {
|
||||
if let Some(t) = &mut sh.text {
|
||||
ed::format_all(t, f);
|
||||
}
|
||||
match &mut sh.kind {
|
||||
deckcraft_model::ShapeKind::Table(t) => t.rows.iter_mut().flat_map(|r| &mut r.cells).for_each(|c| ed::format_all(&mut c.text, f)),
|
||||
deckcraft_model::ShapeKind::Group { children, .. } => shapes(children, f, depth + 1),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn styles(l: &mut deckcraft_model::text::ListStyle, f: &dyn Fn(&mut RunProps)) {
|
||||
// Level 1 always, so a deck without a default text style gets one.
|
||||
if l.level(0).is_none() {
|
||||
l.set(0, Default::default());
|
||||
}
|
||||
l.levels.iter_mut().flatten().for_each(|lv| f(&mut lv.run));
|
||||
}
|
||||
s.edit(|doc, _| {
|
||||
for sl in &mut doc.slides {
|
||||
let sl = Arc::make_mut(sl);
|
||||
shapes(&mut sl.shapes, &f, 0);
|
||||
ed::format_all(&mut sl.notes, &f);
|
||||
}
|
||||
let extra = [doc.notes_master.as_mut(), doc.handout_master.as_mut()];
|
||||
for m in doc.masters.iter_mut().chain(extra.into_iter().flatten()) {
|
||||
let m = Arc::make_mut(m);
|
||||
shapes(&mut m.shapes, &f, 0);
|
||||
m.layouts.iter_mut().for_each(|l| shapes(&mut l.shapes, &f, 0));
|
||||
[&mut m.title_style, &mut m.body_style, &mut m.other_style].into_iter().for_each(|l| styles(l, &f));
|
||||
}
|
||||
styles(&mut doc.default_text_style, &f);
|
||||
Ok(())
|
||||
})?;
|
||||
ok()
|
||||
}
|
||||
fn highlight(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let c = color_param(p, "color");
|
||||
format_run(s, &move |r| r.highlight = c.clone())
|
||||
|
||||
Reference in New Issue
Block a user