Repository navigation
Expand file tree
/
Copy pathreleases.json
More file actions
1162 lines (1162 loc) · 235 KB
/
Copy pathreleases.json
File metadata and controls
1162 lines (1162 loc) · 235 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
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{
"latest": "3.0.2",
"releases": [
{
"version": "3.0.2",
"date": "2026-10-07",
"kind": "patch",
"summary": "A maintenance release: smarter floating window sizing, automatic cleanup to free up disk space, and a refreshed AI sidebar with Gemini latest models and thinking levels, Claude Opus 5.5 and Mistral 4",
"changes": [
"Floating windows that open larger than the screen (like Discord) are now fitted between the bar and dock instead of spilling over the edges",
"Restoring a maximized window that was originally full-screen now scales it to a quarter of the screen centered, rather than snapping back to full-size",
"Updates now automatically clear older package downloads and unused app runtimes to free up disk space",
"The AI sidebar upgrades to Claude Opus 5.5 and Mistral 4, with Gemini now always using the latest models",
"Gemini models adopt thinking levels (minimal, low, medium, high) with a new /think command",
"In the AI sidebar, Gemini models fold into a single picker entry until chosen to keep the list clean"
],
"commits": [
"4196ca0a4 release: 3.0.2, smarter floating windows, automatic disk cleanup, and refreshed AI models",
"ef8961e3a update: unused Flatpak runtimes are removed after each update",
"9f61de2d7 system: pacman's package cache cleans itself every week",
"24b7d2dd7 ai: target Mistral 4 via mistral-large-latest",
"b036113f5 ai: target Claude Opus 5.5 for Messages API provider",
"6dcc88f6c ai: Gemini models stay folded under one entry until chosen",
"110774ba4 ai: Gemini thinking levels replace deprecated parameters",
"8064d83e6 windows: restoring a full-size float gives it a quarter of the screen",
"ad77fca2e windows: a float that opens bigger than the screen fits between the panels"
],
"commitNotes": {
"ef8961e3a": "Runtimes stay installed after the apps that needed them update to a newer one or are uninstalled. Right after a successful Flatpak update, for apps installed for everyone and for the user alike, the update now removes the runtimes nothing uses. Runtimes someone installed on purpose are pinned by Flatpak and stay, and a failed cleanup never fails the update.",
"9f61de2d7": "Every update leaves the previous package files in pacman's cache, which grows until someone clears it by hand. mainstream-system 1.0-16 enables pacman-contrib's weekly paccache timer through the Mainstream preset, once, on the install or update that brings it, so an administrator who turns it off keeps it off. It keeps the three newest versions of each installed package, and a drop-in also clears the cached files of packages that are no longer installed.",
"24b7d2dd7": "- Update Mistral provider to Mistral 4 using the mistral-large-latest endpoint.\n- Update description highlighting 1T parameter flagship capabilities and tradeoffs, translated across all locales.\n- Add retired model aliases for mistral-medium-3, mistral-3, and mistral to smoothly migrate saved settings.",
"b036113f5": "- Update standalone Anthropic provider from Claude Opus 4.8 to Opus 5.5.\n- Add claude-opus-4-8 to retiredModels mapping to maintain backward compatibility.\n- Update model descriptions across all translations.",
"6dcc88f6c": "The Google API choices previously exposed both Gemini Flash and Gemini Pro directly in the model picker at all times, cluttering the list even when another model was in use. Like the Claude and Codex plans, Gemini now collapses under a single \"Gemini\" door entry when inactive, selecting Gemini Flash by default and revealing Flash and Pro in /model only while Gemini is answering. Switching away folds the models back and restores the door.\n\nAlso, the Antigravity plan models are streamlined down to Gemini Flash and Gemini Pro with reasoning depth controlled via /think, and aliases for retired model identifiers are mapped to their equivalents.",
"110774ba4": "- Replace deprecated thinking_budget and sampling parameters (temperature, top_p, top_k) with Gemini 3 thinking_level (\"minimal\", \"low\", \"medium\", \"high\").\n- Add /think command and dynamic reasoning status indicator in sidebar AI chat.\n- Target Google's rolling latest models (Gemini Flash and Gemini Pro) to follow latest releases automatically.",
"8064d83e6": "A floating window whose own size already fills the space between the panels, such as an app that opened at full size, came back from maximized looking unchanged. Restoring it by scrolling down on its title bar, double-clicking it, pressing the maximize button or pressing Super+D now sets it to half the width and half the height of that space, centered. A float with a smaller size of its own still returns to it.",
"ad77fca2e": "Apps that remember being maximized, like Discord and Vesktop, reopen at the whole screen's size, and the open-time check only moved them, so they hung past the right edge and under the dock. A float that opens bigger than the room the bar and dock leave is now cut down to that room, the same fit a title bar double-click gives. Workspace moves and overview drops still only move a window."
}
},
{
"version": "3.0.1",
"date": "2026-10-05",
"kind": "patch",
"summary": "A polish release: tray icons that keep their colors, faster logouts and switches to Gaming Mode, Wi-Fi channels set for your country, and fixes across boot, windows, sharing and the AI sidebar",
"changes": [
"Tray icons keep their own colors: auto contrast no longer turns colorful icons into one flat color, and only shifts an icon as far as it needs to stand out from the bar.",
"Logging out and switching to Gaming Mode are faster: apps that close to the tray, like Discord, no longer hold them up, and startup apps come back after a crashed session",
"Wi-Fi now picks your country from your time zone so every channel your router uses is available, and you can change it in Settings",
"Encrypted installs no longer show an Early TPM SRK Setup failure at startup, and Repair Install keeps the disk unlock step in place",
"A boot image that fails its checksum now asks before starting instead of stopping the machine",
"Early work on Steam Frame support (experimental): the bar stays on your own network while the Steam Frame adapter is plugged in, the PC stays awake while SteamVR's status window is open, and the sidebar warns before turning Wi-Fi off with the adapter plugged in",
"A floating window you can see is now always a window you can click, even over a maximized window",
"In the AI sidebar, code blocks fill in completely while a reply streams, and the chat keeps scrolling with the pointer over them (thanks @benoitjadinon)",
"NVIDIA installs include the NVML Python bindings, so GPU monitors such as TRCC can show GPU use and temperatures",
"Folder sharing keeps Samba installed while it is on, copies the password as sensitive, and turns off without waiting for the keyring",
"On the MacBook edition, system updates no longer stop with an invalid signature from the T2 kernel's repository",
"Every account can now change its own password with just the current one, and a wrong password says so",
"Bluetooth stays on in Gaming Mode, so your devices still connect there and reconnect when you return to the desktop",
"The Welcome app's Translate button now opens Mainstream's translations on GitHub",
"You can now reboot from the Update settings Reboot required pill",
"The Update page's advanced options now keep their settings from one update to the next",
"Settings > Bar adds a day-first date with periods, like 05.10.2026",
"Gemini 3.8 Flash is now the default AI model"
],
"commits": [
"c768bd4f1 release: list the saved update options in the 3.0.1 notes",
"33f23a261 update: the advanced switches keep their choices between updates",
"8572501fa release: list the day-first date in the 3.0.1 notes",
"da7bcbc71 settings: a day-first date with periods",
"ae3d93e3a release: 3.0.1, colorful tray icons, faster logouts and Gaming Mode switches, and Wi-Fi set for your country",
"3ee38cd71 keyring: the switch to the signed repo no longer breaks pacman on the MacBook edition",
"41567e8d8 ai: a touchpad swipe over a code block still scrolls the chat",
"453f7eadc welcome: send translators to GitHub instead of Crowdin",
"b977cda19 update: the update and its finisher share the lock helper and the config.json path",
"f40f78dd6 tray: judge an icon's contrast from one sample taken when it changes",
"6f6dccc39 mac: the wl driver and 7zip installs check pacman's result through the shared helper",
"02ddc6d2e update: drop the extra clear of a hold a killed run left behind",
"589cec4b6 windows: a floating window you can see is a window you can click",
"623f5b953 boot: no Early TPM SRK Setup failure on screen",
"eff46de3c session: apps that close to the tray no longer hold up logout and the Gaming Mode switch",
"ddffaa7c9 gaming: Bluetooth stays on in Gaming Mode",
"b96a50a02 accounts: a wrong current password says so",
"edf38b8a7 installer: the internet notice follows the live connection",
"b9811390c settings: open at a page in the window that is already open",
"ec150c05b sharing: keep the sharing lock where other accounts cannot reach it",
"e841d2943 sharing: samba and wsdd stay installed while sharing is on",
"b9e920730 shell: an update that finishes while the screen is locked reloads after unlock",
"178305268 welcome installer: an app the mirrors rebuilt points to the Update page instead of failing",
"f33c56e41 launcher: AppImages from file search open in Files instead of the browser",
"210ee0537 launcher: app drawer tiles stay in place when the app list refreshes",
"2680d164a bar: workspace numbers stay sharp at fractional scales",
"a458e607f update page: the Reboot required pill reboots after a second click",
"e7dd637ef session: startup apps come back after a crashed session",
"aa9554f16 update: the exp-update tester runs every test in its own home, away from the session",
"4ae35de81 update: a dry run changes nothing and leaves the desktop alone",
"9fa41f970 welcome: correct the VR row, Steam Link reaches standalone headsets too",
"134df86b5 wifi: the lists hold still during a password prompt, not the service",
"fb3509f2e settings: restart instead of mixing versions after an update",
"a6df9abb8 accounts: change your own password with only your current password",
"6ea184639 translations: placeholders for the new update, settings and Welcome strings",
"91edc2130 update: the helper reports a stopped run correctly and checks fail closed",
"4f1d832db update: release updates finish deferred files and settings in the right order",
"e3995b8e4 accounts: safer password handling and ownership in the account tools",
"11b75ae61 sharing: one add at a time, shared helpers, and a clean package removal",
"11d4d8d11 settings: correct the comment on loading pages after an update"
],
"commitsTotal": 70,
"commitNotes": {
"33f23a261": "The Update page's advanced switches lived only as long as the Settings window, so each new window started from the defaults again. They are now saved in config.json under updates.advanced with the same defaults, and a theme apply leaves them alone, since it keeps the whole updates section. The custom arguments field still starts empty each time.",
"da7bcbc71": "Settings > Bar > Date Format gains \"Date First dd.MM\", the dotted style used in Germany, Russia, Poland and elsewhere, and the short and full dates follow it as dd.MM and dd.MM.yyyy. The login screen and the to-do date picker only read whether the day comes first, so they work with it as is. The new name is an English placeholder in every language file.",
"589cec4b6": "Hyprland 0.56.2 draws every floating window that may sit over a maximized floating window on top of it, but finds the window under the pointer by stacking order, so after clicking the maximized window the one still drawn above it stopped getting clicks and they went to the window behind. When a floating window is maximized, windows of other apps that end up below it now fade behind it as they do on other desktops, while its own dialogs, file choosers and the focused window are raised so they stay usable, and the dock raises the window it shows.",
"623f5b953": "systemd 262 exits 1 from the TPM setup when a boot image carries no signed PCR policy, and mkinitcpio 42.2 now runs that step inside the boot image, where encrypted installs leave the failure on screen above the passphrase prompt. mainstream-system 1.0-15 treats that exit as success for the three affected services, so the TPM key is still set up and the reason stays in the journal, and existing machines that logged the failure rebuild their boot image once through the usual checked path.",
"ddffaa7c9": "Steam powers the Bluetooth adapter off at every Gaming Mode start until its own Bluetooth toggle has been set once, and back on the desktop blueman turns that into a soft block that outlasts reboots, so devices stopped reconnecting. mainstream-gaming 1.0-14 turns Steam's toggle on before each start when it has never been set, and lifts a soft block an earlier session left behind. A toggle switched off in Steam is left alone.",
"ec150c05b": "The lock lived in /run/lock, which any account can write to, so another user could create or hold it first and block every sharing change. It now lives in a root-only folder under /run/mainstream, readable by nobody else, and is opened without truncating.",
"e841d2943": "A samba that came in as another package's dependency could be removed by an orphan cleanup while folders were shared. Setting up sharing, repairing the account or trusting a network now marks samba and wsdd as explicitly installed, through a small service that waits for pacman instead of holding up the page, and upgrading to 1.0-15 does the same for machines already sharing.",
"b9e920730": "Reloading the shell under the lock destroyed the session lock while still locked, which left a dead lock screen and kept the shell from locking again until it restarted. A release that arrives while locked now waits, and the shell reloads two seconds after unlock, once the workspace restore and keyring unlock have finished. A 3.0.0 shell, which cannot wait by itself, is released by the update once the screen unlocks.",
"e7dd637ef": "A session that ended without its shutdown hook left its targets active, so the next login started nothing they want. Login now stops the targets an earlier session left, unless another desktop or Gaming Mode session is still using them, and entering Gaming Mode ends the desktop session targets the way logging out does.",
"a6df9abb8": "Standard accounts no longer need an administrator to change their own password. A separate entry point only ever changes the caller's own password after checking the current one, wrong guesses are throttled, and admins changing someone else's password keep the admin path."
},
"merges": 2
},
{
"version": "3.0.0",
"date": "2026-10-01",
"kind": "feature",
"summary": "A rebuilt Welcome app, smoother gaming with a new Gaming settings page, a Hug-style Dock and per-monitor wallpapers, with updates and shutdowns that keep your work and your changes safe",
"changes": [
"The Welcome app has been rebuilt: pick your language, window layout, hot corner and wallpaper, then style the Bar, Dock and window decorations while a live preview of your desktop updates as you go",
"Welcome's apps page now fits on one screen, sorted into Play, Create and Work, with anything you already have installed marked",
"Games run even better out of the box: GameMode (a performance booster) and MangoHud (an on-screen performance display) come installed, and the desktop stays responsive while a game loads from a hard drive",
"New Gaming page in Settings: one switch keeps games smooth while something heavy runs in the background, and two more can help some games stutter less or load faster",
"Gaming Mode opens on your default screen at your desktop's resolution, so Steam looks sharp from the first start, and Super+G closes your apps as carefully as logging out does",
"New Extra VR Headset Support option: standalone headsets like Quest, Pico and Vive Focus can now play your PC's games (available in the Welcome app)",
"New Hug style for the Dock: it runs the whole length of the edge like a taskbar, with smaller icons to match",
"The Dock's Notch style now curves into the screen edge as smoothly as the Bar's and can be made rounder, and each Dock style remembers its own roundness",
"Two new Dock window indicators: show the count badge together with either the dashes or dots",
"Name your workspaces: add the new Workspace title widget (previously the Window title widget) to the Bar, right-click it to name the workspace you're currently on, and click it to jump between named ones",
"The Dock's right-click menu can now send an app straight to a workspace you've named",
"New Network speed widget for the Bar: live download and upload speeds, with connection details on hover and the Wi-Fi list a click away",
"The Bar page gains three options: an unread count on the notification indicator, workspace numbers in Roman numerals or Chinese characters, and tooltips that wait for a click",
"Bar and Dock icons now stay readable on any color or transparency you pick, switching between light and dark on their own",
"Turning off Rounded Corners now squares off everything, from windows, the Bar and the Dock to sidebars, notifications, pop-ups, dialogs and menus, and turning it back on restores them all",
"Title bars respond to your mouse: double-click to maximize or restore, middle-click to close, and use your scroll wheel to switch a window between minimized, normal and maximized, even with the title bar buttons switched off",
"Title bar buttons can be resized or recolored, and title bars keep separate colors for dark and light mode",
"Title bar buttons can be hidden until your pointer arrives (thanks @MatrixCODEBreak)",
"The launcher now matches your Dock's color, transparency and roundness and blurs the windows behind it",
"With the Launcher Overview off, the launcher can now open straight onto your full app list; the option is in the Launcher Overview section of the Interface page",
"New Media tab in the left sidebar: album art, playback controls and synced lyrics for whatever is playing, colored from the album (thanks @pctrade)",
"A Gemini plan option joins the Claude plan and Codex plan options in the left sidebar: sign in with your Google account, free or with Google AI Pro or Ultra",
"The AI icon on the bar now changes based on which model you are using",
"Pressing Ctrl+P now pins the left sidebar and keeps it open while you work or control your media; clicking a window just moves it aside instead of closing the sidebar",
"Each monitor can have its own wallpaper. Right-click the desktop on any screen and choose Change Wallpaper, and your colors will still follow your main wallpaper",
"Each monitor can have its own Scrolling Overview layout, gap and scale (thanks @yayuuu)",
"Choose where notifications appear (any corner, or centered at the top or bottom) and how long they stay, from the new Notifications section on the Interface page",
"New Sharing page in Settings: share folders with Windows, Mac and Linux computers on your home network. Turn it on once, then right-click a folder in Files and choose Share on Network. Folders are only shared on networks you trust",
"Share a saved Wi-Fi network as a QR code another device can scan to join, so nobody has to read out the password",
"Typing a Wi-Fi password is no longer interrupted, and the network list stops reordering under your pointer",
"Two new touchpad switches: ignore a resting palm while you type, and turn the touchpad off while a mouse is in use",
"The Accounts page gives a new account the same desktop a fresh install gets",
"New simple session menu on new installs: Lock, Shutdown, Reboot and Logout as one row of large buttons. Switch to the full menu from the Power page in Settings",
"Logging out, restarting and shutting down now let your apps close properly, so each one can save its work and your browser keeps all of its tabs. If an app won't close within 30 seconds, the shutdown is cancelled and you're told which one (thanks @knelsonmeister)",
"Updates keep running if you close Settings, with the result waiting when you come back",
"The Update page also tells you up front when a restart will be needed and ends with a Reboot Now button",
"The Update page can play a chime when it finishes so you can step away; the option is under Advanced Options",
"Updates keep your changes: decorations, layouts, touchpad gestures and your own edits to settings files stay as you left them, while everything you never touched takes each release's new defaults",
"The Update page stays responsive through long updates",
"Updates now clear out leftover files",
"The hot corner no longer steals clicks or covers the screen while the overview is off",
"Applying a theme is faster and more reliable",
"The wallpaper picker fits portrait screens",
"Manual partitioning comes to the installer: lay out your drives yourself, and it checks your layout before writing anything",
"The installer's \"Log in automatically\" option has been fixed, and signing in that way no longer asks you to unlock your saved passwords",
"T2 Macs now start the MacBook edition's ISO with more of the boot options they need, and older Macs with Broadcom Wi-Fi get a matching driver",
"Mainstream is even leaner: the desktop install is smaller and the ISO is down from 3.5 GB to 3.1 GB with no loss in features",
"Files is also on hand during install so you can see what is on a disk, and one Apply button sets up all your displays",
"Mainstream now runs in VMware",
"Brazilian Portuguese gains another 149 translated strings (thanks @douglasboardman)",
"Plus many smaller fixes and polish, including sharper icons on scaled displays"
],
"commits": [
"ea005a58e fix(tray): re-register stubborn status notifier items after reload",
"f9368ebb9 fix(tray): preserve visibility for unsampleable and late-updating icons",
"dd0a64bf3 menus: every context menu is drawn by one shared card and row",
"b268580de launcher: closing it restores the blur depth without starting Python",
"1584360bb title bars: every title bar setting except the buttons applies without a reload",
"dba3bbd38 terminal colours: pictures with an alpha channel, CMYK or one GIF frame no longer fail",
"f6fb19396 wallpaper: a new wallpaper's colours are read from one small copy",
"5e7ee4a6f themes: the theme grid keeps its cards, and old full-size previews are shrunk once",
"b7e08421d themes: Settings comes back as soon as the save's screenshot is taken",
"1f56be4bd title bars: colour, opacity and hover changes apply without reloading Hyprland",
"008139f32 settings: clicking through colour styles on the Quick page regenerates once, not once per click",
"fb80e40f8 themes: exporting and importing large themes is faster",
"9fb4aa6ad themes: a theme keeps one wallpaper, and importing over the applied theme keeps it on screen",
"ce763e1ae themes: exports leave out the weather location, and one index rebuild serves every change",
"b048c77ea shell: a new wallpaper is decoded once for its average colour and edges, not twice",
"90b3c5ef5 launcher: opening it sets the stock blur depth without starting Python",
"49315947c border gradient: no Hyprland reload at startup, and colour changes are pushed live",
"285d197c7 themes: Day/Night Themes doesn't re-apply the theme already on screen when the shell starts",
"17da3bc69 themes: the theme library and title bar settings are read without a process per file",
"fba205497 settings: the open page is only rebuilt when the palette actually changed",
"252907bb6 wallpaper: a new JPEG wallpaper is decoded at reduced size for its scheme and terminal colours",
"9270d8b55 themes: Ctrl+Super+N skips missing themes and goes through the shell",
"a8dd1b0b9 themes: an imported theme's decoration strings can't break general.lua",
"0931bc401 themes: the folder colour rebuild no longer overrides the icon theme a theme applies",
"ea7538bb0 terminal colours: every kitty is recoloured, and an unchanged palette is skipped",
"ca704cd0c wallpaper: AI clock styling asks about each wallpaper once, not on every colour change",
"f1069e190 themes: applying a theme with a video wallpaper points at that video's thumbnail",
"808059821 wallpaper: light/dark toggles and accent colours reuse the cached terminal palette",
"8f4be5d4d wallpaper: switchwall reads its settings once and skips writes that change nothing",
"5108b96d7 wallpaper: a video wallpaper that is already playing is left running",
"724c0e127 wallpaper: the login background is only re-encoded when the wallpaper changes",
"60f4d5a7e themes: an apply reloads Hyprland once instead of twice",
"d7952fca0 wallpaper: installing the missing video wallpaper tools asks pacman for each one",
"048a32892 themes: applying a theme repeats less work",
"ea6e660f9 themes: a light/dark toggle can't undo a theme apply that is still running",
"7efc43125 themes: a cancelled or failed apply no longer leaves its colours behind",
"c857256d8 wallpaper picker: the folder path is no longer cut off at the bottom",
"28e2f7783 shell: the remaining panels and dialogs square off with Rounded Corners",
"81fd70402 gaming: entering Gaming Mode closes the desktop's apps the way logging out does",
"af6428142 gaming: the first start comes up at the desktop's resolution, not Steam's scaled one"
],
"commitsTotal": 231,
"commitNotes": {
"dd0a64bf3": "ContextMenuCard, ContextMenuItem and ContextMenuPopup in the common widgets\nhold the look the desktop menu had: its deeper shadow, the row inset, the\nmenu hover tints and the row sizes. The desktop, dock, tray, launcher,\nto-do, theme card, text field and image menus now use them in place of\ntheir own copies, so a new menu matches by using them. All of them square\noff with Rounded Corners, and a tray entry the app has disabled now shows\nas disabled.",
"b268580de": "Opening the launcher reads the compositor's blur depth with one hyprctl\ncall and swaps in the stock depth only when it differs; closing puts the\nread depth back with one more, unless something else changed it while the\nlauncher was open. An answer that can't be read falls back to the earlier\nway through decorations.py.",
"1584360bb": "plugins.lua exposes the function it runs on every reload, and the shell\ncalls it after changing a title bar file, so the colour, opacity, hover and\nscroll settings, and a reset that leaves the buttons as they were, apply\nwithout re-running the whole config. A config loaded before plugins.lua had\nthe function reloads instead.",
"f6fb19396": "Scheme detection, matugen and the terminal colour generator each decoded a\nnew picture in full before shrinking it. They now share one 512 px copy,\nmade the first time any of them needs it and named for the picture's path,\ndate and size, so two pictures with the same name no longer share one.\nDetection reads a video wallpaper's thumbnail instead of the video file it\ncouldn't open, so Auto picks a real scheme for video wallpapers, and a\ndetection that fails is no longer kept as the answer.",
"5e7ee4a6f": "The grid is a keyed ScriptModel, so applying, saving or renaming a theme\nonly rebuilds the cards that changed instead of every one. Previews saved\nbefore the 1024 px cap are brought down to it once, so a library from\nbefore no longer decodes full screenshots on every visit to the page.",
"1f56be4bd": "The bar colour is composed the way plugins.lua composes it and handed over\nwith hl.config eval, and an edit to the mode that isn't on screen only\nwrites its file. Showing buttons on hover is pushed the same way. A push the\ncompositor refuses falls back to the reload.",
"9fb4aa6ad": "Updating a theme with a picture of another type removes the old one, export\npacks only the picture meta.json names, and import uses that one rather than\nthe first in alphabetical order. Importing over the theme that is applied\nkeeps the live wallpaper in a copy outside the folder being replaced and\npoints config.json at it, as deleting a theme does.",
"ce763e1ae": "bar.weather and bar.seededWidgets join the one list of personal settings\nthat saving, applying and exporting all read, so an older theme exported\ntoday no longer carries its author's weather city. Saving, renaming,\ndeleting and importing now rebuild index.json through one shared helper,\nso a rename can no longer publish an interrupted import as a theme.",
"49315947c": "With both gradient lanes off, the startup reset is no longer sent when the\noverride file has nothing to clear, and the configurator skips writing and\nreloading when an edit changes nothing. A gradient that only changed colour\nis pushed with hl.config eval instead of a full reload.",
"9270d8b55": "A theme whose folder is gone is no longer picked, which used to stop the\ncycle on it for good. The switch is asked of the shell, as the Themes page\ndoes, so it cancels a running apply, repaints when done and reports a\nfailure, and it is refused while Day/Night Themes is choosing the theme.",
"a8dd1b0b9": "Line breaks and other control characters in a string are written as Lua\nescapes, so a value can no longer end a line with its string still open.\nEscapes are read back when general.lua is read, so a saved value no longer\ngains a backslash on every save and apply.",
"ea7538bb0": "With two or more kitty processes the reload signal went out as one bad\nargument and no terminal changed colour. The kitty and escape-sequence\ntemplates are now filled in one sed pass each instead of one process per\ncolour, and nothing is rebuilt or sent when neither the palette nor the\ntemplates changed.",
"8f4be5d4d": "The unused cursor-position lookup is gone, config.json is read in one jq pass\ninstead of about ten, and neither config.json nor the colour scheme and GTK\ntheme settings are rewritten with the value they already hold.",
"5108b96d7": "switchwall no longer restarts mpvpaper on every monitor or re-extracts the\nthumbnail when the same video is playing with the same options, so light/dark\ntoggles, style picks and theme re-applies don't blank the screen. At login\nthe shell only restores playback instead of rebuilding the palette, which\nalso stops it clearing a picked accent colour.",
"048a32892": "The theme's meta.json and config.json are each asked about in one jq pass,\nthe interface and font settings are only written when they differ from what\nis set, and the decorations, interface, fonts and window rules steps run\nside by side before the reload.",
"7efc43125": "Cancelling an apply now stops the whole colour run, matugen and its hooks\nincluded, instead of only switchwall. A rollback rebuilds the previous\ntheme's colours and mode from the restored config rather than putting back\nconfig.json alone."
},
"merges": 4
},
{
"version": "2.3.1",
"date": "2026-09-14",
"kind": "patch",
"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"
],
"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": [
"afbec82c9 release: 2.3.0, sign in from any keyboard, a moving wallpaper on the login and lock screens, and three new categories of apps",
"f2f2dbedf update: a uv cache error no longer fails the developer extras step",
"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\""
],
"commitsTotal": 41,
"commitNotes": {
"afbec82c9": "The update panel now describes 2.3.0: the login screen's keyboard layout\npicker and the layout that follows from the installer through to Settings,\nvideo wallpapers on the login and lock screens with a frame rate you can hold\ndown, three new categories on the Welcome app's Set up your apps page, Windows\nfound in the boot menu wherever it is installed, and a second package server\nto fall back on. The 2.1.0 entry also drops the copy of 2.0.0's list it was\ncarrying, which update notifications have described on their own since 2.1.0.",
"f2f2dbedf": "The extras pass no longer runs uv's cache housekeeping, which could fail the\nwhole step over a permission error in a cache the install deletes anyway. uv is\nhere only to build the color venv at install time, and nothing on the desktop\nmanages Python projects with it, so its step is skipped alongside hyprpm.",
"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 f",
"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",
"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 candidat",
"c6c40cc83": "This reverts commit 7474149f91cf6b15e009d0a052542a70dbff059f"
},
"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": [
"344c8688d release: 2.2.0, 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",
"337df04d9 fix: parse all keyboard layout variants from base.lst.",
"194f63377 readme: say each thing once",
"397febdfa readme: the desktop as it ships now",
"5be43e4cb accounts: fit the login image to what the login screen draws",
"0dd829dd5 limine: drop the boot entries that lead nowhere",
"9db06801d limine: remove the systemd-boot the install replaces",
"1756939b5 dock: a larger stock icon",
"f0517324e bar: ship the layout the bar is actually arranged in",
"d62d61a3c update: refuse an option this build does not know",
"d1d85055e readme: the features 2.2.0 adds",
"50b5167a8 bar: no pill where the system tray has nothing to show",
"e6abbe0b4 setup: seed the interface look the ISO install seeds",
"e48009cda limine: stop offering boot entries that do not boot",
"94dd009c0 welcome: wait for the package database, and say when an install failed",
"3216105c0 icons: give the three app tiles an edge to sit on",
"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"
],
"commitsTotal": 82,
"commitNotes": {
"337df04d9": "Previously the layout catalog came from two disconnected sources:\nbase layouts parsed live from base.lst, plus a hardcoded list of\nseven variants (Dvorak, Colemak, Colemak-DH, Workman, Programmer\nDvorak, French B\u00e9po, German Neo 2). The awk script exited as soon\nas it reached base.lst's \"! variant\" section, so every variant XKB\nactually ships beyond that hardcoded seven \u2014 e.g. the German\nMacintosh layout \u2014 never reached the picker.\n\nRework the awk script into a section-aware state machine that tags\neach printed line \"layout\" or \"variant\" depending on which part of\nbase.lst it came from. SplitParser now branches on that tag,\nbuilding either a {code, name} base-layout entry or a {code,\nvariant, name} variant entry.\n\nKeep the old hardcoded list, but only as a fallback: an entry is\nadded only if base.lst didn't already supply that exact\ncode+variant combination. Some of these \u2014 Colemak-DH in particular\n\u2014 are recent additions to xkeyboard-config and may be missing on\nolder systems.\n\nNo other",
"5be43e4cb": "The picked file was copied through untouched, so what the login screen got was\nwhatever happened to be chosen. The screen draws the avatar at 272 logical\npixels, which is over 500 real ones on a HiDPI panel, and a picture smaller\nthan that arrives enlarged and soft with nothing said about it. A large one is\nwrong the other way: a multi-megabyte photo left in /var/lib to fill a circle a\ncouple of hundred pixels across.\n\nIt is now squared from the middle and capped at 512. Never enlarged, because\nscaling up here would add weight and no detail, and the login screen would have\ndone the same scaling anyway. When the result is smaller than the screen will\ndraw it, the status line says so with the size, so a picture that will look\nsoft says so while the picker is still open rather than at the next logout.\n\nidentify reads the first frame only, so an animated source is measured by the\nframe that gets written rather than by however many follow it. A file that\ncannot be measured falls back to the",
"0dd829dd5": "Turning FIND_BOOTLOADERS off stops new entries being written and does nothing\nabout the ones already there. limine.conf keeps its entries across runs on\npurpose, since ensure_limine_header refreshes the header and leaves everything\nbelow it alone, so an \"EFI fallback\" written on an earlier run outlives the\nsetting that produced it and no later run takes it away.\n\nBoth dead entries are now pruned from the config, matched on where they point\nrather than on what they are called, because Limine names them in its own words\nand a user may have renamed them. Windows and any real system are chainload\nentries too, so only the two paths that go nowhere are named: the systemd-boot\nthis install replaced, and EFI/BOOT/BOOTX64.EFI.\n\nThat second one is Limine's own removable-media copy, which is why the file is\nleft in place: some firmware looks nowhere else, and deleting it would cost the\nmachine a boot path. It is the entry offering it as a destination that has no\nreason to exist, since choosing it",
"9db06801d": "The script has always meant to take the old loader away once Limine was proven\nworking, and on some machines it quietly stopped. The test was a bare\n`bootctl is-installed`, which only finds the ESP on the layouts bootctl likes.\nThis script already knows that, which is why $ESP at the top falls back to\n/boot/efi when --print-esp-path answers with nothing. Asking bootctl again\nwithout telling it where to look meant that on exactly those machines the\nanswer was no, the branch never ran, and the superseded loader stayed on the\ndisk to be offered back as an entry that boots nothing.\n\nBoth calls now say which ESP they mean. Anything bootctl still declines to\nclaim is taken by hand: the loader binary under EFI/systemd is what makes the\nentry appear, so removing it is enough, and its loader directory is inert\nwithout it and may belong to another system sharing the ESP, so that is left\nin place. The NVRAM entry outlives the files it points at, so it goes too,\nmatched on the exact name systemd-b",
"1756939b5": "Stock icon size goes from 35 to 40, the middle of the 16 to 64 the slider\noffers, so the dock reads at a comfortable size before anyone touches it and\nkeeps the same room to move in either direction.\n\nIt is the stock value, so an install that has set its own size keeps it:\niconSize defaults to -1 and only then falls back to the stock number. This is\nwhat a fresh install starts with and what the reset button returns to.\n\nThe pin glyph scales by fittedIconSize over the stock size, so a default dock\nkeeps the ratio it had at 35 and the glyph is unchanged.",
"f0517324e": "Workspaces open the left section instead of closing the right one, which is\nwhere a workspace strip sits on most other desktops and where the eye goes\nfirst. Media crosses to the right, so it is no longer sharing a group with\nresources and the two sides carry a similar weight rather than the left holding\nboth.\n\nThe center is unchanged, and no widget is switched on or off by this. Only\nwhere they sit and which group they share.\n\nA running install keeps whatever it has: the layout is read from config.json,\nso this decides what a fresh install starts with and what \"Reset to defaults\"\nreturns to. Both read this one declaration, so they cannot drift apart.",
"d62d61a3c": "Running the update with --edge on a fresh 2.1.0 install did not follow the\nbranch, and reported a topgrade failure instead: \"error: unexpected argument\n'--edge' found\", rc=2.\n\nNeither symptom was about topgrade or about edge. The ISO bakes its own copy of\nthis helper under /usr/local/bin, and that copy is only brought level with the\nrelease by updatems-system, which runs partway through the first update. So a\nfresh install can be one release behind for exactly one run, and on that run the\nSettings page offers a flag the installed helper has never heard of.\n\nWhat made it confusing rather than merely unsupported was the catch-all at the\nend of the argument loop. It exists to pass bare step names through to topgrade,\nbut it took anything, so an unknown flag became a topgrade argument. The flag's\nown work was skipped in silence and the error surfaced against a tool that had\nno part in it.\n\nNo flag passthrough to topgrade is documented in the usage block above, so an\noption arriving at the ",
"d1d85055e": "The desktop widgets and their frosted backdrops, the menu that opens on a\nright click, the wallpaper transitions, the Manage page for default apps and\nwhat starts at login, and support for Intel and T2 Macs.\n\nThe ISO sizes on the install line still describe 2.0.0 and wait on the 2.2.0\nimages being built.",
"50b5167a8": "The tray is on the bar by default, and on a machine where nothing has\nregistered an icon it drew a pill about the width of its own margins with\nnothing inside it. A fresh install is exactly the case that has no tray icons\nyet, so the first thing a new user saw was an empty one.\n\nTwo things kept it on screen. The tray asked to be visible whenever the bar was\nin its full form, without regard to whether it held anything, so moduleVisible\nnow also asks whether the service has any item pinned or unpinned. That is the\nsame question releaseUpdates already answers a few lines below, for the same\nreason.\n\nHiding the widget alone was not enough. A group is sized from its layout plus\nits padding, so a widget that switches itself off leaves the layout empty while\nthe background keeps drawing around the padding. Groups now go with their\ncontents, which also covers any other widget that empties out rather than only\nthis one.",
"e6abbe0b4": "The ISO ships /etc/dconf/db/local.d/00-dark-mode, which sets the dark scheme\nalong with the GTK theme, icon theme, cursor theme and cursor size. The script\ninstall set the scheme and the icons and stopped there, so gtk-theme and the\ncursor were left at the GNOME defaults on a fresh Arch machine.\n\nSettings fills the System look pickers by finding the current value in the list\nof installed themes. Adwaita is not in that list, so the lookup found nothing\nand App style rendered blank, with the cursor picker one step behind it for the\nsame reason. Aligning the script install with the ISO's values fixes both.",
"e48009cda": "FIND_BOOTLOADERS reads the ESP it is installed on and nothing else, which was\nalready known to make it useless for finding another operating system. What it\ndid instead was fill the menu with two entries that go nowhere.\n\nOn a script install over an existing Arch system, limine replaces the\nbootloader that was there, and the probe then finds that same systemd-boot\nstill sitting on the ESP and advertises it. The other entry is\n$ESP/EFI/BOOT/BOOTX64.EFI, which is limine itself, so choosing \"EFI fallback\"\nchainloads limine back into the menu the user was trying to leave. Both were\nnoted in the comments as wanting pruning; turning the probe off removes the\nreason to prune.\n\nNothing that boots is lost. An operating system worth offering is registered\nexplicitly with limine-entry-tool --add-efi, which is how Windows already gets\ninto the menu, and that path is untouched.",
"94dd009c0": "Two of our own timers reach for pacman on a schedule, and one of them lands in\nthe same minute a new user is on the Welcome app's install page:\nmainstream-keyring-init first fires 20 seconds into the boot and runs a\nfive-attempt `pacman -Sy` loop to get [mainstream] signed and answering. The\ninstaller walked into that lock and pacman refused the transaction outright, so\nSteam Big Picture and OBS both failed on a fresh machine for no reason the user\ncould see.\n\nThe fleet already knows about this. mainstream-repair, mainstream-update-helper\nand the keyring job all test for db.lck and stand down until their next tick.\nThe installer had no such test, and it has no next tick either, because a person\njust pressed a button. So it waits instead, and clears the lock only when it is\ndebris with no pacman behind it, which is the same judgment the others make.\n\nThe failures were also invisible. Every install arm ended on a notify, and\nnotify succeeds, so a failed step handed run_item a zero and th",
"3216105c0": "Settings, Welcome and Uninstall Apps drew a flat #2a2b32 tile with nothing\naround it, which is close enough to the dock behind them that the tile\ndissolved and only the glyph read. The three they sit beside all answer this\nsome way: Spotify fills its shape, MPV carries a light ring, Software is\nnear white.\n\nThe tile is lit from above now, with a bevel that catches along the top edge\nand darkens along the bottom, over a soft shadow. It stays dark, so the teal\naccents on the mug and the progress ring keep the contrast they were drawn\nfor.\n\nThey also read larger than everything around them, because the artwork ran to\nall four edges where an app icon normally leaves a margin. The icons beside\nthem cover between 84 and 91 percent of their canvas. The whole drawing, tile\nand glyph together, now sits in a group scaled to 90 percent, so it carries\nthat margin and reads at the size the rest of the dock is drawn to.\n\nThe shadow is built from feGaussianBlur, feOffset, feFlood, feComposite and\nfeM",
"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 slo",
"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 parall",
"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",
"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 conne",
"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\ncann",
"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, whic",
"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 i",
"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."
},
"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,