mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 10:37:03 -06:00
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:
@@ -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
|
||||
@@ -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}))
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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 =
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"}));
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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)))
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user