Review: Thesaurus pane with a built-in English word list

Review > Thesaurus (Shift+F7) opens a Thesaurus pane on the selected
word, or on the word at the caret. Clicking a synonym while editing
text puts it in place of that word in one undo step, and the pane
then looks up the new word. You can also type a word into the pane to
look it up.

The word list is written for DeckCraft and has no third-party source:
about 260 senses of common presentation vocabulary in
engine/data/thesaurus.txt. It is embedded, so the Thesaurus also
works offline and in the browser build. Lookups try simple English
endings and keep the word's capitalisation. New commands:
review.thesaurus (a query) and review.replaceWord. Ribbon big buttons
now carry an accessibility label.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Nikita Rakitin
2026-10-13 23:34:30 +03:00
co-authored by Cursor
parent 457406a7a5
commit 1b326e2e87
10 changed files with 546 additions and 5 deletions
+264
View File
@@ -0,0 +1,264 @@
# DeckCraft's built-in thesaurus: headword|synonym, synonym, ...
# Written for DeckCraft (no third-party word list). Lowercase; one meaning group per line;
# a headword may appear on several lines (one per sense).
ability|capability, capacity, skill, competence, talent
able|capable, competent, qualified, skilled
accept|agree to, approve, adopt, embrace
accurate|precise, exact, correct, right
achieve|accomplish, attain, reach, realize, deliver
achievement|accomplishment, success, attainment, milestone
add|include, append, attach, insert
advantage|benefit, edge, strength, asset, upside
affect|influence, impact, change, alter
aim|goal, objective, target, purpose, intent
allow|permit, enable, let, authorize
analyze|examine, study, assess, evaluate, review
answer|reply, response, solution
approach|method, strategy, way, technique, plan
area|region, field, domain, zone, sector
arrange|organize, order, sort, plan
ask|request, inquire, query, question
assess|evaluate, gauge, judge, review, appraise
assist|help, aid, support, back
attempt|try, effort, endeavor, bid
bad|poor, weak, inferior, unfavorable, negative
basic|fundamental, essential, core, elementary, simple
begin|start, commence, launch, initiate, open
benefit|advantage, gain, value, payoff, upside
best|finest, top, leading, optimal, premier
big|large, major, substantial, significant, sizable
boost|increase, raise, lift, improve, strengthen
brief|short, concise, succinct, quick
bright|vivid, brilliant, luminous, clever
build|construct, create, develop, assemble, establish
business|company, firm, enterprise, organization, venture
buy|purchase, acquire, obtain
careful|cautious, thorough, meticulous, attentive
cause|reason, source, origin, driver
change|alter, modify, adjust, revise, transform
change|shift, transition, adjustment, revision
cheap|inexpensive, affordable, low-cost, economical
check|verify, confirm, examine, inspect, review
choice|option, alternative, selection, pick
choose|select, pick, opt for, decide on
clear|plain, obvious, evident, transparent, lucid
client|customer, buyer, account, patron
close|near, nearby, adjacent
close|end, finish, conclude, shut
common|frequent, usual, typical, widespread, shared
company|business, firm, organization, enterprise, corporation
complete|finish, conclude, finalize, accomplish
complete|whole, full, entire, total
complex|complicated, intricate, involved, elaborate
concern|worry, issue, problem, risk
consider|weigh, contemplate, evaluate, think about
continue|proceed, carry on, keep going, persist, maintain
cost|price, expense, charge, outlay
create|make, build, produce, design, generate
critical|crucial, vital, essential, key, decisive
current|present, existing, ongoing, latest, up-to-date
customer|client, buyer, consumer, user
data|information, figures, facts, statistics
decide|determine, choose, resolve, settle
decrease|reduce, lower, cut, decline, drop
define|specify, describe, explain, outline
deliver|provide, supply, bring, hand over, ship
demonstrate|show, prove, illustrate, display
describe|explain, outline, portray, depict
design|plan, layout, blueprint, pattern
design|create, plan, devise, draft
develop|build, create, grow, expand, evolve
difference|distinction, contrast, gap, variation
different|distinct, various, diverse, unlike
difficult|hard, challenging, tough, demanding
discuss|talk about, debate, examine, review
easy|simple, straightforward, effortless, painless
effect|result, impact, outcome, consequence
effective|successful, efficient, productive, powerful
effort|work, attempt, endeavor, exertion
end|finish, conclusion, close, completion
enough|sufficient, adequate, ample
ensure|guarantee, make sure, secure, confirm
error|mistake, fault, flaw, bug, slip
essential|vital, crucial, key, necessary, fundamental
establish|set up, found, create, institute
estimate|forecast, projection, approximation, guess
evaluate|assess, appraise, judge, rate, review
event|occasion, function, happening, incident
example|instance, case, illustration, sample
excellent|outstanding, superb, exceptional, first-rate
expand|grow, extend, enlarge, broaden, scale
expect|anticipate, foresee, predict, await
explain|clarify, describe, illustrate, account for
fail|fall short, miss, flop, break down
fast|quick, rapid, swift, speedy
feature|characteristic, attribute, capability, function
final|last, closing, concluding, ultimate
find|discover, locate, identify, uncover
finish|complete, end, conclude, wrap up
focus|concentrate, center, target, emphasize
focus|emphasis, priority, attention, core
future|upcoming, coming, forthcoming, prospective
gain|increase, rise, improvement, profit
get|obtain, acquire, receive, gain
give|provide, offer, supply, grant
goal|aim, objective, target, purpose, ambition
good|fine, great, strong, solid, positive
great|excellent, outstanding, terrific, remarkable
grow|increase, expand, rise, develop, scale
growth|expansion, increase, development, rise
help|assist, aid, support, enable
help|assistance, aid, support, guidance
high|elevated, tall, top, strong
idea|concept, notion, thought, proposal
identify|recognize, pinpoint, detect, determine
impact|effect, influence, consequence, result
important|significant, key, major, crucial, vital
improve|enhance, upgrade, refine, strengthen, boost
include|contain, cover, incorporate, comprise
increase|raise, boost, grow, expand, lift
increase|rise, growth, gain, jump
information|data, details, facts, knowledge
innovative|inventive, creative, original, novel, pioneering
issue|problem, concern, matter, question
job|task, assignment, role, position
keep|retain, maintain, preserve, hold
key|main, central, critical, essential, principal
large|big, sizable, substantial, major, extensive
last|final, latest, previous, closing
launch|start, introduce, roll out, release, unveil
lead|guide, direct, head, steer, run
learn|discover, find out, master, study
less|fewer, smaller, reduced, lower
little|small, minor, slight, modest
look|appear, seem
look|view, glance, examine, inspect
low|minimal, modest, small, reduced
main|primary, chief, principal, central, key
maintain|keep, preserve, sustain, uphold
major|significant, important, key, principal, big
make|create, build, produce, generate
manage|run, handle, oversee, direct, lead
market|industry, sector, segment, marketplace
measure|gauge, assess, quantify, track
meet|satisfy, fulfil, reach, achieve
meeting|session, conference, discussion, review
method|approach, technique, process, procedure, way
model|framework, template, pattern, design
more|additional, extra, further, added
move|shift, transfer, relocate, transition
need|requirement, demand, necessity
need|require, call for, demand, want
new|fresh, recent, novel, modern, latest
next|following, upcoming, subsequent
number|figure, count, total, quantity, amount
objective|goal, aim, target, purpose
offer|provide, propose, extend, present
old|previous, former, legacy, outdated, existing
open|start, launch, begin, unveil
opportunity|opening, chance, prospect, possibility
option|choice, alternative, possibility, route
organize|arrange, structure, coordinate, order
outcome|result, consequence, effect, end result
overview|summary, outline, synopsis, recap
part|portion, piece, segment, section, component
partner|ally, collaborator, associate, affiliate
people|staff, team, individuals, personnel
plan|strategy, scheme, roadmap, proposal, program
plan|organize, arrange, schedule, prepare
point|aspect, detail, issue, argument
popular|well-liked, widespread, in demand, favored
possible|feasible, achievable, viable, potential
present|show, display, introduce, demonstrate
present|current, existing, ongoing
price|cost, rate, charge, fee
priority|focus, concern, precedence, goal
problem|issue, challenge, difficulty, obstacle
process|procedure, method, workflow, system
produce|make, create, generate, manufacture, yield
product|offering, item, solution, good
profit|earnings, return, gain, margin
progress|advance, headway, development, improvement
project|initiative, program, undertaking, venture
propose|suggest, recommend, put forward, offer
provide|give, supply, deliver, offer
purpose|aim, goal, objective, intent, reason
quality|standard, caliber, excellence, grade
question|query, issue, inquiry
quick|fast, rapid, speedy, swift, prompt
rapid|fast, quick, swift, speedy
reach|achieve, attain, hit, arrive at
reason|cause, rationale, motive, basis
recent|latest, new, current, fresh
recommend|suggest, advise, propose, advocate
reduce|cut, lower, decrease, lessen, trim
region|area, territory, zone, market
relevant|pertinent, applicable, related, appropriate
remove|delete, eliminate, take out, drop
replace|substitute, swap, supersede, change
report|account, summary, review, statement
require|need, demand, call for, necessitate
research|study, investigation, analysis, inquiry
resource|asset, supply, means, capacity
result|outcome, effect, consequence, finding
review|assessment, evaluation, examination, analysis
review|examine, assess, evaluate, check, revisit
revenue|income, sales, earnings, turnover
risk|danger, threat, exposure, hazard
role|part, function, position, responsibility
run|operate, manage, conduct, execute
safe|secure, protected, reliable
same|identical, equal, matching, equivalent
save|keep, store, preserve, conserve
see|view, observe, notice, spot
sell|market, offer, trade, retail
service|support, assistance, offering
share|portion, part, stake, percentage
share|distribute, spread, communicate, pass on
show|demonstrate, display, present, reveal, illustrate
significant|important, notable, considerable, major, meaningful
similar|alike, comparable, related, equivalent
simple|easy, straightforward, plain, basic
size|scale, extent, magnitude, dimensions
small|little, minor, compact, modest
solution|answer, fix, remedy, resolution
solve|resolve, fix, settle, work out
specific|particular, precise, exact, distinct
start|begin, launch, commence, kick off, initiate
step|stage, phase, measure, action
strategy|plan, approach, policy, roadmap, tactic
strong|robust, solid, powerful, sturdy
structure|framework, organization, layout, arrangement
success|achievement, accomplishment, win, triumph
successful|effective, thriving, prosperous, winning
suggest|propose, recommend, advise, imply
summary|overview, recap, synopsis, digest
support|help, back, assist, sustain
support|help, assistance, backing, aid
sure|certain, confident, positive, definite
target|goal, objective, aim
task|job, assignment, duty, activity
team|group, crew, staff, squad, unit
test|trial, check, experiment, evaluation
test|try, check, evaluate, verify
think|believe, consider, reckon, suppose
time|period, duration, while, moment
tool|instrument, utility, device, aid
total|sum, whole, aggregate, overall
trend|pattern, tendency, movement, direction
understand|grasp, comprehend, follow, realize
unique|distinctive, singular, one-of-a-kind, unmatched
update|revise, refresh, upgrade, modernize
update|revision, change, upgrade, news
use|employ, apply, utilize, leverage
useful|helpful, practical, handy, valuable
value|worth, benefit, merit, importance
various|diverse, assorted, different, several
view|opinion, perspective, outlook, position
vision|outlook, aspiration, goal, dream
way|method, approach, manner, means
weak|feeble, fragile, poor, limited
well|effectively, successfully, nicely, properly
work|labor, effort, activity, job
work|function, operate, run, succeed
year|annum, twelve months
+43
View File
@@ -16,6 +16,16 @@ pub fn specs() -> Vec<CommandSpec> {
cmd!("comment.resolve", "Resolve Thread", ["Comments"], None, "{index, resolved?: bool}", has_slide, resolve),
cmd!(query "comment.list", "Comments", [], None, "{slide?} → comments", has_slide, list),
cmd!(query "review.accessibility", "Check Accessibility", ["Review", "Accessibility"], None, "{} → [{slide, shape, issue}]", has_doc, accessibility),
cmd!(query "review.thesaurus", "Thesaurus", ["Review", "Proofing"], None, "{word?: default the selected word, or the word at the caret} → {word, senses: [{word, synonyms}]}", has_doc, thesaurus),
cmd!(
"review.replaceWord",
"Insert Synonym",
[],
None,
"{with}: replace the selected word (or the word at the caret) while editing text",
has_text,
replace_word
),
cmd!(query "review.spelling", "Spelling", ["Review", "Proofing"], Some("F7"), "{} → [{slide, shape, word}] (words not in the built-in list)", has_doc, spelling),
cmd!(noundo "show.fromStart", "Play from Start", ["Slide Show", "Start Slide Show"], Some("Cmd+Shift+Enter"), "{}", has_slide, |s, _| start(s, 0)),
cmd!(noundo "show.fromCurrent", "Play from Current Slide", ["Slide Show", "Start Slide Show"], Some("Cmd+Enter"), "{}", has_slide, |s, _| {
@@ -264,3 +274,36 @@ fn custom_show(s: &mut Session, p: &Value) -> Result<Value> {
})?;
ok()
}
/// The selected text when it lies in one paragraph, else the word at the caret.
fn word_under_caret(s: &Session) -> Option<String> {
let st = s.doc().ok()?;
let t = st.selection.text.clone()?;
let body = super::text::body_of(st, &t)?;
let (a, b) = if t.anchor != t.caret { t.ordered() } else { deckcraft_model::edit::word_at(body, t.caret) };
if a.0 != b.0 {
return None;
}
let text = body.paragraphs.get(a.0)?.text();
let w: String = text.chars().skip(a.1).take(b.1.saturating_sub(a.1)).collect();
let w = w.trim().to_string();
(!w.is_empty()).then_some(w)
}
fn thesaurus(s: &mut Session, p: &Value) -> Result<Value> {
let word = str_param(p, "word").map(str::to_string).or_else(|| word_under_caret(s)).unwrap_or_default();
let senses = crate::thesaurus::lookup(&word);
Ok(json!({"word": word, "senses": senses}))
}
fn replace_word(s: &mut Session, p: &Value) -> Result<Value> {
let with = str_param(p, "with").filter(|w| !w.trim().is_empty()).ok_or_else(|| bad("review.replaceWord", "missing `with`"))?.to_string();
if with.chars().count() > 200 {
return Err(bad("review.replaceWord", "too long"));
}
let collapsed = s.doc()?.selection.text.as_ref().is_some_and(|t| t.anchor == t.caret);
if collapsed {
s.execute("text.selectWord", &json!({}))?;
}
s.execute("text.insert", &json!({"text": with}))
}
+1
View File
@@ -15,6 +15,7 @@ pub mod keys;
pub mod links;
pub mod recovery;
pub mod sample;
pub mod thesaurus;
pub mod tools;
use std::sync::Arc;
+119
View File
@@ -0,0 +1,119 @@
//! Review › Thesaurus: synonyms from a small built-in English word list (`data/thesaurus.txt`),
//! looked up offline so it works in the browser build too.
const DATA: &str = include_str!("../data/thesaurus.txt");
/// One sense of a word: its headword and synonyms.
#[derive(Clone, Debug, PartialEq, serde::Serialize)]
pub struct Sense {
pub word: String,
pub synonyms: Vec<String>,
}
fn senses_of(head: &str) -> Vec<Sense> {
DATA.lines()
.filter(|l| !l.starts_with('#'))
.filter_map(|l| l.split_once('|'))
.filter(|(h, _)| h.trim() == head)
.map(|(h, s)| Sense { word: h.trim().to_string(), synonyms: s.split(',').map(|x| x.trim().to_string()).filter(|x| !x.is_empty()).collect() })
.collect()
}
/// The senses for `word`, trying simple English endings when the word itself isn't listed
/// ("goals" → "goal", "improved" → "improve", "growing" → "grow"). Synonyms take the word's
/// capitalisation ("Goal" → "Aim", "GOAL" → "AIM").
pub fn lookup(word: &str) -> Vec<Sense> {
let w: String = word.trim().trim_matches(|c: char| !c.is_alphanumeric() && c != '-').chars().take(64).collect();
let lower = w.to_lowercase();
if lower.is_empty() {
return vec![];
}
let mut tries = vec![lower.clone()];
for (suffix, add) in [("ies", "y"), ("es", ""), ("s", ""), ("ied", "y"), ("ed", "e"), ("ed", ""), ("ing", "e"), ("ing", "")] {
if let Some(stem) = lower.strip_suffix(suffix)
&& stem.chars().count() >= 2
{
tries.push(format!("{stem}{add}"));
}
}
let Some(found) = tries.iter().map(|t| senses_of(t)).find(|v| !v.is_empty()) else { return vec![] };
let upper = w.chars().count() > 1 && w.chars().all(|c| !c.is_lowercase());
let title = !upper && w.chars().next().is_some_and(char::is_uppercase);
found
.into_iter()
.map(|s| Sense {
word: s.word,
synonyms: s
.synonyms
.into_iter()
.map(|x| {
if upper {
x.to_uppercase()
} else if title {
let mut c = x.chars();
c.next().map(|f| f.to_uppercase().chain(c).collect()).unwrap_or_default()
} else {
x
}
})
.collect(),
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn senses_endings_and_case() {
let g = lookup("goal");
assert_eq!(g.len(), 1);
assert!(g[0].synonyms.contains(&"objective".to_string()));
assert_eq!(lookup("change").len(), 2, "verb and noun senses");
assert_eq!(lookup("Goals")[0].synonyms[0], "Aim");
assert_eq!(lookup("GOAL")[0].synonyms[0], "AIM");
assert_eq!(lookup("improved")[0].word, "improve");
assert_eq!(lookup("growing")[0].word, "grow");
assert_eq!(lookup("strategies")[0].word, "strategy");
assert_eq!(lookup("“goal,”")[0].word, "goal", "punctuation around the word");
assert!(lookup("").is_empty() && lookup("zzxq").is_empty() && lookup(" ").is_empty());
assert!(lookup(&"a".repeat(10_000)).is_empty());
}
#[test]
fn data_is_well_formed() {
for l in DATA.lines().filter(|l| !l.starts_with('#') && !l.trim().is_empty()) {
let (h, s) = l.split_once('|').unwrap_or_else(|| panic!("no `|`: {l}"));
assert_eq!(h, h.trim().to_lowercase(), "{l}");
let syn: Vec<&str> = s.split(',').map(str::trim).collect();
assert!(syn.len() >= 2 && syn.iter().all(|x| !x.is_empty() && *x != h), "{l}");
let mut d = syn.clone();
d.sort();
d.dedup();
assert_eq!(d.len(), syn.len(), "duplicate synonym: {l}");
}
}
#[test]
fn commands_look_up_and_replace_the_word_at_the_caret() {
use serde_json::json;
let mut s = crate::Session::with_new();
let id = s.execute("insert.textBox", &json!({"rect": [50, 50, 400, 40]})).unwrap()["id"].as_u64().unwrap();
s.execute("text.edit", &json!({"id": id})).unwrap();
s.execute("text.insert", &json!({"text": "Our Goal is growth."})).unwrap();
s.execute("text.edit", &json!({"id": id, "at": [0, 6]})).unwrap();
let r = s.execute("review.thesaurus", &json!({})).unwrap();
assert_eq!(r["word"], "Goal");
assert_eq!(r["senses"][0]["synonyms"][0], "Aim");
s.execute("review.replaceWord", &json!({"with": "Aim"})).unwrap();
let text = |s: &crate::Session| {
s.doc().unwrap().shapes().iter().find(|x| x.id.0 as u64 == id).and_then(|x| x.text.as_ref()).map(|t| t.paragraphs[0].text()).unwrap()
};
assert_eq!(text(&s), "Our Aim is growth.");
s.execute("edit.undo", &json!({})).unwrap();
assert_eq!(text(&s), "Our Goal is growth.");
assert_eq!(s.execute("review.thesaurus", &json!({"word": "growth."})).unwrap()["senses"][0]["word"], "growth");
assert!(s.execute("review.replaceWord", &json!({"with": ""})).is_err());
}
}
+17
View File
@@ -146,6 +146,7 @@ pub const UI_COMMANDS: &[(&str, &str, Option<&str>, &str)] = &[
("view.zoomOut", "Zoom Out", Some("Cmd+-"), "{}"),
("view.zoom", "Zoom…", None, "{percent}"),
("view.fit", "Fit to Window", None, "{}"),
("review.thesaurusPane", "Thesaurus", Some("Shift+F7"), "{word?}: open the Thesaurus pane on the selected word"),
("view.pane", "Task Pane", None, "{pane: format|animation|selection|comments|designer|background|null}"),
("view.tab", "Ribbon Tab", None, "{tab}"),
("view.collapseRibbon", "Collapse the Ribbon", Some("Cmd+F1"), "{}"),
@@ -184,6 +185,8 @@ pub struct SlideApp {
pub textures: textures::Textures,
pub control_rx: Option<Receiver<ControlRequest>>,
pub dialog: Option<dialogs::Dialog>,
/// The word looked up in the Thesaurus pane.
pub thesaurus_query: String,
pub show: Option<show::Show>,
/// Audio/video playback (editor control bar, slide show, `media.*` commands).
pub media: media::MediaHost,
@@ -239,6 +242,7 @@ impl SlideApp {
textures: textures::Textures::default(),
control_rx: None,
dialog: None,
thesaurus_query: String::new(),
show: None,
media: media::MediaHost::new(audio_out),
status: None,
@@ -473,6 +477,19 @@ impl SlideApp {
self.ui.zoom = Some((pct as f32 / 100.0).clamp(0.1, 4.0));
}
"view.fit" => self.ui.zoom = None,
"review.thesaurusPane" => {
let word = match p.get("word").and_then(Value::as_str) {
Some(w) => w.to_string(),
None => self
.session
.execute("review.thesaurus", &json!({}))
.ok()
.and_then(|r| r["word"].as_str().map(String::from))
.unwrap_or_default(),
};
self.thesaurus_query = word;
self.ui.pane = Some("thesaurus".into());
}
"view.pane" => {
let pane = p.get("pane").and_then(Value::as_str).map(String::from);
self.ui.pane =
+41
View File
@@ -27,6 +27,7 @@ pub fn show(app: &mut SlideApp, ui: &mut Ui) {
"selection" => "Selection",
"comments" => "Comments",
"designer" => "Design Ideas",
"thesaurus" => "Thesaurus",
_ => "Pane",
};
ui.horizontal(|ui| {
@@ -50,6 +51,7 @@ pub fn show(app: &mut SlideApp, ui: &mut Ui) {
"selection" => crate::selpane::show(app, ui),
"comments" => comments(app, ui),
"designer" => designer(app, ui),
"thesaurus" => thesaurus(app, ui),
_ => {}
});
});
@@ -635,3 +637,42 @@ fn designer(app: &mut SlideApp, ui: &mut Ui) {
ui.add_space(8.0);
}
}
/// Review › Thesaurus: look a word up; clicking a synonym puts it in place of the selected word
/// when editing text, otherwise looks that synonym up in turn.
fn thesaurus(app: &mut SlideApp, ui: &mut Ui) {
let t = Tokens::get(ui.ctx());
let mut q = app.thesaurus_query.clone();
let edit = ui.add(egui::TextEdit::singleline(&mut q).hint_text("Search for a word").desired_width(f32::INFINITY));
if edit.changed() {
app.thesaurus_query = q.clone();
}
ui.add_space(6.0);
let senses = deckcraft_engine::thesaurus::lookup(&q);
if q.trim().is_empty() {
ui.label(egui::RichText::new("Select a word on the slide, or type one above.").color(t.text_dim));
return;
}
if senses.is_empty() {
ui.label(egui::RichText::new(format!("No synonyms for \"{}\".", q.trim())).color(t.text_dim));
return;
}
let editing = app.session.active().is_some_and(|d| d.selection.text.is_some());
for sense in senses {
ui.label(egui::RichText::new(&sense.word).font(theme::bold(13.0)));
for syn in sense.synonyms {
let r = ui.add(egui::Button::new(&syn).frame(false)).on_hover_text(if editing { "Insert" } else { "Look up" });
if r.clicked() {
if editing {
run(app, "review.replaceWord", json!({"with": syn}));
}
app.thesaurus_query = syn;
}
}
ui.add_space(6.0);
}
}
#[cfg(test)]
#[path = "thesaurus_tests.rs"]
mod thesaurus_tests;
+1 -2
View File
@@ -1780,8 +1780,7 @@ fn record(app: &mut SlideApp, ui: &mut Ui) {
fn review(app: &mut SlideApp, ui: &mut Ui) {
group(ui, |ui| {
big(app, ui, Icon::Spelling, "Spelling", "app.dialog", json!({"id": "spelling"}));
let r = big_button(ui, Icon::Thesaurus, "Thesaurus", false);
let _ = r.on_hover_text("Thesaurus (coming soon)");
big(app, ui, Icon::Thesaurus, "Thesaurus", "review.thesaurusPane", json!({}));
});
group(ui, |ui| {
big(app, ui, Icon::Accessibility, "Check\nAccessibility", "app.dialog", json!({"id": "accessibility"}));
+54
View File
@@ -0,0 +1,54 @@
//! Review › Thesaurus driven like a person: caret in a word, open the pane, click a synonym.
use deckcraft_engine::Session;
use egui_kittest::Harness;
use egui_kittest::kittest::Queryable;
use serde_json::json;
use crate::{Services, SlideApp};
#[test]
fn thesaurus_pane_swaps_the_word_at_the_caret() {
let mut s = Session::with_new();
s.execute("slide.new", &json!({"layout": "blank"})).unwrap();
let id = s.execute("insert.textBox", &json!({"rect": [100, 100, 500, 50]})).unwrap()["id"].as_u64().unwrap();
s.execute("text.edit", &json!({"id": id})).unwrap();
s.execute("text.insert", &json!({"text": "Our Goal is growth."})).unwrap();
s.execute("text.edit", &json!({"id": id, "at": [0, 6]})).unwrap();
let mut app = SlideApp::new(s, Services::default());
app.ui.tab = "Review".into();
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).with_step_dt(1.0 / 60.0).build_ui_state(
|ui, app: &mut SlideApp| {
app.logic(ui.ctx());
app.ui(ui);
},
app,
);
h.run_steps(3);
h.get_by_label("Thesaurus").click();
h.run_steps(3);
assert_eq!(h.state().ui.pane.as_deref(), Some("thesaurus"));
assert_eq!(h.state().thesaurus_query, "Goal");
if let Ok(img) = h.render() {
let _ = img.save("/tmp/thesaurus.png");
}
h.get_by_label("Objective").click();
h.run_steps(3);
let text = h
.state()
.session
.active()
.unwrap()
.shapes()
.iter()
.find(|x| x.id.0 as u64 == id)
.and_then(|x| x.text.as_ref())
.map(|t| t.paragraphs[0].text());
assert_eq!(text.as_deref(), Some("Our Objective is growth."));
assert_eq!(h.state().thesaurus_query, "Objective", "the pane follows the new word");
// Typing a word that isn't listed says so instead of showing nothing.
h.state_mut().thesaurus_query = "zzxq".into();
h.run_steps(2);
assert!(h.query_by_label_contains("No synonyms").is_some());
}
+3
View File
@@ -39,6 +39,9 @@ pub fn big_button_ex(ui: &mut Ui, icon: Icon, label: &str, enabled: bool, split:
let text_w = lines.iter().map(|l| ui.fonts_mut(|f| f.layout_no_wrap(l.to_string(), font.clone(), t.text).size().x)).fold(0.0f32, f32::max);
let w = (text_w + 10.0 + if split && lines.len() == 1 { 10.0 } else { 0.0 }).max(40.0);
let (rect, resp) = ui.allocate_exact_size(vec2(w, RIBBON_H - 2.0), if enabled { Sense::click() } else { Sense::hover() });
// Named for screen readers and UI tests ("Ink to\nShape" reads "Ink to Shape").
let spoken = label.replace('\n', " ");
resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, enabled, &spoken));
let painter = ui.painter_at(rect.expand(1.0));
let (main_rect, arrow_rect) = if split {
(Rect::from_min_max(rect.min, pos2(rect.max.x, rect.min.y + 38.0)), Some(Rect::from_min_max(pos2(rect.min.x, rect.min.y + 38.0), rect.max)))
+3 -3
View File
@@ -9,7 +9,7 @@ The summary between `<!-- SUMMARY -->
|---|---|---|---|---|---|
| P0 | 72 | 61 | 11 | 0 | 92% |
| P1 | 65 | 38 | 21 | 6 | 75% |
| P2 | 54 | 13 | 15 | 26 | 38% |
| P2 | 54 | 13 | 16 | 25 | 39% |
| Area | Parity |
|---|---|
@@ -21,7 +21,7 @@ The summary between `<!-- SUMMARY -->
| Shapes | 88% |
| Format | 83% |
| Arrange | 100% |
| Text | 80% |
| Text | 81% |
| Tables | 89% |
| Charts | 65% |
| SmartArt | 33% |
@@ -155,7 +155,7 @@ Open P0 items: Contextual tabs (Shape Format, Picture Format, Table Design/Layou
| Text | Text direction (rotated, stacked) | P1 | D |
| Text | Find and replace, replace fonts | P1 | P |
| Text | Spelling check | P1 | P |
| Text | Thesaurus | P2 | M |
| Text | Thesaurus | P2 | P |
| Text | AutoCorrect and smart quotes | P2 | M |
| Text | Symbols and emoji | P1 | D |
| Text | Equations | P2 | M |