Release v1.94.0 (1940) — the stable that gathers three dev builds - #360
Conversation
… stable v1.93.0 is the last stable. Everything since then shipped as pre-release (1931, 1932, 1933) and never reached the stable lane, so these notes roll all three up: 65 merged PRs, from the freeze-profile and bulk-export features to the privilege-readback and app-lock fixes. Written to the three-file contract in release-notes/README.md: - playstore.txt — 483 chars, inside the hard 500-char Play limit; copied byte-identical to fastlane/metadata/android/en-US/changelogs/1940.txt and synced into shizu_store.json by sync-shizu-changelog.sh. - telegram.md — 804 UTF-16 units, 953 once CI wraps it. The caption cap is 1024 and a caption over it posts *nothing* while the workflow still goes green, so this is measured assembled, not as a file. - github.md — the audit trail: highlights, 13 themed sections, and all 65 commits with their PR numbers. check-shizu-manifest.sh passes, including the changelog-matches-playstore.txt check for versionCode 1940 -> v1.94.0. Nothing publishes from this branch: dev-check.yml triggers on push to master, not dev, so the 1933 build already with testers is untouched. Merging dev into master is what fires Play, Telegram and the GitHub release.
chore(release): 1.94.0 (1940) — the stable that gathers three dev builds
…ite already does The website (web/src/lib/site.ts) and .github/FUNDING.yml have both advertised five funding routes for a while. The in-app support sheet offered three — GitHub Sponsors, Patreon, PayPal — so two of the routes a reader saw on the site simply did not exist in the app. Both flavours now carry all five, in the same order: Sponsors first (lowest fees, and it sits beside the source), then recurring-capable, then one-off. Icons are drawn rather than borrowed. SupportDeveloperBottomSheet renders every action icon with Icon(tint = colorScheme.primary), so a real brand mark could never keep its brand colour — which is why PayPal already uses the generic shield_with_heart. brand_kofi is a mug with a heart; brand_buymeacoffee is a lidded takeaway cup with steam. The silhouettes are deliberately unalike because the two rows sit next to each other and two coffee cups differing only in detail are two identical smudges at 24dp. Both were rendered and inspected at 24 and 48 px before landing. Two details worth knowing: - brand_kofi needs fillType="evenOdd". Everything is drawn in a single tint, so the heart has to be a HOLE in the cup; as a second filled subpath it would be invisible. - brand_buymeacoffee strokes its steam instead of filling it. Icon()'s tint is a ColorFilter over the whole drawable, so strokeColor is tinted along with fillColor and those paths need no fillColor at all. Going from three rows to five needed no layout change: both SupportDeveloperBottomSheet and SupportDeveloperTabbedBottomSheet already wrap their content in verticalScroll. Strings are in all five locales, so lint reports no MissingTranslation. Neither new drawable trips VectorPath — the five hints in the report are the pre-existing thor_mono, thor_drawn_foreground and dhizuku icons. assembleFossDebug, assembleStoreDebug, lintFossDebug and lintStoreDebug all pass.
1940 has not published yet — dev-check.yml fires on push to master, and the dev->master merge is still to come — so #356 belongs to this release rather than the next one, and the notes have to say so. Three edits, all in github.md: a bullet under Support, the commit-log line, and the intro's PR count, which is a number the commit log can be counted against and so goes stale loudly. 65 -> 66; the two now agree. playstore.txt and telegram.md are deliberately untouched. Both are size-capped (483/500 chars and 804 units against a ~870 budget), and "two more ways to donate" is not what a Play or Telegram reader needs from a release that fixed a data-wiping freeze. github.md is the audit trail and takes it. check-shizu-manifest.sh still passes, including the changelog-matches- playstore.txt check for versionCode 1940.
… treat `请喝一杯咖啡` reads as "please have a cup of coffee" — addressed at the reader. The missing `我` inverts who is being treated; the idiom is `请我喝杯咖啡`. The Buy Me a Coffee line was a bare noun phrase (`一次性咖啡或按月会员`) that never named the action either. Kept the two lines distinguishable, as the English pair and the two icons already are: Ko-fi leads on frequency (`一次或每月`), Buy Me a Coffee on membership (`按月成为会员`). Only zh-rCN was inverted — es `invita a un café`, fr `offrir un café` and ar `اشترِ قهوة` all already put the user as the payer. Also indexes #356 in the Support section heading of the v1.94.0 notes, which listed the three older PRs but not the change the section's own bullet documents.
…long press Closes #344. The grid used fixed positional buckets — Reinstall+Install on row 1, Clear cache+Extensions on row 2 — so the shape was decided by which slot a tile sat in rather than by how many tiles there were. Install was unconditional in row 1, which meant Reinstall and Clear cache could never go full-width no matter how few tiles were showing. homeActionRows now flows the visible actions into pairs with a single wide leader when the count is odd: 4 -> 2x2, 3 -> wide then a pair, 2 -> one pair, 1 -> one wide tile. Reinstall moves to the end of the flow because it is the only dismissible tile; dismissing it now re-packs the rows after it instead of shifting every other tile up a slot. Half-width tiles have never had room for a title and a description both. At 393 dp across five locales and three font scales, 156 titles and 224 descriptions clipped; en/es/fr already lost 4/9/9 of 13 descriptions at the default font size. Paired tiles therefore drop the description and give the title its second line, and a long press opens a bottom sheet with the full text — measured at 218-266 dp, no scrolling in any locale. Wide tiles keep their description inline and have no long press, since there would be nothing to add. Nothing clips at the default font size in any locale now. The wide-screen rail gets narrowContainer: at roughly a third of a 600 dp window even a full-width tile cannot hold a one-line title plus a description, so every tile there goes full-width and compact. That is the only combination measured to clip nothing in that pane. Two details the design turns on, both covered by instrumented tests: - The dismiss X is a plain Box with combinedClickable, not an IconButton. IconButton inflates an 18 dp icon to a 48 dp target and its clickable consumes the pointer down, so the tile's long-press timer never started and holding that corner dismissed the card instead of explaining it. - Dropping the subtitle Text also drops its string from the tile's merged semantics node, taking the scope of the action away from TalkBack. A zero-size semantics node after the title keeps the content and its order. A long press fires onLongClick instead of onClick, so holding a tile no longer performs its action; the sheet's confirm button hands that back and reuses the tile's own title, costing no new translation. Strings are net zero: show_details lands in all five locales, and home_extensions_subtitle ("Manage & open") leaves them — the sheet needs an explanation, not a label, so Extensions reuses manage_extensions_desc. Verified: 9 unit tests, 10 instrumented tests on API 36, lintStoreRelease clean, 472/472 string parity across en/ar/es/fr/zh-rCN.
…acoffee feat(support): offer Ko-fi and Buy Me a Coffee in the app, like the site already does
The adaptive bento re-packs whatever tiles are visible, but nothing could make either of these two invisible: INSTALL was unconditional and EXTENSIONS was gated only on privilege. That is what #344 actually asked for — the grid was the means, not the request. Both are shortcuts to something still reachable elsewhere: hiding Installer leaves APK intent handling untouched, hiding Extensions leaves the Settings entry. So the preferences stack with the eligibility rules rather than replacing them — Extensions still needs a privilege *and* the preference. Hiding both with no privilege leaves nothing eligible, which is a legitimate answer rather than a bug to guard against. homeActionRows returns an empty list, HomeActionsBento emits no Column, and both HomeScreen layouts drop the spacer that separates the grid from the summary row — a bare 36 dp gap reads as a rendering fault. Plumbed on the showReinstallAllCard path end to end: two DataStore keys defaulting to true, two Settings switches in GENERAL, five locales. HomeActionsTest now crosses the five reachable privilege states with all four preference pairs and asserts each tile's visibility against its own rule, so a tile cannot ride along on another's condition (16 tests, was 9).
feat(home): let users hide the Installer and Extensions tiles, and pack the bento adaptively
#357 merged after the notes were written and, like #356 before it, belongs to this release rather than the next: 1940 has not published yet, since the dev->master merge that fires dev-check.yml is still to come. github.md takes it in full — a Highlights bullet, a "What's Changed" section covering both halves (the adaptive packing and the two Settings switches for GH#344), the commit-log line, and the PR count, 66 -> 67, which is the one number in the file the commit log can be counted against. telegram.md gets one line, which needed making room for. The caption the channel actually receives is the file plus a 141-unit wrapper from dev-check.yml, so the real budget is 1024 minus that, and the file was already at 803 of it. Two lines were tightened losslessly ("check the result, not the exit code"; "Recents hides your app list") to buy the space. Measured assembled, not by hand: 998 of 1024, margin 26. A hand count of the new line said 55 units; it is 68. playstore.txt is deliberately untouched, as it was for #356. It sits at 483 of Play's 500, and the only way to fit a home-tile line is to shave nuance off bullets that matter more to a Play reader than this does — a freeze that stopped wiping data, and privileged actions that now report what really happened. Left unchanged, so the fastlane changelog and shizu_store.json still match it byte for byte. check-shizu-manifest.sh passes, including the changelog-matches-playstore check for versionCode 1940.
… racing for one Play code Google Play allows a version code to be uploaded exactly once app-wide — the limit is per app, not per track. Both master and production upload, and both are fed from dev, so a single chore(release) commit reached both branches carrying the same code and whichever workflow got there first made the other die on "Version code NNNN has already been used". That is the open half of docs/follow-ups/two-branches-one-play-version-code.md, and the version-code guard in production-deploy.yml never could cover it: it compares production against production's own previous tip and cannot see what master did. Route on the last digit instead. versionName is derived as code/1000 . code%1000/10 . code%10, so the last digit IS the patch number and a code ending in 0 is an x.y.0 — 1940 is 1.94.0. dev-check.yml now sets release=false for those, builds them for verification, and publishes nothing. Every code therefore has exactly one uploader: stables from production on `beta`, everything between them from master on `alpha`. This is a fourth option the doc did not list, and it is the one that keeps what the owner rejected option 2 to protect — master really does still publish to a Play test track, it just never publishes a stable. It needs no Fastfile change, so neither lane grows a promote-vs-upload branch. The trade against option 1 is that `beta` no longer carries the bytes `alpha` tested, because a stable never reaches `alpha` at all; the doc says so rather than implying otherwise. Two residuals, both deliberate and both written down. workflow_dispatch still overrides the check, because a manual dispatch is an explicit request to publish — the escape hatch for a stable is production-deploy.yml's own dispatch, not master's. And nothing enforces the rule on the production side, so merging a mid-cycle build into production would re-open the collision from that direction; convention holds there, not a check. `case` rather than $((NEW % 10)): arithmetic expansion errors out on a non-numeric read, which would turn a step deliberately written to fail OPEN into a red build. Behaviour checked against the extracted step: 1940 after 1933 -> false, 1933 after 1932 -> true, 1933 unchanged -> false (the existing rule, intact), 1941 after 1940 -> true, a non-numeric read -> true, still failing open. Also corrects the comments that asserted the collision was unfixed — production-deploy.yml claimed it in two places — and the workflow line numbers this insertion shifted, in release-notes/README.md, pr-ci.yml and telegram-caption-length-guard.md. Two of those were already stale by two lines before this change. First release to exercise it is v1.94.0 (1940), which is why it lands before that merge rather than after it.
docs(release-notes): record #357 in the v1.94.0 notes
…m-production ci(dev-check): leave stables to production, and stop the two branches racing for one Play code
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Warning Review limit reached
Next review available in: 14 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (37)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Brings
masterup todevfor v1.94.0. Five PRs sincemaster's tip: #355, #356, #357, #358, #359.versionCode1933 → 1940.That changed in this very PR, in #359.
dev-check.ymlnow declines to publish anyversionCodeending in0, because a stable belongs toproduction:1940production-deploy.yml(production)beta1933dev-check.yml(master)alphaSo the run this merge triggers builds and verifies only: no Play upload, no Telegram broadcast, no GitHub pre-release. v1.94.0 publishes when
1940reachesproduction— currently at1930,devis 388 ahead and 0 behind, andmerge-treesays that merge is clean.Play allows a version code to be uploaded exactly once app-wide, so before #359 both branches raced for
1940and one of them had to fail. Routing on the last digit gives every code exactly one uploader. Full reasoning indocs/follow-ups/two-branches-one-play-version-code.md, now marked resolved.What is in it
chore(release): 1.94.0 (1940), and the three release-notes files.FUNDING.yml.github.mdin full, one line intelegram.md, PR count 66 → 67.production-deploy.ymlcomments that asserted the collision was unfixed.Release notes, verified
playstore.txtplaystore.txt↔fastlane/…/changelogs/1940.txtcheck-shizu-manifest.shmerge-tree master…devExpected side effect
Commit
59236b5ecarriesCloses #344in its body.masteris the default branch, so this merge will close #344 — which is correct: the setting it asked for shipped in #357. No other closing keyword appears anywhere inmaster..dev, checked.Merging this
masteralso drives the landing page's Vercel Production deploy and hostsshizu_store.json, so the merge still matters even though it no longer publishes the APK. The APK release is a separatedev→productionPR, not yet opened.