jay-compositor 1.14.0

The Jay compositor
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
# Unreleased

# 1.14.0 (2026-07-02)

## Fixes

As always, this release contains many bug fixes. Thanks to the following people
for reporting or fixing bugs:

- @effects3d
- @jfrob
- @khyperia
- @kotarac
- @krakow10
- @Ktrompfl
- @Nojike
- @polycatenane
- @Stoppedpuma

## Every Frame is Perfect

Wayland allows compositors and clients to coordinate changes such that every
frame is perfect. For example, when resizing two tiles, the transition can be
coordinated so that each window fills its respective tile at all times.

Until now, Jay did not make use of this. When resizing a window from the left,
the right side of the window might disappear below the border or might appear
detached from the border.

Jay will now try to make every frame perfect. This is accomplished by delaying
changes on screen until all affected clients have also updated themselves. A
timeout is used so that a slow client does not cause the entire system to lag.

The default timeout is 50 milliseconds, which is ambitious. Sway uses a timeout
of 200 milliseconds and niri uses a timeout of 300 milliseconds.

The timeout can be adjusted in the config and in the control center:

```toml
[transactions]
timeout.millis = 200
```

Note that transactions only affect what is displayed on screen. The cursor
interacts with the actual state, which might be newer. The timeout determines
the maximum input lag of this mechanism, so to speak. In most cases applications
will respond far sooner than the timeout and the difference will be
imperceptible.

Setting the timeout to 0 will give you the previous behavior of Jay, except that
in some rare situations a change might be delayed by one frame where it
previously wouldn't have been.

## Improved Prime Handling

On multi-GPU systems, Jay will now always ensure that buffers rendered on
secondary GPUs are copied to the primary GPU before they are used by the
compositor. This ensures that such copies are not performed implicitly when
rendering, which would increase whole-system latency.

As a side effect, this means that it is now possible to seamlessly switch the
primary GPU at runtime. Previously, this often required applications to be
restarted. For example, on a gaming laptop, you might want to normally run on
the integrated GPU to save energy. But while gaming, you might want to run on
the dedicated GPU instead, to reduce latency.

For clients using version 6 of the wayland dmabuf protocol, Jay is also able to
perform the following optimization: If an application runs on a secondary GPU
and is being scanned out on a display connected to the secondary GPU, Jay will
skip the copy to the primary GPU. This can significantly improve the performance
of such applications. Note that mesa does not yet support version 6 of the
protocol.

All of this can be disabled by setting the environment variable
`JAY_NO_CLIENT_PRIME=1` before starting Jay.

## Repeating and Locked Shortcuts

Shortcuts can now be configured to repeat:

```toml
[complex-shortcuts]
XF86AudioRaiseVolume = {
    action.type = "exec",
    action.exec.shell = "pactl set-sink-volume 0 +10%",
    repeat = true,
}
```

A repeating shortcut fires according to the configured repeat rate and delay.

Setting `allow-locked = true` allows a shortcut to execute while the screen is
locked. Care must be taken to not apply this to shortcuts that would allow one
to circumvent the lock screen.

Thanks to @GK-Gaming for suggesting this.

## Auto Floating of Fixed-Size Windows

When a client hints that a native wayland window is fixed-size, it will now
automatically be mapped floating instead of tiled. This behavior can be
overridden with window rules.

This can improve the handling of some windows such as splash screens. If you
encounter a window where this behavior is incorrect, please open an issue.

Furthermore, when mapping a floating window, the size will now be adjusted to
fall within the min/max size hinted by the client. The user can still resize the
window to any size.

Thanks to @effects3d for suggesting this.

## Focused Border Color

Containers can now be configured to have borders drawn around them:

```toml
[theme]
container-borders = "full"
```

When this setting is active, focused windows can be given a dedicated border
color:

```toml
[theme]
focused-border-color = "#840"
```

This makes it possible to see which window has the focus when window titles are
disabled.

Thanks to @Stoppedpuma for suggesting this.

## Scroll Methods

The libinput scroll method of pointer devices can now be configured:

```toml
[[inputs]]
match.is-pointer = true
scroll-method = "on-button-down"
scroll-button = "BTN_MIDDLE"
```

This does not affect scroll wheels but rather devices where pointer movements
and scroll events are generated via the same input surface. For example, if the
configuration above were applied to a touchpad, it would generate scroll events
instead of relative motion events while the middle mouse button is pressed.

In the control center, the possible `scroll-button` values are visible via a
dropdown menu.

Thanks to @GK-Gaming for suggesting this.

## Scaling with Nearest Filtering

Outputs can now be configured to use nearest filtering instead of linear
filtering:

```toml
[[outputs]]
match.serial-number = "ETW1M02062SL0"
scale = 2.0
scaling-filter = "nearest"
```

This produces a clearer picture when the scaling factor is an integer.

This should only affect applications running via Xwayland. Native wayland
applications should scale themselves.

Thanks to @David-Isenah for suggesting this.

## Setting Initial Workspace Outputs

When creating new workspaces implicitly, such as by executing a
`move-to-workspace` action, the workspace would previously always be created on
the fallback output. Workspaces can now be configured ahead of time to be
created on a specific output:

```toml
[workspaces."1"]
initial-output.name = "left"

[workspaces."2"]
initial-output.name = "right"
```

Thanks to @Stoppedpuma for suggesting this.

## Toml Improvements

The following improvements expose existing settings via the toml config.

### Applying Device Configuration Every Time

The toml config now has a setting to apply device configurations on reload:

```toml
device-config-filter = "all"
```

By default, configuration changes are not applied to existing devices so as not
to overwrite changes made via the command line or the control center.

Thanks to @Stoppedpuma for suggesting this.

### Disabling Input Devices

Input devices can now be disabled from the toml config:

```toml
[[inputs]]
match.name = "Sony Interactive Entertainment Wireless Controller Touchpad"
detached = true
```

Thanks to @ArthurHeymans for suggesting this.

### Configuring the Cursor Size

The cursor size can now be configured via the toml config:

```toml
cursor-size = 30
```

This only affects cursors rendered by the compositor.

Thanks to @GK-Gaming for suggesting this.

## Visualizing Compositing

Sometimes it is useful to know if direct scanout is active or inactive, for
example, to diagnose performance issues. Jay can now display a watermark on
screen while it is compositing; the watermark disappears automatically when
direct scanout is active:

```toml
[shortcuts]
alt-x = "toggle-visualize-compositing"
```

## Upholding the graphical-session.target

Jay will now ensure that the systemd `graphical-session.target` is active while
it is running. This works around a recent regression in xdg-desktop-portal that
otherwise prevents screencasts and similar features from working under most
compositors.

Thanks to @krakow10 for reporting this.

## Jay Environment Variables

Jay reads a number of environment variables at startup to configure itself.
These variables are now documented in the book in the _Env Variables Read by
Jay_ section.

## `dlopen()` Metadata

The Jay executable now contains `dlopen` metadata that can be used by packagers
to determine which libraries Jay will try to load at runtime and for which
purpose:

```shell
~$ systemd-analyze dlopen-metadata $(which jay)
FEATURE DESCRIPTION                      SONAME          PRIORITY
sqlite  required for session management  libsqlite3.so.0 recommended
opengl  required for the opengl renderer libGLESv2.so.2  recommended
opengl  required for the opengl renderer libEGL.so.1     recommended
vulkan  required for the vulkan renderer libvulkan.so.1  recommended
```

## Windows BT.2100 Color Descriptions

Jay now supports version 3 of the color management protocol and BT.2100 color
descriptions used by Wine.

## Protocol Updates and Additions

This version of Jay supports the following new and improved protocols:

| Global              | Old | New |
|---------------------|:----|:----|
| wl_seat             | 10  | 11  |
| wp_color_manager_v1 | 2   | 3   |
| zwp_linux_dmabuf_v1 | 5   | 6   |

# 1.13.0 (2026-05-08)

## Fixes

As always, this release contains many bug fixes. Thanks to the following people
for reporting or fixing bugs:

- @Ktrompfl
- @luvvlyjude
- @polycatenane
- @Stoppedpuma

## The Jay Book

Jay now has a [book](https://mahkoh.github.io/jay/book) that makes it easier to
get started with Jay and to discover its various features. For example, did you
know that you can toggle windows between floating and tiled by double-clicking
on their titles? Or that Jay has a window-management mode that allows you to
drag fullscreen windows between outputs? Previously there was no way to discover
these features if you didn't follow Jay development closely.

The book was written with the help of large language models. If you don't want
to consume that kind of content, the reference documentation of the toml config
and the Jay code itself remains free of AI, as far as I know.

## Session Management

Jay now supports the session-management protocol that allows application windows
to be restored after a restart.

In tiling window managers, restoring the exact layout is often not possible, for
example, tiles created by other applications might no longer exist. Therefore,
restoration is limited to the following features:

- Restoring windows on the original workspace.
- Restoring windows on the original output.
- Restoring fullscreen windows fullscreen.
- Restoring floating windows with the same size and position.

Session state is stored in an SQLite database under `~/.local/share/jay/db`.
This feature is only available if Jay is able to load `libsqlite3.so` at
runtime.

## Overlay Workspaces

Jay now supports overlay workspaces, also known as special workspaces or
scratchpads in other compositors.

```toml
alt-x = { type = "toggle-overlay", name = "overlay" }
```

Overlays are like normal workspaces except that both a normal workspace and an
overlay can be visible on the same output at once.

Overlays are always rendered above all other surfaces including all layer-shell
surfaces and fullscreen windows.

Floating windows can be moved from a normal workspace to an overlay workspace by
dragging them onto the output showing the overlay. If the window is already on
that output, this happens as soon as the window title is clicked.

Conversely, a floating window can be moved off of the overlay workspace by
dragging it onto an output that isn't showing an overlay. If you're holding onto
the title of such a window and then hide the overlay on the output, the window
is also moved to the normal workspace below.

Of course, the normal `move-to-workspace` action also works for overlays.

Windows on overlays are distinguished from other windows by showing a layer icon
in their title. Similarly, if an overlay is visible on an output, it appears in
the bar with a layer icon next to its name. You can hide overlays by
middle-clicking their name in the bar.

See the Overlays chapter in the book for more details.

Thanks to @Stoppedpuma for suggesting this.

## Custom config.so With Privileges

It is recommended to give the Jay executable the `CAP_SYS_NICE` filesystem
capability to improve responsiveness under high system load.

```shell
~$ sudo setcap cap_sys_nice=p $(which jay)
```

Previously, doing this fully disabled loading of custom config.so for the
following reason: Any unprivileged application could have created a config.so,
placed it in `~/.config/jay` and started Jay to run arbitrary code with the
`CAP_SYS_NICE` capability, possibly enabling a local denial-of-service attack.

This meant that the toml config was privileged since it was the only config type
able to run with privileges. Jay's config system was designed from the start to
allow people to write arbitrary config.so's. For example, if someone wanted to
write a config that uses Lua instead of toml, they should be able to do so.

Jay will now load config.so even when running with capabilities as long as the
config.so is owned by the root user. If you want to use such a config.so, simply
`sudo chown root:root config.so`. If you have a config.so that was installed as
root by a package manager, you can create a symlink to it without having to
invoke `sudo`.

## Keymap Shortcuts Group

When using non-Latin layouts, it can sometimes be troublesome to invoke keyboard
shortcuts. For example

```toml
keymap.rmlvo = {
    layout = "us,ru",
    options = "grp:ctrl_space_toggle",
}

[shortcuts]
alt-h = "focus-left"
```

When in the Russian layout, pressing `alt-h` would not invoke the shortcut
because the `h` key does not produce the `h` keysym.

Jay now supports specifying a dedicated shortcuts group for a keymap.

```toml
keymap.rmlvo = {
  layout = "us,ru",
  options = "grp:ctrl_space_toggle",
}
keymap.shortcuts-group = 0
```

This means that shortcuts are always evaluated against the first (starting at 0)
group in the keymap. Pressing `alt-h` will therefore invoke the shortcut because
the `h` key produces the `h` keysym in the US layout.

## Virtual Outputs and Headless Backend

Jay now supports virtual outputs. Virtual outputs act like normal outputs as far
as applications are concerned but are not connected to any physical hardware.
They support arbitrary modes, VRR, and tearing.

Virtual outputs can be created in the control center in the virtual outputs
pane, on the command line, or in the config.

```shell
~$ jay randr virtual-output create abc
```

```toml
[shortcuts]
alt-x = { type = "create-virtual-output", name = "abc" }
```

Once created, they can be configured like any other output.

Jay now also supports a headless backend, that is, a backend that doesn't use
any physical hardware. You can interact with such a Jay instance by using
virtual outputs and any remote-desktop solution.

## Warp To Focus

Jay now supports warping the cursor to the center of the focused window. This
can be done either automatically or with an action.

```toml
# automatic
unstable-mouse-follows-focus = true

# manual
[shortcuts]
alt-x = "warp-mouse-to-focus"
```

Due to limitations in Jay's layout algorithm, the automatic warping is still
considered unstable and its behavior might change in the future.

Thanks to @Ktrompfl for implementing this.

## Config Subcommand

The Jay CLI now has a config subcommand to make it easier to initialize and find
the toml config.

```shell
~$ jay config       
Create/modify the toml config

Usage: jay config <COMMAND>

Commands:
  init      Initialize the toml config file
  path      Print the path to the config
  open-dir  Open the config directory with xdg-open
  help      Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help
```

## CLI JSON Output

Most CLI commands now support JSON output for scripting.

```shell
~$ jay --json randr | jq . | head -10
{
  "drm_devices": [
    {
      "devnode": "/dev/dri/card0",
      "syspath": "/sys/devices/pci0000:00/0000:00:08.1/0000:14:00.0",
      "vendor": 4098,
      "vendor_name": "Advanced Micro Devices, Inc. [AMD/ATI]",
      "model": 5710,
      "model_name": "Raphael",
      "gfx_api": "Vulkan",
```

## Deleting Old Log Files

Jay stores its logs in `~/.local/share/jay/log/jay`. Previously these log files
were never automatically deleted. You can now configure Jay to delete log files
whose Jay instance has already terminated.

```toml
clean-logs-older-than.days = 5
```

Thanks to @khyperia for implementing this.

## Resizing and Moving Windows with Actions

The new `resize` action can be used to move or resize windows.

```toml
alt-x = { type = "resize", dx1 = -10, dx2 = -10 }
```

This works for both floating and tiled windows.

Thanks to @BergmannAtmet for suggesting this.

## Color Management Improvements

Jay now supports the following rendering intents in addition to the perceptual
intent:

- absolute_no_adaptation
- relative
- relative_bpc

## Numerical Workspace Sorting

When automatic workspace sorting is enabled, workspaces are now sorted
numerically instead of lexicographically. For example,

- 1
- 2
- 10

whereas previously 2 and 10 would be swapped.

Thanks to @llyyr for implementing this.

## Vulkan Descriptor Heaps

Jay now supports the VK_EXT_descriptor_heap extension. It will be used if
available with a fallback to VK_EXT_descriptor_buffer as before.

The extension is available on the proprietary Nvidia driver and on radv if the
environment variable `RADV_EXPERIMENTAL=heap` is set before starting Jay.

Since drivers might still have bugs, use of descriptor heaps can be disabled by
starting jay with `JAY_NO_DESCRIPTOR_HEAP=1`.

## Disabling Outputs

Outputs can now be disabled in the `outputs` array.

```toml
[[outputs]]
match.serial-number = "ETW1M02062SL0"
enabled = true
```

Previously this required disabling the connector via the `connectors` array.

Thanks to @marienz for implementing this.

## Showing Workspaces Without Focusing Them

It is now possible to show a workspace on an output without moving the focus to
that workspace.

```toml
alt-x = { type = "show-workspace", name = "2", focus = false }
```

If this causes a workspace to be hidden that contains the focus, then the focus
is still moved to the new workspace. This is therefore primarily useful on
multi-monitor systems.

## Pixel Scroll Multiplier

When using two-finger scrolling on a touchpad, you can now specify a multiplier
that applies to the scroll distance.

```toml
[[inputs]]
match.is-pointer = true
px-scroll-multiplier = 0.5
```

This has no effect on scroll events created by a mouse wheel.

Thanks to @luvvlyjude for suggesting this.

## Toplevel Icons

Jay now supports the toplevel icon protocol that allows applications to show
icons in their window titles. Icons can be disabled and they can optionally be
rendered as grayscale.

```toml
[theme]
show-window-icons = false
window-icons-grayscale = true
```

Thanks to @Skylewl for suggesting this.

## Protocol Updates and Additions

This version of Jay supports the following new and improved protocols:

| Global                          | Old | New |
|---------------------------------|:----|:----|
| wl_fixes                        | 1   | 2   |
| xdg_session_manager_v1          |     | 1   |
| xdg_toplevel_icon_manager_v1    |     | 1   |
| zwlr_virtual_pointer_manager_v1 |     | 2   |

# 1.12.0 (2026-03-16)

## Fixes

As always, this release contains many bug fixes. Thanks to the following people for
reporting or fixing bugs:

- @danielfrrrr
- @disluckyguy
- @effectsbot
- @flammablebunny
- @Jeff-WuYo
- @khyperia
- @kotarac
- @krakow10
- @KyunLFA
- @LeaPhant
- @llyyr
- @luvvlyjude
- @noxvail
- @Richochet1
- @rrtt217
- @sday-ssc
- @Stoppedpuma
- @uku3lig

## Control Center

Jay now has a built-in control center that can be opened with the `open-control-center`
action. In the default config, this action is bound to `alt-c`.

The control center runs in the same thread as the rest of the compositor and therefore has
easy access to all compositor internals. In this version it does not make much use of this
ability and limits itself to exposing the capabilities that are already available through
the configuration and on the command line.

Since the control center runs in the same thread, it also takes away resources from the
rest of the compositor and might increase latency. You should close the control center
when you are not using it.

## Support for Render-Only GPUs

Jay now supports render-only GPUs, that is, GPUs that themselves don't support driving
displays. This is useful on some embedded devices where the GPU that supports accelerated
rendering is render-only and displays are connected to a separate GPU that doesn't support
accelerated rendering. On such devices, you would want to use the render-only GPU as the
primary GPU.

You can test this via `sudo modprobe vgem` which will create a virtual render-only GPU.

## Vulkan Improvements

- Jay now ships pre-compiled shaders. You no longer need to have shaderc installed to
  compile Jay.

- The renderer will now fall back to software rendering in virtual machines where
  accelerated rendering is not available. Previously this would fall back to software
  rendering via OpenGL.

- The blend space can now be configured. Previously the renderer always blended in linear
  space which made some transparent textures much lighter than intended. Since most
  textures are designed to be blended in sRGB space, this is the new default. If you're
  not using HDR, this is also significantly more efficient. The easiest way to temporarily
  change the blend space is in the control center in the outputs pane.

- Shared-memory buffers can now be imported directly into the renderer via the udmabuf
  mechanism. This can significantly improve the compositor-side performance by reducing
  the number of copies from 2 to 1. On integrated GPUs, the renderer will try to use such
  buffers directly as images, eliminating all copies.

- The renderer will now opportunistically use timeline semaphores as syncobjs which
  eliminates a significant number of system calls. This is known to not work with the
  Nvidia driver since it does not use syncobjs for timeline semaphores. A new vulkan
  extension for official timeline semaphore/syncobj interop is being discussed at Khronos,
  which would allow this feature to also work on Nvidia.

## Toml Improvements

- `exec` actions now have a `shell` field to execute a program via the shell. For example,

  ```toml
  [shortcuts]
  alt-x = { type = "exec", exec.shell = "echo $WAYLAND_DISPLAY >file" }
  ```
  
- The config can now be reloaded automatically when the config file has changed. This is
  disabled by default and can be enabled with the top-level, boolean `auto-reload` field.

  Note that reloading the config has side effects such as reverting changes to the theme
  made via the command line and the control center.

  Thanks to @disluckyguy for implementing this.

## Client Capabilities

Client rules can now grant clients additional capabilities. This allows more fine-grained
control over exposed globals in contrast to the existing feature that only allowed
granting all capabilities to a client. See the next section for an example.

## Tagging Clients

You can now add a _tag_ to applications launched via `exec` actions and on the command
line. This tag applies to the application and all of its children. For example, on the
command line

```shell
~$ jay run-tagged my-tag chromium
```

and in an action

```toml
[shortcuts]
alt-x = { type = "exec", exec.prog = "chromium", exec.tag = "my-tag" }
```

This tag can in turn be used in client rules to match clients:

```toml
[[clients]]
match.tag = "my-tag"
capabilities = ["data-control"]
```

Tags are incompatible with `exec.privileged = true`. All capabilities must be assigned via
client rules.

## Pid Subcommand

The new `jay pid` subcommand prints the process ID of Jay and exits. This can be useful
when debugging issues and can be used as follows:

```shell
~$ cd /proc/$(jay pid)
~$ sudo strace -p $(jay pid)
```

## Tray Applications

Jay now supports running almost all wayland-native applications as tray applications. This
can be accomplished with the
[window-to-tray](https://github.com/mahkoh/wl-proxy/tree/master/apps/window-to-tray)
tool from the wl-proxy project.

For example, to have an easily accessible volume mixer in the tray:

```shell
~$ window-to-tray pavucontrol-qt
```

## Keymaps from RMLVO Names

In addition to XKB, keymaps can now also be specified via their RMLVO names that might be
known from other compositors:

```toml
keymap.rmlvo = {
    layout = "us,de",
    variants = "dvorak",
    options = "grp:ctrl_space_toggle",
}
```

Thanks to @khyperia for implementing this.

## Night Light

Jay now supports night-light applications that attach gamma tables to outputs via
`zwlr_gamma_control_manager_v1`.

Thanks to @khyperia for implementing this.

## Improved Multi-GPU Blits

On some systems, displays are connected to GPUs other than the GPU used for rendering. The
embedded scenario with render-only GPUs is one example. Another are laptops with dedicated
GPUs where the internal display is hard-wired to the iGPU and external displays are
hard-wired to the dGPU.

Jay has always supported such systems with the open-source mesa drivers. However, it did
not support using Nvidia devices as the primary GPU and the method used to copy images
from the primary GPU to the output GPU was not optimized.

This release adds several new implementations for these blits:

- direct-pull
- direct-sampling
- indirect-pull
- udmabuf
- direct-push

By default, the compositor will try to use the methods in this order. It is currently not
possible to know which method is the most optimal on a given system without trying them.
In the embedded scenario, direct-push is most likely the most efficient, but on a dual-AMD
system it is likely to crash the compositor due to bugs in the AMD driver.

You can modify the order in which these methods are attempted, and exclude methods, with
the `JAY_PRIME_METHODS` environment variable. For example:

```shell
~$ JAY_PRIME_METHODS=udmabuf,-direct-push jay run
```

This would result in the methods being attempted in this order:

- udmabuf
- direct-pull
- direct-sampling
- indirect-pull

On systems with an Nvidia primary device and mesa secondary devices, you might
need to explicitly select the udmabuf method due to bugs in the Nvidia driver.

## Look & Feel Improvements

- The bar can now be moved to the bottom of the screen.

  ```toml
  [theme]
  bar-position = "bottom"
  ```
  
  Thanks to @kotarac for implementing this.

- The width of the separator between the bar and the rest of the output can now
  be configured.

  ```toml
  [theme]
  bar-separator-width = 2
  ```

  Thanks to @kotarac for implementing this.

- Directional commands now work across neighboring outputs.

  Thanks to @ArthurHeymans for implementing this.

- Jay can now be configured to display workspaces in alphabetical order.

  ```toml
  workspace-display-order = "sorted"
  ```

  Thanks to @kotarac for implementing this.
  
- All configurable sizes can now be reduced to 0 instead of 1.

- The bar and title heights can now be configured separately.

  ```toml
  [theme]
  title-height = 16
  bar-height = 17
  ```
  
## Fallback Output Mode

By default, when opening new windows or executing actions that don't specify an output but
need an output, Jay will use the output that contains the cursor. To enable more
keyboard-centric workflows, Jay can now be configured to fall back to the output that has
the keyboard focus.

```toml
fallback-output-mode = "focus"
```

To allow this to work with empty outputs, it is now also possible to

- specify an output when creating a workspace

  ```toml
  [shortcuts]
  alt-x = { type = "show-workspace", name = "abcd", output.connector = "DP-3" }
  ```

- focus an empty workspace.

Thanks to @khyperia for implementing this.

## Disabling Xwayland

If you don't need Xwayland and want to ensure that no application uses Xwayland,
you can now disable Xwayland outright:

```toml
[xwayland]
enabled = false
```

Thanks to @khyperia for implementing this.

## Simple Input Method

If no external input method is used, Jay will now provide a built-in simple
input method. This IM can operate in two modes:

- By default, it provides completions based on `~/.XCompose`.
- When the `enable-unicode-input` action is executed, it allows inputting
  characters by their unicode code point.

The simple input method can be disabled:

```toml
[simple-im]
enabled = false
```

## Protocol Updates and Additions

This version of Jay supports the following new and improved protocols:

| Global                             | Old | New |
|------------------------------------|:----|:----|
| jay_popup_ext_manager_v1           |     | 1   |
| wl_compositor                      | 6   | 7   |
| wl_data_device_manager             | 3   | 4   |
| wl_seat                            | 9   | 10  |
| wp_color_manager_v1                | 1   | 2   |
| wp_color_representation_manager_v1 |     | 1   |
| zwlr_gamma_control_manager_v1      |     | 1   |
| zxdg_decoration_manager_v1         | 1   | 2   |

# 1.11.0 (2025-07-26)

## Fixes

As always, this release contains many bug fixes. Notably it changes the `top` layer in the
wlr-layer-shell-unstable-v1 protocol to be rendered below fullscreen windows. Previously
such layers were rendered on top of fullscreen windows which deviates from the behavior of
other compositors.

If this negatively affects your experience, try to configure the affected applications to
use the `overlay` layer instead.

Thanks to @disluckyguy for fixing this.

## New and Improved Protocols

This release implements the following new protocols:

- xdg-toplevel-tag-v1

  This protocol allows clients to add a string tag to their windows. These tags can be
  used in window rules (see below).

- wlr-foreign-toplevel-management-unstable-v1

  This protocol allows task bars to display and manage application windows.

  Thanks to @disluckyguy for implementing this.

- wlr-output-management-unstable-v1

  This protocol allows applications to manage the position, mode, etc. of outputs.

  Thanks to @disluckyguy for implementing this.

- pointer-warp-v1

  This protocol allows applications to warp the cursor within their own windows.
  Previously some applications abused the pointer-constraints-unstable-v1 protocol for the
  same purpose.

- tablet-v2, version 2

  This version of the tablet protocol adds support for pad dials.

- pointer-constraints-unstable-v1, position hints

  The implementation now honors position hints set by clients.

  Thanks to @tadeokondrak for suggesting this.

## Disabling the Built-In Bar

The built-in bar can now be disabled:

```toml
show-bar = true

[shortcuts]
alt-a = "show-bar"
alt-b = "hide-bar"
alt-c = "toggle-bar"
```

This can be useful if you want to use an external bar such as waybar.

## Client & Window Rules

Jay now supports client and window rules:

```toml
[[windows]]
match.content-types = ["video"]
action = "enter-fullscreen"
```

These rules are described in detail in
[Window & Client Rules](https://mahkoh.github.io/jay/book/window-rules.html).

## Window Management

This release contains many improvements related to how you can manage windows with the
mouse and keyboard.

- You can now show floating windows above fullscreen windows:

  ```toml
  alt-a = "enable-float-above-fullscreen"
  alt-b = "disable-float-above-fullscreen"
  alt-c = "toggle-float-above-fullscreen"
  ```

- Floating windows can now be pinned to an output. A pinned floating window remains
  visible even if you switch to a different workspace on the same output.

  You can pin a floating window by right-clicking on its title or with the following actions:

  ```toml
  alt-a = "pin-float"
  alt-b = "unpin-float-float"
  alt-c = "toggle-float-pinned"
  ```

  A pinned window has a pin icon drawn in front of its title. You can also configure the
  compositor so that a grayed-out version of the icon is always shown even if the window
  is not pinned:

  ```toml
  [float]
  show-pin-icon = true
  ```

  In this case you can also toggle between pinned and unpinned by left-clicking on the
  icon.

- Floating windows are now restacked when you click, touch, or press down a tablet tool
  anywhere inside of them. Previously this only happened when clicking on the window
  title.

  Thanks to @disluckyguy for suggesting this.

- Fullscreen windows can now be moved to other workspaces and outputs without first
  leaving fullscreen. You can do this with the usual keyboard shortcuts or in
  window-management mode by dragging the window to another output. Recall that you can
  configure a key to enable window-management mode as follows:

  ```toml
  window-management-key = "XF86Macro1"
  ```

  This will enable window-management mode while this key is being pressed.

- In window-management mode, tiled windows can now be dragged with the mouse to move them
  around. Previously this was possible by dragging the window title, but in window
  management mode you can now click anywhere within the window.

  Note that, in window-management mode, dragging the title will actually drag the
  container containing the window.

  Thanks to @Stoppedpuma for suggesting this.

- Jay allows you to always revert the pointer to its default state by pressing the Escape
  key. For example, if you're performing a drag-and-drop operation, you can press Escape
  to abort it. Some people want to use the Escape key legitimately without having this
  side effect on the pointer.

  You can now configure which key performs this operation:

  ```toml
  pointer-revert-key = "Escape"
  ```

  You can disable this feature altogether by setting it to `NoSymbol`.

  Thanks to @kotarac for suggesting this.

- You can now assign vim-like marks to windows and later jump back to them:

  ```toml
  [shortcuts]
  alt-a = "create-mark"
  alt-b = "jump-to-mark"
  ```

  When these actions are executed, the next key press marks the selected window with that
  key or jumps to the window that was previously marked with that key.

  Instead of selecting the key interactively, you can also specify it in the config
  itself:

  ```toml
  [shortcuts]
  alt-a = { type = "create-mark", id.key = "a" }
  ```

  The key names can be found in the `input-event-codes.h` file in your `/usr/include`
  directory. The names should have the `KEY_` prefix removed and must be written
  all-lowercase.

  Alternatively, you can use marks that are identified by a string name instead of a key:

  ```toml
  [shortcuts]
  alt-a = { type = "create-mark", id.name = "my mark name" }
  ```

  These marks live in their own namespace and cannot be accessed with the interactive key
  selection.

- Jay now maintains a focus history. The history allows you to navigate between your
  windows in the order in which you focused them:

  ```toml
  alt-a = "focus-prev"
  alt-b = "focus-next"
  ```

  You can customize the behavior of these actions with the following settings:

  ```toml
  [focus-history]
  only-visible = false
  same-workspace = true
  ```

  If `only-visible` is `true`, then actions will only move the focus to windows that are
  already visible. Otherwise it will make windows visible before focusing them, moving
  between tabs and workspaces as necessary.

  If `same-workspace` is `true`, then only windows on the same workspace will be focused.

  The defaults are `false` for both.

- When switching to a workspace, Jay will now put the focus on the last window that was
  previously focused on that workspace, which might be a floating window. Previously Jay
  would always put the focus on the last _tiled_ window that was previously focused.

- You can now navigate between tiled and floating layers without using the mouse:

  ```toml
  [shortcuts]
  alt-a = "focus-below"
  alt-b = "focus-above"
  ```

  This should allow you to put the keyboard focus on any window without having to use the
  mouse.

  Thanks to @Stoppedpuma for suggesting this.

## Toml Improvements

These improvements apply to the toml-based configuration:

- You can now assign names to actions:

  ```toml
  actions.xyz = [
      { type = "move-to-workspace", name = "1" },
      "enter-fullscreen",
  ]
  ```

  These actions can then be executed by prefixing their name with a `$`:

  ```toml
  [shortcuts]
  alt-a = "$xyz"
  ```

  This can be useful when the same action is used in multiple places.

  You can also re-define or un-define actions at runtime:

  ```toml
  [shortcuts]
  alt-a = {
    type = "define-action",
    name = "xyz",
    action = [
      { type = "move-to-workspace", name = "2" },
      "enter-fullscreen",
    ],
  }
  alt-b = { type = "undefine-action", name = "xyz" }
  ```

  These types of redefinitions can be used for a limited amount of dynamic behavior. For
  example, you can have the same key cycle between a number of workspaces.

- Jay now supports input modes:

  ```toml
  [shortcuts]
  alt-x = { type = "push-mode", name = "navigation" }

  [modes."navigation".shortcuts]
  Escape     = "pop-mode"
  w          = "focus-up"
  a          = "focus-left"
  s          = "focus-down"
  d          = "focus-right"
  q          = "focus-prev"
  e          = "focus-next"
  r          = "focus-above"
  f          = "focus-below"
  m          = "create-mark"
  apostrophe = "jump-to-mark"
  ```

  These modes allow you to define shortcuts that are overlayed on the normal shortcuts
  while the mode is active.

  If you have a foot pedal or same easy-to-reach key, you can use Jay's complex-shortcuts
  mechanism to enter the mode while you're holding the key down:

  ```toml
  [complex-shortcuts.XF86Macro2]
  action = { type = "push-mode", name = "navigation" } # Executes when the key is pressed
  latch = "pop-mode"                                   # Executes when the key is released
  ```

  If you prefer key chords, you can use the `latch-mode` action:

  ```toml
  [shortcuts]
  alt-x = { type = "latch-mode", name = "navigation" }
  ```

  This acts like `push-mode` except that the mode is automatically popped when the next
  shortcut is executed.

  By default, modes inherit from the default shortcuts, however, you can also configure
  them to inherit from another mode:

  ```toml
  [modes.m1.shortcuts]
  # ...

  [modes.m2]
  parent = "m1"
  [modes.m2.shortcuts]
  # ...
  ```

  In this case, the shortcuts that are active while the `m2` mode is active are normal
  shortcuts, overwritten by the `m1` shortcuts, overwritten by the `m2` shortcuts.

  Note that you can explicitly unset a shortcut by assigning it the `none` action.

## Support for `cap_sys_nice`

Jay now supports running with the `cap_sys_nice` capability:

```shell
~$ sudo setcap cap_sys_nice=+p /path/to/jay
```

If the compositor is started with this capability, it will set its own scheduler policy
to `SCHED_RR` which will allow the compositor to stay responsive even if the system is
under heavy load.

If the Vulkan renderer is used, it will also request a high-priority context which can
allow the kernel to give it priority when scheduling GPU operations.

Note that the capability and scheduler policy will not be inherited by child processes.

## Miscellaneous Changes

- The `jay-config` crate now supports `Seat::get_keyboard_output` and
  `Connector::workspaces` functions to retrieve the output that has the keyboard focus and
  retrieve the list of workspaces located on an output.

  Thanks to @khyperia for implementing this.

- The libinput click-method and middle-button-emulation settings can now be set via the
  configuration or on the command line.

- The use of hardware cursors can now be disabled in the toml configuration:

  ```toml
  use-hardware-cursor = false
  ```

- The primary selection protocol can now be disabled:

  ```toml
  middle-click-paste = true
  ```

  Thanks to @kotarac for suggesting this.

- Jay can now be compiled against musl libc.

  Thanks to @elde-n for implementing this.

- Jay now works with some status programs that produce incorrectly formatted i3status
  output.

  Thanks to @disluckyguy for implementing this.

# 1.10.0 (2025-04-22)

- Various bugfixes.
- Implement cursor-shape-v1 version 2.
- Implement xdg-shell version 7.
- Implement color-management-v1.

  This release adds support for color management if the vulkan renderer is used
  and the vulkan driver supports the VK_EXT_descriptor_buffer extension. Color
  management is not available with the opengl renderer.

  There are two limitations:

  - No tone mapping is performed. That is, HDR content that is brighter than
    what the framebuffer can store will be clipped.
  - No gamut mapping is performed. That is, content that is more colorful than
    what the framebuffer can store will be clipped.

  The color management protocol is disabled by default. You can enable it on the
  command line via `jay color-management enable` or in the config file via
  `color-management.enabled = true`. If you are not using an OLED display, you
  probably want to keep it disabled and instead rely on the tone mapping
  performed by applications such as mpv. Changing this setting usually requires
  applications to be restarted.

  You can use this application to test various settings:
  https://github.com/mahkoh/wayland-color-test
- Outputs can now optionally use the BT.2020/PQ color space.

  On the command line this can be controlled via
  `jay randr output <OUTPUT> colors ...`. In the config file it can be
  controlled via the `color-space` and `transfer-function` fields in the output
  configuration.

  When using the PQ transfer function, the output should also be configured to
  use at least a 10 bpc framebuffer format.
- The reference brightness of outputs can now be configured via
  `jay randr output <OUTPUT> brightness ...` or in the config file via the
  `brightness` field in the output configuration.

  This is primarily useful when using the PQ transfer function and the display
  does not support configuring the brightness via its hardware overlay.

  This configuration has no effect unless the vulkan renderer is used and the
  vulkan driver supports the VK_EXT_descriptor_buffer extension.
- The vulkan renderer has been significantly improved to ensure high performance
  even with color management enabled:
  - VK_EXT_descriptor_buffer is used if available.
  - Only the damaged areas of the screen are rendered.
  - Draw calls are instanced so that only one draw call is required per texture
    even if there are multiple small damage areas.
  - Blending is now performed with a dedicated 16 bpc texture in linear space.
  - The blend buffer is deduplicated between outputs with the same size.
  - Areas of the screen where the topmost texture is opaque bypass the blend
    buffer and render directly to the frame buffer.

# 1.9.1 (2025-02-13)

This release updates the kbvm crate to fix an issue that would cause applications to
interpret the AltGr key as the Alt key.

Reported by @Honkeh in https://github.com/mahkoh/jay/issues/364.

# 1.9.0 (2025-01-27)

This release replaces xkbcommon by the kbvm crate.

This is a huge change in how input is handled. The intention is that this is completely
invisible to users.

Therefore this release contains only this change. You can downgrade to the previous
release to switch back to xkbcommon without any loss in functionality. In this case please
report what is broken.

# 1.8.0 (2025-01-27)

- Various bugfixes.
- Implement ext-data-control-v1.
- Implement wl-fixes.
- Implement ei_touchscreen v2.
- Implement idle-notification v2.
- Add an idle grace period. During the grace period, the screen goes black but is neither
  disabled nor locked. This is similar to how android handles going idle. The default is
  5 seconds.
- Implement ext-workspace-v1.

# 1.7.0 (2024-10-25)

- Various bugfixes.
- Tiles and workspaces can now be dragged with the mouse.
- Vulkan is now the default renderer.
- Emulate vblank events on the nvidia driver.
- Allow X windows to scale themselves.
- Implement ext-image-capture-source-v1.
- Implement ext-image-copy-capture-v1.
- Implement screencast session restoration.
- Fix screen sharing in zoom.
- Implement wp-fifo-v1.
- Implement wp-commit-timing-v1.
- Implement jay-tray-v1. You can get tray icons and menus by using
  https://github.com/mahkoh/wl-tray-bridge.

# 1.6.0 (2024-09-25)

- Various bugfixes.
- Improve compatibility Nvidia hardware.
- Implement format negotiation for screencasts.
- Allow configuring 6, 8, or 10 bit framebuffer formats.
- Upload shm textures on a separate thread in the Vulkan renderer.
- Disable implicit sync in KMS.
- Implement frame scheduling for KMS.
- The JAY_MAX_RENDER_TIME_NSEC environment variable has been removed.

# 1.5.0 (2024-09-02)

- Add fine-grained damage tracking.
- Add support for adaptive sync.
- Add support for tearing.
- Add support for touch input.
- Add support for libei.
- Add support for RemoteDesktop portal.

# 1.4.0 (2024-07-07)

- Add window management mode.
- Various bugfixes.

# 1.3.0 (2024-05-25)

- Add remaining layer-shell features.
- Add JAY_MAX_RENDER_TIME_NSEC environment variable.
  This can be used to delay rendering until shortly before a page flip, reducing input
  delay.
  This is an unstable feature that might change in the future.
- Various bugfixes.
- Improve performance of Vulkan renderer.

# 1.2.0 (2024-05-05)

- Add support for wp-security-manager-v1.
- Add support for xdg-dialog-v1.
- Add support for ext-transient-seat-v1.
- Add support for wp-drm-lease-v1.
- Focus-follows-mouse can now be disabled.
- Add support for pointer-gestures-unstable-v1.
- Configs can now handle switch events (laptop lid closed/opened).
- Add support for tablet-v2.
- Add support for linear framebuffers (hardware cursors/screensharing) on NVIDIA if the Vulkan renderer is used. (The OpenGL renderer does not support this.)

# 1.1.0 (2024-04-22)

- Screencasts now support window capture.
- Screencasts now support workspace capture.
- Add support for wp-alpha-modifier.
- Add support for per-device keymaps.
- Add support for virtual-keyboard-unstable-v1.
- Add support for zwp_input_method_manager_v2.
- Add support for zwp_text_input_manager_v3.
- Add support for push-to-talk.
- Various bugfixes.

# 1.0.3 (2024-04-11)

- Partially disable explicit sync on nvidia drivers.

# 1.0.2 (2024-04-10)

- Fixed a bug that caused the portal to fail.

# 1.0 (2024-04-07)

This is the first stable release of Jay.