Skip to content

1.5.1

Choose a tag to compare

@github-actions github-actions released this 07 Sep 21:01
· 28 commits to master since this release
7935919

Fixed

  • capitalize raised the first letter of the STRING, where its own documentation, the demo gallery and CSS all promise the first letter of every word. doc/typography/text-transform.md has read "converts the first character of each word" since the page was written, the gallery's quick reference advertises Title Case, and the token is a port of CSS text-transform: capitalize, which raises every word initial. The code was text[0].toUpperCase() + text.substring(1), so capitalize this text rendered Capitalize this text, and the gallery's own demo row was indistinguishable from normal-case because its source string The quick brown Fox already starts with a capital. references/tailwind-divergence.md carries no capitalize row either, so this was an accidental gap rather than one of Wind's deliberate divergences. The transform now raises every word initial through one hoisted regex, and what counts as a word was measured in Chromium over 32 strings rather than read off the spec: a letter, a digit, an underscore and an apostrophe continue a word, a combining mark or an invisible format character attached to one continues it with them (so decomposed NFD text does not capitalise mid-word, while a zero-width space still breaks), and anything else opens a new one. So "quoted words" becomes "Quoted Words", well-known becomes Well-Known, read/write becomes Read/Write and u.s.a. becomes U.S.A., while 3rd party stays 3rd Party, wind_ui stays Wind_ui and l'orange stays L'orange. The rest of each word is left alone, so an acronym the caller passed in stays intact (the HTTP client becomes The HTTP Client), and the match is the initial letter alone, so the original spacing survives exactly as typed. Each initial goes through the same locale mapping the rest of the transform uses, so under Locale('tr') izleyici ışıkları capitalises to İzleyici Işıkları rather than Izleyici Işıkları. Expect the change on upgrade: every multi-word string carrying capitalize now renders every word capitalised, where before only the leading letter moved. The gallery's Text Transform page also gains the Locale-aware Casing section that the locale fix below landed without. (lib/src/widgets/w_text.dart, doc/typography/text-transform.md, example/lib/pages/typography/text_transform.dart, skills/wind-ui/)

  • WText's uppercase, lowercase and capitalize cast under the ambient locale instead of Dart's locale-independent rules. String.toUpperCase() maps i to I, but Turkish and Azerbaijani distinguish a dotted from a dotless i: the uppercase of i is İ and the uppercase of ı is I. Any heading carrying the utility therefore rendered IZLEYICILER and GÜVENLIK in a Turkish app, which are not words, and because the transform rides a CLASS NAME rather than a call site the defect followed the utility into every consumer package; a consumer could only fix the strings it passes in itself. Measured, only one mapping per direction is actually missing: 'izleyici'.toUpperCase() is IZLEYICI while 'Kullanılan'.toUpperCase() is already KULLANILAN, and going down I lowers to i where Turkish needs ı while 'İZLEYİCİ'.toLowerCase() already gives a clean izleyici. The swap runs before the general cast in both directions, or the letter it looks for has already changed. The locale is read through Localizations.maybeLocaleOf, so a widget pumped with no Localizations ancestor (every bare widget test) keeps the previous behaviour rather than throwing. This is the only user-COPY casing site in the package; every other toUpperCase / toLowerCase normalises a class name or a wire token and stays locale-independent. (lib/src/widgets/w_text.dart, doc/typography/text-transform.md, skills/wind-ui/)

Quality

  • The weekly external-link pass failed on a status that says nothing about link rot. The 2026-09-07 run went red on three 504 Gateway Timeouts from github.com, all of them this repo's own pages (the compare/1.5.0...HEAD link and the 1.3.0 and 1.4.1 tag links), every one of which loads in a browser. Neither obvious cause survives a look at the source: lychee-action already defaults its token input to ${{ github.token }} and exports it as GITHUB_TOKEN, so lychee is authenticated, and a token addresses rate limiting anyway, which arrives as 403 or 429; and lychee's --max-retries already defaults to 3. So the job was failing on the one thing it does not exist to catch, because a 5xx says the host is up and unhappy rather than that the link is gone. --accept now widens lychee's default 100..=103,200..=299 with 429 and the whole 500..=599 range: stopping at 504 would have covered the failure that prompted this and left the same class failing one door down, since Cloudflare's 520, 521, 524 and 530 sit outside it and are exactly an origin that did not answer. Measured against the pinned lychee 0.24.2 rather than assumed: with the defaults a 404, a 429 and a 504 all fail the run, and with the accept list the 429 and the 504 pass while the 404 still fails it with exit 2, which is what rot looks like; on a second probe carrying 503, 508, 521, 524 and 530 the narrow range rejected five of seven links and the widened one rejects only the 404. A request cache was tried and dropped: actions/cache declares post-if: success(), so the save is skipped on exactly the failing runs a retry would follow, and the whole pass costs under four seconds for 939 links. (.github/workflows/docs-link-check.yml)