-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreleases.json
More file actions
927 lines (927 loc) · 209 KB
/
Copy pathreleases.json
File metadata and controls
927 lines (927 loc) · 209 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
{
"latest": "2.3.1",
"releases": [
{
"version": "2.3.1",
"date": "2026-09-14",
"kind": "fix",
"summary": "A patch on 2.3.0: Gemini 3 models can use tools in the AI sidebar again, and Brazilian Portuguese gains another 52 translated strings",
"changes": [
"The AI sidebar's Gemini 3 models no longer stop with an error the moment they run a tool, so asking one to change your wallpaper or look something up on your system works through to the answer",
"More of the interface is translated in Brazilian Portuguese (thanks @douglasboardman)"
],
"commits": [
"2dbd074a2 ai: Gemini 3 models can run a tool without erroring on the next turn",
"9c176898b i18n: improve Brazilian Portuguese translations"
],
"commitsTotal": 2,
"commitNotes": {
"2dbd074a2": "Asking Gemini 3 to do something that calls a tool failed with \"Function call is\nmissing a thought_signature\" as soon as the tool returned, because the reply\nsent back the model's own call without the signature Gemini 3 attaches to it.\nThe signature is kept and replayed now, along with the arguments the call was\nmade with, which were being dropped from that history too."
},
"merges": 1
},
{
"version": "2.3.0",
"date": "2026-09-14",
"kind": "feature",
"summary": "Better live wallpaper support and a smoother path from install to desktop: your keyboard layout now follows you from the installer through login and into your Desktop, Windows shows up in the boot menu wherever it's installed, and installs and updates no longer stall when GitHub is unreachable",
"changes": [
"Your video wallpaper now plays on the login screen and lock screen instead of only showing a single frame of itself",
"A video wallpaper now pauses while nothing can see it, so it stops drawing power behind a fullscreen app",
"New Live wallpaper setting under Power Saving on the Power page: that allows you to limit the FPS of your live wallpaper to save power",
"Your keyboard layout now follows you the whole way: the one you choose in the installer reaches the login screen, your session and the Keyboard page in Settings",
"The login screen has a new keyboard layout picker in the bottom left, with a searchable list of every layout",
"Three new categories on the Welcome app's Set up your apps page: Office with LibreOffice and OnlyOffice, Image Editing with GIMP and Krita, and Game Streaming with Sunshine and Moonlight",
"Blender now sits beside OBS Studio under Content Creation, and every pick on the page shows the app's own icon instead of a generic symbol",
"An install from the ISO no longer stops when GitHub is unreachable, and package updates now fall back to a second server when GitHub cannot be reached",
"Windows now shows up in the boot menu wherever it is installed, including on a second drive",
"The World clock desktop widget now holds up to eight cities instead of four, the city you pick takes effect right away",
"Frosted widget backgrounds is dimmed on the Background page while a video wallpaper is set",
"Ctrl + Super + K to switch keyboard layouts is now more consistent"
],
"commits": [
"c22ba125b login screen: a background can only be placed under your own name",
"f8ead70d2 wallpaper: a new frame rate takes effect on the first try",
"9fed1c1dd world clock: choosing a city keeps the ones beyond the current clock count",
"f6a820141 welcome: Desktop Gaming installs on a legacy NVIDIA driver branch",
"0e86ce6a1 keyring: the fallback mirror is added on the run that first points at GitHub",
"9b1f34e3e update: --help prints, a dry run changes nothing, and a failed dotfiles step is named",
"60542f0e4 wallpaper: a video one can be held to 15 or 30 frames a second",
"0b7bc3355 power: drop the suspend notice the revert left behind",
"e175f1b25 widgets: the frosted backgrounds switch says when it cannot apply",
"f0e52ae8d wallpaper: a moving one reaches the login screen too",
"b6d4d959e lock: a video wallpaper keeps moving under the blur",
"3b416b696 ai: a chat on its way out stops reaching for itself",
"a0cca65be widgets: the tab indicator holds its place while tabs are rebuilt",
"0e2aa3f6b ai: the model selection settles before anything reads it",
"c66ce511b bluetooth: the device list stops asking to be rebuilt",
"16ceab67e packages: a second server to fall back on",
"321cb6941 mirror: the SourceForge key is found wherever the job runs",
"3c831e3ca mirror: a copy of this repo lands on SourceForge",
"cb2f6bf3d boot menu: Windows shows up wherever it is installed",
"7cdac7e50 welcome: Office sits above Image Editing on the Set up your apps page",
"4f5b2ebb1 streaming: the host stays awake, and older NVIDIA cards decode on the card",
"90ca70d6e welcome: a pill on the install page points at the apps below the fold",
"06cae601b welcome: the install picks show their real app icons",
"1504be6e5 welcome: Blender beside OBS on the Set up your apps page",
"d4b3b5c2f welcome: image editing, office and game streaming picks on the Set up your apps page",
"b99307df1 wallpaper: stop paying for a video nobody can see",
"35fe64032 world clock: up to eight cities, a city change that takes at once, and a date that turns at midnight",
"89b0f07f9 keyboard: the layout you switch to is what the login screen and the next session start in",
"86674f3bc input sources: choosing none puts everything back",
"737757cac translations: seed the update tooling's newer strings",
"47cac5b12 keyboard: one layout from the installer to the login screen, the session, and Settings",
"e480df1d6 keyboard: switch every keyboard at once and keep the bar's layout list current",
"f4699cc68 update: a failed dotfiles update no longer reads as success",
"025e2e69d repair: never pin to a tag the fetch did not bring in",
"eec725b0c updatems: rebuild a damaged clone instead of dying on it every run",
"25c9a3733 exp-update: write new files before replacing old ones",
"d7d53abe7 updatems: recover session env so exp-update's reload hold works",
"c6c40cc83 Revert \"power: leave idle suspend off on the legacy NVIDIA driver branches\"",
"3f8272370 Revert \"power: gate idle suspend on the legacy NVIDIA branches only\""
],
"commitsTotal": 53,
"commitNotes": {
"c22ba125b": "The privileged helper that copies a wallpaper into the login screen theme now\naccepts only a destination named for the account asking, so one account on a\nshared machine cannot replace another account's login background. The clearing\npath already checked this; the copying path did not.",
"f8ead70d2": "Picking a frame rate in Power settings now brings the desktop wallpaper back at\nthe rate you picked rather than the one before it, and a video wallpaper set\nwhile the screen is locked starts playing instead of leaving the lock on the\nold one. Settings passes the rate on the command line, because its own write of\nthe config file lands after the wallpaper has already restarted.",
"9fed1c1dd": "Setting a city while the desktop is showing fewer clocks than you have\nconfigured no longer discards the cities past that count, so raising the count\nagain brings them back instead of resetting them to defaults. The write copies\nthe stored list rather than the view that is clamped to the count.",
"f6a820141": "Picking Desktop Gaming on a machine running a legacy NVIDIA branch now installs\nSteam and the rest of the gaming stack, instead of aborting the whole\ntransaction over a 32-bit driver that conflicts with the one already there. The\n32-bit package is matched to the branch that is actually installed, the way the\ngamescope installer already does it.",
"0e86ce6a1": "A machine coming off the install media now gets the SourceForge mirror line\nbeside the GitHub one on the same run that writes it, rather than waiting for\nthe next six hourly tick, and a disabled [mainstream] block no longer warns\nabout the mirror on every tick forever. The mirror check matches whole lines,\nso a commented out block is not mistaken for a live server.",
"9b1f34e3e": "updatems --help now prints the help text instead of aborting on an unset\nvariable, a dry run no longer sets a damaged clone aside and re-clones it, and\nan update that fails only at the dotfiles step now reports that specifically\nrather than a generic \"completed with errors\" on machines whose package repo\nwas unreachable. The offline check moved to the end of the exit code ladder so\nthe more specific codes above it are no longer masked.",
"60542f0e4": "Power settings gains a Live wallpaper rate at the end of Power Saving, offering 15 fps, 30 fps or the file's own rate, and it holds on the desktop, on the lock screen and on the login screen alike. The desktop's player is told directly; the other two are shown through a copy taken on a timer, so nothing redraws faster than asked.",
"0b7bc3355": "The Power page no longer shows a note about automatic suspend being held off, which had been showing on every machine since the thing it reported was removed.",
"e175f1b25": "Choosing a video wallpaper now greys out frosted widget backgrounds and says why, instead of leaving a switch that looks on while nothing appears. A moving wallpaper is drawn beneath the desktop by another process, so there is nothing there for a card to blur.",
"f0e52ae8d": "Picking a video wallpaper now copies it beside the still the login screen already uses, and picking a still one takes it away again. The privileged helper carries the video under the same checks as the picture, and can only drop the caller's own.",
"b6d4d959e": "Locking with a video wallpaper now shows the video itself moving under the blur, rather than a blurred still of it. The lock plays the same file for as long as it is up, because the compositor hides the desktop's own video surface once a lock comes up.",
"3b416b696": "Chats are discarded and rebuilt whenever translations land, and the one being\ntorn down evaluated its strings again with its id already gone. Those strings\nare now left empty.",
"a0cca65be": "A TabBar with no current tab reports -1, which an item property cannot take.\nThe indicator now keeps its tab until a real one is there.",
"0e2aa3f6b": "Resolving the model id ran a handler that rewrote the model map while a\nbinding was still reading it, which Qt took for a loop. The handler now\nleaves that first resolution to the startup pass, which does the same work.",
"c66ce511b": "The counter numbering new devices was read and written inside the list's own\nbinding, so recording a device asked for the list to be built again. The\nnumber now comes from the map itself.",
"16ceab67e": "Installs now carry the SourceForge mirror alongside the GitHub one, so package updates keep working when GitHub cannot be reached. pacman tries the servers in order, so nothing changes while the first one answers, and machines already installed pick the line up on their next update.",
"321cb6941": "The key and the pinned host key now live at a fixed path instead of one that depends on which home directory the job resolves.",
"3c831e3ca": "Every push to mainstream and every tag is copied to the SourceForge repository, so there is a second place to fetch releases from when GitHub cannot be reached. Branches and tags travel, pull request refs do not.",
"cb2f6bf3d": "A Windows install on a second drive now appears in the boot menu, not only one that shares the EFI partition Mainstream was installed on, and a machine that gains Windows later picks it up on its next system update. Every fixed EFI partition is checked, and the entry records the partition itself so it resolves whichever drive it came from.",
"4f5b2ebb1": "Sunshine now holds an idle inhibitor while it runs, so a stream no longer ends with the host locking the screen or dropping to sleep, and a ./setup install on one of the frozen NVIDIA branches gets the video driver the ISO already installs.",
"90ca70d6e": "A \"More apps below\" pill sits over the bottom edge of the Set up your apps list until the end is reached, and clicking it turns a page. It is the same button the sidebar chat uses to scroll to the bottom.",
"06cae601b": "Each row on the Set up your apps page now carries the icon the app has in the app grid, drawn from the installed icon theme, so the picks look like what lands after the install. Sunshine ships its own logo since Papirus has none for it.",
"1504be6e5": "The Content Creation row now offers Blender in the slot next to OBS Studio, installed from the repositories like the rest.",
"d4b3b5c2f": "The install page now offers GIMP and Krita, LibreOffice and OnlyOffice, and Sunshine and Moonlight next to the gaming, video editing and content creation picks. The page scrolls with an edge fade since six categories no longer fit the fixed window. OnlyOffice comes from Flathub; everything else installs from the repositories, and the helper package moves to pkgrel 3.",
"b99307df1": "A video wallpaper decoded continuously whether or not anything was in front of\nit, so a fullscreen game or a maximized editor left mpvpaper working through\nframes that never reached a pixel.\n\nmpvpaper already knows how to handle this and was never asked to. Both call\nsites now pass -p -a FULL, which pauses playback whenever the wallpaper is\ncovered, including by a fullscreen window.\n\nPause rather than stop. Stopping releases everything but has to re-open the file\nto come back, which shows as a hitch the moment a window closes. Pausing keeps\nthe decoder warm for an instant resume, and the idle cost of a paused mpv is\nsmall next to decoding frames into a surface nobody is looking at.",
"35fe64032": "Settings offered eight clocks but the widget stopped at four, a city picked after the first edit never reached the widget until the shell restarted, and the local date was read once and kept. The widget now shows as many clocks as Settings allows and grows with them, city changes flow through the config the way every other setting does, and the date follows the shell's clock.",
"89b0f07f9": "Ctrl+Super+K only moved the compositor's active layout, and nothing remembered it: the login screen and the next login started from the head of the list again. A switch now tells localed that the active layout leads, and the next session start follows it, the same way it follows a pick made on the login screen. One tool owns the list for Settings, the session start and the bar, and no installer pins the login screen to the install-time layout any more.\n\nThe bridge and the bar never take an input method's virtual keyboard as the one that is typed on.",
"86674f3bc": "Switching the input source back to a plain keyboard left fcitx5 starting at every login and sitting between the keyboard and the apps, one key behind on every layout switch. Choosing none now removes everything the engine path added, including the package's own autostart entry.",
"737757cac": "The corrupt-clone recovery and the failed-dotfiles exit code added messages to updatems, the update helper and mainstream-repair without keys in the locale files, so every locale fell back to English for them.",
"47cac5b12": "The layout chosen in the installer now reaches the login screen, the session\nand Settings \u2192 Keyboard, and a change in any of them reaches the others. The\nlogin screen gets a layout picker, and the layout picked there leads the\nsession's list after login.\n\nThe picker works through a small bridge script, since the greeter's QML cannot\nrun hyprctl itself.",
"e480df1d6": "Ctrl+Super+K aimed switchxkblayout at Hyprland's current keyboard, which is whichever device sent the last key event, releases included. On keyboards whose modifiers and letters arrive from different evdev nodes the switch could land on a device nobody types on, and the bar then reported the other device's layout on release. Every keyboard now switches together.\n\nThe bar's layout service only re-read the layout list after a config reload followed by a keymap event. Settings applies a new list with hyprctl eval, which rebuilds the keymaps with no reload, and the reload it runs afterwards changes nothing and so carries no keymap event. The indicator stayed hidden until the first switch. The service now refreshes on a reload and on a keymap event that arrives while it knows of one layout, and Settings asks it to refresh after an eval.",
"f4699cc68": "The helper folded two different outcomes into exit code 100: developer extras\nfailing, and the Mainstream dotfiles failing to update. The Settings page\nrendered 100 as \"Update completed successfully\" on purpose, since a cargo or\nnpm pass failing is not something a regular user should be alarmed by. That\nreasoning is sound for extras and wrong for dotfiles, and because the two\nshared a code the page could not tell them apart. A user whose dotfiles step\nhad failed was told the update completed successfully and left on 1.3.2.\n\nDotfiles failures now exit 101. The page says the dotfiles did not update and\npoints at the summary line that names the reason. 100 keeps its meaning and\nits wording, so the benign case reads as it always has.",
"025e2e69d": "The repair tool fetched tags, printed \"using the current checkout\" if that\nfailed, and then pinned to the highest tag in the clone anyway. On a clone\nwhose fetch had just failed those tags were only as new as the last fetch\nthat worked, so a machine with 2.2.0 current and 1.3.2 applied was reset to\n1.4.2, told \"pinned to 1.4.2\", and then run through 1.4.2's setup scripts,\none of which predates the UKI boot layout and failed on it. A repair had\nmade the machine worse.\n\nThe fetch is checked by its own status now rather than sed's, a damaged clone\nis rebuilt the way updatems rebuilds one, and a fetch that still fails means\nno tag is picked at all, which is what the message always claimed.\n\nBehind that is a floor: the chosen tag is compared with what this machine has\napplied, and a tag older than that is refused with the reason printed. A\nrepair should never move a machine backwards, whatever the clone holds.",
"eec725b0c": "One empty object under .git/objects in the cache clone, the mark of a write\ncut short by a power loss or a full disk, made every fetch fail. Dying on that\nis correct, but it left the update path closed until somebody found the\ndirectory and deleted it by hand. On the machine that hit this, the failed\nupdate ran through to a summary reading \"completed successfully\", the repair\ntool pinned the clone to an old tag, and the install script fell over on the\nsame object. All three read the clone as something to preserve.\n\nIt is a cache. Nothing in it cannot be cloned again, apart from the applied-tag\nmarker and whatever an auto-stash holds. When a fetch fails with the signature\nof a damaged repository, the marker is copied out, the clone is moved aside\nunder a dated name, a fresh one is cloned, the marker is put back, and the\nfetch runs once more. The old tree is kept rather than deleted and its path is\nprinted, because the stash inside it may be the user's own edits and there is\nno telling from here whether the damaged object is one of theirs.\n\nOnly a damaged clone is rebuilt. A fetch that failed for want of a network\nstill dies as before; throwing the clone away over a dropped connection would\nbe the wrong lesson from this.",
"25c9a3733": "A reload landing part-way through the config copy has to find a tree it can\nstill load, and whether it does was down to the order files happened to come\nout of find.\n\nNew files are now written before files being replaced. A tree carrying modules\nthe running shell.qml does not reference yet loads fine; a shell.qml that has\nalready been replaced and references a module not yet written does not. That\nsecond shape is what reached users as\n\n module \"qs.modules.ii.desktopMenu\" is not installed\n\non the update into 2.2.0, where desktopMenu was a directory that did not exist\non their machine yet.\n\nThe reload hold is still the guard, and it engages again now that updatems\nrecovers the session environment it needs. This is underneath that: it makes the\nwindow the hold covers survivable rather than fatal, so the same bug returning\nsomewhere else costs a reload rather than a broken shell.\n\nThe conflict resolver is untouched. Ordering happens in the stream feeding it,\nso replace, keep, old, new and backup all behave exactly as before, and every\nfile is still handed over exactly once.",
"d7d53abe7": "exp-update already pauses Quickshell's watchFiles and Hyprland's config\nautoreload for the duration of the file sync (0.run.sh's _qs_live/_hypr_live\nchecks + holdReload/resumeReload + misc:disable_autoreload), specifically to\nstop either from reloading against a half-written config tree.\n\nThat hold silently never engaged when run through updatems: it runs via\n`runuser -u $ORIG_USER -- env HOME=...`, which carries none of XDG_RUNTIME_DIR,\nWAYLAND_DISPLAY, or HYPRLAND_INSTANCE_SIGNATURE. Without them _qs_live() and\n_hypr_live() report \"not running\" and exp-update skips the hold entirely, so a\nmid-copy reload could hit a shell.qml referencing a module that had not landed\non disk yet.\n\nRecover the three before invoking exp-update. XDG_RUNTIME_DIR from our own uid,\nWAYLAND_DISPLAY from `systemctl --user show-environment` where execs.lua's\nhyprland.start hook puts it, and the instance signature from the directories\nunder $XDG_RUNTIME_DIR/hypr.\n\nThe signature is chosen by asking each candidate rather than by taking the\nnewest. Hyprland leaves an instance directory behind when it exits uncleanly,\nand mtime order alone would hand back a dead socket on the next boot after a\ncrash.\n\nBoth holds are checked after the recovery and a warning is printed if either is\nstill unreachable. The bug this fixes was not that the hold failed, it was that\nit was skipped in silence and the update looked fine, so the recovery not taking\nshould not be silent either.",
"c6c40cc83": "This reverts commit 7474149f91cf6b15e009d0a052542a70dbff059f",
"3f8272370": "This reverts commit b691d83b5627f159ce612a1f1847b31889688a70."
},
"merges": 2
},
{
"version": "2.2.0",
"date": "2026-09-09",
"kind": "feature",
"summary": "The desktop comes alive: right click your wallpaper for a menu, choose how one wallpaper transitions to the next, and place eleven new widgets anywhere you like, with sharper text on scaled displays and support for Intel and T2 Macs",
"changes": [
"New simpler default bar layout",
"New Manage page in Settings for setting your default apps and what starts at login",
"Eleven new desktop widgets: the clock (now in a third style, Pixel, alongside digital and cookie), weather, a calendar, world clocks, notes, a to-do list, timers, system resources, a music visualizer, media controls and a picture widget (thanks @pctrade)",
"Choose how one wallpaper transitions to the next: crossfade, slide, zoom, wipe, circle, ripple, peel, glitch, CRT and shatter, or let it pick at random (thanks @pctrade for the shader transitions)",
"Right-click the desktop for a menu that lets you change your wallpaper, switch themes, and personalize your bar and dock, all from the same place",
"New Ctrl + Super + N keybind for switching through themes on the fly",
"The keyboard layouts now gain extra variants, including new Macintosh and International layouts, among others",
"When the Default Overview is selected for Trigger Overview, it can now be toggled closed from the left hot corner that opened it",
"Night Light's Details button now opens the Display settings it names",
"Increased support for Intel Macs from 2012 to 2015 and added support for T2 Macs from 2018 to 2020",
"Idle suspend is now left off on the legacy NVIDIA driver branches by default",
"The updater will now tell you if the system is mounted read-only when you try to update from inside a snapshot, and how to finish recovery",
"The update helpers will now speak your language, not just English",
"Refreshed translations across all 13 languages",
"Text is sharper on scaled displays",
"General performance improvements"
],
"commits": [
"622c0a71b themes: a key that moves to the next saved theme",
"a9c4d70bb settings: name the page a deep link opens, rather than counting to it",
"27dda2b38 welcome: draw the bar the way the bar draws itself",
"d8c1cf03a welcome: draw the bar the tutorial's reader actually has",
"f5a4f92be bar: rearrange the widgets a fresh install opens with",
"c91dd3d14 background: frosted glass behind the desktop widget cards",
"ff53ba4ee install: remove the previous package name before building",
"ba8d6d07d bluetooth: ask BlueZ to connect the profiles that are missing",
"654c3be2c meta: sponsor links, and a way to translate without git",
"09e265938 apps: every role on the Manage page starts out set",
"04509e7f0 shell: big text is drawn at its size, not magnified from a small one",
"61944a037 mac: the running kernel decides whether the T2 works, not the package set",
"89dc34074 shell: leave the deliberate opt-outs where they were",
"1493712c5 apps: ship Papers and let it open the documents",
"4969b21e4 shell: ask the compositor how the screen is scaled, then draw text for it",
"51d411b43 mac: tell present firmware apart from needed firmware",
"8f078e14c mac: an empty firmware manifest no longer aborts the fetch",
"d4b5eaa7c updatems: the Mac refresh reports what actually happened",
"1b7786da4 mac: what is true about a T2 depends on which kernel booted",
"27569b022 mac: pick the headers that match the kernel actually running",
"0c71ff877 desktop: the wallpaper menu casts a shadow you can see",
"ccb21f9df desktop: switching theme from the wallpaper menu",
"5ebf6caee desktop: the wallpaper menu leads with the wallpaper",
"181f575ad settings: the dock wears the bar's glyph, the other way up",
"ceb52e06d settings: the bar's icon in the sidebar is the right way up",
"2bb087434 desktop: the wallpaper menu opens the bar and the dock too",
"3ba7e5ab1 installer: show what this Mac needs plugged in before it can be installed",
"9479868ae overview: the corner that opened it closes it again",
"6d4b634a3 background: the slide carries a wallpaper its own width, not the screen's",
"91bf70958 background: the clock's settings go away when the clock is off",
"681565c35 mac: bring up the keyboard and trackpad in the live session",
"1ac8de510 update: retry a flatpak download that drops partway through",
"da8eaf094 settings: a Manage page for default apps and what starts at login",
"2890c5b23 update: updatems and updatems-system speak the shell's language too",
"171ef7a27 update: the helper speaks the shell's language",
"195f055cc recovery: name the boot entry that takes the user back",
"127f202d0 update: say plainly when the system is mounted read-only",
"8fb8c3da9 hypr: a floating window stays within reach wherever it is moved",
"0903c4752 mac: the 2012-2015 Broadcom chips, the 2015 MacBook's SPI bus, and existing installs",
"d750a8f6f mac: a T2 Mac fetches its own Wi-Fi and Bluetooth firmware from Apple"
],
"commitsTotal": 66,
"commitNotes": {
"622c0a71b": "Ctrl + Super + N walks the library in the order the Themes page lists it, so\nthe key and the page agree about what comes next, and wraps at the end. It\nsits beside the other keys that step through something, next keyboard layout\non K and cycle panel family on P.\n\napply-theme.sh already records which theme is live, so that marker is what\nsays where we are rather than a second piece of state to keep in step. A\nmarker naming a theme that has since been deleted lands on the first one,\nwhich is where a machine that has never applied one starts too.\n\nOne saved theme has nothing to move to, and switching to itself would still\ncost a full re-apply, so that case says so instead.",
"a9c4d70bb": "The Night Light dialog's Details button opened Decorations. It passed page\nindex 7 and the list has grown since, so Display sits at 9 now. The comment\nbeside it named the index too, which is how the two drifted apart together.\n\nsettings.qml already resolves a page by its component file, which is what\nthat path exists for, so all three dialogs name the file now and none of them\ncan be moved out of alignment by reordering the list again. Night Light also\nnames its section, so the page opens where the button said it would.",
"27dda2b38": "The mockups had drifted from the shipped bar. The groups are what the panels\nare pointing at, and at a third transparent the pills behind them washed into\nthe strip, so a reader saw a row of loose icons where the bar shows discrete\npills. They are filled now, and the five cards that drew their pills by hand\nrather than through PillBg follow the same fill.\n\nThe contents follow too: the utility buttons are the screenshot, the recorder\nand the microphone rather than a colour picker and a keyboard, the weather is\na sun and a Fahrenheit reading, and the clock sits in its own pill showing the\ntime alone. The status group keeps sound and settings. The date a real clock\ncarries needs room the mockup does not have, and spending it there pushed the\nweather into the status icons.\n\nThe workspace strip is narrower so the middle has somewhere to sit, which\nmeans the resting indicator can no longer take its height from the slot's:\nit squares off the slot's width instead, and centres itself in the slot, so\nit stays a circle and keeps a ring showing around the icon riding on it.\n\nThe file drag panel drew neither the tray nor the utility buttons, and its\nclock read a different hour from every other panel. It draws the same bar as\nthe rest now.",
"d8c1cf03a": "Every mockup in the tour showed the window title on the left, the workspaces in\nthe middle and the tray on the right, which is not the bar a new install opens\nwith any more. All five now follow the shipped arrangement: what's playing and\nthe tray on the left, the utility buttons, clock and weather in the middle, the\nstatus icons and the workspaces against the right edge.\n\nThe guided card's steps follow the same order, left to right and then down to\nthe dock. The step for the window title is gone, since the title ships switched\noff and the pointer had nothing to land on, and one for the utility buttons\ntakes its place. Pointer positions were measured against a render rather than\nguessed: two of them had been sitting under the wrong pill.\n\nThe file the drag card carries is steered by the workspace pill's own position\nrather than by a second copy of it, so it lands on the pill instead of on the\nmiddle of a bar the pill no longer sits in.",
"f5a4f92be": "Media and the tray move to the left, workspaces to the end of the right, and\nthe center keeps the clock with the battery, weather and release chip beside\nit. The window title starts switched off, as the active window is already named\nby the workspace it sits in.\n\nOnly a first run and the settings page's \"Reset to defaults\" read this. A\nmachine that has already saved a layout keeps the one it has, so nobody's bar\nrearranges itself under them on update.",
"c91dd3d14": "Ported from pctrade/end4-pC, which built the effect and the per widget\nplumbing. The widgets sit on top of the wallpaper inside one surface, so the\ncompositor has nothing to blur there and the picture has to be sampled here.\n\nThree things differ from the original, all aimed at the low end:\n\nThe effect hangs off a Loader rather than a visible flag. Setting visible false\ndoes stop the per frame work, but it still builds a sampler, a blur, a mask and\na layer for each of eleven cards, and taking a texture reference on the\nwallpaper costs it its own scene graph node. Off now builds nothing at all.\n\nThe wallpaper is grabbed on demand instead of every frame. It is a still\npicture between transitions, so live sampling spent a dozen render target\nswitches per frame redrawing something that had not changed. The grab is\nscheduled from the handlers that can actually move the sampled region,\nincluding the wallpaper's own opacity, which is what carries a crossfade.\n\nThe sampled region follows parallax. mapToItem cannot be bound to, and reading\nonly the widget's position missed the case where the wallpaper slides under a\nwidget that never moves, leaving a stale slice of picture on the card.\n\nSampling targets the wallpaper itself, never its parent: the parent also holds\nthe widget canvas, so a card would end up blurring itself.\n\nClock, visualizer and custom image keep their own shapes and are left alone.\nThe world clock skips its 4x1 mode, whose card is transparent by design, and a\nvideo wallpaper puts nothing in the scene to sample, so the card falls back to\nits plain surface.\n\nThe settings label for the wallpaper effect, and the comments beside it, say a\nwallpaper transitions to the next rather than gives way to it, which is what\neverything else on the subject calls it.",
"ff53ba4ee": "A machine carrying the pre-rename packages could not finish ./setup. pacman\nacts on replaces[] only during a sysupgrade, and the meta-packages arrive by\npacman -U (makepkg -i) and pacman -S (the [mainstream] prebuilt). The old name\ntherefore kept owning its files and mainstream-microtex-git aborted on a file\nconflict at /opt/MicroTeX, stopping the run before quickshell was ever built.\n\nremove-previous-package-name drops the package named in replaces[], reading it\nin a subshell because install-local-pkgbuild sources every PKGBUILD into one\nshell and three of them declare no replaces[] to overwrite a stale value.\n\nBoth install paths call it. The prebuilt branch needs it as much as the source\none: without it, pacman -S fails on the old quickshell files and falls back to\nthe source compile the prebuilt exists to avoid.\n\nDeclaring conflicts[] instead does not work: that prompt defaults to no, so\n--noconfirm answers no and the transaction fails as unresolvable.\n\nillogical-impulse-kde and the old microtex debug split have no counterpart to\nname them, so they join the deprecated list.",
"ba8d6d07d": "A speaker could report itself connected and play nothing, every boot,\nuntil someone knew to toggle it off and on.\n\nbluetoothd runs at boot and reconnects trusted devices before any user\nsession exists to offer it an audio endpoint, so it brings up the\ncontrol channel alone: the device is connected, its buttons work, and\nno sink is ever offered. Nothing retries, because from BlueZ's side\nnothing failed. Traced on a real one, the device object had no\nMediaTransport child at all.\n\nBlueZ already handles this. Its Connect \"connects all profiles the\nremote device supports... if only subset of profiles is already\nconnected it will try to connect currently disconnected ones\", which is\nthis case exactly. The shell hook that used to run at startup called\nbluetoothctl connect and so got that behavior for free.\n\nMoving that into device.connect() lost it, and not visibly: quickshell\nrefuses the call at its own layer when a device is already connected\nand never asks BlueZ, so the guard against connected devices was\ncorrect for the API while the capability itself was gone. The request\ngoes back out through bluetoothctl, and goes out for connected devices\ntoo, since those are the ones with a profile missing. Confirmed against\nthe live speaker: Connection successful, same sink, transport intact.",
"654c3be2c": "GitHub Sponsors covers people who want to give every month. Ko-fi\ncovers the rest: someone who installed this on a Saturday, liked it,\nand would drop a few dollars without opening an account. The two sit in\ntheir own section rather than under Support, which is Discord and docs\nand the issue tracker, not money.\n\nTranslating meant cloning the repository and hand-editing JSON, which\nasks a native speaker to be a git user first. The Crowdin project is\nlinked from the translations line instead, since the people who can fix\nthe Japanese are not necessarily people who open pull requests.",
"09e265938": "Four of the ten roles were never given a default, so each fell to\nwhichever desktop entry happened to claim the type. On a real machine\nthat produced a browser sitting under Email, because a browser declares\nmailto and nothing else did, and Spotify sitting under Music, because a\nstreaming client declares audio types and was being handed local files\nit cannot really play.\n\nThe setup run now names a browser, a file manager and an audio player\nalongside the video, text, image and document ones it already named.\nThe file manager was the odd one out in the other direction: the\ninstalled system had always set it and the setup run never had.\n\nNothing here overrides a later choice. It only decides what a machine\nstarts with, which is the difference between a considered default and\nwhatever registered first.",
"04509e7f0": "The screen-scale rule was right for interface text and backwards for\ndisplay text. Qt rasterizes its distance-field atlas once at a fixed\nsize near 54 px and magnifies that one picture for anything larger, so\nthe desktop clock at its default 90, the lock screen's at 133, and the\n700 the slider allows were all enlargements of a small rasterization,\nwith the strokes blooming and the corners rounding off. They were drawn\nat their real size before this.\n\nSize now decides first, and the screen only decides below it. Above 40\npx the pixel-grid hinting that chews a 14 px label is invisible, and\neverything the interface itself is set in sits between 10 and 23, so\nthe two cases separate cleanly. The clock, the lock clock, the weather\ntemperature and the calendar numerals all come back.\n\nThe dock's window-count badge goes the other way and is pinned to\ndistance-field glyphs: it rides the hover magnification, and the icons\nbeside it escape that by rasterizing at the hover peak, which a glyph\ncannot do.",
"61944a037": "Whether the built-in keyboard and trackpad work is a fact about the\nkernel that booted, and this asked which kernels are installed. A\nMacBook that has the T2 kernel but was started from an entry for\nanother one was told its input works, which is the one situation where\nit does not.\n\nIt reads the running kernel's own modules now. An installer chroot is\nthe case that made the package set look like the right answer: uname\nthere names the kernel of the image doing the installing, whose modules\nare outside that root, so their absence is the signal to ask pacman\ninstead. Both branches exercised: a running T2 kernel, a running stock\nkernel with linux-t2 installed, and a chroot with and without it.",
"89dc34074": "The sweep that pointed every render type at the screen scale went too\nwide. Four sites were already asking for distance-field glyphs on\npurpose, and turning them into the general rule flips them to hinted\nones on any screen at a whole-number scale, which is the mode each was\nwritten to avoid.\n\nTwo are the Material text fields, where hinting was rejected upstream\nbecause it makes small placeholder text look wrong, and that holds at\nevery scale. The other two are drawn under a transform: the screen\ntranslator's annotations are rotated to sit along the text they cover,\nand the on-screen display's icon turns through half a circle while it\ngrows. A glyph hinted onto whole pixels and then rotated is pixelated\nwhatever the screen is doing, so neither has a scale to be conditional\non.\n\nThe on-screen display icon is also the one MaterialSymbol in the tree\nthat sets its own render type, so the sweep had quietly undone the pin\nthat keeps icons off the distance field. It keeps its own answer, which\nis the opposite one, for its own reason.",
"1493712c5": "Nothing installed opened a PDF, so the type fell to the browser, which\nclaims it when no one else does. Papers joins the default apps beside\nLoupe, in the one manifest both installs read, and the setup run points\nthe document types at it the way it already does for images and video.\n\nThe Manage page moves the same family when someone picks a different\nviewer, rather than switching PDFs and leaving comics and DjVu behind on\nthe app they just replaced.",
"4969b21e4": "Hinting a glyph onto a whole-pixel grid is the sharper choice when the\nscreen is drawn at a whole number, and the wrong one when it is not: on\na 150% screen Hyprland asks the client for a whole-number surface and\nresamples the result down itself, so the hinted stems land between\npixels and letters built from straight strokes come out chewed. A\ncapital T shows it worst, having two long strokes and nothing to hide\nthem behind.\n\nEvery earlier attempt to condition this failed for the same reason, and\nit is not the one it looked like. Qt cannot see a fractional scale at\nall: a window on this 150% screen is told its device pixel ratio is 2,\nand in a standalone window the Quickshell one reads as null. So each\ntest read as whole and quietly chose the mode it was written to avoid,\nwhich left only the choice between chewed text here and softer text\neverywhere else. Hyprland does know, and says so, which is the same\nreason settings.qml already reaches for it instead of Screen.\n\nScreenScale asks it and hands out the answer per monitor, so a screen\nat 100% or 200% keeps the sharper mode it had and only a fractional one\nchanges. It answers 0 until the compositor replies, a moment after a\nprocess starts, and the distance-field mode covers that gap since it is\nnever much the worse of the two. Verified in a standalone window: the\nscale arrives as 1.5 and the binding does re-evaluate through the call,\nwhich is the trap DesktopEntries.byId is on record for.\n\nEvery site that set the mode by hand now asks the same question, text\nfields among them, so nothing renders a different way from the text\nbeside it.",
"51d411b43": "The chip test answers whether this machine needs Apple's firmware,\nwhich is not the same as whether it has it. An image built with the\nfirmware baked in, and every machine that has already fetched it, were\nstill being told the firmware is missing. A second predicate asks the\nreal question, against the same filenames the fetch script checks.",
"8f078e14c": "grep -c prints 0 and still exits 1 when nothing matches, so the guard\nafter it appended a second 0 and the running total became arithmetic on\ntwo lines. The script runs under set -e, so that killed it at the point\nwhere the firmware had already been unpacked but the drivers had not\nbeen reloaded and the timer that would try again had not been stopped:\na machine left with its Wi-Fi firmware on disk, no Wi-Fi, and a fetch\nreporting failure. A count that is not a number is now read as none.",
"d4b5eaa7c": "The firmware fetcher's Python modules install into a directory that\ndoes not exist yet on a machine that never had them, and install\nwithout -D does not make one, so every one of those copies failed on\nexactly the machines the refresh was there to repair. It makes the\ndirectory now, for every file this script installs.\n\nAnd two pacman calls were piped into sed for indentation, which hands\nthe pipeline sed's status rather than pacman's. sed succeeds on any\ninput, so a driver install that came back empty handed was reported as\na driver that is now present, and a missing 7zip never raised a word.\nBoth read PIPESTATUS, the way the keyring step above already does.",
"1b7786da4": "The report told every T2 machine its keyboard, trackpad, audio and fan\ncontrol do not work, which stopped being true on the MacBook edition\nthe moment that edition started shipping the kernel that drives them.\nIt asks now, through a predicate that answers from the installed kernel\npackage and falls back to whether the bridge driver is in the running\nkernel's module tree, so a hand-installed linux-t2 counts too.",
"27569b022": "The Broadcom driver for the 2012 to 2015 cards is built on the machine,\nagainst kernel headers chosen by name. The name was matched against\nArch's kernels and its three usual variants, so on an image that boots\nlinux-t2 nothing matched, it fell back to Arch's headers, and the\ndriver would have been built for a kernel that is not running: no Wi-Fi\non exactly the machines that path exists for.\n\nIt knows that kernel now, and there is a way to ask whether the running\nkernel is the stock one, so the caller can tell when the headers staged\non an image are the wrong ones to reach for.",
"0c71ff877": "The menu already had one, at the shell's shared strength, which is\nfaint enough to disappear against a bright wallpaper. It is the one\nsurface here with nothing behind it, so it carries a deeper one.\n\nDeeper rather than wider on purpose: the card keeps one elevation\nmargin from the screen edges, so a larger blur would be cut off exactly\nwhere a menu opened near an edge needs it.",
"ccb21f9df": "A theme is the one thing in this menu that changes everything else in\nit at once, so it sits alone at the end behind its own rule rather than\namong the rows it would rewrite.\n\nIt carries the icon the Settings sidebar gives the page, as the rest of\nthe menu does.",
"5ebf6caee": "A menu opened on the desktop was answering about the bar first. The two\nrows for the desktop itself now come first and together, which also\nputs Change Wallpaper back beside Personalize Desktop after the two new\nrows had been inserted between them. Then the pair of strips along the\nscreen's edges, then the monitor, which is the least desktop thing in\nthe list.\n\nThe rule between the groups is the one the dock's menu has always\ndrawn. It lived inside that file as a private component, so it moved\nout to the shared widgets where both menus can reach it, which is what\nthe desktop menu's own comment said it wanted when it was written.",
"181f575ad": "The two entries were different pictures that happened to look alike:\nboth a rectangle with a line near the bottom, so the one for the strip\nat the top of the screen and the one for the strip at the bottom read\nas the same thing.\n\nThey are one glyph now, turned for the bar and left alone for the dock,\nso the only difference between them is the edge the strip sits on,\nwhich is the only difference between the two things themselves. Both\nplaces that show them, the Settings sidebar and the wallpaper menu, use\nthe same pair.",
"ceb52e06d": "The Bar row asked to be turned over and had been drawn the other way up\nsince the day it was written. The page list carried iconRotation, the\nrow that draws it never read one, and the delegate never passed one\nalong, so the glyph sat with its strip at the bottom: the shape of a\ndock, on the entry for the bar, on a bar that sits at the top.\n\nThe shared navigation button has carried this for its own rows all\nalong. This one now has it too, so the two entries read as a pair\nrather than as the same picture twice.",
"2bb087434": "The menu already reached the background and the displays, which meant\nthe two things people change most often, the bar and the dock, were the\ntwo it did not. Both now sit at the top of it.\n\nThey carry the icons the Settings sidebar gives those pages, so the row\nand the page it opens read as the same thing.",
"3ba7e5ab1": "The live session writes a line for the Macs whose built-in keyboard,\ntrackpad or Wi-Fi cannot work there, and nothing read it. The one place\nthat sentence could reach the person it is about is the window they are\nalready looking at, so the installer reads it and puts it above\neverything else.\n\nThe file is read in onLoaded rather than bound, because text() is a call\nand a binding through it would be evaluated once, while the file is\nstill loading, and never again.",
"9479868ae": "The hot corner had a close path for the built-in overview and could\nnever run it. While the overview is up the pointer stops reaching the\ncorner's surface underneath, so the corner never sees anyone come back:\nit could open the overview and then had no way to shut it. The corner\nwas stacked above the overview and it made no difference, so this is\nnot about which surface is on top.\n\nThe overview answers in the same rectangle instead, on the same dwell,\nfiring the same ripple, so the gesture looks identical in both\ndirections. Only while the corner is what opens this overview, so\nnothing changes for anyone whose corner does something else.\n\nTwo things had to be right for it to feel like a toggle rather than a\ntrick.\n\nIt arms on where the pointer already is, not after a wait. The overview\nappears once the ripple has played, by which time the pointer has\nusually left the corner, so a rule that armed on the way out spent the\nfirst return doing nothing and the second closing it. Held in the\ncorner is the one case that waits, because that is where the pointer is\nwhen the corner just opened this.\n\nAnd the corner itself now wants a real exit before it opens anything\nagain, where it used to want only a pause. After a close the pointer is\nstill in the corner, so once the pause ran out the next twitch read as\na fresh approach and reopened what had just been closed. Coming back\nhas to mean coming back.\n\nThe rectangle both of them answer in is one measurement now, not two.",
"6d4b634a3": "Slide ended with a jarring step: a column of the outgoing wallpaper\nchanged to the incoming one all at once, after the movement had already\nread as finished.\n\nThe wallpaper panning needs a picture wider than the screen, so both\npictures are scaled up by at least a tenth and sit at a negative x. The\nslide moved each of them by one screen width, which is less than that,\nso the leaving picture stopped with a strip of itself still on screen\nand clearing it at the end of the run swapped that strip in a single\nframe. On a 2880 wide screen with the stock wallpaper it was 144 px.\n\nBoth pictures now travel the same distance, taken from the picture\nrather than the screen, so the leaving one reaches the edge exactly as\nthe arriving one lands and there is nothing left to clear. The distance\nis right in the other direction too, for a picture smaller than the\nscreen, which the panning centers instead of overhanging.\n\nThe width of the strip fell as the panning moved through the\nworkspaces, so this was worst on the first workspace and already clean\non the last, which is why it could look intermittent. The preview in\nSettings never showed it: its pictures are exactly stage sized, so\nthere its travel and its width were already the same number.",
"91bf70958": "Switching the desktop clock off left its whole panel behind: where to\nput it, when to show it, which style to wear, and a page of settings for\neach of the three styles, all still on offer for a widget that is not on\nthe screen. The switch now takes them with it, the way the widgets\nfurther down the page already treat theirs.\n\nThe style panels needed no gate of their own. They ask stylePresent()\nwhich style is in play, and a clock that is switched off is wearing\nnone, so answering that one question covers all of them.\n\nThe pixel panel was reading the style setting itself rather than asking,\nso it missed the answer, and it had also missed the rule the other two\nfollow about a style that only appears on the lock screen. It asks now.",
"681565c35": "Every line we had for the SPI keyboard and trackpad of a 2015 to 2017\nRetina Mac ran from post-install-boot, inside the installed system, and\nput the modules in the initramfs. The live image the installer runs\nfrom had none of it. So a machine whose only pointer is its trackpad\ncould arrive at the installer unable to use it, which reads as a broken\nimage rather than as a driver that did not come up.\n\nThe session now loads that stack itself, and reloads the driver once if\nthe trackpad is still missing five seconds later, which is the shape of\nits known probe failure: the keyboard arrives and the trackpad does\nnot. A machine whose trackpad did appear is left alone, because\nreloading a working input device under someone's fingers is worse than\ndoing nothing.\n\nThe function key setting was wrong in both directions. fnmode 2 is\nfkeysfirst, which puts brightness and volume behind fn, the opposite of\nwhat the keycaps read, what macOS does, and what our own documentation\npromises. And on exactly the models above it never applied at all,\nbecause their keyboard is applespi and hid_apple never binds it. Both\ndrivers now carry the same setting, and it is the one that was meant.",
"1ac8de510": "A user's update ended red with \"Update finished with exit code 1\" over\none interrupted transfer. flatpak had fetched Flathub's index, lost the\nper-architecture summary mid-body, and stopped. Everything else in the\nrun had succeeded and nothing on the machine was wrong.\n\nflatpak does not retry that case. It files a connection reset under an\nerror code its own retry list does not carry, so its five attempts\ncollapse to one and a single dropped download ends the run. libostree\nfixed the same bug in its fetcher in 2023; flatpak reads these files\nwith its own client, so that fix never reaches them.\n\nEach half of the step now runs again after a pause if it did not\nfinish, the way the pacman step above it already does. A connect or\nresolve failure is the exception: flatpak really does spend five\nattempts on those, so a sixth would only make a dead network take\nlonger to say so.\n\nWhat is read out of the output decides whether to try again and nothing\nelse. flatpak prints a fetch error as a passing warning when the app it\nbelongs to is already installed, so a run can carry that line and still\nfail for a real reason, and a step that reported on the text rather\nthan the exit code would call a full disk a network problem. The code\nthis step reports is still the update's own.\n\nThe two halves are also named now. They are separate processes writing\nto one stream, so an error from the first landing beside \"Nothing to\nupdate.\" from the second read as a single run contradicting itself,\nwhich is how the report described it.",
"da8eaf094": "Changing which app opens a file meant knowing that a browser is\nx-scheme-handler/https and that a file manager is inode/directory, and\nthe keys were a separate matter again: SUPER + B ran the first browser\noff a list in the config whatever the rest of the desktop had been told\nto use. Manage puts both under names people use.\n\nDefault Apps lists ten jobs, each with the app that does it. Picking one\nwrites every file type that job covers at once, so a photo and a\nscreenshot open in the same viewer, and for the five jobs a key\nlaunches it writes the app that key opens too. The offered apps are the\nones that can do the job, the ones that say they are that kind of app\nahead of the ones that merely open the files, with the app in place\nfirst and every app on the machine one switch away.\n\nAuto Start Apps lists what opens on its own, with a switch\nbeside each and a field to add another. Turning off something the\nsystem starts puts a stub in your own autostart directory in front of\nit, which is the whole of what can be done without root, and an app\nadded here can be dropped again.\n\nscripts/apps/default-apps.sh and scripts/apps/autostart.sh hold the\nmapping and do the writing, so the roles are described in one place\nrather than split between the shell and the config. variables.lua reads\nthe chosen app for a key on every reload and falls back to the list it\nalways had, so a machine where nobody has picked anything behaves\nexactly as before.",
"2890c5b23": "The two scripts the helper runs after the packages print into the same\npane, so their messages go through the same lookup now, values through\nprintf, the file labels included. Which language: updatems reads the\ncaller's own setting; updatems-system runs as root and takes the owner of\nthe clone it works from, which names the user whether they came through\nsudo or pkexec. The helper forces a locale for pacman's own messages and\nthe scripts it starts inherit that, so it hands the language it settled on\ndown to them beside it.\n\nThe lookup itself grew careful where the review found it credulous: a\ntranslation is only a printf format if it kept the key's placeholders and\ngained no other percent sign, since the shell's own strings use %1 and\ntranslators write 100%; only a string counts as a translation, as in the\nshell; a root script with no caller reads the home that holds the shell\nfor its language as well as its files; and nothing in it trips set -u\nor an unknown user. The summary table's labels and the two literal\nvalues it prints are translated, as are the stash reminders and the\nplaceholder for no tag.\n\nThe checker skips comments and variables, and a workflow runs it on every\nchange to these scripts or the source file, so a message can no longer\nship without its key.",
"171ef7a27": "The update pane translates its own labels and then streams the helper's\noutput into it in English, since a shell script had no way to reach the\nshell's translations. It has one now: a small library that picks the\nlanguage the way the shell does, the language.ui setting or the caller's\nlocale, and looks each message up in the same JSON files, the shipped\ncopy and the generated one, keyed by the English text and falling back\nto it. The helper sources it before forcing the locale it needs for\npacman's own messages, so that choice never leaks into what the user\nreads, and every message it prints goes through the lookup; the ones\nthat carry values go through printf.\n\nA message only reaches translators once it is in en_US.json, so a small\nchecker lists the script strings that are not there yet and, asked to,\nappends them to every locale in English the way new shell strings ride\nin. The helper's forty are in. The library travels with the helper on\nboth install paths and on update.",
"195f055cc": "The last step told the user to use the normal \"linux\" boot entry, and\nmost people cannot picture which one that is. It now says where it sits,\nright under the Mainstream OS list, in a line that still fits the box:\nthe sixth step is the longest the box takes, and this one is shorter.",
"127f202d0": "A root filesystem mounted read-only failed every step in its own words,\nwhich read like four separate faults: the sudoers rule could not be\nwritten, pacman could not take its lock, flatpak warned on every ref it\nholds, and the dotfiles were deferred behind a package failure. It is one\ncondition with one of two causes, a machine started from a snapshot in the\nboot menu, which is read-only by design, or a filesystem the kernel took\nread-only after an error. The helper now checks the root mount first and\nsays which it is and what to do: the normal boot entry or Restore Snapshot\nin the first case, a reboot and a look at the disk in the second.",
"8fb8c3da9": "The rule that opened a floating window where its title bar can be reached\nonly ran at open. A float moved to a workspace on another monitor, or\ndropped on one by an overview, could still land with that bar beneath the\npanel, and a window taller than the room arrived with its bar under the\ntop bar when it turned floating on the way. The same check now runs after\nevery workspace move, one turn of the loop later, since the move is\nannounced before the window is put in its new place.\n\nThe launcher's overview spread a drop over the whole screen while its\nthumbnail shows the screen less what the panels reserve, so a window\ndropped near the top sat under the bar. The drop now maps into that room\nand asks the config for the move to make, so the title bar stays within\nreach and the answer holds on any monitor.\n\nThe room itself was measured in physical pixels on the far edges, since a\nmonitor's width and height arrive that way while positions and reserved\nedges are logical, so on a scaled monitor nothing ever held a window\nshort of the right or bottom edge. It is measured in logical pixels now.",
"0903c4752": "The BCM4360 in the 2013-2015 Macs and the BCM4331 in the 2012 ones are\nthe two chips brcmfmac never drove, so those machines had no Wi-Fi at all;\nArch carries Broadcom's out-of-tree wl driver as a DKMS package that\nblacklists the in-tree drivers itself, and the library now names the set\nit needs for the kernel in use, which the setup installs on sight of the\nchip.\n\nThe 2015 12-inch MacBook reaches its keyboard through a plain PCI SPI\ncontroller rather than Intel's LPSS bridge like every later model, so it\ngets its own module list for the early boot image.\n\nThe Mac quirks ran at install time only, so a Mac installed before one\nexisted never gained the firmware fetch, the wl driver, or the SPI and\nNVMe fixes. The system update applies the same library now, a no-op on\nanything that is not a Mac, and leaves the initramfs to the next kernel\nupdate as the rest of that script does.",
"d750a8f6f": "The Broadcom chips Apple paired with the T2 run on firmware that is not in\nlinux-firmware and may not be redistributed, so nothing ships here. The\ndrivers themselves are in the stock kernel, so what a T2 Mac lacks is the\nfiles alone, and it can get those from Apple for itself: it asks Apple's\nrecovery service for its own recovery image, the same one Internet Recovery\nwould fetch, verifies it against Apple's chunklist, opens it in place and\nlifts the firmware out under the names brcmfmac and hci_bcm4377 look for.\n\nThe fetch runs once from the setup when there is a network, and a timer\nkeeps asking on every boot until the files land, since a T2 Mac has no\nWi-Fi to fetch them over until then; the script stops the timer once they\nare in place. The recovery download comes through OpenCore's macrecovery\nand the renaming through the Asahi installer's firmware modules, both\ncarried under sdata/mac with their notices; Bluetooth's entry point had to\nimport FWPackage from its core module, as the Wi-Fi one does."
},
"merges": 1
},
{
"version": "2.1.0",
"date": "2026-09-04",
"kind": "feature",
"summary": "The big customization release, delivered smoothly: shape the bar, dock and title bars from Settings, with color and transparency throughout, and a fix for doubled title bar buttons after updating",
"changes": [
"Fixed doubled title bar buttons after updating from 1.4.2",
"Update notifications now describe every release you are behind, not just the newest one",
"A new edge channel for early adopters: one switch in the update panel's advanced options, and fixes and new features reach you as they land, before they are released",
"Refreshed translations across all 13 languages, and Hebrew translations are restored under their correct locale"
],
"commits": [
"6acec31e1 release notes for 2.1.0, carrying the 2.0.0 list forward",
"ae6a159a5 crowdin: document which tool owns the sync",
"f60eac82f i18n: sync from Crowdin, and give Hebrew its real locale code",
"855b70327 crowdin: a configuration the CLI can actually run",
"d0b1c7b60 update: say what to do when a file conflict cannot be adopted",
"a72f00f3a exp-update: clear a hold a killed run could have left behind",
"a22d532c1 exp-update: let nothing reload while the files are being replaced",
"3f80702f5 updates: an edge channel, for the work that is pushed but not released",
"8ef91fb28 updates: describe the release a machine is behind, not just the newest",
"881f52a78 migrations: run them on the update path too, not just on install"
],
"commitNotes": {
"6acec31e1": "A machine on 1.4.2 that updates now lands on 2.1.0 without ever having\nbeen shown what 2.0.0 brought, so the 2.1.0 entry repeats the 2.0.0 list\nunder a note saying why it is there. The next release drops the repeat.\n\nThe migration keeps every backup it makes instead of overwriting the\nprevious one when a restored file sends it around a second time.",
"ae6a159a5": "The GitHub integration uploads new source strings and returns translations as\na pull request, so the CLI is only there for pushing strings up ahead of the\nnext scheduled run. Pulling with `crowdin download` stays out of the workflow,\nbecause running it in the same cycle as merging the integration's PR means\nwhichever one finished last wins.\n\nAlso drops a `branch` key that read as load-bearing but is not a configuration\noption at all. The branch comes from -b on the command line, and without it an\nupload lands at the project root and registers the source file a second time.\nThe name changes whenever the integration is rebuilt, so read it with\n`crowdin branch list` rather than writing it down here.",
"f60eac82f": "Hebrew was kept as he_HE, which is not a locale: the language is he and\nthe country is IL. The 488 translations under that name had never reached\nCrowdin, whose own export produces he_IL, so they were invisible to the\nproject and would have been overwritten by the first sync. They are in\nhe_IL.json now, uploaded, and they survive the round trip.\n\nEverything else is Crowdin's current state, which retires strings the\nsource no longer has and keeps the 87 added since the last upload. 6,791\ntranslations across thirteen locales, every file at 1994 keys.",
"855b70327": "The file was missing an api_token, so the CLI refused to run against it.\nIt is read from the environment now, so nothing secret is committed.\n\nIt was also missing preserve_hierarchy, which defaults to false and\nflattens an upload to the project root. That is how the same source came\nto be registered twice, once at its real path and once at the root, with\ntranslators entering every string in both. Reported by Renato while\nworking on pt-BR.\n\nThe sources sit in a Crowdin branch rather than at the project root, and\nsaying so is what stops the CLI reporting every file as omitted for want\nof a matching source. A Crowdin branch is its own copy of the strings and\nhas nothing to do with the git branch sharing its name.",
"d0b1c7b60": "A package wanting a path that is already on disk and owned by nobody is\nusually software installed by its own vendor script before it was\npackaged here. Adoption is refused in that case on purpose, because\ntaking the path over unasked could throw away whatever is inside it, but\nthe refusal read as the update failing for no stated reason.\n\nThe path, the package that wants it, and the command that resolves it are\nnow printed, built from the conflicts pacman reported so the next one is\nhandled without changing anything here. Adoption itself is unchanged.",
"a72f00f3a": "The holds outlive the script that took them, and a run killed outright\nnever releases. Clearing both before taking them costs nothing when there\nis nothing to clear, and spares the next person a session where their\nsettings quietly stop applying.",
"a22d532c1": "An update rewrites the files the running system is built from, one at a\ntime, and everything watching them reacts to each write. Hyprland reloaded\npart-way through and put a screen of config errors in front of someone who\nhad done nothing wrong. The shell came back assembled from a tree that was\nhalf old and half new: panels showing the wrong things, and the controls\nthat would have put it right were the ones that had broken, so the way out\nwas a terminal or the power button.\n\nBoth are held for the copying and released afterwards, each with one\nreload of the finished tree. Releasing always reloads, so held is never\nsomewhere either can be left, and one trap releases both however the run\nends. A shell without the handler refuses the hold and the update carries\non as before.",
"3f80702f5": "A fix pushed after a release cannot reach the people who hit the bug\nuntil the next tag, and a release cannot be watched landing on a real\nmachine without tagging one to find out what it does. Both are the same\ngap: there is no way to follow the branch.\n\nEdge follows origin instead of the newest tag. The applied marker carries\nthe commit, so a later push reads as newer the way a new tag would, and\nturning it off returns the machine to the newest release through the same\ncomparison as always. Nothing about the release path changes.\n\nThe switch sits last in Settings > Update > Advanced, beside the AUR one,\nwhere the levers that want a deliberate hunt already live. That row was\nhidden without an AUR helper, which is most machines, so the condition\nmoves to the AUR switch and the row stays.",
"8ef91fb28": "The notification read the newest release and nothing else, so a patch\nlanding days after a feature release was the entire story anyone still on\nthe older version would ever see of it. The release it fixed reached them\nas a line counting how many they had missed.\n\nIt now describes whichever pending release carries the span: security\nfirst because that is the reason to act, then the feature release, then a\npatch when that is genuinely all there is. The newest version is named\nalongside the count so a later fix does not look left out.",
"881f52a78": "The repair that retires the plugin machinery from a machine's own\ncustom/general.lua sat in the install path alone: 3.files.sh is sourced\nunder install, and the update path never referenced it at all. custom/ is\ndeliberately kept across updates, so everyone who updated held on to the\nold copy and the shipped hyprland/plugins.lua was loaded beside a second,\nolder version of the same machinery, its own plugin loading and ABI guard\nincluded.\n\nIt moves to a shared file that install and exp-update both call, because\na repair kept in one path reaches only the people who did not need it.\nThe behaviour is unchanged: a file without the old machinery is left\nalone, and one that has it is moved aside rather than deleted."
}
},
{
"version": "2.0.0",
"date": "2026-09-03",
"kind": "feature",
"summary": "Shape the bar, dock and title bars from Settings: four bar styles, a dock with a settings page of its own, and color and transparency throughout, all carried by your themes",
"changes": [
"Both the Bar and the Dock have gained new styles",
"The Bar gains a new \"Notch\" style to live alongside its \"Hug\", \"Float\" and \"Rect\" styles",
"You can now change the Bar and bar widgets Shape, Transparency, Color, and Bar Width",
"Any floating bar style can be set to be three strips instead of one",
"The Dock now graduates to its own settings menu",
"The Dock gains both \"Rect\" and \"Notch\" to live alongside its original \"Float\" style",
"The new Dock menu also allows you to customize the Dock's Size, Shape, Transparency, and Color",
"Change how the Dock behaves, not just how it looks",
"Set how big an icon grows on hover, and what hovering does: \"Magnify\" or \"Glow\"",
"Choose how open windows are marked: \"Dashes\", \"Dots\" or a \"Count badge\"",
"Choose the animation that plays when you click an app",
"Turn the buttons on either side of the Dock on or off",
"You can now customize the Title Bar Color and Transparency",
"Themes now, in addition to what they previously carried, carry all of the new settings added in 2.0.0; all Bar, Dock, and Title Bar customizing including Style, Color, Transparency, Shape, and all new behavioral customizations. HAVE FUN!",
"You can now position monitors in the Display settings by dragging them, or by changing them precisely with the new display offset settings",
"You can now customize your keybinds and search through them in plain words instead of the commands behind them",
"The Float layout is now treated like a first class citizen and got real attention this release, now with a much better workflow",
"Sign in to Claude or ChatGPT directly from the sidebar using an existing subscription, or get guided through getting started with local, free AI (Ollama) with a simple, easy to follow first time setup",
"Sidebars open from the side your vertical bar is on",
"Set wallpapers directly from Files",
"Weather's temperature settings (Fahrenheit, Celsius) now respond to your location and are set automatically",
"Night light's \"automatic\" now uses your actual sunrise and sunset",
"Plug in a USB printer and print, with no driver to hunt down and nothing to set up",
"Update handling is even more careful, and the Recovery settings page can now repair the package keys",
"The brightness device can be chosen on the Services page in Settings (thanks @M7MMAD-OMAR)",
"The right sidebar automatically fits short displays (thanks @Nitin Dixit)",
"Want to tune your graphics card? LACT is one checkbox away during install",
"Improved Firewall setup, and we are now even more transparent with our new Firewall page on our website listed under the Security section: https://mainstreamos.org/#firewall",
"Made it easier to set up Remote access. Check out our new Remote Access page on our website at https://mainstreamos.org/#remote-access",
"Increased the ISO's hardware compatibility across the board, including adding Ventoy support",
"Improved multilingual installations. Pick your language and your keyboard layout and it will be set up for you at the login screen and inside the OS automatically",
"Now including Chinese, Japanese and Korean fonts when you choose Chinese, Japanese or Korean",
"Chinese, Japanese or Korean language input is set up and installed automatically when you choose that language, allowing you to type your own language from the first boot",
"Focus master moves from SUPER + M to Ctrl+Super+Space so it does not clash with the media playback keybind",
"Wallpapers fill a rotated monitor instead of leaving it short",
"Num Lock is preserved across sessions (thanks @jussiemion)",
"The tray's overflow keeps focus after its menu closes (thanks @jussiemion)",
"The login screen now keeps the keyboard layout you chose"
],
"commits": [
"c4a4f4a49 release: 2.0.0, Shape the bar, dock and title bars from Settings: four bar styles, a dock with a settings page of its own, and color and transparency throughout, all carried by your themes",
"c8eb45509 hyprbars: stop a reload adding a second set of buttons",
"528b0f59f readme: add the 2.0.0 features to the feature list",
"282a4d67f ai: install a plan CLI without leaving the sidebar",
"80d9decaa settings: hide the title bar reset once there is nothing to put back",
"c29dd14c6 i18n: carry the new AI setup and reset strings into every locale",
"3930c9dd1 ai: fix the Codex plan never installing on a fresh machine",
"8a0be6b7c sddm: name the greeter setup failure",
"66370e09a limine: recognize our boot entry when it is not named after the machine-id",
"0cbf59b59 setup: stop retrying forever when there is nobody to answer",
"53cb25316 keyring: prove the key is trusted before the repo switch becomes one way",
"044d16b4e mac: shared Apple hardware library, applied on both install paths",
"6d6eb294c packaging: declare the printing dependencies on mainstream-system",
"4eebec7eb keyboard: picking an input source sets it up even on a bare machine",
"601c85c6d cursor: a killed shake watcher stops disabling the hot corner",
"6d31fb3c8 setup: the overrides file is restored from the repo, checked",
"253c8b503 hypr: an unset HOME no longer takes the config with it",
"083f1bbe6 titlebars: one stock opacity, agreed on by both sides",
"5d33f469c weather: a partly cloudy sky stops reporting as overcast",
"7fa9f0669 settings: the title bar colour waits for the drag to settle",
"041831434 hypr: a window rule stops reading an error as a number",
"cb680338e ai: a plan shows its models only while it is the one in use",
"86ee6977c update: the system refresh runs the release it is applying",
"7fc7f68c3 printing: the queue maker ships with the package that brings ipp-usb",
"587abb49b sddm: the greeter reads the keyboard layout instead of being told it",
"022d53384 themes: the title bar's stock value agrees with the code",
"2176459f6 settings: a saved choice survives a slow start and a theme apply",
"e3aa62837 hypr: plugin loading stays behind its version guard",
"0bee96a16 update: an upgrade stops overwriting what the machine owns",
"fa5a795b4 update: an existing machine gets the new printing experience",
"b161233f1 settings: the title bar opacity slider works before any color",
"5f99ab4a8 settings: keybinds speak plain words instead of commands",
"bfc9ad979 ai: a ChatGPT plan joins the sidebar the way the Claude one did",
"1f8c6461f printing: the queue maker triggers the way ipp-usb does",
"b7ce239fa ai: the model picker stops cutting the list at ten",
"0a8078285 printing: a permanent queue instead of a browser daemon",
"c22010777 ai: a Claude plan signs in from the sidebar and brings five models",
"7968575ab settings: the title bar color hint waits to be asked",
"955e00c63 dock: a restored Chromium folds back into its pin",
"f9f7772da settings: title bars get their way back"
],
"commitsTotal": 152,
"commitNotes": {
"c8eb45509": "add_button appends and the plugin cannot be asked what it already holds,\nso every reload of this file added another close, maximize and special\nbutton to every bar. A theme apply and the title bar switch both reload,\nso the row grew each time one was used.\n\nThe mark goes on the plugin's own table, which is what makes its life\nmatch the buttons': a config reload leaves both in place, and an unload\nand load of the plugin clears both together, so the buttons come back\nwith it.\n\nA table that will not take the mark reads as unmarked every time, and the\nbuttons are added again rather than skipped. Two of each is untidy; none\nat all leaves a window with no way to close it.",
"528b0f59f": "The bar and dock styles and their settings, title bar colors, the AI\nsidebar, the language and font work and the firewall page were missing\nfrom the list people read first.",
"282a4d67f": "Signing in to the ChatGPT plan opened a terminal that reported codex as\nan unknown command. The install step reported success while installing\nnothing: it was a pipeline, so the shell returned the status of the\nreading end, which succeeds on empty input, and the flow went on to open\na sign-in window for a command that was never there.\n\nAn install script now ends on the command it was meant to provide, so a\nzero means the tool is really present and every way an install can fall\nshort is caught, not just that one. The download also retries itself,\nincluding on the error a failing CDN actually returns, which curl does\nnot retry by default.\n\nWhen the download will not come at all, the same CLI is in the system\nrepositories and one prompt installs it from the banner. The helper\nbehind that prompt takes a name from a list it does not get to extend,\nnever a package name, because a root helper that installs whatever\nstring it is handed is a way to install anything as root, and it checks\nthe co",
"80d9decaa": "The control sat there at stock settings too, where pressing it changes\nnothing, and a button that does nothing reads as one that is broken. It\nnow shows only while the colour or the opacity differs from what the\nbars ship with, and the row goes with it so no gap is left behind.\n\nThe stock opacity had been written twice, in the reset and in the check\nagainst it, which is the kind of pair that drifts. It is named once now.\n\nThe comparison carries a tolerance because the value makes a round trip\nthrough a file as text and the slider quantises to whole percents, so an\nexact match would not survive either.",
"c29dd14c6": "Placeholders in the source language, the way an untranslated string reads\nin these files, so the walkthrough and the title bar reset are not blank\nfor anyone before the translations land.",
"3930c9dd1": "Signing in to the ChatGPT plan opened a terminal that reported codex as\nan unknown command. The install step pointed at a download that returns\n404, and because it was a pipeline the shell reported the status of the\nreading end, which succeeds on empty input. A zero came back, the CLI was\nrecorded as installed, and the sign-in window opened for a command that\nwas never there. It only looked healthy on machines that already had\ncodex from somewhere else.\n\nCodex has no rootless installer of its own, so it now comes from the\npackage manager the way local AI already does, and the sidebar names the\ncommand rather than pulling 290 MB behind a prompt it cannot explain.\n\nThe trust in the exit status is fixed separately, since that is what hid\nthis: an install script now ends on the command it was supposed to\nprovide, so a zero means the tool is present rather than that something\nin the pipeline was willing to exit quietly.",
"8a0be6b7c": "The visual installer retries a failed helper from its exit status, but the\nSDDM helper gave it no source location. The install log therefore ended at\n\"Configuring the SDDM Wayland greeter\" with only rc=1 to explain it.\n\nThe helper now prints its failed command and line number before it exits.\nThe keyboard-layout probes also treat an absent layout as an empty value,\nwhich is normal on a fresh system and must not trip set -e with pipefail.",
"66370e09a": "The gate before removing the previous bootloader looked for a machine-id\ncomment in limine.conf. limine-entry-tool writes that comment only while\nit names unified kernel images after the machine-id, and post-install\nsets CUSTOM_UKI_NAME, so on our own installs the images are named\nmainstream_* and no machine-id is ever written. The gate could not pass\non a machine configured the way we configure it, and ./setup stopped\nthere on a system that boots perfectly well.\n\nIt now accepts either form, and the custom-name form has to be backed by\na unified kernel image that actually exists, so an entry pointing at a\nmissing file is still refused.",
"0cbf59b59": "The failure prompt defaults to repeating the command, and read returns\nempty at EOF, so any run whose stdin is not a terminal answered its own\nprompt with the default and looped. It is not a harmless spin either:\nthe retried command can have side effects, and this loop was observed\nre-running a bootloader installer over and over.\n\nEOF now ends the loop and the command reports its failure. Answering with\nan empty line at a real prompt still repeats, so the interactive\nbehaviour is unchanged.",
"53cb25316": "Switching [mainstream] to SigLevel = Required and deleting the baked in\nlocal repo were both gated on the exit status of pacman-key --add and\n--lsign-key. Those can report success while nothing reaches the keyring,\nand the switch then leaves a machine demanding a signature it cannot\nverify, with the offline fallback already gone. pacman -Syu fails for\nevery repository after that, core and extra included, so the machine\ncannot update at all and has no way back.\n\nTrust is now read back out of the keyring instead of inferred, against a\nfingerprint hardcoded here rather than taken from the key file. The\ndestructive steps are reordered behind that proof: the config is backed\nup, the signed database has to fetch and verify, and only then is the\nlocal repo removed. A failed fetch rolls the config back.\n\nWhen trust cannot be established the repo is commented out rather than\ndropped to SigLevel = Never, because an unverified repo is a worse\noutcome than one that is switched off while the rest o",
"044d16b4e": "Intel Macs from 2015 to 2017 boot on a stock kernel but arrive with the\nmedia keys inverted, a keyboard the initramfs cannot see, wifi that\nreports a wrong password on a correct one, and an NVMe that will not\ncome back from suspend. applespi, hid_apple and applesmc are all\nmainline now, so what is left is module options and initramfs entries\nrather than drivers.\n\nThe library follows gpu-config.sh: one file, sourced by the ./setup and\narchiso paths, writing nothing until it is asked to. T2 machines are\nclassified and reported but not configured, because their keyboard,\ntrackpad, audio and fan control sit behind a patched kernel this project\ndoes not ship, and saying that plainly beats a dead keyboard with no\nexplanation.\n\nThe PCI probe matches against a captured string rather than piping into\ngrep -q, so grep closing the pipe early cannot send SIGPIPE to lspci and\nread as absent hardware under pipefail.",
"6d6eb294c": "The package ships mainstream-printer-setup, its udev rule and a drop-in\nfor ipp-usb.service, but declared neither cups nor ipp-usb. On a machine\nthat lacks them the queue maker is inert and the drop-in targets a unit\nthat is not installed, so driverless USB printing never comes up.\n\nipp-usb pulls avahi, libusb and glibc only, so cups has to be named\nexplicitly for lpadmin, lpstat and ipptool.",
"4eebec7eb": "The environment variables and the autostart line were appended only if\nthe two files under custom/ already existed, and those are seeded at\ninstall and never touched again, so a machine without them took the\nwhole path silently: the profile was written, the script reported\nsuccess, and the sidebar said the input method was ready while nothing\ncould actually type in it. The files are created when absent, the way\nthe GTK settings beside them already were.",
"601c85c6d": "The watcher marks the effect with a file so a shell starting mid-zoom\nstill knows to hold the hot corner back, and it removes the file when\nthe effect ends. A watcher that never reaches that point \u2014 killed\noutright, or taken down with the session \u2014 leaves the mark behind, and\nthe corner then reads it forever: overview by hot corner simply stops\nworking. The mark now names the process that wrote it, and a watcher\nstarting up drops one whose writer is gone. The reader also stops\nfalling back to a different directory than the writer uses, where it\ncould either miss a real effect or hold the corner on a file left by\nsomething else entirely.",
"6d31fb3c8": "The migration moves a user's custom general config aside and puts the\nshipped stub back, and that restore was the one path here not anchored\nto the repo root, with its result discarded. Run from anywhere but the\nrepo it would report success having written nothing, leaving the user\nwith no overrides file and only the copy it had moved away.",
"253c8b503": "The saved Num Lock state is read from a path built by concatenating\nHOME, which raises when HOME is unset, and the read happens inside the\ntop level config call: monitors, input, decoration and animations all\nfail with it. Guarded the way every sibling file already guards it.",
"083f1bbe6": "The settings service fell back to fully solid for an unreadable value\nwhile the compositor config fell back to the plugin's own 53 percent,\nso a bar drawn one way could be described the other: the slider read\n53 over a title bar painted solid. Both now name the stock value.",
"5d33f469c": "The fallback provider names its conditions in one string, and cloudy\nis a part of partlycloudy, so the overcast test matched first and the\npartly cloudy entry below it could never be reached. Whenever the\nusual provider is down, the bar showed full cloud on every partly\ncloudy day. The narrower name is tested first.",
"7fa9f0669": "The picker commits on every pointer move so the swatch previews the\ncolour as it is chosen, and applying a title bar colour reloads the\ncompositor. One drag across the swatch therefore spawned dozens of\nreloads, reconfiguring every window each time. The colour now rides\nthe same debounce the opacity slider beside it already used.",
"041831434": "hl.get_config answers with a value and an error, and a call sitting in\nthe last argument position passes both along, so a key that is absent\nhanded tonumber the error string where it expects a numeric base and\nraised. The absent key is the title bar height whenever hyprbars is\nnot loaded, which is exactly what the version guard arranges for a\nplugin built against another Hyprland, so on those machines every\nfloating window opened unplaced behind a runtime error. Both lookups\nare parenthesized down to the one value they meant to read.",
"cb680338e": "Two signed-in subscriptions put nine plan entries in the picker at\nonce, eight of them for a provider the user was not talking to. A\nplan now keeps its models to itself until it is the plan answering:\nthe others are represented by the single entry that was already there\nfor signing in, which reads as an invitation once the sign-in is done\nand lands on that plan's first model when picked. Switching away puts\nthe models back.",
"86ee6977c": "The update helper calls this script by name, which resolves to the\ncopy the previous release installed, and the script refreshes itself\nonly at the very end. Anything added to it therefore sat out the\nrelease it shipped in and first ran on the one after, which is how\nthe printing work and the greeter reseed would both have missed the\nmachines they were written for. It now hands over to the copy in the\npulled release before doing any work.",
"7fc7f68c3": "An upgrade from 1.4.2 installs ipp-usb for the first time, and ipp-usb\nclaims a USB printer's interface the moment it arrives: any queue\nbuilt on the classic backend stops reaching the printer. The queue\nmaker that puts a working driverless queue in its place was delivered\nby the update scripts, which on that first hop are still the copies\nthe old release installed, so the daemon would have arrived without\nit and left those machines unable to print at all.\n\nBoth now travel together. The queue maker, the printer-app unlock and\nthe test-page hook are files mainstream-system installs, so they land\nin the same pacman transaction as ipp-usb on every road in: an\nupgrade, a fresh setup, or the ISO. The scripts keep only what a\npackage cannot do, which is editing cupsd.conf and clearing a config\nan unreleased revision left behind, and the package scriptlet rereads\nthe udev rules and runs the queue maker once so an attached printer\nis set up without waiting for a replug.",
"587abb49b": "The greeter's layout was written into its config at install time and\nnowhere else, so every refresh of that file carried it off and left\nthe password box on US. The update path does exactly that refresh,\nand the machine most hurt is the one that cannot type its password\nafterwards. The config now derives the layout when the greeter starts,\nfrom the same keyboard configuration the system keeps, so a replaced\nfile configures itself. Console keymaps are normalized to the XKB\nnames Hyprland expects (jp106 becomes jp, fr-latin1 becomes fr) and\nanything unrecognizable is left alone rather than guessed at.",
"022d53384": "The schema the theme system reads still called stock title bar\nopacity fully solid, while the shell had moved it to the plugin's own\n53 percent. A theme saved on an untouched machine therefore recorded a\nvalue that machine was not using, and applying it turned every title\nbar solid. The schema now says what the code says, and the page-wide\nwindow reset leaves the title bar keys to the section that owns them.",
"2176459f6": "Two ways a stored setting could be lost. The model picker reads its\nselection from persistent state, which is the default until the file\nloads, so a local model arriving in that window looked like the local\nAI default waiting to be filled in and overwrote whichever model the\nuser had chosen. The read now waits for the state to load, and a\nmachine with no state file counts as loaded so a fresh install is not\nleft waiting forever.\n\nThe other is the config file: a write held during a theme apply is\nretried rather than dropped, which is right for a setting changed in\nthat window but wrong when the apply edited the file itself, since\nwhat is in memory is then older than the disk. A write held across an\noutside edit is dropped now, so a deleted theme's wallpaper path stops\ncoming back.",
"e3aa62837": "The installer prepended a bare hl.plugin.load() for hyprbars and for\nscrolloverview into custom/general.lua, pointing at the same paths\nhyprland/plugins.lua already loads. That file loads them only when the\nbuilt .so matches the running Hyprland, which is what keeps a stale\nplugin from taking the compositor down at login; an unguarded copy of\nthe line beside it walks straight past that check. Both prepends go,\nleaving the guarded loader as the only one.",
"0bee96a16": "Two files a machine writes for itself were being replaced by the\nshipped copies on every update. custom/general.lua promises in its own\ntext that it is written once and never touched again, yet it now\ndiffers from the stub it ships as, so an unattended update took the\nbackup-and-replace path and carried off both a user's overrides and\nthe keyboard layouts Settings keeps there. monitors.lua is rewritten\nby Settings > Display with explicit monitor blocks, and replacing it\nthrew every tuned resolution, scale and refresh rate back to the\nauto-scale guess. A repo .updateignore now covers both.\n\nThe updater could also record a release as applied while delivering\nnothing: with no last-applied tag to park on, the jump to the target\ncan land where the clone already sits, leaving an empty range to read\nand a marker that stops the next run from trying again. That case now\ncompares the whole tree instead.",
"fa5a795b4": "The queue maker, the printer app's polkit unlock, the surviving test\npage, and the retry policy only reached fresh installs; a machine\nthat updates never re-runs the installer, and the updater's printing\nblock stopped at the spooler socket and mDNS. It now installs the\nwhole set from the pulled repo, cleans the unreleased cups-browsed\nconfig out of the way, and starts the queue maker once so a printer\nalready plugged in gets its permanent queue on this very run. The\npackages ride the mainstream-basic upgrade on the same pass, and the\ntest page hook becomes one shared file instead of a script heredoc.",
"b161233f1": "Without a color pick the opacity slider wrote a value nothing read,\nbecause the composer refused to touch bar_color unless a color of the\nuser's own existed, and the slider read as broken. A saved opacity\nnow composes over the plugin's stock gray at the exact alpha the\nuntouched bar ships with, so the slider's first nudge is continuous\nwith the look on screen: the default position sits at 53, the track\ncarries a mark there, and the section reset returns to it. A machine\nthat never touched the section still leaves the plugin alone.",
"5f99ab4a8": "The keybind list led every row with its raw action, so Super+Shift+P\nread as a playerctl invocation with the human meaning underneath in\nsmall print, and the filter only matched the command text. Rows now\nshow one name: the bind's own description when it has one, otherwise\na settings-side name table that knows every bind the system ships,\nfrom media keys to the workspace loops whose Lua source is all the\nparser can see of them. The filter searches those names, so typing\nplay finds the media key without anyone knowing what playerctl is.\nThe table lives entirely in Settings: the cheatsheet keeps its own\ncurated list, and a bind the table does not recognize keeps its raw\ntext, so a user's own commands are never mislabeled.",
"bfc9ad979": "The plan machinery generalizes from one provider to a table: setup\nentries, plan models, detection, install, terminal sign-in, and the\nsigned-in handoff are all keyed by api_format now, so Codex is rows\nin that table rather than a second copy of the code. Signing in to a\nChatGPT plan from the sidebar brings Codex Sol, Terra, Luna, and\nGPT-5.5 into /model, chatting rides codex exec with a read-only\nsandbox and resumes its thread between messages, and session and\nweekly usage meters appear under the chat, fed from the same endpoint\nthe CLI's own status screen reads. An old CLI strategy for codex from\nthe testing branch was a never-verified plain-text sketch; this one\nspeaks the documented JSONL stream. Also fixed along the way:\nclearing the chat now drops CLI session ids, which used to quietly\nthread the old conversation back into the next request.",
"1f8c6461f": "The udev rule matched the USB interface device, a form whose systemd\nwants never proved itself in the field, and it missed HP's\nnon-standard 255/9/1 class. The rule now mirrors ipp-usb's own\nactivation lines exactly, so the queue maker fires for precisely the\ndevices ipp-usb claims, and a drop-in on ipp-usb starts it as well:\neither road reaching the daemon reaches the queue.",
"b7ce239fa": "The suggestion row capped itself at ten chips, which was invisible\nuntil the plan models joined and pushed Sonnet and Haiku past the\ncliff. The flow wraps onto new rows, so the cap was never about\nspace; it loosens to thirty, enough for a real model shelf while\nstill bounding a runaway completion list.",
"0a8078285": "cups-browsed answered the visibility problem with discovered queues,\nwhich the Chromium family buries behind See more and prints to\nthrough a temporary queue rebuilt per job, the path that fails with\nnothing on screen. The machine that prints reliably holds a permanent\nIPP Everywhere queue on the ipp-usb port, so the install now builds\nexactly that: a udev-started oneshot waits for ipp-usb to expose the\nprinter, reads its name, and runs lpadmin -m everywhere, covering\nhotplug and boot alike. cups-browsed retires along with its scoping\nconfig and the drop-in that started it; the printer app unlock, the\ntest page fix, and the retry policy stay.",
"c22010777": "The shelved Claude Code plan work comes back, fitted to the sidebar\nas it stands now. A single Claude (Pro/Max plan) entry sits in the\npicker until the sign-in completes; picking it raises a banner that\ninstalls the CLI if needed and hands the OAuth link to the browser.\nOnce the login is live, the entry gives way to five plan models,\nFable, Opus, Opus 1M, Sonnet, and Haiku, each answering /model like\nany other and each riding the subscription instead of an API key.\nRequests run the CLI with stream-json, resume their session, and a\nfailed run explains itself: the CLI reports failures as successes\nwith an error flag, and reading the subtype alone showed an expired\nlogin as a model with nothing to say. Usage meters for the session\nand the week appear under the chat while a plan model is active.\n\nRe-landing surfaced a standing defect: the local model fetch assigned\nmodelList by hand, severing its binding to the model map, so models\nregistered after that assignment never reached the picker.",
"7968575ab": "StyledToolTip counts a parent with no hovered property as hovered, so\nthe hint attached to the title bar color field sat open for as long\nas the Decorations page did. ColorField now reports hover the same\nway ConfigSlider already does, and any tooltip attached to a color\nfield shows on hover like everywhere else.",
"955e00c63": "Chromium's native Wayland windows report the class chromium-browser,\na name no desktop entry answered to, so a window brought back by\nsession restore grew its own dock entry wearing the icon theme's\nplain chromium icon. The override entry now declares that class as\nits StartupWMClass, the same road the Spotify override already rides.\nThe grouping model also learned to re-resolve once the desktop entry\ndatabase finishes its lazy startup scan; windows restored at login\nused to keep whatever raw grouping they were dealt before it landed.",
"f9f7772da": "The title bar color field refused to be emptied even though empty is\nits stock state, so the picker hid its Reset button and there was no\nroad back to the bar's own color short of editing files. The field now\nallows empty, with the plugin's stock gray as the swatch stand-in, and\nthe section gains a reset that returns color and opacity together. The\npage-wide window reset now takes the title bar appearance back too."
},
"merges": 3
},
{
"version": "1.4.2",
"date": "2026-08-26",
"kind": "patch",
"summary": "ISO-only update: more room on the boot partition, plus a bootable check before finishing",
"changes": [
"New installs reserve more boot partition space and get a final boot-setup check"
],
"commits": [
"22391f34e release: 1.4.2, ISO-only update: more room on the boot partition, plus a bootable check before finishing",
"3b75cc126 release: 1.4.1, Better GPU/laptop support, plus new touchpad controls and keyboard layouts"
]
},
{
"version": "1.4.1",
"date": "2026-08-25",
"kind": "patch",
"summary": "Better GPU/laptop support, plus new touchpad controls and keyboard layouts",
"changes": [
"Added a keyboard layout menu for quickly adding and switching between language layouts, including popular options like Dvorak, Colemak, AZERTY, QWERTZ, and QWERTY",
"Added a toggle to enable/disable touchpad",
"Improved Nvidia GPU and laptop compatibility"
],
"commits": [
"00d3d0a12 gpu-config: let the modern driver answer for its own suspend",
"a89fdb40d gpu-config: a driver package is not a driver",
"94a11404d limine: let the menu find the other systems on the disk again",
"f5af23e90 updates: wait for the repo rather than race the first boot",
"2bef4fcc2 touchpad: add enable/disable toggle for touchpad",
"45a61343a settings: manage keyboard layouts alongside keybinds",
"fffbb8c02 localsend: read the body the sender actually sent",
"5281b7951 release: 1.4.0, Update UI Quickshell to 0.3.1"
],
"commitNotes": {
"00d3d0a12": "How a driver survives suspend is the driver's own business, and mainline changed\nits answer at 595: nvidia-utils now ships nvidia-sleep.conf asking for kernel\nsuspend notifiers, and its install scriptlet switches off the suspend, hibernate\nand resume units on the way past, because the handshake they perform is gone.\nThe legacy branches still ask to preserve video memory and still need those\nunits.\n\nBoth answers were being written to every card. The file here is called\nnvidia.conf while theirs is nvidia-sleep.conf, so it stacks rather than\nreplaces, and a Turing machine ended up holding both mechanisms at once with\nthree services switched back on underneath them. Turing and newer now get the\nmodeset options and nothing else, and say nothing about sleep at all. Maxwell\nand older are untouched.\n\nThe kms hook is left alone. It was being removed to keep the nvidia modules out\nof the initramfs, but that hook collects modules by matching paths inside the\nkernel tree, and nvidia's live beside ",
"a89fdb40d": "An NVIDIA card came up with no driver at all on a machine where the driver\nwas installed. The open driver ships its module already built, under the one\nkernel directory it was built for, while asking only for \"a kernel\" and not\nthat one. When the kernel moves on its own the package is still current and\nits module is still there, just somewhere nothing will look, and the card is\nleft with nothing driving it: no device node, no outputs.\n\nThe library asked pacman whether a driver was present, which is the one\nquestion that cannot see this. It now asks whether a module exists for the\nkernel the machine is about to boot, and works out which kernel that is from\nthe module trees rather than from the running one, since inside an installer\nthe running kernel belongs to the installer.\n\nWhich tree to believe takes three answers, not one. The kernel already\nrunning wins outright, which can only happen on a live machine and is exactly\nright there. Otherwise the stock kernel, since that is what an i",
"94a11404d": "Matches the ISO path. Probing was off because it offers the default EFI loader\nalong with whatever it finds, and that loader is Limine itself here, so the menu\ngained an entry that booted the menu. Off also meant systemd-boot and rEFInd\nwent unseen, and a machine sharing a disk with another Linux showed nothing of\nit. Windows is unaffected: the probe never looked for bootmgfw.efi, which is why\nit is added by name separately.",
"f5af23e90": "The [mainstream] repo is put in place during the first boot, and for the moment\nthat takes there is no database behind it. Anything asking in that window gets\nnothing: the Welcome app's installs fail on packages the machine cannot see yet,\nand an update is announced that would fail the same way if the user took it. The\nwindow closes on its own in seconds, so the answer is to wait for it rather than\nreport a problem that is about to stop being true.\n\nThe repo is asked by what comes back, not by an exit status. pacman -Sl on a repo\nthat is configured but unsynced exits 0 and puts its warning on stderr, so the\nstatus says yes at exactly the moment the answer is no. An empty listing is the\nreal signal.\n\nA machine with no [mainstream] configured at all is a different thing from one\nwhose repo has not arrived yet, and is treated as such. There is no first boot to\nwait through there, so the installs let pacman speak for itself and the update\ncheck carries on as before, rather than going quiet",
"2bef4fcc2": "A dropdown on the Mouse page turns the touchpad off and on. Enabled is a\nper-device option rather than one of the input.touchpad settings, so it is\napplied with hl.device() against the touchpad hyprctl reports, and the same\nstatement is persisted to the user's env.lua \u2014 what eval applies now is\nbyte-for-byte what replays next session, so the two cannot drift. The device\nname is checked before it sits inside a quoted Lua string, the same last-stop\ndiscipline the layout writer keeps, and a machine with no touchpad shows no\ncontrol for one.\n\nThe persisted block goes through the managed-block writer the keyboard\nlayouts already use, under a marker of its own.",
"45a61343a": "The Keyboard settings page keeps Hyprland's layouts above the shortcut editor,\nrather than making either setting a separate destination. It reads the XKB\nlayout catalog from the system, applies each change immediately, and writes a\nsmall managed override so the selection survives a restart.\n\nThe picker searches layout codes and names, leaves already-enabled layouts out\nof its choices, and uses the same compact controls as the keybind editor. The\nlayout-cycle shortcut is shown in the page with the same keycaps as the\ncheatsheet and is bound to Ctrl+Super+K, where it moves the focused keyboard to\nits next enabled layout.\n\nThe keybind editor is a section inside the shared page instead of a nested\nscrolling page, so the two sections follow one continuous scroll area. The page\nkeeps the name every other destination carries, and its two sections live under\nit the way the bar and connectivity pages keep theirs. The override writer is a\nscript rather than a program inside a string, where it ca",
"fffbb8c02": "A sender is free to stream a file without saying up front how long it is, and\nphones do exactly that. Only Content-Length was being read, so a chunked upload\nmeasured zero, the copy loop never ran once, and the empty file that had already\nbeen opened was renamed into place and answered with 200. What arrived looked\nlike a delivered file and opened as nothing. Sending was unaffected, because the\nsender here sets a length on every request.\n\nChunked bodies are now decoded, and a request that carries neither a length nor\na chunked body is refused rather than quietly stored as nothing.\n\nThe size the manifest promised is checked against what arrived. A body cut short\nleaves the partial file behind and reports the failure, instead of renaming a\nshort file over a name the user has no reason to distrust."
}
},
{
"version": "1.4.0",
"date": "2026-08-23",
"kind": "feature",
"summary": "Update UI Quickshell to 0.3.1",
"changes": [
"Now running on Quickshell 0.3.1",
"LocalSend file drops now work on the vertical bar too",
"Bluetooth input devices now reconnect more reliably"
],
"commits": [
"03511127a quickshell: update to 0.3.1.r1",
"7fd1cf44d media: one drop target serves both bars' widgets",
"04f0d354e bluetooth: start the applet the tray-icon mask was silently keeping down",
"6fc1deda5 bluetooth: stop scanning the air while something is already connected",
"5c421cb38 bluetooth: reconnect trusted audio devices at startup",
"52158a0c9 bluetooth: pair before trusting a device"
],
"commitNotes": {
"03511127a": "Pin _commit to 0fed22a2c47d9568ddf13cf61586b3f2ac4378a2, the current\ntip of master and one commit past the v0.3.1 tag (1a4716c).\n\n0.3.1 is a bug-fix-only release (session lock, JsonAdapter,\nScriptModel, and pipewire crash fixes, among others). No new\ndependencies, so depends/makedepends are unchanged.",
"7fd1cf44d": "The horizontal media widget opens the controls popup in transfer mode when a\nfile is dragged over it, and the vertical bar's widget is a parallel copy that\nnever received the drop target \u2014 a drag over it fell through to nothing, on\nthe bar where the widget looks the same and promises the same.\n\nThe target is one component now, under both widgets, the way the tray and the\ngroup pill are already shared between the two bars: hovering with files opens\nthe popup as a visible place to release, the popup's own drop area finalizes,\nand a send already in flight is left undisturbed. The popup already places\nitself beside whichever bar it was opened from, and the next thing a drag\nshould do lands on both bars by construction.",
"04f0d354e": "The autostart override that was meant to hide blueman-applet's tray icon\ndisabled the applet instead: Hidden=true in an autostart entry removes the\nentry, it does not quiet it. That mattered more than an icon. The applet is\nwhat registers blueman's BlueZ agent \u2014 the service bluetoothd consults before\nhonoring a connection a device opens on its own \u2014 and the KDE tools that used\nto provide one left with them. Since then no agent has run at all.\n\nDaily use never noticed, because nothing in it asks: connections the user\nstarts need no authorization, and trusted devices are waved through. The gap\nis a paired device that is not trusted dialing back in by itself \u2014 a mouse\nwaking from sleep. Refused for want of anyone to ask, it retries on every\nmovement, and the user watches it connect and disconnect without end. Trusting\ndevices on connect was the working remedy; this restores the thing whose\nabsence made it necessary.\n\nThe session starts the applet itself, through a script that first writes",
"6fc1deda5": "The night light panel started a Bluetooth scan when it opened and stopped one\nwhen it closed, carried over whole from the Bluetooth panel it was written from.\nNothing in it has anything to do with Bluetooth. Opening it put the adapter into\na scan, and a scan is one of the surer ways to unsettle a mouse or keyboard that\nis already connected \u2014 the link it has to hold is competing with the search for\nlinks it does not.\n\nThe list of devices found so far had the same trouble from the other direction.\nIt recorded the order they turned up in and then announced that the record had\ngrown, and the only thing reading that record is the sort inside the very list\nthe recording happens in, so the announcement asked the list to build itself\nagain. Building it recorded whatever was new, which announced again. Each new\naddress restarted it, and while a scan is running they arrive without pause.\nThe record is kept, the announcement is not: the sort reads what was written\nmoments earlier in its own run, ",
"5c421cb38": "BlueZ restores the adapter at login but does not initiate a connection to\nremembered devices. The old Hyprland hook filled that gap by sleeping four\nseconds, parsing bluetoothctl's trusted-device listing, and launching one\nbackground connect process per result.\n\nMove that one-shot request into the shell's Bluetooth service. After the same\nstartup delay, it asks each trusted, disconnected device BlueZ classifies as\naudio to connect. Speakers and headsets therefore regain startup recovery without\nhard-coding a device address.\n\nInput devices are deliberately excluded. A mouse or keyboard is left to its\nnormal Bluetooth reconnection path instead of being given an unsolicited host-side\nconnection request during startup. The standalone script and its formatted-command\nparsing are no longer needed.",
"52158a0c9": "The sidebar and connectivity panels labelled their unpaired-device action Pair,\nbut sent BlueZ a Connect request. Connect only asks for compatible profiles;\nPair is the request that establishes and stores the bond before discovering\nservices.\n\nBoth panels now send the pairing request and wait for the paired state before\nmarking the device trusted. A failed or cancelled pairing therefore no longer\nleaves a discovered device trusted, while a completed pairing keeps the\nprevious Always connect behavior until it is forgotten.\n\nThe startup reconnect path remains audio-only, so this does not reintroduce\nthe old host-initiated mouse connection attempts."
}
},
{
"version": "1.3.2",
"date": "2026-08-19",
"kind": "patch",
"summary": "Stop managing AQ_DRM_DEVICES on hybrid NVIDIA laptops",
"changes": [
"Name the Intel module the kernel actually bound",
"Show the color, not the alpha behind it",
"The controls open beside the widget on a vertical bar too",
"A color field can be emptied where having no color means something",
"A color field's swatch and box stand as tall as its button"
],
"commits": [
"9fb87008a fix(gpu-config): stop managing AQ_DRM_DEVICES on hybrid NVIDIA laptops",
"84127def8 gpu: name the Intel module the kernel actually bound",
"cd61965e7 color field: show the color, not the alpha behind it",
"617184c4e media: the controls open beside the widget on a vertical bar too",
"fd401b47e widgets: a color field can be emptied where having no color means something",
"cf85bd8bb widgets: a color field's swatch and box stand as tall as its button"
],
"commitNotes": {
"9fb87008a": "nvidia_write_aq_drm() pinned Aquamarine's AQ_DRM_DEVICES to the NVIDIA\ncard alone whenever a hybrid GPU setup was detected. On most hybrid\nlaptops the internal panel is wired to the iGPU, not the dGPU, so this\nhanded Hyprland a render device with no connector for the built-in\nscreen \u2014 session never comes up, looks like a boot failure.\n\nConfirmed on an HP Omen Max 16 (RTX 5080): env.lua carried\n hl.env(\"AQ_DRM_DEVICES\", \"/dev/dri/by-path/pci-0000:02:00.0-card\")\nwith no iGPU entry, matching this exact failure mode.\n\nA joined-list fix (NVIDIA first, plus every other connector-capable\ncard, so the panel's actual owning GPU stayed reachable) was tried\nbefore committing anything, on the theory that the missing connector\nwas the root cause. Testing on the same hardware showed it still\nfailed to boot. That points to the actual problem being NVIDIA's side\nof the multi-GPU render/blit handoff itself, not connector coverage \u2014\na known-fragile path per the Hyprland wiki's own multi-GPU/NVIDIA\nnote",
"84127def8": "The initramfs was told i915 for every Intel card. Which of i915 and xe claims a\npart is the kernel's call and it moves from one release to the next, so on the\nnewest hardware \u2014 where xe takes it \u2014 the initramfs carried a module that was\nnot driving anything and early KMS quietly did not happen. Nothing broke; the\ndriver still loaded later, and the flicker-free boot it was there for simply\nwent missing on the parts most likely to want it.\n\nReading it off the card settles it. The generation tiers were no help: the one\ncovering Xe spans parts on both sides of the split, so mapping tier to module\nwould have put xe in front of an Arrow Lake, which is worse than the hardcoded\nanswer it replaced. Asking what is bound cannot disagree with the kernel, and\nfollows it for free the release a default changes. i915 when nothing answers,\nwhich is both the older parts' module and what was named here before.\n\nThe card list the compositor is given grew a seam for its lookup directory at\nthe same time, b",
"cd61965e7": "The box takes a string, but a caller with a color rather than a typed one hands\nover whatever Qt makes of it \u2014 and Qt writes a color that carries alpha as nine\ndigits, the leading pair being that alpha. Nothing here expected them. The\nswatch painted the alpha too, so a color the interface draws at a tenth opacity\narrived as a near-invisible smear of itself; the box was sized for seven\ncharacters and showed a fragment of the nine; and the reader that guards typing\nrejects eight digits outright, so the value could not be edited back into shape\neither. A color nobody could see, read, or correct.\n\nThe reader now takes that form and drops the alpha, and the swatch and the box\nread through it rather than off the raw value. What a color is worth and how\nmuch of it shows through are two settings here, and only the first belongs in a\nbox that says which color it is.",
"617184c4e": "The popup was taught to open under the widget that asked for it, but only along\none axis: the placement returned early on a vertical bar and the margin was\nfixed at half the screen, so wherever the media widget sat on a side bar the\ncontrols appeared at the middle. The widget there never published itself either,\nso there was nothing to place against had the placement run.\n\nIt now measures the same way on either edge and slides along whichever axis the\nbar leaves free \u2014 across under a top or bottom bar, up and down beside a left or\nright one \u2014 clamped to the screen with the same gap at both ends. The stock\nmiddle spot remains what is used until the widget has been measured, and\nwhenever it cannot be.",
"fd401b47e": "A field could be given a color but never returned to not having one: an emptied\nbox normalises to nothing, and nothing was read as a typo and refused. Clearing\nit now says \"no color of my own\" wherever that is a state worth having.\n\nOff unless asked for. A gradient lane cannot draw an empty string, so refusing\nit there is still the right answer, and every field that exists today keeps the\nbehavior it had. It also takes a genuinely blank box rather than merely an\nunparseable one, so a half-typed code still snaps back instead of quietly\ndropping the color that was set.",
"cf85bd8bb": "The swatch took its height from the text control's drawn outline and the button\nbeside them took its own, so a row meant to be read as one control was three\nthings of two heights. Both now take the button's, which is the tallest of them\nand the one with a hard edge to line up against.\n\nThe same component draws the border gradient colors, so those rows sit level too."
}
},
{
"version": "1.3.1",
"date": "2026-08-17",
"kind": "patch",
"summary": "Offer a certificate when a receiver asks for one",
"changes": [
"1.3.0, everything in its place"
],
"commits": [
"38d58c79a localsend: offer a certificate when a receiver asks for one",
"603a22619 release: 1.3.0, everything in its place"
],
"commitNotes": {
"38d58c79a": "Receivers began requiring the sender to present a certificate during the TLS\nhandshake. Sending never offered one, so the connection was refused before a\nbyte of the file moved, and the failure arrived as a certificate alert rather\nthan as anything about the transfer. Nothing here changed; the other end did,\nwhich is why this worked for months and then stopped.\n\nOne is generated on first use and kept, so a peer goes on seeing the same\ndevice rather than a new one per transfer. Which certificate it is does not\nmatter to the handshake \u2014 that one is offered at all does. It is presented to\nevery receiver, since one that does not ask simply ignores it, and a receiver\nthat refuses to load it is no worse off than before."
}
},
{
"version": "1.3.0",
"date": "2026-08-17",
"kind": "feature",
"summary": "The dock goes on any edge you like, and the bar arranges itself",
"changes": [
"Put the dock on any edge of the screen: top, bottom, left or right",
"The bar steps aside when you give the dock the edge it was using",
"Themes now carry which edge the dock sits on, along with everything else they remember",
"The flexible-space widget is retired \u2014 the bar packs its own sections now, and a layout you already saved moves across on its own",
"Bar popups open centered under the widget they belong to, wherever you have put it",
"A popup near the edge of the screen stays on screen instead of running off it",
"The media widget keeps its size and its popup in the right place outside the center",
"The timer keeps a steady width while it counts down, instead of nudging its neighbors",
"Dividers stay inside the bar's rounded background",
"Close the window rule editor from its own corner, and switch between rules without closing it",
"Pick a window rule's app by name instead of by window class",
"Drives you have mounted stay on the tab you mounted them from",
"Wallpaper thumbnails show up for filenames with punctuation in them",
"Media controls come back from the shortcut, and a second browser's player no longer disappears"
],
"commits": [
"47a6af33c bar: the retirement scrub reads every group form the bar accepts",
"a22771894 themes: a theme with no dock edge leaves the dock where it is",
"350ad10da widgets: start the ripple where the pointer actually is",
"ba1ba669b dock: pick an edge, and step aside for the bar",
"f116b6620 media: hide a built-in player only for a browser the bridge runs in",
"306812069 media: the controls IPC drives the shared open state",
"6593fdc24 bar: harden the layout model and its editor",
"a7cf2e745 bar: vertical side pills keep the bar's visible width",
"67f4fd306 bar: resources gives way to media only when they share a pill",
"45c4db442 bar: retire the spacer; the sections pack themselves",
"6b245fd1e bar: hover popups stay inside the screen like the volume one",
"98515b5ca bar: give a side section's spare width a single owner",
"76899afe4 fix(bar): stop anchoring the timer row inside its group layout",
"db4c29ee8 fix(bar): keep the timer tray a steady width while it ticks",
"98cfbaab2 bar: media holds its width beside other widgets off center",
"b3a3e0cd2 bar: center popups on the pill, not the window",
"d274af5dc bar: the media popup opens under the widget, wherever it sits",
"facf31326 bar: media keeps its size outside the center sections",
"df5e4288e rules: cancel from the editor's corner, switch rules without closing",
"3af94951c rules: pick from apps by name, not windows by class",
"643157b80 decorations: name the blur xray option on its switch",
"114e1aed9 mounter: keep mounted drives on the tab that mounted them",
"9eb844837 bar: keep the line-separated dividers inside the bar",
"c2d09742f picker: hash the thumbnail name the way the generator writes it"
],
"name": "Everything in its place",
"commitNotes": {
"47a6af33c": "A layout group is not always {widgets: [...]}. It can be a bare string, or\ncarry items instead, and the bar, its editor and the seeding pass all go\nthrough one tolerant reader for exactly that reason. The scrub did not: it\nread .widgets directly, saw nothing in a group written either other way, and\nconcluded the group was empty. Empty groups are dropped. So a config holding\none spacer anywhere and one older-form group anywhere lost the second on load\nand had the loss written back, with no backup to undo it.\n\nThis is the same shape as the fault the widget seeding pass carried, which was\nfixed by handing it the tolerant reader. The scrub gets the same treatment,\nand one more guard besides: a group with nothing retired in it is now returned\nexactly as it was read, in whatever form it was written. Only a group that\nactually held a retired id is rewritten, which is the only case where changing\nits form is the point.\n\nWhile here, the spacer pivot no longer insists the spacer be alone in its\n",
"a22771894": "Every theme saved before the dock could move names no edge, and applying one\nreplaces config.json wholesale, so the key simply vanishes from the file. That\nis the worst of both answers: the adapter keeps the value it already has, so the\ndock stays put and nothing looks wrong, while the file on disk no longer says\nwhere it goes. The next start reads no edge and falls back to the default, and\nthe dock moves somewhere the user never chose, one boot removed from the theme\nswitch that caused it.\n\nThe live edge is written in instead, so the screen and the file agree. A theme\nthat does name an edge still wins outright \u2014 that is the theme owning its\nlayout, which is the point \u2014 and this only fills the silence.",
"350ad10da": "The ripple lives inside the button's background, which the control insets on\nboth axes, but only the vertical inset was being taken off the press\ncoordinates \u2014 and the reach was measured across the whole control rather than\nthe background. Nothing showed it until dock buttons gained a left and right\ninset, which put the ripple ten pixels off the pointer on a side dock.",
"ba1ba669b": "The dock was hardwired to the bottom, so a bottom bar and the dock\nstacked on one edge and fought over the same hover strip. The dock\nnow takes a position - bottom, top, left or right - from a dropdown\nthat offers all four, renders vertically on the sides, and gives way\nto the bar when the bar is moved onto the edge it saved. Asking for\nthe bar's own edge from the dock's setting is the one case the dock\ndoes not give way: the bar crosses to the far side of its axis and\nthe dock takes what was asked for, because refusing the choice or\nquietly answering with a different edge are both worse than moving\nthe thing that was not being talked about. One edge value drives the window anchors, the reveal\nslide, the layout flow, the magnify origin, the running-window dots\nand every popup's direction; the anchor sets swap through states\nbecause a live flip through conditional bindings passes an illegal\nthree-anchor moment that wedges the layout. The bar and dock edges\nresolve in one shared place th",
"f116b6620": "The built-in was hidden whenever any per-window bridge of the same\nbrowser make existed, so with a second browser running unbridged its\nonly player vanished from the popup while the bar kept its title. No\nname-level linkage ties a bridge to its browser - the bridge embeds\nits own host pid while the browser names its player after its D-Bus\nconnection or its pid depending on version - but the bridge host is\nalways a child of the browser that owns the built-in's connection, so\na helper resolves both connections to processes and matches ancestry.",
"306812069": "The IPC handlers wrote the loader's active property directly, and an\nimperative write severs its binding to the shared open flag - after\none IPC call the widget click, Esc, the global shortcut and the\ntransfer auto-close all stopped reaching the popup. Nothing consumes\nthe loader directly: the keybind already goes through the shared\nflag, and the bridge extension never talks to the shell.",
"6593fdc24": "The editor offers a bar-managed widget back when a layout arrives\nwithout one, floats such entries to the front of their group so drags\ncannot reorder around something it does not show, and reads groups\nthrough the shared ObjectUtils helper instead of a diverged copy.\nChromeless detection ignores entries the bar cannot render, a missing\nenabled key counts as enabled everywhere, the space-taking widget\nlist lives in one predicate, media stops yielding when the window\ntitle already stretches the pill, pilled groups no longer build a\nhidden duplicate of every widget, and the timer tray can no longer be\nsquashed while its reveal animates.",
"a7cf2e745": "The side sections span the whole window, but on the floating style\nthe bar's visible body is inset by the gap, so their pills ran wider\nthan the bar they sat on while the center pills came out at the\nbar's own width. Both side columns now inset by the same gap, and\nevery pill reads as one shape.",
"67f4fd306": "The meters beyond memory hid whenever anything was playing, wherever\nthe widget sat - the collapse exists to hand the shared pill's room\nto media, and a resources widget in its own pill was shrinking for a\nplayer it was not even next to. Standing alone it now keeps the full\nreadout; beside media it still hands the room back while a track\nplays.",
"45c4db442": "With each side row carrying its own stretch filler, the left cluster\npacks left and the right cluster packs right on their own, and the\nspacer was a movable way of asking for what is now the fixed rule.\nThe widget is gone from the bar, the default layout and the customize\nmenu.\n\nA layout saved before this still names the spacer, and the bar draws\nnothing for an id it no longer knows \u2014 so the room the spacer held\ndisappears and every widget beside it slides over, on a bar the user\nnever asked to rearrange. Leaving that to the editor was not enough:\nit only rewrites a section once someone opens it, so the bar stays\nrearranged until they do. The config drops retired ids as it loads\ninstead, once, taking a group left holding nothing with them. The\neditor reads the same list, so the two cannot come to disagree, and a\nfresh install names no retired id and so is never written to at\nstartup \u2014 the one thing this file must not do while a first login is\nseeding it from outside.\n\nDropping the widg",
"6b245fd1e": "The volume and media popups clamp their position to the screen, but\nthe shared hover popup placed itself purely by centring on its widget,\nso weather, resources or the update indicator near a screen edge\npushed their popups past it. The same clamp now applies to every\npopup that centers on a bar widget, on both bar orientations.",
"98515b5ca": "Fill meant two different things: share-the-set-width inside a center\npill, and take-the-section outside it. Splitting those claims, only\nthe widgets whose job is genuinely width still take a section \u2014 the\nspacer, which exists to eat room, and the window title, which needs\nroom to shrink to an ellipsis. Everything else keeps its fill inside\ncenter pills and its set width everywhere else, instead of clock and\nmedia ballooning in turn.\n\nThe media-and-resources pairing is the one exception: media asks for\nits minimum and takes back the room resources leaves, an exchange\nthat only means something inside a pill with a set width. That pill\nkeeps its width floor and media keeps its fill in every section, so\nthe pair reads the same wherever it sits.\n\nThat alone still left the surplus unowned: a row wider than its\ncontent does not push items to its edge \u2014 columns with no stretch\nshare the leftover in proportion to their size, so free-standing\npills drifted apart across the section. The stock arr",
"76899afe4": "The tray is a BarGroup, whose default property places children in a\nGridLayout, and anchoring an item a layout manages is undefined\nbehavior that also logged a warning on every relayout. Centring\nthrough the layout's own alignment puts the row in the same place\nwithout fighting it.",
"db4c29ee8": "The time readouts render in the UI font, whose digits each take their\nown width, so the pill grew and shrank a few pixels every second as the\ndigits changed. Tabular figures give every digit the same advance; the\npill now only resizes when a readout gains a digit or a section appears,\nwhich the existing width animation already covers.",
"98cfbaab2": "Media fills inside its pill so a center pill's spare room goes to the\ntrack title \u2014 the pill there has a set width, so the fill is bounded.\nOff center the pill is as wide as its group-mates make it: put media\nbeside the window title or clock and the pill legitimately fills the\nsection for them, and media's fill then split that surplus too, growing\nit past its set width. The fill now applies only in center pills, so\noff-center media takes its preferred width and the surplus stays with\nthe widget the section fill exists for. Shortened resources rides the\nsame scope, being the other half of the stock pairing.",
"b3a3e0cd2": "Every anchored bar popup centerd itself using the visible pill's width\nbut positioned the window, and the pill sits a shadow margin inside the\nwindow's anchored edge \u2014 so clock, weather, volume and the rest all\nrendered that margin off center toward the right, or downward on the\nvertical bar. The offset now subtracts the pill's inset, written as the\nsame expression the background uses for it, so the two cannot drift\napart again.",
"d274af5dc": "The popup's position was arithmetic from the stock layout \u2014 half the\nscreen minus half the popup minus the widget width \u2014 which pinned it to\nthe center-left spot the widget used to occupy, however the bar is\narranged now.\n\nThe widget hands itself to the popup through a shared Item reference,\nset on every path that opens it: click, receive view, and a file\ndragged onto it. The popup measures that widget against its bar window\nonce per open \u2014 the loader rebuilds the window each time, so the\nmeasurement is current without binding against the layout \u2014 centers\nitself under it, and clamps to the screen. Anything short of a clean\nmeasurement leaves the stock position in place, and the measurement can\nnever take the click down with it: publishing is one assignment, and\nthe placement runs guarded inside the popup. The vertical bar keeps its\nown placement.",
"facf31326": "A group holding a fill-width widget fills its section when placed off\ncenter, which exists so a window title can shrink to an ellipsis rather\nthan run past its section. Media is on the fill-width list for a\ndifferent reason \u2014 leftover room inside its own pill goes to the track\ntitle \u2014 and promoting that to section level meant a media group moved\nto the left or right side stretched across the whole section. The pill\nnow fills only for widgets that genuinely want section width; media and\nthe shortened resources keep their fill inside the pill.",
"df5e4288e": "Editing a different rule meant cancelling, scrolling back, and opening\nagain: every card's Edit button went dead while the editor was open,\nand the only way out was the Cancel at the very bottom of a long\neditor. A close button now sits in the editor's top corner, and Edit\nstays live on every rule but the one being edited.\n\nSwitching does not repoint the open editor \u2014 a used ConfigSwitch has\nhad its checked: binding broken by the click, so the editor is torn\ndown and rebuilt for the new rule, the same teardown closing has\nalways relied on. Unsaved changes are dropped on a switch, exactly as\nthey are on Cancel. Reorder and delete stay locked while the editor is\nopen, since moving a rule under an editor keyed to its index would\nsave over the wrong one.",
"3af94951c": "The picker listed one row per open window under its raw class and\ntitle, so three browser windows made three entries and a person hunting\nfor their file manager had to know it answers to org.gnome.Nautilus.\nIt now lists each open app once, under the name its desktop entry\ngives it; a class with no entry is tidied instead \u2014 reverse-DNS prefix\ndropped, dashes opened into spaces, first letters raised. Selecting one\nwrites the same anchored class pattern as before.",
"643157b80": "The switch carries the option's real name, xray, so what it writes can\nbe looked up against Hyprland's own docs. The tooltip already says what\nit does in a sentence.",
"114e1aed9": "A drive picked on the Local tab vanished from it the moment Mount was\npressed, reappearing only under a separate Mounted tab \u2014 which read as\nthe drive disappearing. Mounted entries now stay put: a Mounted category\non the Local tab for block devices and a Mounted shares category on the\nNetwork tab for network filesystems, each with the same one-click\nunmount row the old tab had, and the tab itself is gone. The row moves\nto a shared component so both categories draw the entries one way.",
"9eb844837": "The separator component sizes itself with Layout margins, which only\nexist inside a layout. The two dividers flanking the center groups are\nanchored children instead, so those margins did nothing and the\ntop/bottom anchors stretched each line across the bar's full height \u2014\npast the rounded background, which the Float corner style insets. They\nnow center at the same short height the layout margins produce, and the\ncomponent carries that height itself so any anchored use gets it.",
"c2d09742f": "The freedesktop thumbnail name is an md5 of the file URI, and the two\nhalves disagreed on what that URI looks like: the bulk generator gets\nits URI from GIO, which leaves @ & = , : $ + raw in a path, while the\npicker encoded them through encodeURIComponent. Any wallpaper with one\nof those characters in its name hashed to a thumbnail that was never\nwritten and drew as an empty tile. The picker now puts GLib's raw set\nback after encoding, and the magick fallback's own urlencode is held to\nthe same rule so both generators write one name. Semicolons stay\nescaped on every side \u2014 GLib escapes them too."
}
},
{
"version": "1.2.0",
"date": "2026-08-12",
"kind": "feature",
"summary": "Windows the shape you want them, and rules for the ones that are different",
"changes": [
"Shape your windows from Settings: corner radius, border thickness, and the gaps between them and around the screen",
"Set how see-through a window is and tune the blur behind it",
"Dim the windows you are not using, by as much as you like",
"Give your borders a gradient of your own colors, or leave them following the wallpaper",
"Shape the shadow under a window: its size, how sharply it fades, how dark it is, and which way it falls",
"Pick how windows animate \u2014 Expressive, Smooth, or Minimal \u2014 or drop in an animation file of your own",
"A window rule editor: teach one app where to open, how to look, and whether it floats",
"Themes now carry shape, transparency, blur, dim, borders, shadow, animations and window rules as well as your colors and interface changes",
"Set a folder as your wallpaper and let it rotate on a timer",
"Qt apps now take their colors from your wallpaper along with everything else",
"Your session is captured while it runs, so a crash no longer loses your window layout"
],
"commits": [
"4d7a7486b readme: bring the front page up to 1.2.0",
"193d32686 update: curate the 1.2.0 release notes and let a release carry a name",
"813f2c88d hyprbars: build the title bars from our fork",
"8f56791f6 system: a way to pull an upstream plugin fix onto built machines",
"3ec465332 welcome: route the Resolve update button through the session",
"7846abb72 gaming: stop naming the legacy lib32 driver pacman cannot resolve",
"8f9b5ae48 settings: build the editing menu when it is asked for",
"ffdd7dac5 changelog: credit the to-do list fix, and say desktop rather than shell",
"593941ebd scrolloverview: stop the remembered commit outranking the branch",
"99d6832fc dock: stop drawing a desktop action's icon name as text",
"98db9073e hyprland: frost behind app menus so see-through ones stay readable",
"1b67db16b overview: give each wallpaper its own scaled copy",
"ec875adb7 settings: stop doing work whose result is thrown away",
"ca1bf64ef themes: read the borders a previous release turned off",
"9b204117a settings: finish what the last two changes started",
"6b3d1a71a hyprland: keep a bad rule from taking the good ones with it",
"deb7b7cf1 themes: stop the decoration writer from breaking general.lua",
"38a38e133 settings: keep what a rule holds and what a switch shows",
"42f28c42b themes: hold the whole border toggle when an old theme restores",
"7ba676f27 themes: keep a theme's own values from reaching the compositor as code",
"0ba6d1ff2 themes: restore decorations whole, not just the keys a snapshot names",
"c03548021 themes: carry window rules with a theme",
"fdd069756 decorations: add a window rules editor",
"7f4c5df90 hyprland: give the settings shell its own window rules file",
"01ad9606c decorations: one press back to stock for the window sections",
"cc0da30a3 decorations: the first section names the windows it governs",
"e7dee1b99 decorations: animation styles that travel with a theme",
"caeb8653f decorations: gradient border from the palette or two colors of your own",
"70c25ee6e decorations: blur noise, saturation and see-through",
"f5a09782d decorations: shadow sliders",
"f250d71b5 decorations: corner radius, border thickness and window gaps",
"c6787734f decorations: expose blur depth and window transparency",
"53c3b59e1 decorations: one reader and writer for the decoration settings",
"bcea466c7 settings: text fields carry the shell's own context menu",
"34d249890 display: spell color the way the rest of the shell does",
"0b783abbf settings: save a slider only when the user moves it",
"d4d80ac53 hyprland: let a see-through window frost what is behind it",
"95862df83 hyprland: let the override file hold a key with a dot, and a table",
"8a1dccb6e themes: bound what waits, keep what was chosen, cap what grows",
"3205817aa themes: stop the colour pipeline corrupting the files it writes"
],
"name": "Let there be RICE",
"commitsTotal": 55,
"commitNotes": {
"4d7a7486b": "Names what the release added, and corrects how the shell's provenance is\ndescribed: the dock is no longer offered as an example of what came from\nupstream now that four fifths of it is new, and a theme is described by\nwhat it carries rather than as saved colors \u2014 colors being the one thing\nupstream is already known for.",
"193d32686": "The generated notes were commit subjects, which read as a work log rather\nthan a description of what changed for someone using it. A release can\nalso carry a name now \u2014 1.2.0 is Let there be RICE \u2014 carried through the\nmanifest as an optional field so unnamed releases show no empty slot.",
"813f2c88d": "The fork carries a fix confirmed on the affected hardware: title bars\nrender wrong on NVIDIA whenever blur is on, transparent on one driver\nbranch and a solid block on another, because the rounded-corner mask\nclears and writes the stencil buffer without opening the stencil mask\nfirst. Closed, the clear and the write are silently dropped and the\ntest that follows reads whatever was left in the buffer.\n\nThe fork's branch is based on the commit that builds against the\nHyprland we ship rather than on current upstream, which has already\nmoved to headers 0.56 does not carry. Merging upstream forward is a\n0.57 job, kept on mainstream-next until then.",
"8f56791f6": "The rebuild scripts try their cached last-good ref first and stop at the\nfirst ref that builds, so a machine that already builds the plugin never\nlooks at the branch again. That is what keeps a routine Hyprland patch\nbump down to seconds, but it also means an upstream fix stays invisible\nuntil Hyprland changes enough to break the cached commit \u2014 arriving by\naccident, months later, or not at all. Nothing in Repair Install cleared\nit either.\n\nDropping the cached ref makes the next run fall through to the branch.\nA serial recorded per machine keeps that to once, so ordinary upgrades\nstill hit the cache. Machines that predate the marker only record where\nthey stand, since there is no way to tell whether their ref is behind\nand refreshing the fleet on arrival would rebuild everyone for nothing;\na fresh install skips the rebuild too, its baked plugin being current.\n\nBump _PLUGIN_REFRESH_SERIAL when a fix lands upstream that machines need.",
"3ec465332": "The notification's Install button runs in the weekly check's detached\nwaiter, which lives in the user manager's cgroup rather than the login\nsession's scope \u2014 polkit maps that to no session at all, so the\npasswordless rule (active local wheel) cannot match and there is no\nagent to prompt, and the update was refused every time. The same pkexec\nfrom the Welcome app works because the app is a child of the session.\n\nThe button now asks the compositor to spawn the pkexec, making it a\nsession child with exactly the standing the rule already trusts. The\ndirect call stays as the fallback for a terminal run, and the refusal\nnotification stays for whatever remains.",
"7846abb72": "The legacy NVIDIA editions install the branch-matched 32-bit userspace\n(lib32-nvidia-580xx-utils and kin) with the driver itself, and those\npackages exist in no online repo \u2014 the first-boot keyring switch removes\nthe baked file repo that carried them. pacman -S resolves every named\ntarget against the sync databases before --needed can skip it, so the\nGaming Mode installer naming the already-installed package aborted the\nentire transaction with 'target not found', taking Steam and the rest of\nthe stack down with it.\n\nThe lib32 driver is now added only when it is absent and resolvable:\nalready installed means nothing to do, resolvable (mainline\nlib32-nvidia-utils from multilib) means install it, neither means warn\nand carry on so the rest of the gaming stack still lands. A nouveau\nsystem no longer picks up the mainline proprietary 32-bit userspace by\ndefault either \u2014 lib32-mesa already covers it.",
"8f9b5ae48": "Every text field built the menu along with itself, and the menu asks the\nfield whether there is anything to paste. That question goes to the\nclipboard, which is quick to answer when something owns the selection and\nhas no answer at all when nothing does \u2014 the installer on live media being\nexactly that case, where a password box is often the first field to exist.\n\nNothing needs the menu until a right click, so nothing builds it until then.",
"ffdd7dac5": "The curated bullet called it the shell, which is what we call it among\nourselves and not what anyone reading a changelog calls the thing their\nfonts changed on.",
"593941ebd": "The last commit that built was tried before everything else, so once a\nmachine had one that still compiled it kept it forever and nothing merged\nafterwards ever reached it. It is now the way back rather than the first\nchoice, tried only if the branch itself will not build.\n\nNothing recorded which sources a plugin came from, only which Hyprland, so\na run could not tell that it had already built these very sources \u2014 the\ndaily timer recompiled them every night to arrive back where it started.\nThe stamp carries the commit as a second line, and a stamp written without\none reads as unknown, which is what carries existing installs across.",
"99d6832fc": "The menu names its icons as Material Symbols, and a desktop action carries\nan icon theme name instead. The symbol font drew that name as its own\nletters, so Code's actions read COM.VISUALSTUDIO.<>.OSS \u2014 <> being the\nfont's own ligature for the word code.",
"98db9073e": "A menu belonging to a transparent window was drawn straight onto the\nwallpaper, which left the text sitting on whatever happened to be behind\nit. Nothing frosts a popup unless the compositor is told to.",
"1b67db16b": "Every theme keeps its wallpaper as wallpaper.jpg, and the scaled copy the\noverview plugin is handed was named after that basename alone. All themes\ntherefore shared one cache entry, and the freshness check compares only\ntimestamps, so whichever theme wrote it last was shown behind every other\none. Key the entry by the path it came from and the size it was scaled to.",
"ec875adb7": "The border gradient rewrote its override file and reloaded the whole\nconfig on every palette change, which is every wallpaper change and so\nevery step of a slideshow \u2014 with both lanes off that was two resets of\nkeys already absent. It remembers what it last wrote and says nothing\nwhen nothing differs.\n\nThe window list was fetched when the page loaded, but it is only read\nwhile the editor is open and opening it fetches a fresh one, so that\nfirst list was always discarded. It also carried two fields no caller\nreads.\n\nThe theme snapshot asked the schema which profile names ship; it asks\nthe script that owns the schema instead, as the apply already does.",
"ca1bf64ef": "That release recorded the setting by commenting its line out, so an\nupgraded install has -- border_size = 4 where the field should be. That\nread as nothing set, which showed the switch on while the compositor had\nno borders, and a theme saved from there turned them back on for good.\nA commented field now reads as the off value the old toggle meant by it.\n\nWhich profile names ship is also answered here now, rather than by each\ncaller reading the schema and coming to its own conclusion.",
"9b204117a": "Editing a rule kept the keys the editor has no widget for, but not the\nvalues it can only half-show: a workspace named rather than numbered\nbecame 1, a rounding became nothing, a size on a tiled window was\ndropped. Those are set aside with the rest and written back as they\nwere.\n\nA second save while the first was still running killed it, and the\nkilled run's exit read as a failure, which reloaded the file over the\nnewer list \u2014 deleted rules came back. Saves queue instead. A theme\nlanding under an open editor now closes it, so a save cannot land at an\nindex into a list that has been replaced.\n\nA custom effect's fields were changed inside the array, which notifies\nnothing, so the Save button never noticed the rule had become saveable.\n\nThe reset re-armed seven switches; the two border-gradient lanes and\nTitle Bars were not among them and kept showing the old state.",
"6b3d1a71a": "A store that could not be understood read as an empty list, so the page\nshowed no rules and the next save wrote that emptiness over the file \u2014\nevery rule gone, with nothing said. The reason travels with the result\nnow; a regenerate refuses rather than publishing nothing, and the page\nholds back every write until the file makes sense again.\n\nEffect values were never checked for shape, so a size that was a number\ninstead of a pair either crashed the writer, leaving the rules\nunwritable, or was quietly coerced into something else. And a newline in\na pattern ended the Lua string it sat in, silently killing every rule\nafter it while the run still reported OK.\n\nThe two files are published under one lock, so a reader cannot catch the\nstore from one write and the generated rules from another. The apply\npasses --no-reload, which is the second of the two reloads a theme was\nstill paying for.",
"deb7b7cf1": "Two ways it could write a file Hyprland cannot parse, and hyprland.lua\nrequires general.lua without a pcall, so a broken one takes env, rules,\ncolors and keybinds down with it.\n\nA value was matched up to the first comma, so a colour written the way\nHyprland also accepts it \u2014 rgba(20, 20, 20, 0.5) \u2014 was read as rgba(20\nand the replacement landed in front of the rest of the line. Quoted\nstrings are now matched whole.\n\nAdding a setting anchored on the last line that began at the top level\nof its block, which is the opening line of a nested table when the block\nends with one; the new line went inside that table and left a stray\ncomma after its brace. The anchor is now the last line that ends back at\nthe top level.\n\nThe file is also read, changed and written back under a lock, with a\ntemp name of its own: the settings page is live while a theme applies,\nand both edit this file. A shared temp name meant one writer could\nrename a file the other was still filling.",
"38a38e133": "Editing a rule rebuilt it from the editor's own fields, so a rule made by\nhand or carried by a theme lost anything this editor has no widget for.\nThose keys ride along untouched now.\n\nThe preview compiles the pattern with the browser's regex, but Hyprland\nmatches with RE2 \u2014 a pattern one accepts and the other doesn't was\ntreated as invalid and blocked the save. It only means there is nothing\nto preview, and says so.\n\nA colour field stopped following its value after the first typed edit,\nand the switches on the decorations page kept their old positions\nthrough a reset: both had lost their bindings to an assignment, and both\nare re-armed. Summaries build their text through placeholders so a\ntranslation can move the number, and the write helper nothing called is\ngone.",
"42f28c42b": "A snapshot from before the border and corner keys were split carries the\nold bools, and borders off meant no border, no gaps and no resize edge \u2014\nnot just a zero border size. The schema already spells out what each old\nbool drove, so the restore reads that table instead of the two mappings\nit knew by name.\n\nA failed snapshot left an empty object behind, which now reads as reset\neverything to stock; it leaves no file at all, which an apply skips. The\nstock animation profile is loaded through pcall like the chosen one, so\na broken profile can no longer take the input and misc config down with\nit, and animationProfile joins the motion group its sibling is in.",
"7ba676f27": "Both files render values from a theme into Lua the compositor evaluates,\nand a theme can arrive by import from anywhere. The window-rule custom\nvalue accepted any brace-wrapped text, so a table constructor could hold\na call instead of a list; it now takes a comma-separated run of literals\nand nothing else. Decoration strings went in unescaped, so a value\ncarrying a quote could close it and continue as an expression; the quote\nand the backslash are now escaped on both paths that reach Hyprland, and\nthe vector pair is coerced to numbers rather than pasted.",
"0ba6d1ff2": "A theme saved before a setting existed says nothing about it, and the\nrestore used to leave such keys alone \u2014 so applying an older theme kept\nwhatever the previous theme had put there, and the older theme stopped\nlooking like its save. The new restore verb fills unnamed keys from the\nschema defaults instead: stock is what the machine showed when the theme\nwas saved. Snapshots from before two keys were renamed still say\nborders / roundCorners; a false there now restores as its modern\nspelling's zero. The completed set feeds both the file write and the\nlive keyword push, so the compositor hears the same values the config\ngot.",
"c03548021": "A saved theme snapshots the rules store, empty list included \u2014 no\nrules at save time is part of the look. Applying restores through\nthe owner script's write verb rather than a copy, so an imported\ntheme's rules pass the same validation the settings page gets.\nThemes saved before rules existed carry no file and leave the live\nrules alone.",
"fdd069756": "Per-app exceptions to the sections above it: pick an open window,\nor type a pattern and watch which of the windows open right now it\ncatches, then choose from curated effects \u2014 placement, transparency,\nfrost, chrome, tearing, idle inhibit \u2014 with a custom escape hatch\nfor the rest. Cards reorder because later rules win.\n\nLast section on the page, so the list grows without pushing other\nsettings around. The editor is built fresh on every open: a clicked\nswitch keeps its own state over a binding, and a persistent editor\nstops following the draft reset. Saving or cancelling scrolls the\npage back to the section, where a new rule's card is.",
"7f4c5df90": "Rules the shell manages live as JSON and compile whole into\nuserrules.lua, loaded after rules.lua so a rule made in settings\noutranks a shipped one for the same window. One direction only:\nnothing parses Lua back, and rules written by hand elsewhere are\nnever read, shown or touched. The write verb holds every effect to\na schema \u2014 a custom effect is a field name plus a literal that must\nlook like one, so the generated file cannot carry anything else\ninto the compositor's config.",
"01ad9606c": "Every window section returns to what a fresh install has, from the same\ntable the track marks are read from \u2014 the reset and the dots on the\nsliders cannot disagree about what stock is. Title bars go through their\nown service, which owns the flag and the plugin's reload; the border\ngradient resets through config.json, where its service is watching.\nSystem look and below are left alone: the cursor and the fonts belong to\nthe whole desktop, not to its windows.\n\nThe reader's assignments moved into a function the reset shares, so what\na key drives is written once.",
"cc0da30a3": "Every section in the window half of the page says so in its title; the\ntoggle section at the top is part of that half and now says so too.",
"e7dee1b99": "The animation set moves out of the main config into named profile files \u2014\nexpressive is the stock set verbatim, joined by snappy, smooth, bouncy and\nminimal. A profile is the whole set at once: curves, speeds and styles are\ntuned against each other, so they travel together rather than as fourteen\nseparate knobs. The name in animations/active picks the file at config\nload; a missing or unreadable name falls back to the stock profile, and\nthe file is read with dofile so a reload always sees what is on disk.\n\nThe Decorations page offers the profiles in an Animations section of its\nown, greyed with the animations toggle. The picker lists whatever the\nprofile directory holds: shipped profiles keep their translated names and\ntheir order, everything else is named by its filename \u2014 so a profile\nsomeone wrote themselves shows up without the page having to know it\nexists.\n\nThe choice rides the decoration schema as a namefile key: it reads and\nwrites the one-word file rather than a Lua field, rea",
"caeb8653f": "Two modes. The default draws from the palette's primary and tertiary at an\nangle and is re-derived whenever the palette moves, so it keeps up with the\nwallpaper the way everything else does \u2014 a pair of literal colors would be\nright once and then wrong after the next wallpaper. The role pair itself is\nnot offered as a choice: every other combination sits too close together to\nread as a gradient at all, so that setting was four ways of picking the same\nflat border and one that worked.\n\nThe second mode holds the two colors literally and stops tracking the\npalette, which is the point of asking for them. They are entered with a\nswatch, a hex field and an eyedropper. The eyedropper is the one that\nmatters: a color worth putting in a border is usually already on the screen\nsomewhere, and hyprpicker returns whatever is under the cursor.\n\nEither way it is written to the shell's override file, which is required\nafter the generated palette, so matugen regenerating its own border color\nleaves this",
"70c25ee6e": "The last three the blur block holds. Noise puts grain over the blur, which\nis what hides the banding a heavy blur leaves on a gradient. Saturation\ndecides how much colour it keeps from what is behind. See-through blurs\nthe desktop rather than the windows stacked underneath.\n\nThe first two are sliders; the third is a switch, being a switch.",
"f5a09782d": "Shadow size, falloff and darkness get a section of their own, greyed\nwhile shadows are off, the same way blur's rows behave.\n\nShadow falloff is the setting that could not be reached before. It sits\nafter the offset pair inside the shadow block, and a scan that stopped at\nthe first closing brace never got that far. It runs 1 to 4, the range the\ncompositor honors \u2014 it clamps anything higher to 4, so a longer track was\nselling distinctions that all rendered the same shadow. The shipped\nconfig said 10 and drew 4; it says 4 now.\n\nSize and falloff only shape the shadow; whether any of it shows is the\nalpha of its color, and that had no control. A darkness slider carries it.\nThe hue is kept from whatever the color already is, so a theme that tints\nits shadow keeps the tint while the darkness moves. The color travels\nthrough the schema as the whole rgba string, which gives the reader and\nwriter their first string-typed key: parsing strips the quotes, rendering\nrestores them and escapes what it",
"f250d71b5": "Four more settings the compositor has always had, in a Shape section\nbeside the toggles that switch them on: corner radius, border thickness,\nand the gaps between windows and around the screen edge. Each is a row\nagainst the schema.\n\nA slider row can ask for a shorter track. The label had 120px whatever it\nsaid, and anything longer ran underneath the track \u2014 \"Fullscreen windows\"\nsat on top of its own slider. Long labels are cut short rather than\nallowed to overlap, and the rows that need the room take a 340px track\ninstead of the rest of the line.\n\nThe Borders switch no longer resets the gaps. It set them to 4 and 5 on\nits way through, from when there was nowhere else to put them; with rows\nof their own that would undo whatever was chosen there. A theme saved\nbefore those rows existed still zeroes them, so an old theme looks the way\nit did.\n\nThe marker each track carries sits at the start of its own range rather\nthan at 1, which on a track that stops at 0.8 was drawn past the end.",
"c6787734f": "Five settings the compositor has always had and the page never offered:\nblur radius and passes, the focused and unfocused window\nopacities, and how far an unfocused window is dimmed. Each is one row\nagainst the schema \u2014 no new parsing, no new restore path, and a theme\nsaved now carries all of them.\n\nA slider emits on every pixel of a drag, and each write is a process and a\nround trip to the compositor, so the changes are collected and sent once\nthe drag settles. Several sliders moved together arrive as one change.\n\nBlur radius and passes are greyed rather than hidden when blur is off, and\nthe dim amount likewise when dimming is off: the setting stays where it\nwas found instead of the page rearranging itself as things are toggled.\n\nThe two opacities are not written in the shipped config \u2014 Hyprland\ndefaults them \u2014 so their lines appear the first time one is moved.\n\nBoth sections go above System look rather than below it. System look picks\nthe app style, the icon set and the mouse cursor,",
"53c3b59e1": "The settings page, the snapshot a saved theme takes, and the restore an\napply performs each parsed and rewrote general.lua themselves, in three\nlanguages \u2014 regexes in the page's JavaScript, in the snapshot's Python, and\nin apply-theme's. Six settings across three implementations was tolerable;\nit is the shape that stops being tolerable, because nothing keeps the three\nin agreement and a disagreement shows up as a setting that saves but does\nnot come back.\n\nThey share one now. decorations.py knows where each setting lives and\nnothing else does; a schema file lists the settings and every site iterates\nit. Adding one is a JSON object rather than three regexes.\n\nWhere a setting lives is derived from its hyprctl keyword rather than\nwritten down twice: decoration:blur:size is the field `size`, inside\n`blur`, inside `decoration`.\n\nBlocks are found by matching braces. The old scan ran to the next `}`,\nwhich cannot reach a field sitting after a nested block has closed \u2014\nrender_power is behind s",
"bcea466c7": "A right click on a text field opened the toolkit's editing menu, drawn in\na style nothing else on the desktop uses. The fields draw the same menu\nevery other right click gets \u2014 the shadowed surface card with ripple rows\n\u2014 offering cut, copy, paste and select all, each row live only when its\naction can do something. A field showing dots instead of characters keeps\nits content out of the clipboard: cut and copy stay disabled however much\nis selected. The toolkit's own menu is switched off, since leaving it in\nplace would open both.",
"34d249890": "Two strings on the Display page kept the British spelling while every\nother string says color. The translation keys follow the source strings,\nso each file carries the new key in the old one's place.",
"0b783abbf": "A slider's value is bound to the setting it edits and animates towards it, so\na setting changed anywhere else \u2014 a theme apply, another window, an edit to\nconfig.json \u2014 walks the handle across a run of intermediate values. Saving on\nvalueChanged wrote one of those back, and once written the binding settled\nthere: the clock font sliders, with no equality guard at all, came to rest a\nfraction below whatever they were aimed at.\n\nmoved() is emitted for a drag or a key press and never for a change that came\nin through the binding, which is the distinction that was missing. The track\nstill follows the bound value, so a setting changed elsewhere moves the handle\nwithout being written back.",
"d4d80ac53": "A window rule blanked blur on every window, so the blur settings reached\nonly the shell's own surfaces, and a window made translucent showed what\nwas behind it untouched. The compositor already skips opaque windows \u2014\nblur is computed only where alpha falls below one \u2014 so windows at full\nopacity look and cost exactly what they did, and the transparency\nsettings are what bring the frost in.\n\nThe xwayland menu rule stays: those buffers carry their own shadows, and\nblurring them draws a halo around every context menu.\n\nThe blur brightness line is gone because one is the value at which both\nof its shader branches leave the image untouched.",
"95862df83": "Two limits in the writer behind HyprlandConfig, both reached by the border\ncolour and neither by anything using it today.\n\nA key whose last part contains a dot came out as\n{general={col.active_border=...}}, which Lua will not parse \u2014 a bare name\ncannot hold a dot. It is written as a bracket string now. Every caller so\nfar separates with colons only, so nothing was producing this yet.\n\nValues could only be scalars. A gradient in Lua is a table:\n{colors={\"rgba(..)\",\"rgba(..)\"},angle=45}. The single-string form hyprlang\ntakes is rejected outright \u2014 \"invalid color\" \u2014 so the shell had no way to\nexpress one. --set-lua writes a value through untouched for that case.\n\nThe file this writes is required after both the generated palette and the\ncustom overrides, so what it holds survives a theme apply: with a border\nset here, matugen regenerating its own value leaves the override standing,\nand resetting the key puts the palette back.\n\nAn edit applies itself: the file is replaced atomically, which ",
"8a1dccb6e": "Locks and fallbacks.\n\nThe derivatives lock had no timeout, and the section it guards re-encodes\nwith ImageMagick and can reach pkexec for the login background. Bounded at\nthirty seconds; a run that gives up still does its work, so a wallpaper\nchange costs a derivative that may land out of order rather than a\nsubshell parked forever.\n\nThe Papirus base is checked before anything is published. Only */places is\noverridden and the rest is inherited, so with no base on disk the theme\nresolves to bare hicolor \u2014 every icon on the desktop. Nothing to layer on\nnow means nothing published and the icon theme left as it was. The\nreadback also samples an alias beside folder.svg, since the links are laid\ndown one at a time and a directory holding the newer folder.svg over\naliases from the previous colour otherwise reads as finished.\n\nA restage that is skipped leaves a marker. Waiting is what this script may\nnever do, but forgetting is not required: a burst now lands the last\npalette rather than the f",
"3205817aa": "config.json. The three places switchwall edits it \u2014 the wallpaper path, the\nthumbnail path, the accent colour \u2014 each read it, filtered it through jq\ninto a temp named after the file itself, and renamed that over the top,\nwith no lock. The temp name being fixed is what turns a lost update into a\nbroken file: two writers open the same path, one renames a document the\nother is still writing, and what lands is truncated JSON. An unreadable\nconfig is an empty wallpaper path, which is the invisible bar. They share\none helper now, with a unique temp beside the target and a short bounded\nlock; failing to take the lock still writes, since a rewrite that raced is\nbetter than one that never happened.\n\ngeneral.lua. Both writers replaced it in a way that empties it first \u2014 one\ncopying in from /tmp across filesystems, the other opening it for writing\nand refilling on close \u2014 while switchwall leaves a hyprctl reload running\nin the background that can parse it at any moment. A reload landing inside\nth"
}
},
{
"version": "1.1.0",
"date": "2026-07-31",
"kind": "feature",
"summary": "A bar you arrange, themes you can share, and a look you choose",
"changes": [
"Export a theme to a single file and import it on another computer",
"A saved theme now remembers your app style, icons and pointer",
"Applying a theme or wallpaper finishes in about a third of the time",
"Switching themes no longer flashes the new layout with the old colors",
"A theme that fails to apply now says so instead of doing nothing",
"Show, hide, reorder and group any widget on your bar",
"Drag two bar widgets together to combine them into one pill",
"Choose your app style, icon theme and mouse cursor in Settings",
"Pick fonts from a searchable list that shows each one in its typeface",
"Most apps now use the font you pick for the desktop",
"Pick how a dock icon animates when it launches an app: Bounce, Pulse, Pop or Wobble (thanks @sanaruca, end-4/dots-hyprland#3553)",
"The last item on a to-do list is no longer tucked behind the add button (thanks @ar-Raqmi)",
"Video wallpapers just play, with no package to install yourself",
"Pick a cursor size from the sizes your theme can actually draw",
"Turn on Shake to Locate to magnify or enlarge a lost cursor",
"Mirror one display onto another from the monitor settings",
"A bar icon and a notification tell you when a new release is out",
"The script install now lands on the newest release, with --edge to follow the branch instead"
],
"commits": [
"fc489658c gaming: fix gaming-mode-install conflicts on legacy nvidia installs",
"6b89a4367 gpu-config: detect any installed NVIDIA driver, not all of them",
"880a9650c system: re-install Limine BIOS stages after a limine upgrade",
"b88763b93 Update release-notes.json",
"dce475292 resolve: add an Install button to the update notification",
"42584c974 welcome: fix duplicate Component.onCompleted breaking launch",
"af918239c shell: surface Resolve update state in Welcome",
"3651449d9 resolve: add mainstream-resolve-update-check notifier",
"9abb6e5a7 previews: store them small instead of holding them decoded",
"7e0dc3973 bar: stop the retry timer from replacing the icon lookup",
"298b4c480 dock: stop the retry timer from replacing the icon lookup",
"fa22e9d0d keybinds: shell to desktop",
"e59933c12 Update desktop.webp",
"59652912d Update: comment out the version notice for now",
"279b3f030 update: put the version in a notice of its own",
"7294116fa update: build the changelog from tags, with curated notes on top",
"51b31d0fa translations: register the strings added since 1.0.5",
"2bbacaaf1 settings: draw the display arrangement in logical coordinates",
"003aabe9e sddm: migrate an existing greeter config to Lua on update",
"75e85438a sddm: move the greeter's Hyprland config to Lua",
"45d340364 plugins: arm the ABI guard on fresh installs instead of passing them open",
"dd6b20bf6 plugins: stamp the ABI guard with the package release, not just the version",
"8dfc1fdd6 themes: keep the saved-theme previews decoded between visits",
"b94b321c5 dock: stop rebuilding every app entry when a window opens",
"049964f57 dock: animate the icon when an app launches",
"251af81da bar: stop seeding a widget into a saved layout",
"cee970e26 gpu-config: classify pre-GCN fusion APUs as old AMD",
"963589125 gpu-drivers: drop the Vulkan driver on pre-GCN AMD",
"d9c41c47f gpu-config: select Intel packages by GPU generation",
"4a80440ca resolve: carry the fixes worth taking from the AMD and NVIDIA installers",
"2967d5ff3 dock: quiesce a window preview before closing it",
"8af023054 overview: hand the tile back when a close is refused",
"31fc57c07 settings: show which release the system is on",
"85e4637cf extras: stop claiming the app store handles system packages",
"5753697d4 wallpapers: add the primary lockup at 8K",
"a464a7151 install.sh: default to the newest release, add --edge for the branch",
"a445163e4 bar: describe the stock layout once",
"7ceb2fc70 manifest: stop describing the desktop, and drop the version nobody reads",
"21c254948 bar popups: leave the hover ones out of the dismissal plumbing",
"903e9315d updates: keep the release cache out of the tree updatems resets"
],
"commitsTotal": 112,
"commitNotes": {
"fc489658c": "The \"Steam Big Picture\" step of mainstream-welcome-install always adds lib32-nvidia-utils for any NVIDIA GPU (PCI vendor 10de), regardless of which driver branch is actually installed. On cards that need a legacy branch \u2014 e.g. a GTX 1050 (Pascal) running nvidia-580xx-utils \u2014 this pulls in mainline nvidia-utils and conflicts with the already-installed legacy driver on nvidia-libgl. Since the transaction runs --noconfirm, pacman can't prompt to resolve it, and the whole step fails.",
"6b89a4367": "_gpu_nvidia_has_driver passed all seven driver package names to a single\n`pacman -Qq`, which exits 1 unless every one is installed. Since the branches\nconflict with each other that can never hold, so the probe was always false and\nsilently skipped the entire NVIDIA path in both orchestrators: no\nnvidia_drm.modeset=1 on the kernel cmdline, no /etc/modprobe.d/nvidia.conf, no\ndeferred KMS, no suspend/hibernate/resume units, and none of the GBM_BACKEND /\nLIBVA_DRIVER_NAME / NVD_BACKEND / hypridle tweaks.\n\nQuery one package at a time and return on the first hit. The test harness stubbed\nthe probe out, so the semantics were never exercised; fixture it at the new\npacman seam instead and assert any-of behaviour.",
"880a9650c": "Limine boots a BIOS machine from stages 1-2 in the MBR and stage 3\n(limine-bios.sys) on the boot filesystem, all from one package build. Upgrading\nlimine replaces stage 3 and cannot touch the MBR, and Arch ships no hook to\nreconcile them \u2014 so the boot after a routine `pacman -Syu` drops straight into\nfirmware setup with no bootloader and no error.\n\nShip limine-bios-sync and 61-limine-bios.hook from mainstream-system, which the\nISO already installs. Package-owned means it reaches machines already in the\nfield on their next upgrade, not just fresh installs. No-op on UEFI.",
"dce475292": "The weekly check told the user a new DaVinci Resolve was out and left them to\nrun the installer themselves. Put the action on the notification instead.\n\nPressing Install now hands off to `pkexec mainstream-welcome-install\nresolve-update` \u2014 the same privileged path the Welcome app's install button\nalready uses, and already covered by 49-mainstream-welcome.rules, so this adds\nno new privileged code, no new polkit rule, and no password prompt on a local\nsession. Nothing runs unless the button is pressed; --no-action removes it.",
"42584c974": "The Resolve version check was added as a second Component.onCompleted on the\nroot ApplicationWindow, which already had one for reapplyTheme(). QML allows one\nassignment per property per object, so the whole config failed to load:\n\n Property value set multiple times\n\nMove refreshResolve() into the existing handler. Same behaviour, one handler.",
"af918239c": "The Updates service shells out to checkupdates, which by construction cannot\nsee a local package \u2014 so the one piece of software Mainstream installs that way\nwas the one the update UI could say nothing about.\n\nUpdates.qml gains the Resolve state, read from `install-davinci-resolve --check\n--json` so the version logic stays in one place. refreshResolve() is on demand\nonly, deliberately not wired to the existing 120-minute checkInterval: one\nrequest to Blackmagic every two hours from every install is the stampede the\ntimer's RandomizedDelaySec exists to avoid. Every failure path leaves the\nproperties untouched, so the UI degrades to exactly what it showed before.\n\nwelcome-tutorial.qml relabels the two Resolve rows \u2014 Install / Installed /\nUpdate to X.Y.Z \u2014 and locks a row that is already current so the button cannot\nkick off a no-op. Package-to-row matching is prefix-based, so a beta claims its\nrow without implying a version comparison.",
"3651449d9": "A thin wrapper around `install-davinci-resolve --check --json` \u2014 it owns no\nversion logic of its own, so the two can never disagree about what \"out of\ndate\" means.\n\nIt notifies and nothing else: no pkexec, no sudo, no call into\nmainstream-welcome-install. The notification names the new version and the\nexact command to run; running it is the user's decision.\n\nEach version is announced once, tracked in\n$XDG_STATE_HOME/mainstream-resolve/last-notified, since the timer fires weekly\nand Blackmagic can go months between releases. --force re-notifies, --quiet\nreports through the exit status only.\n\nExit codes mirror --check (0 / 10 / 1), with one deliberate exception: a failed\ncheck exits 0 in silence. This runs on a timer, and a laptop that wakes on a\ntrain with no network must not produce an error notification.\n\nresolve: fix silent exit when no OpenCL ICD is installed\n\nverify_opencl counted the installed ICDs with `ls /etc/OpenCL/vendors/*.icd |\nwc -l`. With no ICD present the glob goes unma",
"9abb6e5a7": "8dfc1fd went to some trouble to avoid re-decoding the saved-theme previews: a\nsingleton held a reference on every one so Qt's sweep couldn't drop them, and\nthe size and fill mode had to match across the pins and the cards or the pixmap\nkeys diverged silently. All of it was in service of a preview.png that grim\nwrote at monitor resolution \u2014 several megabytes and a few hundred milliseconds\neach, for an image nothing draws larger than 400x225.\n\nCapture caps it at previewMaxDimension on the way out now. What is left is cheap\nenough to decode that holding it was more machinery than the problem justified,\nso the pins and the Instantiator are gone and ThemeLibrary is back to being the\nindex, the last-applied slug and their watchers. `>` on the resize only ever\nshrinks, and a missing magick leaves the full-size shot rather than the save\nlosing its preview.\n\nThe two views of the live wallpaper had the same problem and no fix at all: both\nread the wallpaper itself with cache: false, and Quick Se",
"7e0dc3973": "Same defect as the dock, one directory over: the retry timer assigned to\ndesktopEntry, which is a binding, so the first retry froze the property at that\nattempt's result.\n\nTask buttons run off the same TaskbarApps.apps model and stopped being rebuilt\non every window open at the same time the dock's did, so they lost the same\naccidental repair. The lookup reads the entry list to pick up entries that\narrive late, and the timer bumps a counter instead of assigning.",
"298b4c480": "Chromium and mpv would sometimes fall back to a generic icon and stay on it\nuntil a reload.\n\ndesktopEntry is a binding, and the timer that retries the lookup assigned\nstraight to it. Assigning to a bound property throws the binding away, so from\nthe first retry onward the property was frozen at whatever that one attempt\nreturned \u2014 null included, which is how the icon ended up on the guessIcon\nfallback and stayed there.\n\nIt never showed before because every window that opened handed the model a\nfresh object for every app, so the list view dropped and rebuilt each delegate\nand the binding came back with it. Keeping entries between runs keeps the\ndelegates too, so a binding thrown away while the database was still filling in\nnow stays thrown away for the rest of the session. That is also why it only\never hit some apps: the ones whose id resolves past byId into the heuristic and\nfuzzy paths are the ones still coming back null when the timer first fires.\n\nThe lookup is readonly now and read",
"279b3f030": "Sitting beside the System Update heading, the version read as part of the\nheading rather than as something being reported. It moves into the same kind of\nnotice the page already uses for its other two remarks, so it is separated from\nthe title and sits with the rest of what the page has to say.\n\nThe icon carries the state \u2014 a check when there is nothing to move to, the\nupdate glyph when there is \u2014 which is what the colour was doing before. Inside\na notice the old colour would have been read against the wrong background.\n\nThe arrow still only appears when there is a release to point at.",
"7294116fa": "The manifest derived everything from commit subjects, so a release could only\never say what one commit said. The first release had no predecessor tag to diff\nagainst and derived nothing at all.\n\nrelease-notes.json now overrides summary and changes for named versions only.\nEverything else stays derived, so the default cannot drift and every deviation\nis visible in one file. Kind stays tag-driven \u2014 the tag is the right place to\nsay how important a release is. A curated list is not capped either: the cap\nexists because a derived list is however long the commit range happened to be,\nwhile a curated one is exactly what someone chose to say.\n\nAlongside that, every release carries its unedited range: commits as\n\"<short sha> <subject>\", commitNotes for the commits that wrote a body, merges\nwhen the first-parent walk stepped over any, and commitsTotal when the cap\ntruncates the list. These are derived and deliberately not overridable, because\ntheir whole value is that anyone can reproduce them ",
"51b31d0fa": "Sixty-five interface strings landed with the bar layout editor, theme import\nand export, the Decorations page, the release indicator and its popup, the\ncursor and shake-to-locate settings, and the font picker. None of them existed\nin any language file, so none could be translated at all.\n\nAll fourteen files gain the same sixty-five keys, each holding the English\ntext. That is what the extraction tool writes for a new key, and it is what the\nrest of these files already look like: between half and three quarters of every\nlanguage is still the English placeholder waiting on a translator.\n\nDeliberately not machine-translated. A placeholder announces itself \u2014 a\ntranslator can find every outstanding string by looking for a value equal to\nits key \u2014 while a plausible but wrong translation reads as finished work and\nsurvives review. Getting them right is for people who speak the language.",
"2bbacaaf1": "The monitor-arrangement canvas sized each rectangle from the mode's\npixel width/height while positioning it from x/y, which hyprctl reports\nin logical coordinates. On a scaled display the rectangle came out too\nbig and its neighbour was pushed too far away \u2014 a 4K at 1.666667 was\ndrawn 3840 wide and shoved the next monitor 3840 across instead of 2304.\nRotation was ignored entirely, so picking Portrait left the preview\nrectangle landscape.\n\nAdd a logicalSize() helper that swaps the axes for transforms 1/3/5/7\nand divides by the scale, matching what Hyprland does, and use it for\nthe sizes in canvasLayout, resolveEffectivePos and the canvas delegate.\nThe scale comes from monitors.lua rather than hyprctl's JSON, which\nprints it to two decimals and would turn 1.666667 into 1.67 \u2014 a 5px\nerror per monitor that accumulates across the row.\n\nPreview only: the applied configuration was already correct, because\nPosition writes an auto-center-* keyword and Hyprland does the real\narithmetic. The prev",
"003aabe9e": "The switch to a Lua greeter config only reached fresh installs. exp-update,\nwhich is what updatems runs, never re-runs the setups step, so a machine\ninstalled before the switch kept its .conf and kept showing the deprecation\nnotice at every login with nothing to clear it.\n\nupdatems-system already refreshes the greeter's neighbours \u2014 the background\nhelper and both polkit files \u2014 on every update, and runs as root, so the\nmigration goes there. It only acts when the greeter directory already exists,\nso it will not set one up on a machine that never asked for one.\n\nHyprland looks for hyprland.lua before hyprland.conf, so writing the new file\nis what actually resolves it. The old one is moved to hyprland.conf.old rather\nthan deleted, and only once the write has succeeded: if a greeter somehow will\nnot start, renaming one file puts it back. That is the same thing the installer\nalready does with a user's own pre-Lua config.\n\nThe config itself moves out of the heredoc into sdata/sddm/hyprland.l",
"75e85438a": "The ISO writes this config from post-install-login; ./setup writes the same one\nfrom here, so both had to move together or a scripted install would still show\nthe .conf deprecation notice on the login screen.\n\nByte-identical to the archiso copy. The old .conf is removed first, so a machine\ninstalled before this stops carrying both.",
"45d340364": "The guard reads two stamps: /var/lib/hyprland-plugins/hyprland-version for the\nHyprland this system has, and <plugin>.builtfor for the one each .so was built\nagainst. It returned true whenever either was missing, and nothing but the\nrebuild scripts ever wrote them. Those run from the pacman hook on a hyprland\nupgrade, or from a timer five minutes after boot -- neither has happened yet the\nfirst time a new install reaches the greeter. So every fresh install loaded both\nplugins with no version check at all, which is the one moment a mismatched .so\nis most likely, and a segfault at dlopen there is a login loop.\n\nThe system version now gates the check. Absent, this is not an install whose\nplugins we manage and the check still passes open. Present, a .so with no stamp\nis refused rather than trusted -- the same conclusion quarantine_stale_targets\nalready reaches when it moves a stampless plugin aside.\n\n./setup now writes both halves: the system version before it builds, so a failed\nbuild sti",
"dd6b20bf6": "The guard records which Hyprland a plugin was built against, and it read that\nfrom pkg-config, which reports 0.56.1 and knows nothing about the package\nrelease. A rebuild that only moves the release changes the ABI: 0.56.1-1 to\n0.56.1-2 landed today, rebuilding Hyprland against aquamarine 0.14.0, which\nflips _aq_0.13 to _aq_0.14 inside the hash a plugin checks at load. The stamp\neither side of that read 0.56.1, so the guard would have compared them equal and\nhanded back a plugin built against the older ABI.\n\nIt asks pacman first, which reports 0.56.1-2, and keeps pkg-config as the\nfallback so a rebuild against a hand-built Hyprland still works.\n\nAll four scripts move together. The version they write is read back by\nquarantine_stale_targets, by each .so's .builtfor file, and by the check in\ncustom/general.lua that runs at every login \u2014 change one and the comparison\nfails closed, silently disabling both plugins.",
"8dfc1fdd6": "Settings swaps one Loader's source to navigate, so the Themes page is torn\ndown and rebuilt every time it is opened \u2014 and again on every apply, since\na theme change bumps Appearance.themeRevision and forces a page reload. Each\nrebuild re-read and re-decoded every preview.png, which are full-monitor grim\nscreenshots: 24 MB and about 450ms of decode across five saved themes, scaling\nlinearly with the number of themes.\n\nThree things had to change together, because any one alone leaves the reload\nvisible:\n\nThe index, last-applied slug and their loader processes move to a new\nThemeLibrary singleton, so a rebuilt page has its grid populated on the first\nframe instead of after two subprocesses return. That removes the empty-grid\nphase and the \"\u2014 None \u2014\" flash in the Day/Night dropdowns.\n\nThe previews drop cache: false. The ?v=<created> token already in their URLs\nis the invalidation mechanism and is restamped at millisecond resolution\neverywhere preview.png is written, so a replaced screensho",
"b94b321c5": "Opening an app made the whole dock slide sideways for a moment and settle back.\nIt was never the click: the app list is a binding over the open windows, so a\nwindow appearing re-ran it and handed the model a brand new object for every\napp, including all the ones that had not changed. The model treated each as a\nnew item, the dock dropped and re-added every icon, its appearance transition\nran, and the animated width moved the centred row.\n\nThat also explains why it only happened on a launch. Dragging re-runs the same\nbinding but suppresses the transition while reordering, and clicking an app that\nis already open or right-clicking it never touches the window list at all.\n\nEntries are now kept between runs and updated in place, so unchanged apps keep\ntheir identity and their delegates. An app that genuinely appears or goes away\nstill gets a new or destroyed entry and still animates.\n\nWorth having on its own: every open window used to rebind every dock delegate.",
"049964f57": "Cherry-picked from upstream, with the launch animation defaulting to Bounce.\n\nThree places needed a decision rather than a straight merge:\n\nUpstream played the animation on every click. Here a click also opens an app\nfolder or toggles a window preview, and neither is a launch \u2014 animating those\nwould say something happened that did not. It now plays only in the branch that\nactually starts the app.\n\nThe icon loader keeps its exclusion of folders, which have their own loader and\nnever launch, and gains the animation transform.\n\nThe tint overlay had to repeat that transform. It is anchored to fill the icon,\nbut anchors follow geometry and scale/rotation do not touch geometry \u2014 so with\nTint app icons on, the tint would have sat still while the icon moved under it.\n\nThe picker sits under Tint app icons in Interface settings, where the rest of\nthe dock's appearance options are.",
"251af81da": "Slotting the release indicator into layouts saved before it existed meant\nrecording which ids had been offered, and recording that meant writing the\nconfig file from the load handler. At 1.0.5 that handler was one line and the\nshell wrote this file exactly once, when it created it \u2014 nothing else touched it\nat startup.\n\nThat is a property worth having back. A first login sets the wallpaper path from\noutside the shell a moment after login, and it works only because nothing else\nwrites the file afterwards. Adding a startup write puts a second writer into\nthat window for the sake of one widget appearing on its own.\n\nSo the seeding goes, and with it the record it needed. Someone upgrading will\nnot find the release indicator in their bar; it is in the layout editor with\neverything else. A widget you have to add yourself is a smaller cost than a\nfirst boot that comes up unthemed.\n\nThe legacy-switch migration stays: it only fires for a file written before\nbar.layout existed, which no fresh ins",
"cee970e26": "Llano/Sumo, Wrestler and Trinity/Richland sit well above the pre-GCN id\nceiling and advertise HD 7xxx/8xxx names, so neither the id test nor the\nname regex saw them. They were handed the amdgpu-only autoconfig arm\ndespite needing the radeon module, and a RADV package set that has no\nsupport below GCN1.\n\nMatch their four id windows exactly, so the GCN APUs sharing those pages\nstay modern: Kabini/Temash, Mullins, Carrizo/Stoney and Kaveri.",
"963589125": "RADV is GCN1+ only, so vulkan-radeon enumerates zero devices on pre-GCN\nparts, and because it provides 'vulkan-driver' it also satisfied the\ncatch-all and suppressed the lavapipe fallback, leaving no working Vulkan\nat all. Install mesa alone there and let the catch-all supply swrast.\n\nVA-API is part of mesa itself now, so the retired libva-mesa-driver\ntarget goes with it. Aligns ./setup and the gpu-drivers tool with the\nISO install.",
"d9c41c47f": "Intel package selection carried no generation signal, so every Intel GPU\nreceived one flat set: the iHD VA-API driver alongside the legacy i965\ndriver, and never an OpenCL runtime.\n\nAdd INTEL_GEN (xe|modern|legacy|none) to gpu_detect. Intel device ids are\nnot monotonic, so the tiers match known id prefixes newest-first with a\nname fallback for Arc/Iris Xe; anything unrecognised falls to 'modern',\nwhich is safe on any Gen8+ part.\n\nThe tiers track the two boundaries that actually change packages:\n xe Gen12+/Arc \u2014 the only tier upstream's compute runtime and the VPL\n runtime support; gets intel-compute-runtime + ocl-icd + vpl-gpu-rt\n modern Gen8-Gen11 \u2014 iHD VA-API, no OpenCL. Upstream moved Gen8-11 compute\n to legacy1 packages that Arch does not ship, so the runtime would\n be ~346 MB that cannot enumerate a device\n legacy Gen4-Gen7.5 \u2014 i965 VA-API, the driver that actually covers this\n hardware; predates iHD entirely\n\nRoute 2.setups.sh and scr",
"4a80440ca": "Four changes, drawn from two community installers and checked against this\nmachine rather than taken on trust.\n\nA launcher wrapper replaces the bare symlink, and the desktop entry now routes\nthrough it, so a menu launch gets the same treatment as a terminal one. It\nunsets QT_QPA_PLATFORMTHEME and QT_STYLE_OVERRIDE, which the session sets to\nqt6ct while Resolve's bundled Qt 5.15 ships no platformthemes plugins at all,\nand on a hybrid laptop it exports the PRIME offload variables. Without those,\nthe GL context comes from the iGPU while OpenCL runs on the NVIDIA card, and\nclCreateContext fails across the split \u2014 Resolve hangs on the Color page. The\ndetection runs at launch, not at install, so a MUX flip or a dock is picked up,\nand every gate is required: opencl-nvidia depends only on zlib, so an NVIDIA\nbox can have no GLX vendor library, where exporting the vendor name turns a\nworking launch into a GLX failure. Two lspci calls, because lspci honours only\nthe last -d and one call would hav",
"2967d5ff3": "The dock's preview tiles bound a ScreencopyView straight at the toplevel\nwith no gate, so both close paths tore the window down underneath a live\ncapture. hidePreview() only starts a fade \u2014 the Loader stays active and\nthe capture stays attached \u2014 so the toplevel died while the tile was\nstill capturing it, and Wayland answers that with a fatal EINVAL\nprotocol error that takes the whole shell with it.\n\nLatch the capture off before dispatching the close on both the\nmiddle-click and the close-button path. The latch is one-way for the\nlife of the tile: nothing here tells a client refusing to close apart\nfrom a close still in flight, and the next preview builds the tile\nfresh. Same guard the overview carries.",
"8af023054": "The closing latch that quiesces a window preview before dispatching a\nclose never cleared, so a client that refuses to quit \u2014 an unsaved-changes\nprompt is the usual reason \u2014 left its tile inert for the rest of the\noverview session: no hover, no click, no tooltip, and a stuck hover tint.\n\nSplit the latch. `closing` now drives only the input gates and clears\nitself after a one-second grace period, while a new monotonic\n`captureSuppressed` drives captureSource. Nothing here can tell a refused\nclose apart from one still in flight, so the capture stays down until the\nnext overview open rebuilds the tile; re-attaching to a toplevel that then\ndies is the fatal Wayland error this guards against.",
"31fc57c07": "About names the release next to the brand, and the System Update header\ncarries the transition the button is about to perform, so the version is\nvisible both where you look up what you are running and where you decide\nwhether to update. The arrow only appears while a release is actually\nwaiting; otherwise the header shows the installed version alone.\n\nContentSection gains titleExtra alongside headerExtra. The version belongs\nagainst the title it qualifies, and headerExtra sits the other side of the\nspacer that pushes buttons to the far end of the row, so it could only ever\nrender against the Copy button.\n\nReading these values instantiates ReleaseUpdates in the settings process as\nwell as the shell, so announcements are now gated behind _announceEnabled,\nwhich only the shell sets. Without that, opening Settings could raise a\nsecond notification for a release already announced. This mirrors how\nThemeManager keeps its Day/Night scheduler to the main shell.",
"85e4637cf": "Arch builds gnome-software without the PackageKit plugin \u2014 its shipped plugin\nset has flatpak, fwupd, appstream, even dpkg, and no packagekit at all, and\npackagekit is not among its optional dependencies. So the store has never\nhandled native packages here and cannot be configured into doing so; the\nplugin is a compile-time option, not a setting.\n\nThe description said otherwise, and every person installing Mainstream read it\nin the Included Extras list. The PKGBUILD's optdepends is generated from this\nsame file, so it corrects itself on the next build.",
"a464a7151": "The bootstrap cloned the mainstream branch and left the clone on its tip,\nso a script install landed ahead of every numbered release with no record\nof which release it descended from. Two things followed from that missing\nmarker: the desktop's release indicator read the install as unversioned\nand stayed silent, and the next updatems run reset the clone to the newest\ntag rather than seeing it was already past it.\n\nResolve the newest release tag with the same pattern updatems matches,\ncheck the branch out at it, and write .updatems-applied-tag once setup\nsucceeds. --edge keeps the previous behaviour and marks itself with the\nrelease it descends from, so an edge install still reports a version and\nupdatems leaves it alone until a release reaches it.\n\nBoth channels land on a branch rather than a detached HEAD, since setup's\nbranch detection tries to bounce a detached HEAD back to main or master\nand neither exists in this clone.",
"a445163e4": "The arrangement the bar ships with was written out twice \u2014 once as the\nconfig defaults a first run gets, once inside the settings page for its\n\"Reset to defaults\" button. Nothing held the two together, so adding a\nwidget meant remembering to edit both, and forgetting would have given\nReset a different bar than a fresh install.\n\nDeclare it on Config and have the adapter and the settings page read the\nsame thing.",
"7ceb2fc70": "The generator explained itself in terms of what the desktop does with the\nresult \u2014 which dot colour a kind produces, and a per-kind notification\nfilter that no longer exists. Say what the field is instead and leave the\npresentation to the thing presenting it.\n\nThe schema number went out with every manifest and nothing has ever read\nit. A version nobody checks is worse than none: it suggests a bump would be\nhandled somewhere.",
"21c254948": "A popup that only shows while the pointer is over its widget closes when\nthe pointer leaves, and has nothing to say to the focus grab. It was still\nlistening for a dismissal it could never act on, and unregistering itself\non teardown from a list it was never added to \u2014 so every bar widget you\nhovered over built a connection to the grab and emitted a signal into\nnothing. Only the click-triggered popups take part now.",
"903e9315d": "The manifest copy and the record of which release has already been\nannounced were written into the dotfiles clone, next to the tag file\nupdatems owns. They are untracked there, and updatems stashes untracked\nfiles and hard-resets that tree on every run \u2014 and re-clones it outright\nwhen the branch has moved on. So the record of what the user had already\nbeen told was thrown away by the very thing that made a new release worth\ntelling them about, and the notification came again for a release they had\nalready seen.\n\nPut the cache under the cache directory and the record under state, where\nthe rest of the shell's own files live. Only the applied tag stays in the\nclone, because updatems writes that one."
}
},
{
"version": "1.0.5",
"date": "2026-07-22",
"kind": "patch",
"summary": "A one-click Repair Install, plus graphics driver and screen sharing fixes",
"changes": [
"Repair Install on the Recovery page re-runs setup and rebuilds plugins",
"Adding the desktop to existing Arch now fits the matching NVIDIA driver",
"Without hardware Vulkan, Gaming Mode explains itself instead of bouncing you to login",
"Screen sharing and recording keep working after the desktop restarts"
],
"commits": [
"e057265e2 setups: fix GPU vendor detection + load gpu-config before it's used",
"5f7415a10 mainstream-system: own the plugin machinery + configs, add Repair Install",
"c4691cfad gaming: refuse to switch sessions without hardware Vulkan",
"633e23d96 hyprland: re-sync the screencast portal on compositor (re)start"
],
"commitNotes": {
"e057265e2": "detect_gpu_vendors matched the name 'ati', a substring of 'VGA compatible\ncontroller' \u2014 so every machine falsely detected an AMD GPU and installed the\nmesa/vulkan-radeon stack (seen on a GTX 1050). Match the PCI vendor IDs from\nlspci -nn instead: [10de] nvidia, [1002] amd, [8086] intel.\n\nAlso move the sdata/lib/gpu-config.sh source to the top of the file. It was\nsourced ~450 lines after setup_gpu_drivers runs, so gpu_detect /\ngpu_classify_nvidia_driver were 'command not found' and the NVIDIA driver\nclassification silently failed (no proprietary driver selected on a fresh\n./setup install).",
"5f7415a10": "Extracts the plugin-rebuild machinery (hyprbars/scrolloverview rebuild scripts,\nhooks, timers, status-notify, limine-restore-auto, preset) out of mainstream-extras\ninto a new mainstream-system package that also owns canonical /usr/share templates\nfor the two plugin configs. Its install scriptlet seeds /etc from those templates\nand comments out any stale version pin (HYPRBARS_REF/SCROLLOVERVIEW_REF) \u2014 the fix\nfor the unowned /etc/hyprbars.conf that froze hyprbars at v0.55.0 and blocked\nrebuilds on Hyprland 0.56. mainstream-extras becomes an apps metapackage depending\non mainstream-system; install-deps builds it first.\n\nAdds a Recovery-page 'Repair Install' button: pkexec mainstream-repair pins the\ndotfiles clone to the latest release tag, runs ./setup install-setups\nnon-interactively, re-asserts mainstream-system, un-pins the plugin configs, and\nrebuilds the plugins \u2014 streamed into the log pane, no terminal. updatems and\ninstall-setups also ensure mainstream-system."
}
},
{
"version": "1.0.4",
"date": "2026-07-21",
"kind": "patch",
"summary": "The next update picks up the config and packages an earlier one missed",
"changes": [
"An update no longer applies just part of a release",
"A failed package sync is retried once before the update stops",
"Closing a window from the overview no longer risks crashing the shell"
],
"commits": [
"cc5bafd6d update: apply the full last-applied-to-tag range and retry pacman once",
"e4ab2ca87 overview: quiesce a window preview before closing it"
]
},
{
"version": "1.0.3",
"date": "2026-07-20",
"kind": "patch",
"summary": "Safer update handling, fresher plugin rebuilds, and a quick-toggle fix",
"changes": [
"The desktop update is held back when the system package update fails",
"Title bar and overview plugins rebuild from the newest published source",
"Game mode shows its real state in the Classic quick toggles"
],
"commits": [
"51bbb7300 update: defer dotfiles when the package step fails",
"4a790d84b plugins: check out branch refs from origin so cached clones track pushes",
"52827ac9b extras: start the rebuild timers on install, not just next boot",
"6588a1b9e extras: own the plugin rebuild system and recovery wrapper",
"002606654 shell: dispatch through hl.dsp instead of legacy names",
"101e6127c quicktoggles: read bool-typed animations option on Hyprland 0.56"
]
},
{
"version": "1.0.2",
"date": "2026-07-20",
"kind": "patch",
"summary": "A Hyprland update can no longer strand you at the login screen",
"changes": [
"Title bars and the overview stay off until they rebuild for a new Hyprland",
"Restore Snapshot in Settings now makes the snapshot you booted from permanent",
"Super plus scroll moves workspaces the way you scroll again"
],
"commits": [
"c202752aa keybinds: track Hyprland 0.56 scroll axis naming fix",
"ff8320423 hyprbars: build from upstream hyprland-plugins",
"d368b3524 recover: drive the limine-snapper-sync 1.31 restore prompts",
"85da839bf plugins: cap rebuild commit walk-back at 15",
"7f5b29e88 plugins: survive Hyprland updates that outrun plugin ABI"
],
"commitNotes": {
"7f5b29e88": "A rebuild that fails for a new Hyprland version now quarantines the\nversion-mismatched .so instead of leaving it for dlopen to segfault on \u2014\nthe user logs into a plugin-less session with the existing status\nnotification instead of looping at SDDM, and the 24h retry timer restores\nthe plugin once upstream ships a compatible commit. Successful builds\nstamp each .so with the Hyprland version they were built for, and the\nconfig refuses to load a stamped plugin that does not match the installed\nHyprland \u2014 which also covers booting an older system snapshot against a\nnewer plugin in the shared home subvolume."
}
},
{
"version": "1.0.1",
"date": "2026-07-19",
"kind": "patch",
"summary": "Two small fixes for virtual machine guests and the installer's welcome screen",
"changes": [
"Copy and paste between your host and a QEMU/KVM virtual machine",
"The installer's welcome screen notes when you're already connected"
],
"commits": [
"f57c08e2d execs: start the SPICE agent inside virtual machines",
"7a73e9921 installer: note when the network is already connected"
]
},
{
"version": "1.0.0",
"date": "2026-07-18",
"kind": "feature",
"summary": "The first Mainstream OS release: a Hyprland desktop and an Arch-based installer ISO",
"changes": [
"Configure the whole desktop from one Settings app instead of a config file",
"Enter a gamescope Steam Big Picture session with Super+G",
"Retheme the shell, apps, terminal and lock screen from your wallpaper",
"Boot back into an earlier Btrfs snapshot from the Limine menu",
"Preinstall GE-Proton and set it as Steam's default compatibility tool",
"Arrange monitors and set scale, HDR and ICC profiles in Settings",
"Give each workspace its own layout: dwindle, master, scrolling, monocle or float",
"Remap any keyboard shortcut from Settings instead of editing config files",
"Turn window title bars on or off without logging out",
"Save wallpaper, palette and decorations as themes that switch day and night",
"Start with a set of everyday apps, each one optional at install",
"Install more apps from a store with pictures instead of package names",
"Zoom out to a scrolling map of every workspace with Super+O",
"Send files to nearby devices by dropping them on the bar",
"Reopen your windows on their original workspaces after a reboot",
"Install from a graphical installer with a Get Started setup picker",
"Encrypt the whole system with LUKS2 and a boot passphrase",
"Install alongside Windows and add it to the boot menu",
"Detect the graphics card during install and fit matching drivers",
"Meet the desktop with a first-boot tour and one-click extra installs",
"Update packages, Flatpaks and the desktop from one Settings button",
"Install prebuilt signed packages instead of building from the AUR"
],
"commits": []
}
]
}