Commit Graph
930 Commits
Author SHA1 Message Date
Martin BallandClaude Opus 5.5 f90ebc5f3b CP6.6: ONLYINCOMPOUND and COMPOUNDRULE read in the dictionary's FLAG mode
From the rebase onto craft-libs#483: the ONLYINCOMPOUND flag is a flag id
read as FLAG long/num/UTF-8 says, so a stem carrying it is a word only
inside a compound in every flag mode (English included). COMPOUNDRULE
patterns with long or numeric flags write each flag in parentheses,
`(aa)*(bb)` / `(1)*(2)`, as Hunspell reads them (checked black-box with the
hunspell CLI); a bare flag in those modes or an unclosed group is refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 22:34:28 +00:00
Martin BallandClaude Opus 5.5 35285e27eb CP6.2: FLAG num reads flag 0; AF aliases and SUBSTANDARD with long and numeric flags
Follow-ups to the storytold/wordcraft#5142 port:
- Flag 0 is a flag under `FLAG num`, as Hunspell 1.7 reads it (black-box
  check): the Turkish dictionary's first rule is `SFX 0 N 1`, which the
  port refused. Turkish now loads and passes its check words.
- AF aliases hold flags written in the FLAG mode (`AF AaBb`, `AF 12,7`);
  tested against Hunspell's answers on the same files. An alias whose
  flags don't read leaves its stem out, as a bad .dic field does.
- A homonym past the merged-set cap keeps SUBSTANDARD as it keeps
  FORBIDDENWORD and NOSUGGEST (craft-proof has SUBSTANDARD; the PR's base
  didn't).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 22:34:28 +00:00
Harrison SmithandClaude Opus 5.5 561566c6bb proof: a homonym merge never drops FORBIDDENWORD or NOSUGGEST (storytold/wordcraft#4967)
A homonym that took the merged flag set past MAX_FLAGS_PER_ENTRY was
skipped whole, so if it alone carried FORBIDDENWORD or NOSUGGEST the word
became correct or suggestible.

Merged sets now have their own bound, MAX_FLAGS_PER_STEM (8 x the entry
cap), which the compact reader also accepts for stem lines, so to_bytes ->
from_bytes reloads every set build() makes. A homonym past that bound
drops its other flags but still adds its exclusion flags (evicting a
non-exclusion flag if the set is full), and a .dic entry over the entry
cap is still no word but its exclusions apply to its homonyms.

Ported from storytold/wordcraft#5142 (b9777fc92) onto craft-proof.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 22:34:28 +00:00
Harrison SmithandClaude Opus 5.5 13b6108cb4 proof: cap merged homonym flag sets; leave unused directive flags out of character-mode data (storytold/wordcraft#4967)
Review follow-ups:
- Two .dic entries for the same word could each stay within
  MAX_FLAGS_PER_ENTRY while their merged set went past it; the compact
  data then held a set its reader refuses, and the word was lost on
  reload. An entry that would take the merged set past the cap is now
  skipped, as an oversized entry is.
- In one-character mode a FORBIDDENWORD or NOSUGGEST flag that no stem
  or rule uses was written to the compact data, which the old format
  left out. It is left out again, so those dictionaries' bytes don't
  change.

Ported from storytold/wordcraft#5142 (f10bb61ab) onto craft-proof.
Conflict resolved: the used-flag filter covers SUBSTANDARD and KEEPCASE too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 22:34:28 +00:00
Harrison SmithandClaude Opus 5.5 e4182149a5 proof: read FLAG num (and long) affix dictionaries (storytold/wordcraft#4967)
The affix engine read one-character flags only, at most 128 of them, as
bits of a u128. It now reads `FLAG num` (comma-separated numbers 1-65535,
leading digits as in Hunspell) and `FLAG long` (two characters), with up
to 65,536 distinct flags (storytold/wordcraft#4966):

- One reader (`FlagMode`) parses and writes every flag field: .dic flags,
  PFX/SFX flags and continuation classes, FORBIDDENWORD and NOSUGGEST.
- Each flag gets a dense u16 id; a stem's flags and a rule's continuation
  classes are sorted id sets interned in one flat table (set 0 is empty).
- Hostile input: at most 1,024 flags per entry (Belarusian uses 264), a
  bad .aff flag is an error, a .dic entry whose flags don't read is
  skipped, and a FLAG line after flags were read is refused.
- Lookups skip the continuation walk for groups and flags no continuation
  class can reach, so checks are as fast or faster with the Polish,
  Portuguese, Czech, Italian and Russian dictionaries (same answers).
- The compact format writes a `FLAG num`/`FLAG long` line only for those
  modes, so one-character data is byte-for-byte unchanged and old data
  still loads.

LibreOffice's Serbian (Cyrillic and Latin), Belarusian, Sinhala, Kannada
and Nepali dictionaries now load; Danish, Galician, Icelandic and Korean
still stop at COMPOUNDMIN, NEEDAFFIX, FULLSTRIP and ICONV.

Ported from storytold/wordcraft#5142 (f5778bb76) onto craft-proof.
craft-proof already had storytold/wordcraft#735 (AF aliases, SUBSTANDARD,
KEEPCASE, ICONV, IGNORE, 256 one-character flags as a bitmask), which
this commit predates; conflicts resolved by carrying those through the
flag ids: AF aliases resolve to flag text read in the FLAG mode,
SUBSTANDARD and KEEPCASE are flag ids like FORBIDDENWORD and NOSUGGEST,
and lookups exclude up to four flags (Excluded). The FLAG long test
lines in the language modules and slot.rs now use an unknown mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 22:34:28 +00:00
Martin BallandClaude Opus 5.5 6e45fda67c CP2.2: review — British English takes an -ize spelling only when en-US or en-CA spells the word so
craft-libs#483 re-review (P2): ise_spelling turned any z between i/y and a
vowel into s, so en-GB (and the other British Englishes) accepted
misspellings whose -s- form is a word: wize, rize, noize, dizease, advize,
surprize, exercize… (19 reproduced; Hunspell en-GB rejects them all). The
-ize form now counts only when the word itself is in the en-US or en-CA
SCOWL dictionary and its -ise form in en-GB's (organize, realization,
colourize). English with no dialect no longer needs the swap (it reads all
three). Test: british_ize_needs_a_real_ize_word (the 19, plus colorize, wrong
in en-GB and en-AU; organize, realization, colourize, organizing, realizes
right).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 22:21:02 +00:00
Martin BallandClaude Opus 5.5 0675d58341 CP2.2: review — each English checks only its own SCOWL dictionary; English suggestions drop junk splits and names
craft-libs#483 review (P2): English spelling checked the union of the three
SCOWL dictionaries, so dialects crossed (en-US took colourblind, greyscale;
en-GB took colorblind, esophagi). Each English now checks its own dictionary
only. British English also takes the -ize spellings of its -ise words
(SCOWL's en-GB is -ise only; Word accepts both), and en-variants.txt widens
each dialect by its pairs; judgment/judgement come from craft-i18n#114, which
CI now pins. English with no dialect (is_correct) still takes a word any
English knows. Test: scowl_dialects_dont_cross samples the stems one
dialect has and another lacks, for every pair.

P3s:
- English suggestions (a Tuning of their own): a missing space only between
  two lower-case words of three letters or more, ranked after every one-word
  candidate (publically: publicly first; no "Thea tr", "Che Que"), and no
  word the dictionary only capitalizes (Col, Wis) for a lower-case word.
- A FORBIDDENWORD whole word is never rebuilt from compound parts.
- to_bytes writes ONLYINCOMPOUND without compound rules too (round-trip test).
- Failed lookups: the compound part table is filled lazily, and a word that
  doesn't start like a compound part (SCOWL: a digit) skips compounding;
  20,000 failed long words take 83 ms, as without compounding (1.09 s before).
- README: en-CA is not a British-spelling English.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 21:44:18 +00:00
Martin BallandClaude Opus 5.5 e6e948f7d5 CP2.2: English spelling reads SCOWL 2026 through the affix engine
craft-i18n's SCOWL rel-2026.02.25 dictionaries (en-us, en-gb -ise, en-ca)
are embedded by build.rs and read per English: en-US, the British-spelling
Englishes (en-AU, en-NZ, en-IE...) through en-GB, en-CA. Suggestions come from
the run's own dictionary; a word is right when any of the three (or
en-extra.txt) knows it, unless en-variants.txt marks it as the other dialect's.
en-variants.txt stays: SCOWL's size-60 lists keep one spelling per dialect and
Word accepts more (organize and organise in en-GB, judgement in en-US).

English lookup order: dictionaries folder (the run's own English only),
[installed English pack, craft-libs#485], embedded SCOWL, system Hunspell
(own English only; not searched while SCOWL is embedded), the built-in Moby
list. The embedded Moby list is no
longer a spelling source (the built-in list is the same list); it stays for
hyphenation. The dialect policy applies to every source.

The affix engine gains COMPOUNDRULE (one-character flags, * and ?),
ONLYINCOMPOUND and COMPOUNDMIN, which SCOWL uses for ordinals (21st, 112th).
Hostile patterns are capped (64 rules, 32 tokens, 48-byte words) and checked
by a memoised walk. load_english now reads them instead of ignoring them (and
keeps ONLYINCOMPOUND stems as compound parts); only flag-based compounding and
OCONV are still ignored for English.

scowl::notice() exposes the SCOWL/Ispell/Titze notice for About. Embedded
Language data grows by 1,542,396 bytes. CI pins craft-i18n#109's merge commit efb1127.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 21:06:00 +00:00
Martin BallandClaude Opus 5.5 9e5862a1c1 CP1.2: each English uses only its own Hunspell dictionary, else the built-in Moby list
Re-review of craft-libs#482:
- en-GB and en-CA never fall back to an en_US Hunspell dictionary, which marked colour,
  organise, centre… (CI's runner has one); without their own they use the built-in Moby list,
  which has both spellings. en-US looks for en_US/en-US only.
- hunspell::candidates goes name by name across every folder, so a system en_GB beats an
  en_US in the dictionaries folder.
- Built-in suggestions rank swaps, then common words, neighbouring keys, the same first letter;
  no one-letter suggestions but a and i (hte -> the, recieve -> receive, wprd -> word).
- The generator leaves out Moby entries that are far more often typos: teh, abd, nad, ur, u, r
  (wont and cant stay: words). en-words.dic regenerated (343,988 bytes).
- A load that started before a reload no longer notes its load error; reload clears the
  English load errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 20:45:01 +00:00
Martin BallandClaude Opus 5.5 48ed6f8cbd CP1.2: build.rs keys the data tests on a whole data folder, handles non-UTF-8 paths; CI pins craft-i18n
Review of craft-libs#482:
- craft_proof_data is set only when every Language data file is embedded, and the new
  craft_proof_no_data only when none is, so a partial folder (en-us but no pl) runs neither
  the pattern tests nor the no-data tests.
- A CRAFT_I18N_DIR without language-data/ is watched (rerun-if-changed), so the build
  notices when the checkout gains it.
- A CRAFT_I18N_DIR that isn't valid UTF-8 is reported and embeds nothing, instead of a lossy
  path in include_str!.
- proof.yml checks out craft-i18n at 062a6375c322c88b96691a17f7b5fed71c51f7a8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 18:37:02 +00:00
Martin BallandClaude Opus 5.5 c80b6225f1 CP1.2: English without Language data uses the whole Moby list and every installed English dictionary
Review of craft-libs#482:
- The built-in fallback is now the full public-domain Moby word list (data/en-words.dic,
  words only, 343,992 bytes, from craft-i18n's en-us.dic by examples/en_words.rs) plus
  WordCraft's own en-extra.txt (MIT OR Apache-2.0). An unknown word is misspelled; the
  near-miss scan (which flagged fiber, theater, organize… and took 14 s on a 2,000-word
  token) is gone. Suggestions are the words one edit away, looked up, for words of up to 32
  letters. spelling_scales_over_one_long_token runs without the data, with a time bound.
- English Hunspell dictionaries ignore compounding (COMPOUNDMIN, COMPOUNDRULE…; their
  ONLYINCOMPOUND stems are left out) and OCONV instead of refusing the file, and a file that
  can't be read is skipped for the next name or folder.
- A load that started before english::reload() isn't kept (reloads counter, as slot.rs);
  Englishes that use the same .dic share one copy. en-GB/en-CA falling back to en_US is
  documented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 18:37:02 +00:00
961162ad15 CP1.2: README credits craft-libs#30/#31 and lists the dictionary folders
Co-Authored-By: Michał Marcin Brzuchalski <michal.brzuchalski@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 16:14:52 +00:00
Martin BallandClaude Opus 5.5 ed6bea9b6e CP1.2: Spanish address timing test guards against blowups, not machine load
The bound was 150 ms; at opt-level 0 under a parallel test run the fixed
lookup came close to it and flaked. 1 s still fails a return to the
quadratic scan (storytold/wordcraft#3562) without timing the machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 16:14:44 +00:00
Michał Marcin BrzuchalskiandClaude Opus 5.5 144d2e738c CP1.2: search WordCraft's own dictionaries folder on macOS and Linux too
Ported from storytold/craft-libs#31 (9780561, "craft-proof: search the app's
own dictionaries folder on macOS and Linux too"), keeping WordCraft's API
(`hunspell::search_dirs`, `find`, `set_dictionary_dir`) instead of
`Search::for_app`.

The per-app folder was searched only on Windows. macOS now searches
~/Library/Application Support/WordCraft/dictionaries and Linux and the BSDs
$XDG_DATA_HOME/wordcraft/dictionaries (default ~/.local/share; lower-case, as
XDG folders and WordCraft's ~/.config/wordcraft are), after the setting and
WORDCRAFT_DICTIONARIES and before the shared system locations, so an app
started from Finder, the Dock or a launcher finds a dropped-in dictionary
without a setting (storytold/wordcraft#1063). Windows is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 16:13:57 +00:00
Martin BallandClaude Opus 5.5 a715e385b0 CP1.2: hyphenation patterns at language-data/<locale>/hyphenation.pat
Follows craft-i18n's fixed names (ADR 0005).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCZYaSPSpH54kk7rnxD
2026-11-01 16:13:12 +00:00
Martin BallandClaude Opus 5.5 547f303095 CP1.2: craft-proof carries wordcraft crates/proof up to storytold/wordcraft@9c72ddf56
Merges the crates/proof history from storytold/wordcraft a88e00525..9c72ddf56
(storytold/wordcraft#5000, storytold/wordcraft#4235: 21 commits and 5 merges),
split with the same git filter-repo run as the first import, so the shared
history has the same commit ids and every commit keeps its author.

Conflicts in src/lib.rs, resolved with the CP1.2 changes kept: the embedded
`data` module beside the new `affix_suggest`; a dictionaries-folder change
reloads every language slot and English; English load errors still come from
the English source; the dictionaries-folder test helpers stay as CP1.2 split
them, with DICTIONARY_TESTS documented for the user-dictionary tests too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 16:12:06 +00:00
aiongg 921569d0a7 Merge pull request storytold/wordcraft#5000 from benandez/integration/batch-proof
Integration batch proof: 2 approved proofing PRs (merge commit, please)
2026-11-01 10:46:45 -05:00
.CooCoo 24aac64a83 Tests: border lines, tab indents and wide spaces before a word (storytold/wordcraft#4235)
Tests and notes for rules that storytold/wordcraft#4129, storytold/wordcraft#4113, storytold/wordcraft#4109 and storytold/wordcraft#4101 added and
left uncovered:

- A border line at the top of a story with formatting left out of the
  Track Changes markup is not a formatting change, and Reject All keeps
  it.
- The author's own earlier formatting change on that paragraph stays the
  one change.
- Reject All keeps the table of a "+---+" line.
- A border line that a content control's lock stops Backspace from
  deleting stays as it is, and Enter is refused, untracked too.
- With tab indents on, Tab in a table cell moves to the next cell; with
  them off, Backspace before an indented paragraph joins it.
- AutoCorrect and the spelling check's web and e-mail addresses after a
  narrow no-break space and a figure space.

docs/typing-parity.md describes each, and lists a no-break space typed
after a word, which does not finish the word for AutoCorrect, as a
known gap.
2026-11-01 10:46:25 -05:00
Martin BallandClaude Opus 5.5 13eb2cf535 CP1.2: owners in the crate README instead of a new CODEOWNERS file
craft-libs has no CODEOWNERS convention.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 10:56:07 +00:00
Martin BallandClaude Opus 5.5 23151270ab CP1.2: English never goes unchecked — embedded data, then system Hunspell, then a built-in core word list
- No embedded Moby list: English (en-US, en-GB, en-CA) uses an English
  Hunspell .aff/.dic from the dictionaries folder or the system (the run's
  English first, then another English), else data/en-core.txt.
- en-core.txt: 11,367 public-domain Moby Hyphenator II words (Grady Ward),
  ranked by lower-case frequency in 50 public-domain Gutenberg books
  (examples/en_core.rs, book ids recorded). 94 KB. Marks only near misses
  of common words (swapped letters; one edit for lower-case words of 4+).
- Unreadable English dictionary (encoding, not UTF-8, no words, size):
  error via dictionary_load_error, falls through to the core list; a bad
  embedded list is reported the same way. Never a panic.
- Non-English text is never checked against English.
- New: english_source(lang) -> Option<EnglishSource>.
- Embedded-data path unchanged; tests with data unchanged.
- Tests: core list flags/accepts, folder Hunspell preferred over core list,
  unreadable dictionaries reported, no_data English now marks misspellings;
  two data-ignored tests now run without data. README, ATTRIBUTION, NOTICE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 10:55:57 +00:00
Martin BallandClaude Opus 5.5 130ac8d404 storytold/wordcraft#5000: review — a KEEPCASE word is suggested in its dictionary case, never re-cased into a flagged form
`Kmm` with `km/K` suggested `Km`, which the checker flags; `Km` itself got no
suggestion. A suggestion whose dictionary form is only correct through a
KEEPCASE stem now keeps that case (`km`). Other stems are re-cased as before
(`MIXCASX` → `MIXCASE`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 10:46:06 +00:00
Martin BallandClaude Opus 5.5 b758ba6943 CP1.2: craft-proof embeds Language data from CRAFT_I18N_DIR; works without it
- Package renamed wordcraft-proof -> craft-proof; [workspace.dependencies]
  entry; public API unchanged.
- build.rs embeds craft-i18n language-data/ (en-us, de, pl, pt, it, ru)
  instead of include_str!/include_bytes! of WordCraft's assets/: warns when
  CRAFT_I18N_DIR is unset, errors under CRAFT_I18N_REQUIRED=1.
- No data: English spelling marks and suggests nothing, hyphenation that
  needs data is off, grammar and rule-based hyphenation work. no_data.rs runs
  every public entry point without data; data-pinning tests are ignored then
  (cfg craft_proof_data).
- es timing bound 75 -> 150 ms: craft-libs tests run at opt-level 0.
- proof.yml (fmt, clippy -D warnings, tests with and without data, release
  guard, wasm32), README, CODEOWNERS for crates/proof.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 10:02:10 +00:00
Martin BallandClaude Opus 5.5 cbc920afee CP1.2: craft-proof crate copied from wordcraft-proof with history
Merges the history of storytold/wordcraft crates/proof (84 commits, up to
wordcraft@a88e00525), split with git filter-repo so blame survives. Issue
references in the imported messages are rewritten to storytold/wordcraft#N.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
2026-11-01 09:55:15 +00:00
aiongg f8f8829cc7 Merge pull request #463 from dotCooCoo/fix/keymap-intl-backslash-key
keymap: the ISO key next to left Shift can be bound to shortcuts
2026-10-31 23:42:00 -04:00
aiongg 970fd6fa39 keymap: test the UK ISO key; the Turkish Q item applies to Windows and Linux 2026-11-01 03:31:51 +00:00
.CooCoo fb08be5fa1 krilla: CMYK, gray and spot colors keep their exact inks (patch 17) (#417)
* krilla: CMYK, gray and spot colors keep their exact inks (patch 17)

Gray, CMYK, Separation and DeviceN colors stored each component as a
u8, so every ink was written as a multiple of 1/255: C75 became
0.7490196 and C4 M4 Y4 K4 became 0.039215688.

- cmyk, luma, separation and device_n Color::new_f32 take components
  from 0 to 1 and write them as given. The u8 constructors are
  unchanged.
- Gradient stops rewritten into one DeviceN, Separation or CMYK space
  keep their components.
- Mesh gradients write corner colors at 16 bits a component.

* Tests: the 75% K mesh corner comment gives the asserted 0.749996
2026-10-31 23:30:23 -04:00
.CooCoo 9d17829194 hayro-syntax: skip a dictionary entry whose value does not parse (#165)
Dict validates a dictionary by skipping its values, and the skip accepts
some values that the parser cannot read: a name with a broken # escape
(/#zz), a number such as 12-.5, and a dictionary followed by stream with
no endstream. Dict::entries and Dict's Debug output unwrapped the result
of parsing every value, so either one panicked on such a dictionary.

Port LaurenzV/hayro#1501 to the vendored copy: entries skips an entry
whose value does not parse, and Debug prints the entries. ArrayIter::next
already ends the iteration on such an item here. Add LaurenzV/hayro#1501's
two tests, update the local hashes of dict.rs and array.rs and the repair
list in SOURCE.json, and note the change in vendor/README.craft-reader.md.
2026-10-31 23:30:03 -04:00
.CooCoo 69c268014e craft-print: serialize calls to the Windows PrintTicket provider (#171)
The first time a process opens a PrintTicket provider for a queue whose
provider is PrintConfig.dll, winspool.drv creates a process-wide cache
without a lock. When two threads make those first calls together,
winspool can keep a pointer to a cache it has already freed, and a later
PTOpenProvider call crashes the process with an access violation in
ntdll. Listing printers from several threads at once reaches it through
two_sided.

Hold one process-wide lock around every call that reaches the provider:
PrintCapabilities::fetch in two_sided and driver_ticket, the
PrintTicketBuilder in driver_ticket, fetch_xml_for_ticket in
imageable_margin, and the start of print_with_progress, where winprint
converts the ticket to a DEVMODE. The job releases the lock at its first
progress report, before it sends progress to the submitting thread. A
lock poisoned by a panic is still taken.
2026-10-31 23:29:47 -04:00
dotCooCoo fc37b20235 keymap: the ISO key next to left Shift can be bound to shortcuts
craft_keymap::Key gains IntlBackslash, the key between left Shift and Z
on ISO keyboards (`<` and `>` on Turkish Q). Key::from_egui and to_egui
map it to egui's IntlBackslash, so the shortcut recorder records it and
the dispatcher runs its bindings. Its shortcut text is `IntlBackslash`
(`Cmd+IntlBackslash`, `Shift+IntlBackslash`), read without regard to
case; `<` and `>` still parse as no key.

The key counts as a symbol position: Shift stays in the chord, and once
the layout has typed `<` there, display_for shows `Ctrl+<`.
MAX_CHORD_TEXT goes from 64 to 72 bytes so that the longest canonical
trigger, `Cmd+Ctrl+Alt+Shift+IntlBackslash` pressed twice, reads back.
2026-10-30 23:00:40 -07:00
Dylan M. Taylor c39e435cc3 Merge pull request #462 from aiongg/update-result-dialog
update: CheckResultDialog for the result of a manual update check
2026-10-31 01:04:55 -04:00
Dylan M. Taylor 487b4d4270 Merge pull request #461 from aiongg/docs-tidy
docs: tidy AGENTS.md and clean-room doc
2026-10-31 01:04:43 -04:00
aiongg c5a1cbfb5f update: CheckResultDialog shows the result of a manual update check 2026-10-31 04:39:40 +00:00
aiongg a2be97cf8e docs: tidy AGENTS.md and clean-room doc 2026-10-31 04:17:35 +00:00
Dylan M. Taylor 4323a0f90e Merge pull request #406 from solarmaster961/fix/flatpak-system-bundle-self-update
fix(linux): self-update a system-wide Flatpak bundle in place
2026-10-30 22:56:43 -04:00
Dylan M. Taylorandcoderabbitai[bot] 003f133f6d Update crates/update/src/install.rs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-10-30 22:48:52 -04:00
VoiceLessQ 7ec82661cb fix(update): cap a GitHub rate-limit wait at one hour
An absurd x-ratelimit-reset or retry-after stopped the API until restart.
GitHub's quota refills within the hour.
2026-10-30 22:44:18 -04:00
VoiceLessQ f0b5b22dce fix(update): read GitHub API answers up to a 16 MiB cap
get_api shared the 4 MiB limit for small answers. A launcher version list of
100 releases can pass that: SolidCraft's was 6.5 MB (craft-launcher#1021).
Other small requests keep 4 MiB.
2026-10-30 22:43:46 -04:00
Dylan M. Taylor 2b8bd856ce Merge pull request #458 from storytold/fixup/crw-365-follow-up
fixup(crw): trim derivation artifacts (follow-up to #365)
2026-10-30 22:42:50 -04:00
Dylan M. Taylor a0fdc73717 fixup(crw): trim shipped derivation artifacts 2026-10-30 22:41:55 -04:00
Dylan M. Taylor b645a09174 Merge pull request #365 from rwoldberg/clean-room-crw-format-decode
Clean room crw format decode
2026-10-30 22:40:13 -04:00
Dylan M. Taylor 0ad3952f5e docs(crw): declare table generator Python version 2026-10-30 22:40:07 -04:00
Dylan M. Taylor 8846e5d34a Merge pull request #449 from Jeremy8776/feat/update-version-pins
craft-update: apps respect the launcher's per-app version choice
2026-10-30 20:55:18 -04:00
JeremyandClaude Opus 5.5 9817c81fd2 craft-update: the version-choice file sits in a host folder Flatpaks can read
A Flatpak launcher wrote versions.json inside its own sandbox, and no Flatpak
app could read the launcher's folder, so pins never reached Flatpak apps, nor
host apps started from their menu entry. The file now sits in its own folder,
ArtCraft/versions under the host's per-user data folder, found from inside a
Flatpak too (HOST_XDG_DATA_HOME, else ~/.local/share). A Flatpak app reads it
with one read-only grant for that folder alone (pins::FLATPAK_GRANT).

folders::host_data_local_dir replaces install's private copy of the same rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 21:55:00 +00:00
Martin Ball 9dce709a53 Merge storytold/wordcraft#4191 2026-10-30 21:16:12 +00:00
Martin Ball 12c0bd61e3 Merge storytold/wordcraft#4575 2026-10-30 21:16:12 +00:00
JeremyandClaude Opus 5.5 0ada678488 craft-update: every check answer uses the launcher's choice as it stands when the check ends
finish_check now re-reads the choice before looking at the answer, so an app
the launcher pinned mid-check reports UpToDate and an up-to-date managed()
whether the network found a release, nothing, or an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 12:52:57 +00:00
JeremyandClaude Opus 5.5 a710ab7897 craft-update: a pin made while a check runs still holds its answer
finish_check used the choice cached when the check started, so a launcher
pin made during the network request still opened the stable prompt. It now
re-reads the choice when the check finishes, as offer does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 12:42:23 +00:00
JeremyandClaude Opus 5.5 0db8c1ba47 craft-update: a pin closes a stale prompt; tracks() skips stable; isolate the pretend test
Review fixes for #449:
- A prompt opened while the app followed stable stayed open after the
  launcher pinned it, and its Update button still installed the stable
  release. Reading a managed choice now closes a prompt that is not
  installing, and an install re-reads the choice before it starts. A
  running or finished install keeps its prompt, which tick reports through.
- Pins::tracks listed an explicit Stable entry despite its docs.
- The pretend-version child test read the developer's real pins file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 12:36:42 +00:00
JeremyandClaude Opus 5.5 7d303b4b50 craft-update: apps respect the launcher's per-app version choice
The launcher lets someone keep an app on an older release or on nightlies,
but the app's own update check never knew, so a pinned app prompted to
update on every start and "Update and restart" undid the downgrade.

`pins` is one small file the launcher writes (versions.json in its per-user
data folder, or the absolute path in CRAFT_VERSIONS_FILE) and apps read:
app id to Stable, Nightly or Pinned(tag), the launcher's own serde shape.
A missing or damaged file means everyone follows stable; an entry a newer
launcher wrote is left to the launcher and kept on save.

`Updater` reads it before each check and before an offer. An app kept off
stable gets no prompt and no network call; a requested check reports
UpToDate, and `Updater::managed()` says why, so the public Status enum is
unchanged and no app breaks on a rev bump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 12:22:38 +00:00
Dylan M. Taylor 04aa1703b3 update: honor pretend versions in app release checks (#436) 2026-10-30 02:12:36 -04:00