Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da2e121358 | ||
|
|
c834b41d39 | ||
|
|
ed588d46c1 | ||
|
|
8f2d28c032 | ||
|
|
31d5f0801d | ||
|
|
c397f69ef4 | ||
|
|
bb1cfea898 | ||
|
|
2a93055068 | ||
|
|
76d4eb6ee0 | ||
|
|
a1feca1e1f | ||
|
|
6743ffebf1 | ||
|
|
9143c15454 | ||
|
|
3428e3682b | ||
|
|
5b8f9187d5 | ||
|
|
a61e9d5efc | ||
|
|
bf15e64b63 | ||
|
|
132e0e8acb | ||
|
|
e2dd68fdbc | ||
|
|
16953fc019 | ||
|
|
545a5c1d96 | ||
|
|
e55e709b72 | ||
|
|
117eabd30b | ||
|
|
38af22aba0 | ||
|
|
cba20bf665 | ||
|
|
827ba7cda4 | ||
|
|
79d9ef8bb4 | ||
|
|
513549784f | ||
|
|
a96105fb09 | ||
|
|
f28d61faa1 | ||
|
|
dc66493caa | ||
|
|
3f86134881 | ||
|
|
b7934d7e87 | ||
|
|
4b5166dfed | ||
|
|
afe17eefc8 | ||
|
|
a7ce15eaea | ||
|
|
44c22ccad3 | ||
|
|
eae17877a1 | ||
|
|
6435955a51 | ||
|
|
959d863ec0 | ||
|
|
09bc5a16f4 | ||
|
|
58bb0f5500 | ||
|
|
eb150b32e3 | ||
|
|
38702fb66b | ||
|
|
593443fc72 | ||
|
|
223ef33bfd | ||
|
|
d8f71f46be | ||
|
|
bc687b90ad | ||
|
|
3f9e83ab97 | ||
|
|
8fff3e413e | ||
|
|
c2b7e43d7f | ||
|
|
16dd251276 | ||
|
|
b830479f09 | ||
|
|
7a45b0d119 | ||
|
|
8dd6cbbfdb | ||
|
|
057c048d01 | ||
|
|
b7ddc028a5 | ||
|
|
1f17c77c97 | ||
|
|
7d3625ecdc | ||
|
|
6a08ae3951 | ||
|
|
8b49982c3a | ||
|
|
73cb1d7d9c | ||
|
|
3c8fe54618 | ||
|
|
05b4f4dc52 | ||
|
|
cae6634074 | ||
|
|
e5899521b9 | ||
|
|
ad2fa1605e | ||
|
|
b4bbaa2e12 | ||
|
|
55bc3353b6 | ||
|
|
a60f15d366 | ||
|
|
9a2fe13242 | ||
|
|
106fca2a61 | ||
|
|
920631373c | ||
|
|
533003e49b | ||
|
|
a6061348c3 | ||
|
|
077362a9d9 | ||
|
|
8557f2107c | ||
|
|
f81d66a16f | ||
|
|
d04eec39b6 | ||
|
|
22e955529b | ||
|
|
e524515ca5 | ||
|
|
7b996a985a | ||
|
|
920c1a7a4c | ||
|
|
3fbd7ef04d | ||
|
|
333bab6d2a | ||
|
|
ba582d04ca | ||
|
|
cf3c4b4152 | ||
|
|
e127e3de5c | ||
|
|
3ea931d456 | ||
|
|
46b782847c | ||
|
|
cbb383959e | ||
|
|
3ac76491c6 | ||
|
|
378bc0cd28 | ||
|
|
be93fb330a | ||
|
|
6d72f4ab83 | ||
|
|
2b32ece92d | ||
|
|
5125b46f03 | ||
|
|
eebe3ab0fd | ||
|
|
001bb9dc4e | ||
|
|
4a96078bf7 | ||
|
|
893fcd6ed1 | ||
|
|
a2be1826bb | ||
|
|
5387b3f92a | ||
|
|
aa8d68d224 | ||
|
|
2f12fd6fb3 | ||
|
|
581e5105cc | ||
|
|
0422675bc1 | ||
|
|
5039441e64 | ||
|
|
c7fd5eceae | ||
|
|
ae5b5a2fcb | ||
|
|
414f5dfe53 | ||
|
|
34918352f1 | ||
|
|
1450162d31 | ||
|
|
6e1b6d9ff3 | ||
|
|
d610206896 | ||
|
|
08a9572772 | ||
|
|
a6dc90601e | ||
|
|
efd3dae0db | ||
|
|
2d594c22cc | ||
|
|
78d22c2514 | ||
|
|
64403079d8 | ||
|
|
dc40ec2715 | ||
|
|
b03849604e | ||
|
|
82bc4e21a9 | ||
|
|
1e91aed14a | ||
|
|
2495b4c4a2 | ||
|
|
e5920b2f56 | ||
|
|
6ce08508fa | ||
|
|
76a2aebb4a | ||
|
|
015e0cbf63 | ||
|
|
735be2debb | ||
|
|
305c9af2da | ||
|
|
f7eed7ce8a | ||
|
|
20c032f8e8 | ||
|
|
6baac9298f | ||
|
|
9054dcad7a | ||
|
|
f9a4579821 | ||
|
|
7cd56bdbda | ||
|
|
b040fbc2d0 | ||
|
|
0f862de208 | ||
|
|
8ee8990f09 | ||
|
|
b6053a7b31 | ||
|
|
fe37673c95 | ||
|
|
491b430b64 | ||
|
|
12fe596a9e | ||
|
|
4118b6dd3b | ||
|
|
1a57581228 | ||
|
|
ceec25af81 | ||
|
|
6088061953 | ||
|
|
b3e55eb20c | ||
|
|
1a349b9e96 | ||
|
|
52266fc7f6 | ||
|
|
0dfc8c48c2 | ||
|
|
2aa8455cbf | ||
|
|
3b1a979d4f | ||
|
|
2a4ed87c6e | ||
|
|
58db9ddf65 | ||
|
|
d53021a0f3 | ||
|
|
0006c79673 | ||
|
|
b86cfaeb73 | ||
|
|
2372416fac | ||
|
|
781c62a0f5 | ||
|
|
ae40fa0328 | ||
|
|
c7e6ed8943 | ||
|
|
9862e886f0 | ||
|
|
d1c5cfbcaf | ||
|
|
69547495aa | ||
|
|
6054398285 | ||
|
|
ff0b97dfb4 | ||
|
|
07422896aa | ||
|
|
c1d93d4c7d | ||
|
|
673fdf0027 | ||
|
|
5efe053c0f | ||
|
|
9344d4da18 | ||
|
|
a8df31d825 | ||
|
|
004eb94b95 | ||
|
|
4822c2fa12 | ||
|
|
9c91ab0977 | ||
|
|
7e1edb844b | ||
|
|
26f141f246 | ||
|
|
a5e0427fe7 | ||
|
|
68070796a0 | ||
|
|
b639ce8741 | ||
|
|
57ce8e5d99 | ||
|
|
34344ed57a | ||
|
|
58c0260e70 | ||
|
|
f5cde54705 | ||
|
|
223a20b271 | ||
|
|
bf4f93de4a | ||
|
|
ddff19c932 | ||
|
|
8307024abe | ||
|
|
914b96d671 | ||
|
|
68125d8d30 | ||
|
|
ff6a273cd1 | ||
|
|
7d560e0675 | ||
|
|
aafd5f4c9c | ||
|
|
1b489cc2e0 | ||
|
|
9ea04aa3c1 | ||
|
|
955c232c20 | ||
|
|
fe71b18636 | ||
|
|
a547e29607 | ||
|
|
8cbd53d362 | ||
|
|
7cf27c3f39 | ||
|
|
8a3f8d5c12 | ||
|
|
8f75954ca9 | ||
|
|
19abd46414 | ||
|
|
1028752cbc | ||
|
|
7ff1e98c3e | ||
|
|
2c7178a475 | ||
|
|
9453a0a0e6 | ||
|
|
7e33d5a326 | ||
|
|
e169427360 | ||
|
|
f495dc8be7 | ||
|
|
a06d8b99d9 | ||
|
|
90066f8e37 | ||
|
|
112db000fd | ||
|
|
45c1f9e5a9 | ||
|
|
edc7b4d55b | ||
|
|
fa0e55cc93 | ||
|
|
d1042ce09b | ||
|
|
3b82ee034d | ||
|
|
2215fdacfb | ||
|
|
7821377f59 | ||
|
|
2c97eec1d0 | ||
|
|
b30f8bc6ea | ||
|
|
cd8a69fa01 | ||
|
|
b8bdbc2f3e | ||
|
|
889d732e03 | ||
|
|
54bd8d6343 | ||
|
|
6f1c81bc04 | ||
|
|
00688d5c1a | ||
|
|
fc8b9178a3 | ||
|
|
fb7c7d35d9 | ||
|
|
198fda7854 | ||
|
|
66a1d5b210 | ||
|
|
5e68aab970 | ||
|
|
ca9d836fac | ||
|
|
82a50de403 | ||
|
|
41606cef86 | ||
|
|
7d9caf407c | ||
|
|
59528ca1c3 | ||
|
|
9373e3d69a | ||
|
|
8acc134a36 | ||
|
|
a4ff57ed85 | ||
|
|
c530190638 | ||
|
|
d6b6b696b4 | ||
|
|
1473605445 | ||
|
|
255b544a6c | ||
|
|
bcbde92c2e | ||
|
|
63f82f1a35 | ||
|
|
0a8e4fead0 | ||
|
|
03ecfef429 | ||
|
|
7a97b7187b | ||
|
|
899dfd7230 | ||
|
|
fc1dbd37f1 | ||
|
|
b9c89c61a2 | ||
|
|
b5dbeae864 | ||
|
|
f08a365fc7 | ||
|
|
5daa0570ac | ||
|
|
9cd6bc90ed | ||
|
|
cfa104baf7 | ||
|
|
f214c25e9c | ||
|
|
fcee8177af | ||
|
|
0d6f9337b4 | ||
|
|
d513c7fe46 | ||
|
|
fa9d080eed | ||
|
|
df47b1bcf6 | ||
|
|
8fea2a2851 | ||
|
|
19a6e32d61 | ||
|
|
0425c3630c | ||
|
|
84db5ccfe7 | ||
|
|
d56e2b5d60 | ||
|
|
ee7e090a1f | ||
|
|
9257e3bd7c | ||
|
|
895ec9fad5 | ||
|
|
9df91587b2 | ||
|
|
db7f1a0374 | ||
|
|
1f045b2abf | ||
|
|
8dad83f720 | ||
|
|
cae0ff3fb7 | ||
|
|
c237ceff9f | ||
|
|
cf6c362a4a | ||
|
|
da99dc3080 | ||
|
|
52ab119a1f | ||
|
|
bbc23a6c65 | ||
|
|
6299e85924 | ||
|
|
4e9b7a68ba | ||
|
|
a75f6e1251 | ||
|
|
1db3b2c2cf | ||
|
|
1b663e56ee | ||
|
|
5ee14136ac | ||
|
|
58342fabc5 | ||
|
|
4da705b752 | ||
|
|
4a47fc4551 | ||
|
|
3abdae490e | ||
|
|
9d3d40d4bd | ||
|
|
b85719bc77 | ||
|
|
77caf92e16 | ||
|
|
1367e07f20 | ||
|
|
bbdbae636a | ||
|
|
10719bc72e | ||
|
|
48d8517ed1 | ||
|
|
bd608f8985 | ||
|
|
d9f988a534 | ||
|
|
074902ecc8 | ||
|
|
d17752188f | ||
|
|
7619492516 | ||
|
|
89b9940c43 | ||
|
|
60e0b537c1 | ||
|
|
5e035e14ed | ||
|
|
9f36be0284 | ||
|
|
ca6a019c1e | ||
|
|
b9f2054d35 | ||
|
|
2182d8df92 | ||
|
|
50dc17484b | ||
|
|
1d9f29b4f1 | ||
|
|
05eebc097f | ||
|
|
6b1870e813 | ||
|
|
8ea4bc293d | ||
|
|
0bd9c6f290 | ||
|
|
87b2c0cdcc | ||
|
|
d79774be1d | ||
|
|
c3483a1ec9 | ||
|
|
406dc1f14f | ||
|
|
bc896c0b03 | ||
|
|
89d974d7ef | ||
|
|
d8b46f239c | ||
|
|
14380e1cdf | ||
|
|
0ece179558 | ||
|
|
cae976b6d8 | ||
|
|
1259dd7bf7 | ||
|
|
6157a2504b | ||
|
|
c2064faa95 | ||
|
|
6d1538d3b1 | ||
|
|
215f9a7d21 | ||
|
|
882c0dcf07 | ||
|
|
06353862cf | ||
|
|
eae55ce2f0 | ||
|
|
12412947ee | ||
|
|
dec46f3ee4 | ||
|
|
5a62b23cc2 | ||
|
|
adb4d7c6a2 | ||
|
|
360fd59771 | ||
|
|
e04db94631 | ||
|
|
e65c98c290 | ||
|
|
1b6c4a9022 | ||
|
|
3246dc4c91 | ||
|
|
97e89a936c | ||
|
|
aaf86a9d13 | ||
|
|
b49cbb7791 | ||
|
|
254ed9afba | ||
|
|
298937b00c | ||
|
|
25ac7b6a9d | ||
|
|
84f5001bb2 | ||
|
|
83790f6875 | ||
|
|
b937d4eb18 | ||
|
|
8d35c0c71f | ||
|
|
a5ce2e9b7a | ||
|
|
2efc225617 | ||
|
|
0c7cbe17fb | ||
|
|
69359bb39d | ||
|
|
f24d757ec8 | ||
|
|
9fd7a8a05b | ||
|
|
a039049084 | ||
|
|
9edeba5cd2 | ||
|
|
93276abc8c | ||
|
|
dd63d5600b | ||
|
|
25be53244b | ||
|
|
2657ff4384 | ||
|
|
bc41125b7f | ||
|
|
a9fc3c6faa | ||
|
|
8ab3f136b8 | ||
|
|
1877b4f9a4 | ||
|
|
a33eb83655 | ||
|
|
991e79310a | ||
|
|
43a2b4ac30 | ||
|
|
6728228387 | ||
|
|
74c7754890 | ||
|
|
fa9444323b | ||
|
|
7e7b338d18 | ||
|
|
0d1dd02184 | ||
|
|
bc90faf5f2 | ||
|
|
2c1e697f68 | ||
|
|
5a21df86e2 | ||
|
|
20fd49f082 | ||
|
|
0e3461c6d7 | ||
|
|
231ac14703 | ||
|
|
aa1e6ebdc8 | ||
|
|
da93adac14 | ||
|
|
926b7e2a5e | ||
|
|
17f08c6d14 | ||
|
|
32cad81e1c | ||
|
|
eaacfdf7ce | ||
|
|
96659fb543 | ||
|
|
d625640051 | ||
|
|
b14756874e | ||
|
|
6bbb786ede | ||
|
|
31d9e8d582 | ||
|
|
bc12aba433 | ||
|
|
eca8b4286d | ||
|
|
fce950d406 | ||
|
|
bcdd56f714 | ||
|
|
8e557fb9fd | ||
|
|
4bb862ee76 | ||
|
|
0c5251a971 | ||
|
|
454752e46d | ||
|
|
596bfc317d | ||
|
|
97444d209a | ||
|
|
ee51227a55 | ||
|
|
db7c850596 | ||
|
|
30344bdaae | ||
|
|
7847eea1a3 | ||
|
|
1642a862d9 | ||
|
|
62f5c1cbc3 | ||
|
|
5e857c0e0a | ||
|
|
5da70164a6 | ||
|
|
fbe381cbb5 | ||
|
|
af25e78120 | ||
|
|
8d98408cfe | ||
|
|
9234ca0f32 | ||
|
|
f66d982ff3 | ||
|
|
cd16ab8e84 | ||
|
|
eaa7fd8bc3 | ||
|
|
4b62cbfb4b | ||
|
|
61a2f8cb6c | ||
|
|
5155d7b742 | ||
|
|
596833f227 | ||
|
|
56a6d4989a | ||
|
|
c43ab2ee56 | ||
|
|
f7b291b465 | ||
|
|
cc71ab8cd9 | ||
|
|
35673f113e | ||
|
|
bd3e4c33f3 | ||
|
|
6a98036c50 | ||
|
|
59bed6b995 | ||
|
|
5a3022a073 | ||
|
|
49d623e5c2 | ||
|
|
ab21a9fece | ||
|
|
41fffbb79b | ||
|
|
36ac1b40ff | ||
|
|
08da71d67f | ||
|
|
65ff0ecb34 | ||
|
|
abc17e7c2a | ||
|
|
c81a3b9432 | ||
|
|
80d46b0d85 | ||
|
|
6e15f8bcf8 | ||
|
|
294217fcba | ||
|
|
8dd1d70900 | ||
|
|
3c99c75ad7 | ||
|
|
9e0a6a758f | ||
|
|
20dbdcedf2 | ||
|
|
a2f5668c44 | ||
|
|
09e9cd4388 | ||
|
|
5c9fd28ae1 | ||
|
|
8f0c9435ff | ||
|
|
96e92485a5 | ||
|
|
f9aae23f46 | ||
|
|
fd024e32d2 | ||
|
|
5010ecf188 | ||
|
|
f0d8e97f8d | ||
|
|
8f81de775e | ||
|
|
ed8ef15f12 | ||
|
|
8a2375b3ef | ||
|
|
d97f1bd34b | ||
|
|
d86293de0f | ||
|
|
54e444d69d | ||
|
|
d5c3aebd26 | ||
|
|
1777ca4446 | ||
|
|
cf6aefa48a | ||
|
|
56e3bd1cf2 | ||
|
|
7b7045202d | ||
|
|
d7969e1ec1 | ||
|
|
1e0bf40040 | ||
|
|
bdffb63ebc | ||
|
|
eff3168f1c | ||
|
|
4db4d947db | ||
|
|
0755fc1e26 | ||
|
|
81a2f18c7c | ||
|
|
9eaadd83fb | ||
|
|
d460ac8bbb | ||
|
|
1d73513989 | ||
|
|
e3f679237d | ||
|
|
45ff97972c | ||
|
|
8bb0ddef42 | ||
|
|
36b75a071b | ||
|
|
04520cf67c | ||
|
|
3f7d934fa5 | ||
|
|
d562706c8b | ||
|
|
3245356f0f | ||
|
|
76974f676d | ||
|
|
0d23bafa3d | ||
|
|
5ddbf184fd | ||
|
|
c61695b142 | ||
|
|
b9b938c658 | ||
|
|
6f27ede8bf | ||
|
|
79ae42f0c3 | ||
|
|
c754946089 | ||
|
|
ab7d385a13 | ||
|
|
c53b38a738 | ||
|
|
d0c7ba401f | ||
|
|
d94cadd3e7 | ||
|
|
cce3e455fa | ||
|
|
b28beec2c6 | ||
|
|
d421046d15 | ||
|
|
b7034e59c1 | ||
|
|
d6c1a87a03 | ||
|
|
d111f7e981 | ||
|
|
28b6321b1c | ||
|
|
0a8e6ed194 | ||
|
|
50907c7289 | ||
|
|
6e28712ea7 | ||
|
|
5458e7d647 | ||
|
|
a5fa6d3afe | ||
|
|
9c8fa8158b | ||
|
|
e6e413bcb7 | ||
|
|
81d14e39ab | ||
|
|
f207587791 | ||
|
|
5cc2df19ed | ||
|
|
0a80d70730 | ||
|
|
25c9955cc6 | ||
|
|
d0e28a5fc9 | ||
|
|
1b55514d98 | ||
|
|
557ebe0a2f | ||
|
|
9a2d6f692e | ||
|
|
bf53a2a262 | ||
|
|
1d15b614f3 | ||
|
|
e6981a7f44 | ||
|
|
48dbdcd835 | ||
|
|
5fdf408378 | ||
|
|
38e7d2c15f | ||
|
|
82b146bdf6 | ||
|
|
2aefa23c36 | ||
|
|
19cc115730 | ||
|
|
429b587529 | ||
|
|
93059c1e48 | ||
|
|
af75bcd741 | ||
|
|
6bd331bc73 | ||
|
|
ee08870925 | ||
|
|
33e23021ae | ||
|
|
9e6a0c7748 | ||
|
|
212ce64c04 | ||
|
|
828f2ebecd | ||
|
|
0434f6ac10 | ||
|
|
7f946e45f4 | ||
|
|
c10395fe5d | ||
|
|
f3bdac4d23 | ||
|
|
be0496af5a | ||
|
|
5da5433b58 | ||
|
|
703c27c639 | ||
|
|
676ba6c885 | ||
|
|
aa83fe4986 | ||
|
|
1c3aac5e82 | ||
|
|
7fec6c8a07 | ||
|
|
b025cc593c | ||
|
|
60cc1c1e29 | ||
|
|
b5ffefe707 | ||
|
|
e2732dce2c | ||
|
|
c1819d6fdc | ||
|
|
5c4cc81b05 | ||
|
|
bbc560706a | ||
|
|
84b334abab | ||
|
|
38a710efe3 | ||
|
|
43e95ff185 | ||
|
|
d6bb6542c4 | ||
|
|
55ee5ef063 | ||
|
|
ee3b45b73a | ||
|
|
b612fb9a89 | ||
|
|
dba9a250ff | ||
|
|
f90fb4512b | ||
|
|
92a2be2b65 | ||
|
|
97fc9dda4c | ||
|
|
5c60489503 | ||
|
|
cfad8dd642 | ||
|
|
36ae9f93ee | ||
|
|
82987d1f69 | ||
|
|
00e8790ec2 | ||
|
|
d0b9348b86 | ||
|
|
7959c20d88 | ||
|
|
cf83e0d849 | ||
|
|
a540f07920 | ||
|
|
7ce1081161 | ||
|
|
6dbfbb1417 | ||
|
|
1039bb18b3 | ||
|
|
fa3d0375c5 | ||
|
|
4bd0d8e8dd | ||
|
|
315520beca | ||
|
|
9f0969ec22 | ||
|
|
7c0e98c55b | ||
|
|
bc57f9579d | ||
|
|
e2f9fa871a | ||
|
|
f133e09dc6 | ||
|
|
e8a2c87c2d | ||
|
|
4f6e335f72 | ||
|
|
df967751f6 | ||
|
|
6aceb2fd1f | ||
|
|
60cbd7bb7a | ||
|
|
c0f1d6624c | ||
|
|
67d146220a | ||
|
|
41db737905 | ||
|
|
3b128585a3 | ||
|
|
cdaf2a838c | ||
|
|
926a6856fb | ||
|
|
c6160da222 | ||
|
|
e729dfd891 | ||
|
|
eb71f936c6 | ||
|
|
c10ebbc863 | ||
|
|
106c3bfa90 | ||
|
|
27a6467896 | ||
|
|
8331a58471 | ||
|
|
ec74c36a6a | ||
|
|
487d98275c | ||
|
|
187a479bd5 | ||
|
|
0bfe6ddabe | ||
|
|
51d8a3cc53 | ||
|
|
06db42129c | ||
|
|
a11583feb1 | ||
|
|
446931bc0a | ||
|
|
34bcc62415 | ||
|
|
a2e3c5d3bc | ||
|
|
ed9646b0c6 | ||
|
|
e0de370340 | ||
|
|
7e5453d47c | ||
|
|
3b2fc2527e | ||
|
|
0a0b3a12c9 | ||
|
|
ba263dbe47 | ||
|
|
8a7c4875bf | ||
|
|
377f2f6a20 | ||
|
|
c69e33ac8c | ||
|
|
121a83040d |
No files matched your search
@@ -66,6 +66,8 @@ homarr/
|
||||
│ ├── analytics/ # Server-side analytics (Umami)
|
||||
│ ├── server-settings/ # Server setting keys/types
|
||||
│ ├── settings/ # User-facing settings UI context
|
||||
│ ├── old-import/ # Legacy Homarr import
|
||||
│ ├── old-schema/ # Legacy Homarr zod schemas
|
||||
│ └── cli/ # Node CLI for ops (brocli)
|
||||
├── tooling/
|
||||
│ ├── typescript/ # Base tsconfig
|
||||
|
||||
@@ -263,6 +263,15 @@ Even hidden options need translation keys or the translation test fails:
|
||||
"widget.mywidget.option.columnWidths.label": "Column widths"
|
||||
```
|
||||
|
||||
### Old-import mappings
|
||||
|
||||
Add to `packages/old-import/src/widgets/options.ts`:
|
||||
|
||||
```typescript
|
||||
columnOrder: () => undefined,
|
||||
columnWidths: () => undefined,
|
||||
```
|
||||
|
||||
### Dynamic storeKey
|
||||
|
||||
Use a key that includes `itemId` and sorted column names. This prevents cross-widget localStorage bleeding:
|
||||
@@ -601,12 +610,13 @@ if (isEditMode) rowContextMenuHandler = undefined;
|
||||
4. Add CSS file for transparent backgrounds (if widget context)
|
||||
5. Add responsive column visibility (if widget context)
|
||||
6. Add hidden options + hydration logic (if column persistence needed)
|
||||
7. Add translation keys (including hidden option labels)
|
||||
8. Add context menu if row actions exist
|
||||
9. Add row expansion if detail view exists
|
||||
10. Add error handling on all mutations
|
||||
11. Add accessibility attributes
|
||||
12. Test at multiple widget widths
|
||||
13. Update docs in `apps/docs/docs/widgets/` if applicable
|
||||
14. Run `pnpm turbo typecheck`
|
||||
15. Run translation spec — catches missing keys
|
||||
7. Add old-import mappings for new options
|
||||
8. Add translation keys (including hidden option labels)
|
||||
9. Add context menu if row actions exist
|
||||
10. Add row expansion if detail view exists
|
||||
11. Add error handling on all mutations
|
||||
12. Add accessibility attributes
|
||||
13. Test at multiple widget widths
|
||||
14. Update docs in `apps/docs/docs/widgets/` if applicable
|
||||
15. Run `pnpm turbo typecheck` — catches old-import mismatches
|
||||
16. Run translation spec — catches missing keys
|
||||
@@ -0,0 +1,8 @@
|
||||
version = 1
|
||||
|
||||
[[analyzers]]
|
||||
name = "javascript"
|
||||
|
||||
[analyzers.meta]
|
||||
plugins = ["react"]
|
||||
environment = ["nodejs"]
|
||||
+1
-2
@@ -67,8 +67,7 @@ HOMARR_WEBSITE_URL=https://homarr.dev
|
||||
# PocketBase API used by Homarr and by the standalone documentation site.
|
||||
# Defaults to HOMARR_WEBSITE_URL.
|
||||
# WORKSHOP_API_URL=https://homarr.dev
|
||||
# Public Workshop website used by links in Homarr, Custom Widgets, onboarding,
|
||||
# and assistant context. Defaults to HOMARR_WEBSITE_URL/workshop.
|
||||
# Public Workshop page opened from Homarr. Defaults to HOMARR_WEBSITE_URL/workshop.
|
||||
# WORKSHOP_WEB_URL=https://homarr.dev/workshop
|
||||
|
||||
# Documentation and PocketBase deployment settings.
|
||||
|
||||
+43
-84
@@ -3,12 +3,12 @@ name: CI
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
branches: ["**"]
|
||||
branches: ["*"]
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
branches: ["**"]
|
||||
branches: ["*"]
|
||||
push:
|
||||
branches: [main, dev, beta, next, v2, release/v2]
|
||||
branches: [main, dev, beta, next, v2]
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
@@ -105,20 +105,18 @@ jobs:
|
||||
|
||||
container:
|
||||
name: Container and E2E
|
||||
needs: [fast-gate, preview-amd64]
|
||||
needs: fast-gate
|
||||
if: |
|
||||
!cancelled() && needs.fast-gate.result == 'success' &&
|
||||
(
|
||||
github.event_name == 'merge_group' ||
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/release/v2') ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
|
||||
(github.event_name == 'workflow_dispatch' && (inputs.operation == 'validate' || inputs.operation == 'publish'))
|
||||
)
|
||||
github.event_name == 'merge_group' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
|
||||
(github.event_name == 'workflow_dispatch' && (inputs.operation == 'validate' || inputs.operation == 'publish'))
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read
|
||||
packages: write
|
||||
outputs:
|
||||
digest: ${{ steps.preview-build.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
@@ -133,18 +131,8 @@ jobs:
|
||||
if: "!env.ACT"
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Reuse amd64 preview image
|
||||
if: needs.preview-amd64.outputs.digest != '' && !env.ACT
|
||||
env:
|
||||
DIGEST: ${{ needs.preview-amd64.outputs.digest }}
|
||||
run: |
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
|
||||
docker pull "${PREVIEW_IMAGE}@${DIGEST}"
|
||||
docker tag "${PREVIEW_IMAGE}@${DIGEST}" homarr-ci
|
||||
docker tag "${PREVIEW_IMAGE}@${DIGEST}" homarr-e2e
|
||||
|
||||
- name: Build container once
|
||||
if: needs.preview-amd64.outputs.digest == '' && !env.ACT
|
||||
if: "!env.ACT"
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
@@ -159,8 +147,8 @@ jobs:
|
||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.ref.name=pr-${{ github.event.pull_request.number || inputs.pr_number || 'none' }}
|
||||
cache-from: type=gha,scope=homarr-e2e
|
||||
cache-to: type=gha,scope=homarr-e2e,mode=max
|
||||
cache-from: type=gha,scope=homarr-linux-amd64
|
||||
cache-to: type=gha,scope=homarr-linux-amd64,mode=max
|
||||
secrets: |
|
||||
TURBO_API=${{ secrets.TURBO_API }}
|
||||
TURBO_TEAM=${{ secrets.TURBO_TEAM }}
|
||||
@@ -201,10 +189,7 @@ jobs:
|
||||
run: pnpm exec playwright install-deps chromium
|
||||
|
||||
- name: Run E2E tests
|
||||
env:
|
||||
NODE_ENV: development
|
||||
CI: "true"
|
||||
run: pnpm exec vitest e2e --retry 1 --maxWorkers 4
|
||||
run: pnpm test:e2e
|
||||
|
||||
- name: Upload E2E artifacts
|
||||
if: failure() && !env.ACT
|
||||
@@ -214,44 +199,27 @@ jobs:
|
||||
path: e2e/test-results/
|
||||
retention-days: 7
|
||||
|
||||
preview-amd64:
|
||||
name: Build amd64 preview image
|
||||
needs: fast-gate
|
||||
if: |
|
||||
(
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository) ||
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/release/v2') ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.operation == 'publish')
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
digest: ${{ steps.preview-build.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
if: "!env.ACT"
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
if: "!env.ACT"
|
||||
- name: Log in to GHCR for preview publication
|
||||
if: |
|
||||
!env.ACT &&
|
||||
(
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.operation == 'publish')
|
||||
)
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
if: "!env.ACT"
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Build and push amd64 preview image by digest
|
||||
- name: Push amd64 preview image by digest
|
||||
id: preview-build
|
||||
if: "!env.ACT"
|
||||
if: |
|
||||
!env.ACT &&
|
||||
(
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.operation == 'publish')
|
||||
)
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
@@ -259,20 +227,22 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
outputs: type=image,"name=${{ env.PREVIEW_IMAGE }}",push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=gha,scope=homarr-linux-amd64
|
||||
cache-to: type=gha,scope=homarr-linux-amd64,mode=max
|
||||
secrets: |
|
||||
TURBO_API=${{ secrets.TURBO_API }}
|
||||
TURBO_TEAM=${{ secrets.TURBO_TEAM }}
|
||||
TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_REMOTE_CACHE_SIGNATURE_KEY=${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
|
||||
|
||||
- name: Show Act publication result
|
||||
if: env.ACT
|
||||
run: echo "Act validated the publication path; external GHCR writes are disabled locally."
|
||||
|
||||
preview-arm64:
|
||||
name: Build arm64 preview image
|
||||
needs: fast-gate
|
||||
if: |
|
||||
(
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository) ||
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/release/v2') ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.operation == 'publish')
|
||||
)
|
||||
runs-on: ubuntu-24.04-arm
|
||||
@@ -320,11 +290,10 @@ jobs:
|
||||
|
||||
publish-preview:
|
||||
name: Publish multi-platform preview image
|
||||
needs: [preview-amd64, preview-arm64]
|
||||
needs: [container, preview-arm64]
|
||||
if: |
|
||||
(
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/release/v2') ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.operation == 'publish')
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
@@ -334,7 +303,7 @@ jobs:
|
||||
packages: write
|
||||
pull-requests: read
|
||||
env:
|
||||
PREVIEW_TAG: ${{ github.event_name == 'push' && 'v2' || format('pr-{0}', github.event.pull_request.number || inputs.pr_number) }}
|
||||
PREVIEW_TAG: pr-${{ github.event.pull_request.number || inputs.pr_number }}
|
||||
steps:
|
||||
- name: Log in to GHCR
|
||||
if: "!env.ACT"
|
||||
@@ -371,34 +340,24 @@ jobs:
|
||||
- name: Publish preview manifest
|
||||
if: "!env.ACT"
|
||||
env:
|
||||
AMD64_DIGEST: ${{ needs.preview-amd64.outputs.digest }}
|
||||
AMD64_DIGEST: ${{ needs.container.outputs.digest }}
|
||||
ARM64_DIGEST: ${{ needs.preview-arm64.outputs.digest }}
|
||||
run: |
|
||||
tags=(-t "${PREVIEW_IMAGE}:${PREVIEW_TAG}")
|
||||
if [[ "${GITHUB_EVENT_NAME}" == "push" && "${GITHUB_REF}" == "refs/heads/release/v2" ]]; then
|
||||
tags+=(-t "${PREVIEW_IMAGE}:pr-6545")
|
||||
fi
|
||||
docker buildx imagetools create "${tags[@]}" \
|
||||
docker buildx imagetools create -t "${PREVIEW_IMAGE}:${PREVIEW_TAG}" \
|
||||
"${PREVIEW_IMAGE}@${AMD64_DIGEST}" \
|
||||
"${PREVIEW_IMAGE}@${ARM64_DIGEST}"
|
||||
|
||||
- name: Verify preview manifest platforms
|
||||
if: "!env.ACT"
|
||||
run: |
|
||||
tags=("${PREVIEW_TAG}")
|
||||
if [[ "${GITHUB_EVENT_NAME}" == "push" && "${GITHUB_REF}" == "refs/heads/release/v2" ]]; then
|
||||
tags+=("pr-6545")
|
||||
fi
|
||||
for tag in "${tags[@]}"; do
|
||||
image="${PREVIEW_IMAGE}:${tag}"
|
||||
manifest="$(docker buildx imagetools inspect "$image" --raw)"
|
||||
for architecture in amd64 arm64; do
|
||||
jq --exit-status --arg architecture "$architecture" \
|
||||
'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \
|
||||
<<<"$manifest" >/dev/null
|
||||
done
|
||||
docker buildx imagetools inspect "$image"
|
||||
image="${PREVIEW_IMAGE}:${PREVIEW_TAG}"
|
||||
manifest="$(docker buildx imagetools inspect "$image" --raw)"
|
||||
for architecture in amd64 arm64; do
|
||||
jq --exit-status --arg architecture "$architecture" \
|
||||
'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \
|
||||
<<<"$manifest" >/dev/null
|
||||
done
|
||||
docker buildx imagetools inspect "$image"
|
||||
|
||||
cleanup:
|
||||
name: Clean up preview image
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
permissions:
|
||||
statuses: write
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -17,20 +16,12 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
run: |
|
||||
for attempt in 1 2 3 4 5; do
|
||||
if gh api \
|
||||
--method POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
/repos/${{github.repository}}/statuses/${{github.event.pull_request.head.sha}} \
|
||||
-f 'state=success' \
|
||||
-f 'target_url=https://docs.renovatebot.com/configuration-options#minimumreleaseage' \
|
||||
-f 'description=Skipped for non renovate branches' \
|
||||
-f 'context=renovate/stability-days' \
|
||||
--silent; then
|
||||
exit 0
|
||||
fi
|
||||
echo "status POST failed (attempt $attempt/5), retrying in 5s"
|
||||
sleep 5
|
||||
done
|
||||
exit 1
|
||||
gh api \
|
||||
--method POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
/repos/${{github.repository}}/statuses/${{github.event.pull_request.head.sha}} \
|
||||
-f 'state=success' \
|
||||
-f 'target_url=https://docs.renovatebot.com/configuration-options#minimumreleaseage' \
|
||||
-f 'description=Skipped for non renovate branches' \
|
||||
-f 'context=renovate/stability-days'
|
||||
@@ -3,7 +3,7 @@ name: Workshop
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [dev, v2, release/v2, main]
|
||||
branches: [dev, v2, main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -27,103 +27,37 @@ jobs:
|
||||
- run: pnpm test:workshop
|
||||
- run: pnpm test:workshop-image
|
||||
|
||||
publish-amd64:
|
||||
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main')
|
||||
needs: validate
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
digest: ${{ steps.build.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: docker/setup-buildx-action@v4
|
||||
- uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: docker/build-push-action@v7
|
||||
id: build
|
||||
with:
|
||||
context: .
|
||||
file: apps/workshop/Dockerfile
|
||||
target: production
|
||||
platforms: linux/amd64
|
||||
outputs: type=image,"name=ghcr.io/homarr-labs/workshop",push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=gha,scope=workshop-linux-amd64
|
||||
cache-to: type=gha,scope=workshop-linux-amd64,mode=max
|
||||
|
||||
publish-arm64:
|
||||
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main')
|
||||
needs: validate
|
||||
runs-on: ubuntu-24.04-arm
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
digest: ${{ steps.build.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: docker/setup-buildx-action@v4
|
||||
- uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: docker/build-push-action@v7
|
||||
id: build
|
||||
with:
|
||||
context: .
|
||||
file: apps/workshop/Dockerfile
|
||||
target: production
|
||||
platforms: linux/arm64
|
||||
outputs: type=image,"name=ghcr.io/homarr-labs/workshop",push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=gha,scope=workshop-linux-arm64
|
||||
cache-to: type=gha,scope=workshop-linux-arm64,mode=max
|
||||
|
||||
publish:
|
||||
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'release/v2' || github.ref_name == 'main')
|
||||
needs: [publish-amd64, publish-arm64]
|
||||
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'main')
|
||||
needs: validate
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
env:
|
||||
WORKSHOP_IMAGE: ghcr.io/homarr-labs/workshop
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: docker/setup-qemu-action@v4
|
||||
- uses: docker/setup-buildx-action@v4
|
||||
- uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Publish multi-platform image
|
||||
env:
|
||||
AMD64_DIGEST: ${{ needs.publish-amd64.outputs.digest }}
|
||||
ARM64_DIGEST: ${{ needs.publish-arm64.outputs.digest }}
|
||||
run: |
|
||||
tags=(-t "${WORKSHOP_IMAGE}:sha-${GITHUB_SHA}")
|
||||
if [[ "${GITHUB_REF_NAME}" == "v2" || "${GITHUB_REF_NAME}" == "release/v2" ]]; then
|
||||
tags+=(-t "${WORKSHOP_IMAGE}:v2")
|
||||
fi
|
||||
if [[ "${GITHUB_REF_NAME}" == "main" ]]; then
|
||||
tags+=(-t "${WORKSHOP_IMAGE}:latest")
|
||||
fi
|
||||
docker buildx imagetools create "${tags[@]}" \
|
||||
"${WORKSHOP_IMAGE}@${AMD64_DIGEST}" \
|
||||
"${WORKSHOP_IMAGE}@${ARM64_DIGEST}"
|
||||
- name: Verify multi-platform image
|
||||
run: |
|
||||
image="${WORKSHOP_IMAGE}:sha-${GITHUB_SHA}"
|
||||
manifest="$(docker buildx imagetools inspect "$image" --raw)"
|
||||
for architecture in amd64 arm64; do
|
||||
jq --exit-status --arg architecture "$architecture" \
|
||||
'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \
|
||||
<<<"$manifest" >/dev/null
|
||||
done
|
||||
docker buildx imagetools inspect "$image"
|
||||
- uses: docker/metadata-action@v6
|
||||
id: metadata
|
||||
with:
|
||||
images: ghcr.io/homarr-labs/workshop
|
||||
tags: |
|
||||
type=sha,prefix=sha-,format=long
|
||||
type=raw,value=v2,enable=${{ github.ref_name == 'v2' }}
|
||||
type=raw,value=latest,enable=${{ github.ref_name == 'main' }}
|
||||
- uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: apps/workshop/Dockerfile
|
||||
target: production
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
-13
@@ -25,8 +25,6 @@ packages/db/migrations/*/migrate.cjs
|
||||
|
||||
# production
|
||||
build
|
||||
tools/homarr-dev/homarr
|
||||
tools/homarr-dev/homarr-dev
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
@@ -86,14 +84,3 @@ apps/docs/.docusaurus
|
||||
apps/docs/build
|
||||
.pnpm-store
|
||||
benchmark-results
|
||||
|
||||
# local performance investigation (never ship benchmark data or restored backups)
|
||||
.widget-performance/
|
||||
.bench/
|
||||
homarr-backup*.zip
|
||||
*.sqlite-wal
|
||||
*.sqlite-shm
|
||||
/scripts/benchmarks/
|
||||
|
||||
# PR description screenshots belong on the pull request, not in the repository
|
||||
.screenshots/
|
||||
@@ -0,0 +1,116 @@
|
||||
---
|
||||
target: Workshop store in the docs
|
||||
total_score: 20
|
||||
p0_count: 1
|
||||
p1_count: 3
|
||||
timestamp: 2026-07-21T11-32-50Z
|
||||
slug: apps-docs-src-components-workshop
|
||||
---
|
||||
|
||||
# Workshop Store Critique
|
||||
|
||||
## Design Health Score
|
||||
|
||||
| # | Heuristic | Score | Key issue |
|
||||
| --------- | ----------------------------------- | --------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Visibility of system status | 1/4 | Fetch failure can show beside an empty-store message, so the system communicates two incompatible states. |
|
||||
| 2 | Match between system and real world | 3/4 | Widget, CSS, voting, revisions, and comments use familiar language. |
|
||||
| 3 | User control and freedom | 2/4 | Filters are reversible, but the crashed route and failed fetch state offer no useful recovery. |
|
||||
| 4 | Consistency and standards | 2/4 | The submission editor uses CodeMirror, while revision editing falls back to a textarea; several tiny custom controls do not follow the shared button vocabulary. |
|
||||
| 5 | Error prevention | 2/4 | Submission validation exists, but disabled progression does not explain what is missing and destructive actions use a brief inline confirmation. |
|
||||
| 6 | Recognition rather than recall | 3/4 | Labels, type choices, and the submission stepper are understandable without documentation. |
|
||||
| 7 | Flexibility and efficiency | 2/4 | Search covers titles only, signed-out users can select Yours, and installation is reduced to ambiguous Copy and Download actions. |
|
||||
| 8 | Aesthetic and minimalist design | 3/4 | The restrained visual system is clean, but the browse toolbar and card action rows become dense. |
|
||||
| 9 | Help users recover from errors | 0/4 | The production route crashes with React error 130; the fetch error has no retry action or useful context. |
|
||||
| 10 | Help and documentation | 2/4 | The editor explains its fields, but the store does not teach the full inspect, trust, download, and install journey. |
|
||||
| **Total** | | **20/40** | **Needs work before release** |
|
||||
|
||||
## Anti-Patterns Verdict
|
||||
|
||||
The surface does not look like obvious AI-generated decoration. Its visual restraint, familiar controls, and practical editor are credible product UI. The weakness is product behavior rather than styling: contradictory states, tiny action controls, an overloaded responsive toolbar, and a primary installation journey that is not explicit.
|
||||
|
||||
The deterministic Impeccable scan returned zero findings for `apps/docs/src/components/workshop`. This is useful but incomplete. The detector did not catch runtime failures, semantic state conflicts, touch-target sizing, or responsive grouping. Browser evidence found a full route crash, React error 130, and an earlier state that rendered both `Something went wrong.` and `No submissions yet`.
|
||||
|
||||
No visual overlay was injected because the available browser evaluation surface is read-only. Screenshots, DOM snapshots, console logs, and source inspection were used instead.
|
||||
|
||||
## Overall Impression
|
||||
|
||||
The submission modal is now one of the stronger parts of the Workshop. The store around it is not release-ready. The single biggest opportunity is to make discovery and installation a dependable, explicit path: load reliably, show one truthful state, help users evaluate a submission, and give them one obvious next action.
|
||||
|
||||
## What Is Working
|
||||
|
||||
1. The visual system is restrained and consistent with the Homarr documentation. Typography, color, radii, and spacing do not compete with the content.
|
||||
2. The submission flow chunks a complex task into Type, Details, and Media. The shared JSON/CSS editor, drag-and-drop import, metadata autofill, and capped media previews reduce creator effort.
|
||||
3. The detail design exposes revision, author, reports, changelog, source content, and comments. This supports inspection rather than treating community code as a blind install.
|
||||
|
||||
## Priority Issues
|
||||
|
||||
### P0: The Workshop route crashes in the production docs build
|
||||
|
||||
**Why it matters:** No visual polish matters if the store becomes a Docusaurus error page. A fresh independent tab consistently reached `This page crashed` with React error 130. The console attributed the thrown error to the Kapa widget bundle.
|
||||
|
||||
**Fix:** Isolate or disable the Kapa script on Workshop routes, prevent third-party widget failures from reaching the Docusaurus route boundary, and add a route-level Workshop boundary that preserves navigation and retry. Reproduce against the built Docker docs image, not only the development server.
|
||||
|
||||
**Suggested command:** `$impeccable harden`
|
||||
|
||||
### P1: Failure and empty states contradict each other
|
||||
|
||||
**Why it matters:** Showing `Something went wrong.` followed by `No submissions yet` makes users believe the store is both broken and empty. The generic message gives no cause and no recovery.
|
||||
|
||||
**Fix:** Make loading, failure, empty, and results mutually exclusive. The failure state should say that listings could not be loaded, preserve the current filters, and provide `Try loading again`. Do not render filters or an empty store as authoritative data after the initial request fails.
|
||||
|
||||
**Suggested command:** `$impeccable harden`
|
||||
|
||||
### P1: Mobile discovery controls are too dense and do not form a responsive hierarchy
|
||||
|
||||
**Why it matters:** The toolbar's inner row contains four type choices, sorting, and outdated visibility without wrapping. Most controls are 24 to 28 pixels tall. At narrow widths this is difficult to scan, easy to clip, and below a comfortable touch target.
|
||||
|
||||
**Fix:** Put a full-width search first on mobile, keep All, Widgets, and CSS as the primary segmented choice, move sorting and outdated visibility into a compact secondary row or popover, and hide Yours when signed out or make it a sign-in affordance. Use at least 40 to 44 pixel touch targets. Add `aria-pressed` or proper tab semantics to the type filter.
|
||||
|
||||
**Suggested command:** `$impeccable adapt`
|
||||
|
||||
### P1: The store does not make the install journey obvious
|
||||
|
||||
**Why it matters:** A first-time user sees Copy and Download without knowing which is recommended, what will be copied, how to inspect safety, or where the file goes in Homarr. This weakens the store's main purpose.
|
||||
|
||||
**Fix:** Make the detail page the decision surface. Use one primary action named for its result, such as `Download widget JSON` or `Copy CSS`, with a short `Install in Homarr` explanation linked to the correct in-app flow. Cards should prioritize title, preview, compatibility, score, and `View details`; secondary utilities should not compete with evaluation.
|
||||
|
||||
**Suggested command:** `$impeccable clarify`
|
||||
|
||||
### P2: Card and moderation actions are too small and too numerous
|
||||
|
||||
**Why it matters:** Vote, copy, download, comment, delete, and report controls compete in a compact footer. Low-contrast icon buttons are hard to discover and hard to use by touch. CSS columns also create a top-to-bottom visual reading order that can differ from the expected row order.
|
||||
|
||||
**Fix:** Use a regular responsive grid, remove hover scaling, keep one or two visible card actions, and move owner/moderation actions to a menu. Keep voting visible but increase its target size. Correct the report copy: reports and reporter identities are moderation data, not public social proof.
|
||||
|
||||
**Suggested command:** `$impeccable distill`
|
||||
|
||||
## Persona Red Flags
|
||||
|
||||
### Jordan, first-time self-hoster
|
||||
|
||||
Jordan opens Workshop to install a widget. The route can crash before any listing appears. In the partial failure state, Jordan sees both an error and an empty store. If listings load, Copy and Download do not explain the Homarr destination or recommended path. Jordan is likely to leave before installing anything.
|
||||
|
||||
### Alex, experienced Homarr administrator
|
||||
|
||||
Alex can understand the JSON and appreciates the source viewer, but title-only search and limited filtering make a larger catalog inefficient. Compatibility and required secret information are not prominent at browse time. Tiny moderation and owner controls slow repeated management tasks.
|
||||
|
||||
### Morgan, mobile user
|
||||
|
||||
Morgan encounters a toolbar with more controls than fit comfortably in one decision group, 24 to 28 pixel targets, and a signed-out Yours filter that leads to an unhelpful no-results state. Submission cards add another dense row of small actions.
|
||||
|
||||
## Minor Observations
|
||||
|
||||
- Future submission steps are focusable buttons even when clicking them does nothing. Disable them or render non-interactive progress markers.
|
||||
- The report textarea relies on placeholder text and needs a persistent accessible label.
|
||||
- The revision editor should reuse the same JSON/CSS CodeMirror component as creation.
|
||||
- Search should include title, description, and author at minimum.
|
||||
- The timed inline `Confirm?` deletion pattern is easy to miss and weak for keyboard and touch users.
|
||||
- The detail-page changelog uses an uppercase tracked label, which is unnecessary in this otherwise restrained interface.
|
||||
- Loading comments should use the same skeleton vocabulary as listings rather than centered status text.
|
||||
|
||||
## Questions to Consider
|
||||
|
||||
1. Is Workshop primarily a catalog for discovery, or a technical exchange for copying source? The primary card and detail actions should commit to one answer.
|
||||
2. Should users evaluate compatibility and required secrets before opening a listing, or only on the detail page?
|
||||
3. Does Workshop need every moderation and ownership action visible inline, or should those actions live behind a menu?
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
target: Workshop store in the docs
|
||||
total_score: 38
|
||||
p0_count: 0
|
||||
p1_count: 0
|
||||
timestamp: 2026-07-21T12-29-41Z
|
||||
slug: apps-docs-src-components-workshop
|
||||
---
|
||||
|
||||
# Workshop Store Critique — Post-Polish
|
||||
|
||||
## Design Health Score
|
||||
|
||||
| # | Heuristic | Score | Evidence |
|
||||
| --------- | ----------------------------------- | --------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Visibility of system status | 4/4 | Loading, initial failure, stale refresh failure, empty, and results states are mutually exclusive; recovery actions are visible. |
|
||||
| 2 | Match between system and real world | 4/4 | Actions describe their result: Download widget JSON, Copy CSS, View details, and Install in Homarr. |
|
||||
| 3 | User control and freedom | 4/4 | Filters can be cleared, errors retried, dialogs cancelled, and destructive submission deletion requires explicit confirmation. |
|
||||
| 4 | Consistency and standards | 4/4 | Creation and revision editing share CodeMirror, buttons follow the shared system, and cards use a predictable responsive grid. |
|
||||
| 5 | Error prevention | 3/4 | Validation, disabled progression, pending states, RLS, and confirmations are strong; disabled form progression could explain unmet requirements more explicitly. |
|
||||
| 6 | Recognition rather than recall | 4/4 | Persistent labels, type descriptions, step status, source previews, and installation instructions minimize recall. |
|
||||
| 7 | Flexibility and efficiency | 4/4 | Search spans title, description, and author; signed-in filtering, sorting, responsive controls, and direct detail actions support repeat use. |
|
||||
| 8 | Aesthetic and minimalist design | 3/4 | The catalog is restrained and scannable; long source documents and the complete creator form necessarily remain dense on small screens. |
|
||||
| 9 | Help users recover from errors | 4/4 | Route, listing, edit, report, copy, OAuth, and detail failures remain local and provide a clear next action. |
|
||||
| 10 | Help and documentation | 4/4 | The detail page explains the exact Homarr destination and links to updated Workshop documentation. |
|
||||
| **Total** | | **38/40** | **Release-quality; minor polish remains** |
|
||||
|
||||
## Anti-Patterns Verdict
|
||||
|
||||
The surface is product-led rather than decorative. The redesign removes the overloaded card footer, hover scaling, tiny touch controls, public moderation details, contradictory states, and ambiguous install actions. The deterministic Impeccable detector reports zero findings.
|
||||
|
||||
## What Improved
|
||||
|
||||
1. The production route no longer collapses into the Docusaurus crash screen without recovery. A route-local boundary protects navigation, and the fresh-volume production path renders without console errors.
|
||||
2. Loading, failure, empty, stale-results, and populated states are truthful and mutually exclusive.
|
||||
3. Search and filters form a clear responsive hierarchy. Mobile targets are 40–44 pixels, signed-out users do not see Yours, and cards no longer overflow at 390 pixels.
|
||||
4. Cards prioritize evaluation: title, author, type, score, description, comments, and View details. Copy, download, reporting, editing, and deletion moved to the detail page.
|
||||
5. The detail page now teaches the installation journey and names its primary action precisely.
|
||||
6. Reports and reporter identities are admin-only through PocketBase rules. Public catalog UI no longer turns reports into social proof.
|
||||
7. The creator and revision dialogs share the CodeMirror editor, use bounded internal scrolling, expose pending/error states, and avoid step-selection layout shifts.
|
||||
|
||||
## Remaining Minor Opportunities
|
||||
|
||||
### P2: Explain disabled progression inline
|
||||
|
||||
The Type and Details steps are understandable, but a disabled Next button could state the missing requirement for keyboard and first-time users. This is a refinement, not a blocker, because required fields and validation messages are already present.
|
||||
|
||||
### P2: Consider a compact source summary on very small screens
|
||||
|
||||
The read-only source viewer is intentionally complete and horizontally scrollable. A future collapse/expand summary could reduce page length for nontechnical visitors without hiding inspection entirely.
|
||||
|
||||
## Verification Evidence
|
||||
|
||||
- Fresh Docker Compose PocketBase volume and production docs image.
|
||||
- Populated catalog with widget, CSS, and outdated examples.
|
||||
- Desktop and 390px mobile layouts in light and dark themes.
|
||||
- Widget detail, installation instructions, CodeMirror source view, and comments.
|
||||
- Signed-in submission wizard through Type and Details, including the shared editor.
|
||||
- Zero browser console errors after the final fixes.
|
||||
- Impeccable deterministic detector: zero findings.
|
||||
|
||||
## Compatibility Note
|
||||
|
||||
An existing local development volume created from an earlier unreleased version of the reset migration still has a stale Workshop view. It is intentionally not migrated or deleted. A fresh volume created from the current unreleased migration is the supported validation target and works correctly.
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
target: Homarr Workshop docs UI
|
||||
total_score: 27
|
||||
p0_count: 0
|
||||
p1_count: 2
|
||||
timestamp: 2026-07-22T15-58-15Z
|
||||
slug: apps-docs-src-components-workshop
|
||||
---
|
||||
|
||||
## Design Health Score
|
||||
|
||||
| # | Heuristic | Score | Key Issue |
|
||||
| --------- | ------------------------------- | --------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Visibility of System Status | 3 | Strong loading, retry, stale-data, pending, and copy states; successful report/edit actions still end silently. |
|
||||
| 2 | Match System / Real World | 3 | Core Workshop language is clear, but `networkScope`, query/action, and schema terminology assumes technical fluency. |
|
||||
| 3 | User Control and Freedom | 3 | Back, cancel, clear filters, vote toggling, and destructive confirmations are present; destructive actions have no undo. |
|
||||
| 4 | Consistency and Standards | 3 | Shared primitives are coherent, though Docusaurus chrome and the denser app-store vocabulary still feel slightly stitched together. |
|
||||
| 5 | Error Prevention | 2 | Validation and confirmations are good, but install safety and credential context appears after download/copy actions on small screens. |
|
||||
| 6 | Recognition Rather Than Recall | 3 | Author, revision, source, filters, and capabilities are visible; installation still asks users to remember a separate Homarr navigation path. |
|
||||
| 7 | Flexibility and Efficiency | 2 | Search, filters, sort, drag-and-drop, autofill, and comment shortcuts help; the install handoff remains manual. |
|
||||
| 8 | Aesthetic and Minimalist Design | 2 | Hierarchy is clear, but repeated panels, badges, uppercase metadata labels, and overlapping install actions add noise. |
|
||||
| 9 | Error Recognition and Recovery | 3 | Errors are plain-language and usually preserve work; the global action error can be detached from the failed control. |
|
||||
| 10 | Help and Documentation | 3 | The installation guide and source explanation are useful; security terminology lacks inline definitions. |
|
||||
| **Total** | | **27/40** | **Acceptable, significant improvements needed** |
|
||||
|
||||
## Anti-Patterns Verdict
|
||||
|
||||
**LLM assessment:** Moderate AI-assisted feel, not outright slop. Domain-specific copy, extensive state coverage, and transparent source/capability information make the feature credible. The generic card grid, repeated rounded panels, icon tiles, uppercase tracked labels, and decorative changelog stripe still expose a component-recipe grammar. It looks competent but not fully edited into a distinctive Homarr store.
|
||||
|
||||
**Deterministic scan:** `detect.mjs --json apps/docs/src/components/workshop` exited 0 with exact output `[]`: zero findings, zero triggered rules, and no false positives. The scan found none of Impeccable's deterministic anti-patterns.
|
||||
|
||||
**Visual overlays:** No reliable user-visible overlay is available. The independent detector agent had no browser backend. The parent fallback opened a fresh browser tab and visually inspected the listing, but the browser API did not expose a mutable evaluation method for the required preflight, so injection and `[Human]` visibility were correctly skipped. Browser/source evidence was used instead.
|
||||
|
||||
## Overall Impression
|
||||
|
||||
The Workshop is already substantially better than a generic gallery: it communicates authorship, source code, API capabilities, secrets, revision history, reports, and discussion. The biggest opportunity is to turn that information into a deliberate trust sequence. Today the interface lets the user act first and understand safety later, especially on mobile.
|
||||
|
||||
## What's Working
|
||||
|
||||
- **State coverage is unusually strong.** Listing failures, stale data, loading skeletons, empty results, filter recovery, comment errors, pending actions, and destructive confirmations have explicit UI.
|
||||
- **Pre-install transparency is excellent.** API hosts, methods, configurable options, network scope, and credential requirements are visible instead of hidden behind a download.
|
||||
- **The new primitive layer is coherent.** Search, filters, menus, switches, dialogs, and code presentation share one restrained interaction vocabulary across light and dark themes.
|
||||
|
||||
## Priority Issues
|
||||
|
||||
### [P1] Safety context arrives after the install action on mobile
|
||||
|
||||
**Why it matters:** Download and copy actions appear in the header, while source capabilities, credential requirements, and installation guidance live in an aside that drops below the long source and discussion sections under the desktop breakpoint. A first-time mobile user can commit before understanding what the widget contacts or what secrets it needs.
|
||||
|
||||
**Fix:** Put a compact “Before you install” summary immediately below the header on small screens: API hosts, network scopes, credential count, and action/query counts. Keep the full sticky details panel on desktop.
|
||||
|
||||
**Suggested command:** `$impeccable layout`
|
||||
|
||||
### [P1] Primary button and own-comment contrast misses AA
|
||||
|
||||
**Why it matters:** The requested theme's primary red with white foreground is about 3.4:1, below 4.5:1 for small text. The same pairing affects primary buttons and the author's comment bubbles.
|
||||
|
||||
**Fix:** Darken the primary red or use a dark foreground in light mode, then verify default buttons and comment bubbles in both themes.
|
||||
|
||||
**Suggested command:** `$impeccable colorize`
|
||||
|
||||
### [P2] The listing's server-rendered fallback is blank
|
||||
|
||||
**Why it matters:** Before hydration, the listing renders an empty block instead of preserving the page's hierarchy and indicating progress. This creates a visible dead zone on slower self-hosted deployments.
|
||||
|
||||
**Fix:** Render the listing heading, toolbar silhouette, and shared skeleton cards as the `BrowserOnly` fallback, matching the detail page's loading treatment.
|
||||
|
||||
**Suggested command:** `$impeccable harden`
|
||||
|
||||
### [P2] Download, copy, source, and install compete for the same task
|
||||
|
||||
**Why it matters:** “Download widget,” “Copy JSON,” a full source viewer, and a separate installation panel all appear as parallel paths. Users must infer the recommended workflow.
|
||||
|
||||
**Fix:** Make “Download widget JSON” the single primary header action. Keep copy beside the source viewer and place the next import step directly beside or immediately after download.
|
||||
|
||||
**Suggested command:** `$impeccable clarify`
|
||||
|
||||
### [P2] Repeated metadata scaffolding creates visual noise
|
||||
|
||||
**Why it matters:** Card shells, badges, icon tiles, uppercase tracked labels, and the changelog stripe make the detail page feel assembled from recipes and slow scanning.
|
||||
|
||||
**Fix:** Flatten the widget details into a compact definition list, reserve badges for status, and remove the decorative changelog stripe.
|
||||
|
||||
**Suggested command:** `$impeccable distill`
|
||||
|
||||
## Persona Red Flags
|
||||
|
||||
**Jordan, first-timer:** “Network scope,” queries, actions, options, and the manifest schema appear without inline explanation. Download versus copy has no stated recommendation. On mobile, credential and installation context arrives after source and discussion.
|
||||
|
||||
**Sam, accessibility-dependent user:** Primary controls and author comment bubbles miss small-text contrast. Copy, optimistic votes, filter result changes, and successful report/edit completion do not expose explicit live-region behavior. Gallery controls and dots are below comfortable touch-target sizes.
|
||||
|
||||
**Casey, distracted mobile user:** The most important safety panel falls to the bottom, while primary actions stay at the top. Long source and discussion content separate the decision from its explanation. The submit type selector remains dense at narrow widths.
|
||||
|
||||
## Minor Observations
|
||||
|
||||
- The internal installation guide uses an external-link icon.
|
||||
- The final publication action says “Submit” rather than “Publish submission.”
|
||||
- Dark-theme tokens are declared in two blocks, increasing drift risk.
|
||||
- Screenshot dots rely on opacity alone and lack `aria-current`.
|
||||
- Successful report and edit actions deserve a small confirmation instead of silent dismissal.
|
||||
|
||||
## Questions to Consider
|
||||
|
||||
- If source review is important enough to say “Review exactly what will be installed,” why can users download before seeing the capability and credential summary?
|
||||
- Is Workshop primarily a code catalog or a safety-conscious app store? The current action hierarchy tries to be both.
|
||||
- Should the successful end state be a downloaded file, or a confident handoff into Homarr with the next step already visible?
|
||||
@@ -30,26 +30,6 @@
|
||||
"options": {
|
||||
"reportUnusedDisableDirectives": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["apps/nextjs/src/**/*.ts", "apps/nextjs/src/**/*.tsx"],
|
||||
"excludeFiles": ["**/*.spec.ts", "**/*.spec.tsx"],
|
||||
"rules": {
|
||||
"eslint/no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"paths": [
|
||||
{
|
||||
"name": "@homarr/widgets",
|
||||
"allowTypeImports": true,
|
||||
"message": "Import a narrow @homarr/widgets export to preserve widget lazy loading."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"**/*.config.js",
|
||||
"**/*.config.mjs",
|
||||
|
||||
@@ -40,6 +40,8 @@ homarr/
|
||||
│ ├── analytics/ # Server-side analytics (Umami)
|
||||
│ ├── server-settings/ # Server setting keys/types
|
||||
│ ├── settings/ # User-facing settings UI context
|
||||
│ ├── old-import/ # Legacy Homarr import
|
||||
│ ├── old-schema/ # Legacy Homarr zod schemas
|
||||
│ └── cli/ # Node CLI for ops (brocli)
|
||||
├── tooling/
|
||||
│ ├── typescript/ # Base tsconfig
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
# Changes
|
||||
|
||||
## Goal
|
||||
|
||||
Remove widget-only services from Homarr's global Integration system.
|
||||
|
||||
The affected services are release providers and Search.ch. They are only used by specific widgets, so keeping them as global integrations added unnecessary setup, UI, database rows, docs pages, and integration package code.
|
||||
|
||||
ArchiveTeam Warrior remains a proper integration but is no longer auto-seeded on first install.
|
||||
|
||||
## Why
|
||||
|
||||
These services do not need shared integration state.
|
||||
|
||||
- Release providers are selected per Releases widget repository.
|
||||
- Search.ch is only used by the Timetable widget.
|
||||
|
||||
Moving them into widget options makes configuration simpler and removes dead-weight integration code from `packages/integrations`.
|
||||
|
||||
## What Changed
|
||||
|
||||
### Release Providers
|
||||
|
||||
- Added release provider definitions in `packages/definitions/src/release-provider.ts`.
|
||||
- Removed release providers from `IntegrationKind`.
|
||||
- Moved release-fetching code out of `packages/integrations` and into `packages/request-handler/src/release-providers.ts`.
|
||||
- Updated the Releases widget to store provider configuration directly in widget options:
|
||||
- `provider`
|
||||
- `identifier`
|
||||
- `versionFilter`
|
||||
- `providerUrl`
|
||||
- Updated the Releases widget editor to use a provider select instead of an integration select.
|
||||
- Updated Docker import mapping so `ghcr.io` maps to `gitHubContainerRegistry`.
|
||||
- GitHub Container Registry now uses the public OCI registry API and accepts full image references such as `ghcr.io/homarr-labs/homarr:latest`.
|
||||
|
||||
### Timetable / Search.ch
|
||||
|
||||
- Removed Search.ch as an integration.
|
||||
- Added a Timetable widget `baseUrl` option, defaulting to `https://search.ch`.
|
||||
- Moved Search.ch request/parsing logic into `packages/request-handler/src/timetable.ts`.
|
||||
- Updated the Timetable API to receive `baseUrl` directly from widget options.
|
||||
|
||||
### ArchiveTeam Warrior
|
||||
|
||||
- Kept as a proper integration (not removed).
|
||||
- Removed `defaultUrl` from the integration definition to prevent auto-seeding on first install.
|
||||
|
||||
### Integration Cleanup
|
||||
|
||||
- Removed creators, exports, docs references, and implementation files for the deleted integrations.
|
||||
- Removed unused dependencies from `packages/integrations/package.json`.
|
||||
|
||||
### Database Migration
|
||||
|
||||
- Added `packages/db/migrations/custom/0003_remove_widget_only_integrations.ts`.
|
||||
- The migration moves old integration-linked widget config into widget options, then deletes the old widget-only integration rows.
|
||||
- Updated the earlier release widget migration to use the new `provider` field instead of `providerIntegrationId`.
|
||||
|
||||
### Docs
|
||||
|
||||
- Updated Releases widget docs to describe providers instead of integrations.
|
||||
- Updated Timetable docs to remove Search.ch integration setup.
|
||||
- Removed obsolete integration docs pages for the deleted integration kinds.
|
||||
- Updated docs sitemap and integration docs mappings.
|
||||
|
||||
### Translations
|
||||
|
||||
- Added English labels for the new widget options:
|
||||
- `widget.timetable.option.baseUrl.label`
|
||||
- `widget.releases.option.repositories.providerUrl.label`
|
||||
|
||||
## How It Works Now
|
||||
|
||||
Release provider configuration lives on each Releases widget repository entry. The request handler resolves provider metadata and fetches releases directly from the provider-specific endpoint. GHCR uses anonymous OCI registry access for public packages.
|
||||
|
||||
Timetable passes its widget URL option directly to its API router and request handler. No global integration lookup is needed.
|
||||
|
||||
ArchiveTeam Warrior continues to use the standard integration system, but users must manually add the integration (it is not auto-created on install).
|
||||
|
||||
## Deliberate Non-Goals
|
||||
|
||||
- No provider secret/token support was added.
|
||||
- No new secret storage model was introduced.
|
||||
- No compatibility layer was kept in `packages/integrations`.
|
||||
|
||||
Widget options are client-visible, so storing provider credentials there would be wrong. If authenticated provider requests are needed later, they need a separate secret-storage design.
|
||||
|
||||
## Verification So Far
|
||||
|
||||
- Focused typechecks passed for affected packages.
|
||||
- Full `pnpm turbo typecheck` passed.
|
||||
- Full `pnpm lint` passed with existing warnings.
|
||||
- Focused release provider normalization tests passed.
|
||||
- Widget translation test passed.
|
||||
- Live GHCR smoke checks passed for `ghcr.io/muchobien/pocketbase:latest` and `ghcr.io/homarr-labs/homarr:latest`.
|
||||
- `pnpm test packages/integrations/test/pi-hole.spec.ts` passed earlier, but failed on rebased reruns because Pi-hole reported `domainsBeingBlocked` as `-2`/`0` instead of `> 1`.
|
||||
+1
-1
@@ -54,7 +54,7 @@ For a single consolidated file with all content, use:
|
||||
- [How to integrate custom input with use-form hook?](https://mantine.dev/llms/q-custom-input-use-form.md): Learn how to add use-form support for custom inputs
|
||||
- [How to prevent Modal from closing?](https://mantine.dev/llms/q-how-to-prevent-modal-from-closing.md): Learn how to prevent Modal from closing when the user clicks outside of it or presses the Escape key
|
||||
- [How to scroll to the top of the form if the form is submitted with errors?](https://mantine.dev/llms/q-scroll-to-the-top-of-the-form.md): Learn how to call a function when the form is submitted with errors
|
||||
- [How to update Mantine dependencies?](https://mantine.dev/llms/q-how-to-update-dependencies.md): Learn how to update `@mantine/*`, `@mantinex/*` and `postcss-preset-mantine` dependencies manually or with ncu script
|
||||
- [How to update Mantine dependencies?](https://mantine.dev/llms/q-how-to-update-dependencies.md): Learn how to update @mantine/_, @mantinex/_ and postcss-preset-mantine dependencies manually or with ncu script
|
||||
- [How to use Dropzone with @mantine/form?](https://mantine.dev/llms/q-how-to-use-dropzone-with-form.md): Learn how to use Dropzone with @mantine/form to handle file selection state
|
||||
- [How to use Mantine template on GitHub?](https://mantine.dev/llms/q-templates-usage.md): Learn how to create a new repository based on Mantine template
|
||||
- [I get hydration warning about data-mantine-color-scheme attribute, what does it mean?](https://mantine.dev/llms/q-color-scheme-hydration-warning.md): Learn how hydration works and how to fix warnings
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
# Product
|
||||
|
||||
## Register
|
||||
|
||||
product
|
||||
|
||||
## Users
|
||||
|
||||
Homarr is for self-hosters and administrators who assemble dashboards for services across home labs and private infrastructure. They need dense, reliable controls and status at a glance, while retaining enough guidance to configure advanced features without hiding how they work.
|
||||
|
||||
## Product Purpose
|
||||
|
||||
Homarr provides one place to organize, observe, and operate self-hosted services. Success means users can build useful dashboards quickly, understand their state, recover from configuration errors, act on services, and reshape their workspace without repeatedly leaving the board. Boards must load immediately, stay visually stable, and keep widgets legible across screen sizes.
|
||||
|
||||
## Brand Personality
|
||||
|
||||
Direct, capable, precise, and approachable. Homarr should feel calm under load, be truthful about system state, and reward users who refine details.
|
||||
|
||||
## Anti-references
|
||||
|
||||
- Decorative dashboards, oversized metric cards, and motion without state meaning.
|
||||
- Wizard flows that fight ordinary scrolling or rigid layouts that force one workflow.
|
||||
- Hidden configuration magic and implementation complexity exposed before it is needed.
|
||||
- Clever custom controls where familiar platform or Mantine interactions already work.
|
||||
|
||||
## Design Principles
|
||||
|
||||
1. Keep configuration, consequences, and system state visible.
|
||||
2. Make simple tasks short without limiting advanced workflows.
|
||||
3. Preserve expert depth while prioritizing urgent information at small sizes.
|
||||
4. Reveal dense workspaces progressively through a consistent advanced focus mode.
|
||||
5. Isolate failures and show loading, stale, partial, empty, and failed states truthfully.
|
||||
6. Use the existing Mantine design language consistently across management and board surfaces.
|
||||
7. Keep spatial actions direct: moving, resizing, nesting, and using fixed rails happen on the canvas with immediate feedback.
|
||||
8. Preserve predictable widget composition with fixed logical tile sizes and horizontal scrolling before controls become inaccessible.
|
||||
|
||||
## Accessibility & Inclusion
|
||||
|
||||
Target WCAG 2.2 AA. Every pointer interaction needs a keyboard and coarse-pointer path. Support screen readers, visible and restored focus, sufficient contrast, reduced motion, non-color status cues, responsive layouts, actionable error messages, and keyboard equivalents for drag and resize operations.
|
||||
@@ -0,0 +1,263 @@
|
||||
# PR #6502 widget-modernization ultra-review
|
||||
|
||||
## Executive result
|
||||
|
||||
**Merge recommendation: changes required.** No P0 was found, but the current branch still has five P1 issues: a pre-existing Custom API authorization/redirect weakness exposed by this subsystem review, an inaccessible manual advanced-focus interaction, a likely fixed-position/transform geometry defect, systemic false empty states for failed queries, and unvalidated integration-provided navigation URLs. Eight P2 issues cover raw error disclosure, unsafe runtime-ref interfaces, localization, responsive/theming drift, test coverage, and inaccurate PR copy.
|
||||
|
||||
Audit health (Impeccable rubric): accessibility 1/4, performance 2/4, responsive design 2/4, theming 2/4, anti-patterns 3/4; **10/20 (Acceptable, significant work required)**. The UI does not look generically AI-generated; its problem is inconsistent technical behavior beneath an otherwise coherent Mantine vocabulary.
|
||||
|
||||
## Reviewed revision and environment
|
||||
|
||||
| Item | Value |
|
||||
| ---------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| PR | #6502, `feat: modernize dashboard widgets` |
|
||||
| Reviewed head | `96e92485a5fd45f3648cfac0453e614779c414ba` |
|
||||
| Base branch / fetched base | `dev` / `d58a47b776a55d04dee2220f59d072b9af8bbed3` |
|
||||
| Merge base | `d58a47b776a55d04dee2220f59d072b9af8bbed3` |
|
||||
| Diff | 359 files, +15,721/-4,478 |
|
||||
| PR state | open, non-draft, `BLOCKED`; no approval decision |
|
||||
| Hosted checks at audit start | Fast gate, Container and E2E, CodeQL, JS/TS analysis, Actions analysis, title, auto-fix: green |
|
||||
| Inline threads | 34 total, 7 unresolved; only 2 remain substantively valid |
|
||||
| Mantine | repository lock: `@mantine/core`, hooks, charts and dates 9.4.1 |
|
||||
| Local tools | Node 24.16.0 and pnpm 9.5.0; repository requires Node >=24.18.0 and pnpm >=11.15.1 |
|
||||
|
||||
The former red baseline `7b7045202d289b7d83946befcee5d2845ea4a706` had a green Fast gate and a failed Container/E2E run (`30748255760`, job `91498308523`). The latest head has a green Container/E2E job (`30756067198`, job `91519039504`). This proves the current suite passed once; it does not satisfy the requested three-run flake characterization.
|
||||
|
||||
## PR-description traceability
|
||||
|
||||
| Claim | Result | Evidence / correction |
|
||||
| --------------------------------------------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| “all 54 widget renderers with compact and advanced modes” | **Inaccurate** | 54 `WidgetKind` values exist. `displayMode="advanced"` reaches 53; `app` is deliberately excluded in `item-content.tsx:51` and documented as compact-only. Change copy to “53 widget renderers; app tiles remain compact,” or implement app advanced mode. |
|
||||
| advanced focus: hover, keyboard, touch, geometry | **Partial** | Entry paths, Escape ownership, reduced motion, close focus, touch button and zero-dimension guards exist. WID-002/003 remain. |
|
||||
| richer widget context actions | **Partial** | `Menu.ContextMenu` is a good Mantine choice and query scope is item-aware. Dispatch still crosses `any`; Custom API action authorization is not represented. |
|
||||
| standardized query identity/cache/status | **Partial** | Query keys, matchers, runtime capture, recovery and tests exist. Thirty-nine of 53 renderer query calls do not read errors; runtime ref registration is render-time mutable state. |
|
||||
| safe polling | **Mostly confirmed** | defaults are limited to `widget` namespace; Custom API resume immediately refetches; per-widget intervals exist. State/ref typing still needs redesign. |
|
||||
| partial-failure rendering | **Partial** | settlement helper and several routers are strong; many renderer-level total failures still appear empty/disconnected. |
|
||||
| bounded integration responses | **Mostly confirmed** | common bounded reader, Custom API 2 MiB, timetable 512 KiB, Traefik bounds and Beszel queue bounds exist. Custom action execution remains outside the hardened fetch path. |
|
||||
| timetable hardening | **Confirmed** | board/item permission checks, saved-value comparison, public-address resolution, private ranges, per-request DNS re-resolution, pinned dispatcher, redirect rejection, timeouts, response bounds and schemas are present. |
|
||||
| precise placement | **Mostly confirmed** | mouse, touch tolerance, keyboard, occupied-cell validation, dynamic bounds, nearest fit and immediate persistence exist. Failure/race/live responsive coverage remains insufficient. |
|
||||
| widget catalog documentation | **Partial** | all kinds have pages and shared interactions; wording overclaims focus behavior and validation counts are stale. |
|
||||
| validation: 40 files/162 tests, 6/6 E2E | **Stale** | current diff contains 57 changed unit-spec files (including three E2E specs); repository has 42 widget spec files and six E2E specs. Update from the exact final run. |
|
||||
| independent reviews “pass” | **Not supported** | unresolved valid findings, current P1s, and unavailable live QA contradict an unqualified pass. |
|
||||
|
||||
## Mantine suitability ledger
|
||||
|
||||
Legend: **U** already used appropriately; **C** consolidation/replacement candidate; **R** relevant but current implementation can stay; **N** not relevant to widget runtime; **D** dependency/version addition. Exact 9.4.1 package archives were inspected in `/tmp`; live docs/Context7 include 9.5.0.
|
||||
|
||||
### Core catalog
|
||||
|
||||
- **U:** ActionIcon, Alert, Anchor, Avatar, Badge, Box, Button, Card, Center, Code, Divider, Flex, Grid, Group, Image, Loader, Menu (`Menu.ContextMenu`), Overlay, Paper, Popover, Portal, Progress, RingProgress, ScrollArea, SimpleGrid, Skeleton, Stack, Table, Tabs, Text, ThemeIcon, Title, Tooltip, UnstyledButton.
|
||||
- **C:** Accordion (dense integration sections), DataList (semantic label/value stats), EmptyState (replace bespoke empty state), FocusTrap and Modal compound API (WID-002), LoadingOverlay (background refetch without deleting content), NumberFormatter, OverflowList/Scroller (responsive action and chip rows), Table.ScrollContainer, Timeline (activity/feed histories), VisuallyHidden and Kbd, CloseButton, Dialog/HoverCard/Notification where persistent errors/actions need semantics.
|
||||
- **R:** Affix, AppShell, AspectRatio, BackgroundImage, Breadcrumbs, Burger, Checkbox, Chip, Collapse, ColorInput/ColorPicker/ColorSwatch/AlphaSlider/AngleSlider/HueSlider, Combobox/ComboboxPopover, Container, CopyButton, Drawer, Fieldset, FileButton/FileInput, FloatingIndicator/FloatingWindow, Highlight, Indicator, Input/JsonInput, List, Mark, Marquee, MaskInput, Menubar, MultiSelect/NativeSelect/NumberInput/PasswordInput/PillsInput/PinInput/Radio/RangeSlider/Rating/SegmentedControl/Select/Slider/Switch/TagsInput/Textarea/TextInput, Pagination, Pill, SemiCircleProgress, Space, Splitter, Spoiler, Stepper, Transition, Tree/TreeSelect, Typography. Use only where an existing widget requirement matches; do not churn working layouts.
|
||||
- **N:** Alpha/color/angle primitives outside configuration forms, AppShell/navigation primitives, TableOfContents, CorePackage documentation.
|
||||
- **D (9.5, not 9.4.1):** Cascader. **RollingNumber and Splitter are present in 9.4.1.**
|
||||
|
||||
### Hooks catalog
|
||||
|
||||
- **C:** `useFocusReturn`, `useFocusTrap`, `useReducedMotion`, `useRovingIndex`, `useLongPress`, `useScroller`, `useElementSize`, `useViewportSize`, `useHotkeys`, `useMediaQuery`, `useResizeObserver`.
|
||||
- **U/R:** focus-within, hover, disclosure, click-outside, merged-ref, interval/timeout, local-storage, event/window listeners, throttled/debounced values and list/set/map/state helpers are available in 9.4.1 and should replace local mechanics only when behavior and tests improve.
|
||||
- **N:** document title/favicon/hash/headroom/page-leave/OS/eye-dropper/fullscreen/network/orientation and other page-level utilities have no direct widget-remediation need. Every hook listed under `## Hooks` in `MANTINE.md` is covered by this rule; no hook requires 9.5 for the recommendations above.
|
||||
|
||||
### Form, dates, charts, schedule, extensions, theme and styles
|
||||
|
||||
- **Form:** existing `@homarr/form` should remain the abstraction over all cataloged form APIs. No widget-runtime replacement; use current validation/status APIs for widget editors (**R**).
|
||||
- **Dates:** all catalog entries are in installed 9.4.1 dates. `Calendar`, `MiniCalendar`, and `TimeValue` are candidates; other picker/input entries are editor-only (**R**). Importing dates directly into `@homarr/widgets` requires adding that workspace dependency (**D**).
|
||||
- **Charts:** 9.4.1 exports Area, Bar, BarsList, Bubble, Composite, Donut, Funnel, Heatmap, Line, Pie, Radar, RadialBar, Sankey, Scatter, Sparkline and Treemap (**C/R**). Prefer Sparkline for tiny trends, BarsList for ranked values, Heatmap for calendar/activity density, Composite for mixed analytics. **BulletChart and SunburstChart are 9.5-only (D)**. Replacement requires accessibility and bundle comparison, not blanket Recharts removal.
|
||||
- **Schedule:** all `AgendaView`, day/week/month/year/resource views and Schedule pages require new `@mantine/schedule` dependency (**D**). AgendaView is the only plausible calendar-widget candidate; its weight and semantics must beat the existing grouped list before adoption.
|
||||
- **Guides/FAQ/Other:** server components, portal testing, scroll lock, accessibility, nested popovers, zoom, responsive styling and version changelogs are relevant references (**R**); framework/setup/contribution/old migration entries are **N**. The 9.5 changelog is informational only.
|
||||
- **Theming/styles:** Colors, ColorSchemes, DefaultProps, MantineProvider, ThemeObject, CSS variables, data attributes, CSS Modules, responsive/RTL/style props/Styles API/performance are **U/R**. WID-009 requires shared layer/color tokens. Emotion, Sass, Vanilla Extract and unrelated global setup are **N**.
|
||||
- **Extensions:** existing modals, notifications, spotlight and rich-text abstractions are **U**; Carousel could consolidate Immich only after behavior parity; Dropzone/CodeHighlight/NProgress are **N** for this audit.
|
||||
|
||||
## Complete 54-widget coverage matrix
|
||||
|
||||
“Test” means a focused widget-local spec exists, not that all required states are covered. Every row still requires the common contract suite in WID-013.
|
||||
|
||||
| WidgetKind | Family | Advanced | Focused test | Principal audit gap |
|
||||
| --------------------------- | ----------------------- | ----------- | ------------ | ---------------------------------------------------------- |
|
||||
| clock | basic | yes | no | query error, locale-format options |
|
||||
| weather | basic/feed | yes | yes | query error; dayjs locale/time patterns |
|
||||
| app | basic/link | **no** | no | PR wording; URL contract |
|
||||
| iframe | custom content | yes | yes | manual focus/iframe trapping; URL privacy thread now fixed |
|
||||
| video | custom content | yes | no | media/focus/live behavior |
|
||||
| notebook | notes | yes (inner) | yes | editor focus inside manual surface |
|
||||
| anchorNote | notes | yes | no | query error, save/error regression |
|
||||
| dnsHoleSummary | network | yes | no | total query error, hard-coded chart colors |
|
||||
| dnsHoleControls | network/security | yes | no | total error and permission matrix |
|
||||
| smartHome-entityState | smart home | yes | no | raw state in aria; action permissions |
|
||||
| smartHome-executeAutomation | smart home | yes | no | mutation/permission/edit-mode E2E |
|
||||
| stockPrice | analytics | yes | yes | total error |
|
||||
| mediaServer | media | yes | no | total error and table a11y |
|
||||
| calendar | calendar | yes | yes | total error; fixed HH:mm; schedule tradeoff |
|
||||
| downloads | media | yes | yes | four-state/component coverage, arbitrary z-index |
|
||||
| mediaRequests-requestList | media | yes | no | total error; external URLs/actions |
|
||||
| mediaRequests-requestStats | media | yes | no | total error; links/colors |
|
||||
| mediaTranscoding | media | yes | yes | total error and responsive table |
|
||||
| mediaMissing | media | yes | yes | total error and external URL |
|
||||
| minecraftServerStatus | infrastructure | yes | no | total error/malformed payload |
|
||||
| networkControllerSummary | networking | yes | no | total error; WAN/unknown localization |
|
||||
| networkControllerStatus | networking | yes | no | responsive rule duplication |
|
||||
| rssFeed | feed | yes | yes | strong stale/partial state; locale relative time |
|
||||
| bookmarks | basic/link | yes | yes | recomputation; URL contract |
|
||||
| indexerManager | media | yes | no | total error; runtime action ref |
|
||||
| healthMonitoring | infrastructure | yes | yes | integration query error |
|
||||
| releases | infrastructure/feed | yes | yes | secret/item authorization needs E2E |
|
||||
| mediaReleases | media | yes | no | total error; external URLs |
|
||||
| dockerContainers | infrastructure | yes | yes | total error; z-index/table semantics |
|
||||
| firewall | networking/security | yes | yes | partial-query matrix; loose router generics |
|
||||
| notifications | infrastructure | yes | no | total error; untrusted href |
|
||||
| systemResources | infrastructure | yes | yes | total error; previous network thread fixed |
|
||||
| coolify | infrastructure | yes | yes | total error; URL validation |
|
||||
| systemDisks | infrastructure | yes | yes | total error |
|
||||
| timetable | calendar | yes | no | backend strong; component locale/live tests absent |
|
||||
| immich-serverStats | media | yes | yes | two ignored query errors |
|
||||
| immich-albumCarousel | media | yes | yes (query) | query error and nested overlay/focus E2E |
|
||||
| paperlessNgx | analytics | yes | no | former error thread fixed, no regression spec |
|
||||
| patchmon | infrastructure | yes | yes | total error |
|
||||
| bazarr | media | yes | yes | total error |
|
||||
| tracearr | analytics | yes | yes | total error |
|
||||
| speedtestTracker | analytics | yes | yes | total error |
|
||||
| uptimeKuma | infrastructure | yes | yes | total error |
|
||||
| audioStats | media | yes | yes | good dual-error handling; partial-state tests needed |
|
||||
| umami | analytics | yes | no | runtime query registration; chart a11y |
|
||||
| vpn | networking/security | yes | no | total error; status/permission matrix |
|
||||
| archiveTeamWarrior | infrastructure | yes | yes | suspense path; docs present |
|
||||
| ups | infrastructure | yes | no | total error |
|
||||
| beszelSystemTable | monitoring | yes | yes | nested chart/focus and live subscription |
|
||||
| beszelSystemGrid | monitoring | yes | no | live/error UI; no focused renderer test |
|
||||
| beszelAlerts | monitoring | yes | no | partial error and status semantics |
|
||||
| beszelSystemStats | monitoring | yes | yes | runtime refs, locale tooltip |
|
||||
| traefik | networking | yes | no | total error; bounded backend positive |
|
||||
| customApi | custom content/security | yes | yes | WID-001/007/008; polling resume fixed |
|
||||
|
||||
## Severity-ranked findings
|
||||
|
||||
### WID-001 — P1 confirmed, pre-existing: Custom API actions lack resource authorization and hardened fetch policy
|
||||
|
||||
- **Location/claim:** `packages/api/src/router/custom-widget/custom-widget-router.ts:57-102,392-451`; “harden Custom API” and action-permission semantics.
|
||||
- **Evidence/reproduction:** any authenticated account can call `customWidget.all`, learn every definition ID and URL, then call `customWidget.execute({definitionId})`. The mutation verifies neither board nor item nor an action permission. It decrypts administrator-provided secrets, uses `new URL()` only, and `redirect: "follow"`. The PR hardened `widget.customApi.getData`, but not this sibling execution path.
|
||||
- **Impact/rule:** unauthorized side effects against internal services and authenticated redirect chains; broken object-level authorization. A view-only account can invoke administrator-created controls outside any board context.
|
||||
- **Correction:** require `{boardId,itemId,definitionId}`; load the item and verify it is `customApi`, belongs to the board, references the definition, and the caller has an explicit execute policy. Until that policy exists, restrict to admin. Share `validateCustomApiUrl`, timeout, redirect rejection, bounded disposal and redacted errors with getData. Compatibility: old direct callers must send item/board context.
|
||||
- **Tests:** procedure tests for anonymous, unrelated authenticated, view-only, modifier, admin, mismatched item/definition, disabled definition, redirects, credential URL, timeout and secret non-disclosure; E2E action button across roles.
|
||||
|
||||
### WID-002 — P1 confirmed, PR-introduced: manual advanced focus is not an accessible modal
|
||||
|
||||
- **Location:** `advanced-focus/context.tsx:194-209`, `items/item-content.tsx:113-189`.
|
||||
- **Evidence:** manual mode renders an Overlay plus a `role="region"` Card. There is no FocusTrap, `aria-modal`, background inertness, scroll lock or backdrop dismissal. Only the close button is initially focused; Tab can leave the surface and activate dimmed board controls.
|
||||
- **Impact/rule:** keyboard/screen-reader users lose the interaction boundary; WCAG 2.1.2, 2.4.3 and ARIA dialog pattern. Iframes/editors make escape especially likely.
|
||||
- **Correction/Mantine:** render manual mode through `Modal.Root` compound parts (or ModalBase), using its portal, FocusTrap, scroll lock, Escape/return-focus and semantic content. Preserve preview mode as non-modal. Configure nested overlays and `removeScrollProps`; keep a 44px `Modal.CloseButton` and explicit title via VisuallyHidden if needed.
|
||||
- **Tests:** RTL portal/focus-cycle/return tests; Playwright Tab/Shift+Tab, nested Menu/Select/Popover/editor/iframe, backdrop and every close path; reduced motion and 200% zoom.
|
||||
|
||||
### WID-003 — P1 probable, PR-introduced: fixed advanced surface remains under transformed Gridstack ancestry
|
||||
|
||||
- **Location:** `advanced-focus.module.css:35-42`; Card remains inside the Gridstack item while only the backdrop is portaled.
|
||||
- **Evidence:** CSS fixed descendants establish their containing block at a transformed ancestor. Gridstack uses transforms for positioned items, so viewport-derived `left/top` can be interpreted relative to the item on affected layouts. A Portal wrapper exists only for Overlay.
|
||||
- **Impact:** off-screen, clipped or incorrectly scaled focus surface across breakpoints/remounts.
|
||||
- **Correction:** portal the focused surface itself (naturally achieved by WID-002) and calculate geometry in the same viewport coordinate system. Do not compensate with transform-specific offsets.
|
||||
- **Tests:** real Gridstack at every layout/viewport, scrolled page, header, dynamic section and resize/remount; assert bounding rect within viewport and finite scale.
|
||||
|
||||
### WID-004 — P1 confirmed, regression-exposed/systemic: query failures still become valid empty/disconnected UI
|
||||
|
||||
- **Location:** 39 of 53 renderer query calls do not read `error`, including bookmarks, weather, calendar, media server, system resources, Coolify, DNS Hole, Tracearr, notifications, Immich, VPN and others listed in the matrix. Query defaults do not set `throwOnError` (`trpc.tsx:69-93`).
|
||||
- **Evidence:** TanStack Query returns `data=undefined` on total failure. Components default to `[]`, `null`, or empty branches, so the board ErrorBoundary is never reached. Network Controller and Paperless were fixed, proving the pattern; it was not completed.
|
||||
- **Impact:** operators are told “empty” or “disconnected” when monitoring failed, violating the product’s truthful-state requirement.
|
||||
- **Correction/Mantine:** introduce a shared `WidgetQueryState` contract distinguishing initial Skeleton, background LoadingOverlay, stale Alert, partial warning, true EmptyState and terminal error. Either explicitly throw only when no usable cached data or render a shared error Alert. Preserve stale data.
|
||||
- **Tests:** parameterized contract tests for every queried widget: initial, success-empty, total failure, stale+failure, partial failure, refetch, remount/persisted cache and sibling isolation.
|
||||
|
||||
### WID-005 — P1 confirmed, PR-introduced/systemic: integration-provided links bypass a shared URL policy
|
||||
|
||||
- **Location:** examples include `media-missing/component.tsx:291`, `notifications/component.tsx:77`, media requests list/stats, Coolify resource/server links, releases, bookmarks and iframe. Only RSS and custom JSX visibly use explicit protocol sanitizers.
|
||||
- **Evidence:** dynamic strings are assigned directly to `href`/new-tab controls. Integration APIs and remote feeds are trust boundaries; no renderer-wide `http/https` guard is enforced.
|
||||
- **Impact:** unsafe schemes, credential-bearing URLs, internal detail disclosure and inconsistent opener behavior.
|
||||
- **Correction:** add one typed application URL helper returning a sanitized URL/undefined, with allowed schemes per use case, credential stripping/rejection, consistent `noopener noreferrer`, and disabled non-link presentation. Validate again server-side for action endpoints.
|
||||
- **Tests:** `javascript:`, `data:`, malformed, credential, relative, http/https and missing URLs for each shared link primitive.
|
||||
|
||||
### WID-006 — P2 confirmed, PR-introduced: runtime query/action state mutates untyped refs during render
|
||||
|
||||
- **Location:** `definition.ts:121-162`; runtime registration calls in media missing/transcoding, audio, calendar, Umami, Beszel and Immich; `widget-context-menu.tsx:157-174`.
|
||||
- **Evidence:** `setWidgetRuntimeQueries` mutates a shared object during render; concurrent/aborted renders can publish uncommitted query identity. Action dispatch casts the definition to `Record<string,unknown>` and `any`, then casts its result back.
|
||||
- **Correction:** make definition generics preserve kind/options/action types end-to-end; replace stringly opaque ref slots with a typed per-item registration API updated in a committed effect (or use `useSyncExternalStore`). Separate query registration from imperative actions.
|
||||
- **Tests:** StrictMode, interrupted/remount, options change and cleanup; compile-time `tsd`/type assertions rejecting wrong action props.
|
||||
|
||||
### WID-007 — P2 confirmed, regression-exposed: raw backend errors are exposed to users and assistive text
|
||||
|
||||
- **Location:** Custom API UI `component.tsx:561-576`; API `custom-api.ts:79-85`; several partial fallbacks use `error.message`.
|
||||
- **Impact:** internal hostnames, request paths and implementation details can appear in tooltips/aria labels and logs.
|
||||
- **Correction/Mantine:** return stable error codes plus redacted public messages; keep detailed causes server-side with correlation IDs. Render localized Alert/Notification details, never raw throwable text.
|
||||
- **Tests:** errors containing credentials, query strings, private hosts and multiline backend bodies must not reach tRPC payload, DOM, accessibility tree or notifications.
|
||||
|
||||
### WID-008 — P2 confirmed, PR-introduced/regression-exposed: locale handling remains incomplete
|
||||
|
||||
- **Locations:** Beszel chart/tooltip dayjs formats, calendar event `HH:mm`, downloads `YYYY-MM-DD HH:mm`, timetable `HH:mm:ss`, weather day/month/time formats; `_inputs/widget-location-input.tsx:199` hardcodes `Intl.NumberFormat("en")`.
|
||||
- **Also:** `beszel/_shared/disk-usage.tsx:41`, stats loading aria label, Network Controller `WAN`/`unknown` are English/raw accessible strings.
|
||||
- **Correction:** centralize app-locale date/time/number/duration/status helpers; load canonical dayjs locale only at the boundary; use localized translation keys for aria/status. NumberFormatter is suitable for simple display but application helpers should own locale.
|
||||
- **Tests:** at least `en-US`, `fr-FR`, `de-DE`, RTL locale, 12/24-hour preference and long translations, including accessibility names.
|
||||
|
||||
### WID-009 — P2 confirmed: layer and color tokens are inconsistent
|
||||
|
||||
- **Locations:** downloads z-index 1000 and rgba row colors; item title z-index 4; item menu/dynamic menu 10; media server white rgba; Indexer Manager hex colors; DNS Hole rgba palette; Umami hex tick colors.
|
||||
- **Correction:** define shared semantic layer tokens relative to Mantine CSS z-index variables and semantic status/chart tokens respecting light/dark/forced colors. Do not replace intentional service branding.
|
||||
- **Tests:** light/dark/forced-colors with nested overlays and tooltip ordering.
|
||||
|
||||
### WID-010 — P2 confirmed: responsive decisions are duplicated and under-tested
|
||||
|
||||
- **Location:** Network Controller Status `component.tsx:44,59-78` (open valid thread); Bookmark `component.tsx:94` recomputation (valid but low-impact thread); similar width/height literals across renderers.
|
||||
- **Correction:** small typed breakpoint lookup helpers per family, memoized only for genuinely expensive layout. Prefer container-query Grid/SimpleGrid where structural. Do not cargo-cult `useMemo` for a cheap pure calculation; benchmark Bookmark before accepting the review suggestion.
|
||||
- **Tests:** boundary tables for minimum/typical/wide/short/advanced sizes and 200% zoom.
|
||||
|
||||
### WID-011 — P2 confirmed: PR/docs overstate behavior and validation
|
||||
|
||||
- **Location:** PR body, board/getting-started copy “advanced preview in place,” validation numbers, and unqualified review pass.
|
||||
- **Correction:** state 53 advanced renderers plus compact-only app; call manual focus an overlay only after WID-002/003; publish exact final-SHA check/test counts and disclose unsupported/live-untested states.
|
||||
|
||||
### WID-012 — P2 confirmed: focused automated coverage is far below the 54-widget/data-state claim
|
||||
|
||||
- **Evidence:** 42 widget-local spec files are helper-heavy; 20+ renderers have no focused spec. Six E2E files exist, but only board interactions and Custom JSX directly exercise this modernization. The earlier Custom JSX login wait failed once and current exact-route wait remains.
|
||||
- **Correction:** add shared renderer contract harness plus family E2Es. Replace Custom JSX’s exact post-submit URL race with a shared login helper that waits for authenticated session/stable destination, then run it three times in CI.
|
||||
|
||||
### WID-013 — P2 confirmed: open review-thread state no longer matches code
|
||||
|
||||
| Thread | Classification |
|
||||
| ------------------------------------------- | ------------------------------------------------------------------- |
|
||||
| iframe complete URL | already fixed/false positive at current line: only host is rendered |
|
||||
| system resources network gate | already fixed, outdated |
|
||||
| RSS total error | already fixed: no-data error throws; stale data retained |
|
||||
| bookmarks memoization | valid P3/P2-low; benchmark first |
|
||||
| network-controller total error | already fixed, outdated |
|
||||
| network-controller breakpoint consolidation | valid P2 maintainability |
|
||||
| Paperless total error | already fixed, outdated |
|
||||
|
||||
Resolve the five stale threads with evidence; address or explicitly disposition the two live suggestions. Re-run review because CodeRabbit was paused during active development.
|
||||
|
||||
## Systemic remediation roadmap
|
||||
|
||||
1. **Security/authorization:** WID-001 and WID-005; add negative-role/protocol tests before merge.
|
||||
2. **Interaction accessibility:** replace manual focus with portaled Mantine modal semantics (WID-002/003), then test nested widget content.
|
||||
3. **Truthful data state:** build the shared query-state contract and migrate all 39 ambiguous sites; keep valid stale data and partial results.
|
||||
4. **Typed interfaces:** replace `widgetStateRef` slot mutation and `any` context dispatch with committed typed registrations.
|
||||
5. **Widget families:** media/downloads, infrastructure, networking/security, smart-home, analytics/calendar/feed, then Beszel; apply URL, state, locale, responsive and a11y contracts in that order.
|
||||
6. **Design-system cleanup:** semantic layers/colors and restrained Mantine consolidation. Do not add Schedule or upgrade to 9.5 without a separate dependency/bundle decision.
|
||||
7. **Docs/PR:** correct renderer count, behavior and exact validation evidence.
|
||||
8. **Final proof:** frozen install on supported Node/pnpm; format, lint, full typecheck, affected and full unit tests, production and docs builds; Custom JSX three times; complete production-container E2E; desktop/tablet/mobile keyboard/touch/light/dark/reduced-motion/zoom/a11y-tree QA. Push nothing until all refer to one unchanged SHA; close every review thread and obtain required approval.
|
||||
|
||||
## Positive findings
|
||||
|
||||
- Timetable is the strongest security work in the PR: saved resource authorization, private-range rejection, DNS rebinding resistance through pinned lookup, redirect rejection, bounds and schemas are coherently tested.
|
||||
- Bounded response helpers, Beszel bounded queue/cancellation, and partial-integration settlement materially reduce failure blast radius.
|
||||
- RSS preserves stale data and distinguishes partial feed failure; Network Controller and Paperless now correctly escalate total failures.
|
||||
- Item-scoped query matchers and tests address sibling-widget refresh isolation; global polling defaults are restricted to the widget namespace.
|
||||
- Touch long-press has movement tolerance; focus scale guards prevent Infinity/NaN; reduced-motion and remount cleanup are present.
|
||||
- Existing Homarr theme, Mantine components and shared UI vocabulary are generally preserved. Most proposed Mantine work is consolidation, not redesign.
|
||||
|
||||
## Validation evidence and uncertainty
|
||||
|
||||
- `MANTINE.md` was fetched from `https://mantine.dev/llms.txt` and passed byte-for-byte `cmp`; `llms-full.txt` stayed in `/tmp`.
|
||||
- Context7 current Mantine docs were queried for overlay/focus, responsive/data-state/accessibility, charts and Schedule. Static 9.4.1 archives verified exact exports; Cascader, BulletChart and SunburstChart are 9.5-only.
|
||||
- Local frozen install could not start because host Node/pnpm are below repository engine requirements. No dependencies, source files or machine toolchain were changed.
|
||||
- Docker API access is denied, so production-container E2E could not be rerun locally. Hosted exact-head Fast gate and Container/E2E are green.
|
||||
- `agent-browser` is not installed. Temporary `npx` execution was rejected by the environment’s security reviewer, so live exploratory QA/a11y-tree inspection was not performed.
|
||||
- External integrations were not available; malformed/partial/permission combinations are static-analysis conclusions unless a focused test already exists.
|
||||
- This review intentionally changes no production code and makes no commit, push, thread resolution or PR edit.
|
||||
|
||||
Finalization must re-query the PR head/checks/threads. If the head differs from the reviewed SHA, this report is a snapshot and the delta requires review before merge.
|
||||
|
||||
**Final refresh (2026-08-03):** head remained `96e92485a5fd45f3648cfac0453e614779c414ba`; all reported hosted checks remained successful; merge state remained `BLOCKED` with no approval decision; unresolved threads remained 7 of 34. The branch did not change during the audit.
|
||||
@@ -13,6 +13,8 @@ This will be a one time occurrence and version > ``1.0`` will have less breaking
|
||||
This time more important than every; **read the breaking changes** list and **create a backup** before updating.
|
||||
Some third party installation methods may take multiple days or weeks to update to the new version.
|
||||
|
||||
See [this guide on how to import your previous Homarr dashboard](/docs/getting-started/after-the-installation#importing-a-zip-from-version-before-100)
|
||||
|
||||
## Changes at a glance
|
||||
- **Asynchronous integrations system**: In versions prior to ``1.0``, we fetched data upon each request synchronously.
|
||||
This means that the user had to wait for the requests to finish so any data would appear.
|
||||
|
||||
@@ -10,11 +10,12 @@ If you are still on a version before 0.15.10, please update to 0.15.10 where we
|
||||
|
||||
<!-- truncate -->
|
||||
|
||||
## Preserve your old installation
|
||||
## Export your data
|
||||
|
||||
Homarr 1.0 is a complete rewrite and cannot reuse the old Docker mounts. Keep the 0.15 installation and its data intact while you build the replacement. The historical `Management` > `Tools` > `Migrate to 1.0` export can still be kept as a record, but current Homarr releases cannot import that ZIP or its secret key.
|
||||
|
||||
Plan to recreate your apps, integrations, and boards manually in the new installation. A SQLite database backup can be restored from the onboarding welcome screen only when it was created by a compatible current Homarr release; it is not a conversion path for a 0.15 database.
|
||||
As 1.0 is a complete rewrite of Homarr and therefor not compatible with the old docker mounts, we need to export your data first.
|
||||
For this you can navigate to the `Migrate to 1.0` page which you can find under `Management` > `Tools` > `Migrate to 1.0`.
|
||||
Here you can select which things you want to export and download the zip file.
|
||||
It will also show you a secret key which you will need to import the secret data like user password hashes, salts and integration secrets.
|
||||
|
||||

|
||||
|
||||
@@ -27,15 +28,28 @@ Please note that if you are using docker you need to specify a diffrent external
|
||||
So in your compose file the ports would be `7576:7575` instead of `7575:7575`.
|
||||
|
||||
## Import your data
|
||||
Once the installation is completed you can access the new installation. During the onboarding you'll need to select `Import from Homarr before 1.0`.
|
||||
|
||||
:::warning Legacy guide
|
||||

|
||||
|
||||
The pre-1.0 ZIP importer is no longer included in current Homarr releases. Keep the old installation available until you have manually verified the replacement. Compatible SQLite backups from current releases can still be restored from the onboarding welcome screen.
|
||||
After that you can upload the zip file you downloaded in the first step.
|
||||
|
||||
:::
|
||||

|
||||
|
||||
After that you'll get a list with all your boards that can be imported. You can configure how it should be imported and can select which screensizes it should import.
|
||||
|
||||

|
||||
|
||||
Once you have selected everything you can click on `Confirm import and continue` which will open the encryption key input dialog where you can input the secret key you got during the export.
|
||||
|
||||

|
||||
|
||||
After that you can continue the onboarding and you should see all your boards and data in the new installation.
|
||||
|
||||
See [After the installation](https://homarr.dev/docs/getting-started/after-the-installation) for more information.
|
||||
|
||||
## Remove the old installation
|
||||
|
||||
After you are happy with the new installation and everything is working as expected you can remove the old installation.
|
||||
Please make sure to backup the data before you remove the old installation.
|
||||
Also once the port 7575 is free again you can change the port mapping of the new installation to `7575:7575` if you want to.
|
||||
Also once the port 7575 is free again you can change the port mapping of the new installation to `7575:7575` if you want to.
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"label": "Advanced",
|
||||
"collapsed": true,
|
||||
"position": 5,
|
||||
"position": 4,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"title": "Advanced Concepts or features"
|
||||
|
||||
@@ -1,175 +1,41 @@
|
||||
# Developer Docker CLI
|
||||
|
||||
The host-side developer CLI builds and runs local Homarr images and pull-request previews with non-blocking background operations. Run it directly from a checkout with `pnpm dev:cli -- ...`; no shell-specific setup script is required. It is separate from the recovery CLI included in production images.
|
||||
The host-side developer CLI builds and runs local Homarr images and pull-request previews. Run it directly from a checkout with `pnpm dev:cli -- ...`; no shell-specific setup script is required. It is separate from the recovery CLI included in production images.
|
||||
|
||||
To install an optional `homarr` binary, run `pnpm dev:cli:install` and make your Go bin directory available on `PATH`. GitHub CLI authentication (`gh auth login`) is only needed for pull-request features.
|
||||
To install an optional `homarr` binary, run `pnpm dev:cli:install` and make your Go bin directory available on `PATH`. GitHub CLI authentication is only needed for pull-request features.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Docker or OrbStack
|
||||
- Go 1.25+ (when building the CLI from source)
|
||||
- GitHub CLI authenticated with `gh auth login` (for pull-request and CI features)
|
||||
- A [Nerd Font](https://www.nerdfonts.com/) enabled in your terminal (recommended for icons, status indicators, and TUI glyphs)
|
||||
|
||||
## Interactive Development Browser
|
||||
## Browse development images
|
||||
|
||||
```bash
|
||||
pnpm dev:cli --
|
||||
# or with global binary:
|
||||
homarr
|
||||
```
|
||||
|
||||
The main browser combines local `homarr:*` images with open pull requests and their GHCR preview status. Select an image to start or stop it, use `R` to build a remote pull request locally in the background or rebuild a local image, or use `p` to force a remote preview pull. Press `d` (or `1`/`2`) to toggle between the image browser and the instance dashboard.
|
||||
The main screen combines local `homarr:*` images with open pull requests and their GHCR preview status. Select an image to start or stop it, use `R` to build a remote pull request locally in the background or rebuild a local image, or use `p` to force a remote preview pull. Press `d` to switch to the instance dashboard and `d` again to return without losing the browser state. The explicit `dev` and `dash` subcommands start on either screen.
|
||||
|
||||
A locally built PR image is folded into its remote PR row only when its recorded revision matches the current GitHub head SHA (marked `◆ local`). When the matching GHCR image is also available, pressing `Enter` opens a local/remote selector with local preselected. Outdated local builds remain separate rows.
|
||||
A locally built PR image is folded into its remote PR row only when its recorded revision exactly matches the current GitHub head SHA. The row is marked `◆ local`, and starting it prefers that local image without a registry pull. Outdated local builds remain separate rows. Navigation and dashboard switching remain available during background builds.
|
||||
|
||||
### Multi-Source Sidebar
|
||||
|
||||
Press `Tab` to cycle between sidebar tabs:
|
||||
|
||||
- **`[logs]`**: Live container output streams (press `f` to follow/pause, `L` to pin to the selected container, `Page Up`/`Page Down` to scroll).
|
||||
- **`[build]`**: Real-time BuildKit step streaming for active local builds.
|
||||
- **`[ci]`**: GitHub Actions CI check statuses, run duration, and workflow links for pull requests.
|
||||
|
||||
### Background Task Overlay
|
||||
|
||||
Pulls and builds run asynchronously in the background. Press `t` to open the tasks overlay, showing:
|
||||
|
||||
- Layer-by-layer download progress and transfer speeds (via Docker Engine API).
|
||||
- Active build stages and logs.
|
||||
- Task cancellation and status.
|
||||
|
||||
### Browser Shortcuts
|
||||
|
||||
| Key | Action |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| `/` | Filter rows by PR number, title, author, branch, CI, image, state, or port |
|
||||
| `↑` / `↓` or `j` / `k` | Navigate rows |
|
||||
| `Enter` or `Space` | Start the available image; choose local or remote when both exist; stop if running |
|
||||
| `R` | Build locally in background (or rebuild local image) |
|
||||
| `p` | Pull remote PR image in background and start container |
|
||||
| `m` | Manage mode (`b` rebuild, `i` delete image, `d` delete data, `c` remove container, `p` prune) |
|
||||
| `t` | Toggle background tasks overlay |
|
||||
| `Tab` | Cycle sidebar tabs (`[logs]`, `[build]`, `[ci]`) |
|
||||
| `l` / `L` | Toggle sidebar visibility / Pin logs to selected instance |
|
||||
| `f` | Follow or pause logs |
|
||||
| `M` | Toggle demo mode for next launch |
|
||||
| `b` | Toggle bot pull requests |
|
||||
| `o` / `a` | Open PR on GitHub / Open running instance in browser |
|
||||
| `c` | Copy instance URL to clipboard |
|
||||
| `r` | Refresh data |
|
||||
| `1` / `2` / `d` | Switch screen (development browser / instance dashboard) |
|
||||
| `?` / `q` | Help overlay / Quit (cancels active background tasks) |
|
||||
|
||||
## Instance Dashboard
|
||||
## Build images
|
||||
|
||||
```bash
|
||||
homarr dash
|
||||
# or
|
||||
pnpm dev:cli -- dash
|
||||
pnpm dev:cli -- build <name>
|
||||
pnpm dev:cli -- build --pr <number> [name]
|
||||
```
|
||||
|
||||
The instance dashboard displays all running and stopped Homarr containers, published ports, health states, and resource volumes.
|
||||
The first command builds the current checkout as `homarr:<name>`. The pull-request form uses a depth-one clone in a temporary directory and defaults to `homarr:pr-<number>` when `name` is omitted.
|
||||
|
||||
| Key | Action |
|
||||
| ------------------ | ---------------------------------------- |
|
||||
| `Enter` or `Space` | Start or stop selected instance |
|
||||
| `s` / `S` | Stop / Restart instance |
|
||||
| `x` | Remove container (preserves data volume) |
|
||||
| `m` | Manage mode for selected instance |
|
||||
| `R` | Rebuild from recorded build provenance |
|
||||
|
||||
## CLI Commands
|
||||
|
||||
The commands below use the installed `homarr` binary. Without global installation, prefix each command with `pnpm dev:cli --` (for example, `pnpm dev:cli -- list`).
|
||||
|
||||
### Launch Instances
|
||||
## Rebuild images
|
||||
|
||||
```bash
|
||||
homarr run dev
|
||||
homarr run --detach dev
|
||||
homarr run --pr 6441
|
||||
homarr run --pr 6441 --demo
|
||||
homarr --pr 6441 --env WORKSHOP_WEB_URL=https://app-v2.preview.homarr.dev/
|
||||
pnpm dev:cli -- rebuild <name>
|
||||
```
|
||||
|
||||
- `--pr <number>`: Pull and run a PR image from GHCR (shorthand: `homarr --pr <number>`).
|
||||
- `--demo` (`-m`): Start with mock integrations and sample data.
|
||||
- `--detach`: Start container in background.
|
||||
- `--env` (`-e`): Pass additional environment variables (repeatable: `--env KEY=VALUE`).
|
||||
Images built by this CLI record their source checkout, revision, and optional pull-request number. Rebuild uses that provenance to select the original checkout or create a fresh temporary pull-request checkout.
|
||||
|
||||
### Build & Rebuild
|
||||
## Run images
|
||||
|
||||
```bash
|
||||
# Build current checkout as homarr:<name>
|
||||
homarr build feature
|
||||
|
||||
# Build a pull request locally in a temporary checkout
|
||||
homarr build --pr 6441
|
||||
|
||||
# Rebuild an existing image from recorded provenance
|
||||
homarr rebuild feature
|
||||
homarr run <name>
|
||||
homarr run --pr <number>
|
||||
```
|
||||
|
||||
Images built with the CLI record their source checkout, revision SHA, and optional PR number. `homarr rebuild` reads this provenance to reconstruct the build without manual configuration.
|
||||
|
||||
### GitHub CI Checks
|
||||
|
||||
```bash
|
||||
# View CI checks for a specific PR
|
||||
homarr ci 6441
|
||||
|
||||
# View CI checks for the current branch
|
||||
homarr ci
|
||||
|
||||
# Watch CI checks until completion
|
||||
homarr ci 6441 --watch
|
||||
```
|
||||
|
||||
### Interactive Shell
|
||||
|
||||
```bash
|
||||
# Open interactive shell in running container
|
||||
homarr shell homarr-dev
|
||||
|
||||
# Run specific command in container
|
||||
homarr shell homarr-dev pnpm db:migration:sqlite:run
|
||||
```
|
||||
|
||||
### Image & Volume Management
|
||||
|
||||
```bash
|
||||
# List local images with build provenance (source, revision, PR)
|
||||
homarr images
|
||||
|
||||
# Delete local image
|
||||
homarr images rm feature
|
||||
|
||||
# List data volumes with owner PR/tag and in-use status
|
||||
homarr data
|
||||
|
||||
# Delete data volume (safely stops and removes owning container first)
|
||||
homarr data rm homarr_feature_data
|
||||
|
||||
# Remove stopped containers (prompts without --force)
|
||||
homarr prune --force
|
||||
```
|
||||
|
||||
### Instance Operations & Diagnostics
|
||||
|
||||
```bash
|
||||
# List instances in a script-friendly table
|
||||
homarr list
|
||||
|
||||
# Follow container logs
|
||||
homarr logs homarr-dev
|
||||
|
||||
# Stop, restart, remove, or open container in browser
|
||||
homarr stop homarr-dev
|
||||
homarr restart homarr-dev
|
||||
homarr remove homarr-dev
|
||||
homarr open homarr-dev
|
||||
|
||||
# Verify Docker daemon, Engine API, GHCR access, and gh CLI authentication
|
||||
homarr doctor
|
||||
```
|
||||
Add `--detach` to run in the background, `--demo` to enable demo data, or repeat `--env KEY=VALUE` to provide additional environment variables.
|
||||
@@ -25,27 +25,3 @@ You should also create a backup of the database, in case you break something.
|
||||
4. Click "Execute" in the top right of the Window. 
|
||||
5. Select /bin/bash and click Execute 
|
||||
6. Run the Homarr command and press enter (eg. homarr reset-owner-password) 
|
||||
|
||||
## Build a local branch harness
|
||||
|
||||
The host-side operations CLI can build the current checkout and run it as a named, browser-ready Docker harness. This is separate from the recovery CLI described above and does not open the interactive developer TUI.
|
||||
|
||||
```sh
|
||||
pnpm cli harness setup feature-board
|
||||
```
|
||||
|
||||
The command builds `homarr:feature-board`, starts a container with a persistent data volume, enables demo mode, and keeps demo mutations writable (`DEMO_READ_ONLY=false`). Docker assigns a free host port by default; use the URL printed by the command or query it later:
|
||||
|
||||
```sh
|
||||
pnpm cli harness port feature-board
|
||||
```
|
||||
|
||||
Pass environment overrides by repeating `--env`:
|
||||
|
||||
```sh
|
||||
pnpm cli harness setup feature-board \
|
||||
--env WORKSHOP_WEB_URL=https://example.test \
|
||||
--env FEATURE_FLAG=true
|
||||
```
|
||||
|
||||
Other commands are `harness build <name>`, `harness run <name>`, and `harness stop <name>`. Use the returned URL with a browser automation harness for feature validation; do not assume the host port is `7575`.
|
||||
@@ -57,8 +57,50 @@ Homarr becomes the instance administrator.
|
||||
Use `pnpm docker:dev:up` instead when you only need Redis in the background. Running the migration also seeds the
|
||||
default data; use `pnpm db:seed` to seed it explicitly later.
|
||||
|
||||
Workshop contributors can set up PocketBase and Docusaurus with the dedicated
|
||||
[Workshop development guide](../../workshop/development).
|
||||
### Run PocketBase and documentation
|
||||
|
||||
Start PocketBase in another terminal:
|
||||
|
||||
```sh
|
||||
cp apps/workshop/.env.example apps/workshop/.env
|
||||
docker compose --env-file apps/workshop/.env -f apps/workshop/docker-compose.yml up --build workshop
|
||||
```
|
||||
|
||||
Then start Docusaurus in a third terminal:
|
||||
|
||||
```sh
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
|
||||
```
|
||||
|
||||
PocketBase runs on port 8090 and Docusaurus on port 3003. Stopping the services preserves the PocketBase volume.
|
||||
|
||||
Open `http://127.0.0.1:8090/_/` and create the first PocketBase superuser. The Workshop UI is available at
|
||||
`http://127.0.0.1:3003/workshop`.
|
||||
|
||||
Point Homarr at the local services with:
|
||||
|
||||
```dotenv
|
||||
HOMARR_WEBSITE_URL=http://127.0.0.1:3003
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090
|
||||
WORKSHOP_WEB_URL=http://127.0.0.1:3003/workshop
|
||||
```
|
||||
|
||||
Restart `pnpm dev` after changing `.env`. GitHub OAuth is optional locally. See
|
||||
[`apps/workshop/README.md`](https://github.com/homarr-labs/homarr/blob/HEAD/apps/workshop/README.md) for OAuth,
|
||||
Workshop moderator promotion, logs, tests, backup, restore, stopping, and intentional volume reset.
|
||||
|
||||
### URL testing
|
||||
|
||||
Docusaurus reads `HOMARR_WEBSITE_URL` and `WORKSHOP_API_URL` when it starts or builds. Homarr also reads
|
||||
`WORKSHOP_WEB_URL` when its server starts, so these values do not rely on `NEXT_PUBLIC_*` build variables. Test a
|
||||
separate API origin before release:
|
||||
|
||||
```sh
|
||||
HOMARR_WEBSITE_URL=http://127.0.0.1:3003 \
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 \
|
||||
WORKSHOP_WEB_URL=http://127.0.0.1:3003/workshop \
|
||||
pnpm dev:docs
|
||||
```
|
||||
|
||||
### Useful commands
|
||||
|
||||
@@ -70,6 +112,7 @@ Workshop contributors can set up PocketBase and Docusaurus with the dedicated
|
||||
- `pnpm dev:cli -- rebuild <name>` — rebuild an image from its recorded checkout or pull request.
|
||||
- `pnpm dev:cli:install` — optionally install the developer CLI as a `homarr` binary.
|
||||
- `pnpm dev:docs` — Docusaurus only on port 3003.
|
||||
- `pnpm docker:workshop` — local PocketBase through Docker Compose.
|
||||
- `pnpm docker:dev:up` — Redis development service in the background.
|
||||
- `pnpm cli` — Homarr operations CLI.
|
||||
- `pnpm db:migration:sqlite:run` — create or update a SQLite database.
|
||||
@@ -79,6 +122,7 @@ Workshop contributors can set up PocketBase and Docusaurus with the dedicated
|
||||
- `pnpm lint` / `pnpm lint:fix` — check or apply oxlint.
|
||||
- `pnpm typecheck` — typecheck all workspaces.
|
||||
- `pnpm test` — unit and contract suites.
|
||||
- `pnpm test:workshop` — disposable Workshop integration suite.
|
||||
- `pnpm build` — production monorepo build.
|
||||
- `docker build -t homarr .` — build a production Docker image.
|
||||
- `docker run -p 7575:7575 -e SECRET_ENCRYPTION_KEY='your_64_character_hex_string' homarr:<name>` — run it.
|
||||
@@ -35,10 +35,7 @@ Using the `PUID` and `PGID` will require you to set the correct permissions on t
|
||||
| `HOMARR_DISABLE_IPV6` | Disables the IPv6 listener of the internal nginx proxy, even when the host supports IPv6. IPv6 is normally disabled automatically when the host has no IPv6 configured. See [#4596](https://github.com/homarr-labs/homarr/issues/4596) | `true` or `false` | `false` |
|
||||
| `HOMARR_WEBSITE_URL` | Main Homarr documentation/site base used by runtime links. Must be an absolute HTTP(S) URL. | HTTP(S) URL | `https://homarr.dev` |
|
||||
| `WORKSHOP_API_URL` | PocketBase API used by Homarr and standalone documentation. | HTTP(S) URL | `HOMARR_WEBSITE_URL` |
|
||||
| `WORKSHOP_WEB_URL` | Public Workshop website used by links in Homarr, Custom Widgets, onboarding, and assistant context. | HTTP(S) URL | `HOMARR_WEBSITE_URL/workshop` |
|
||||
|
||||
Workshop service operators can find its server-only configuration in the
|
||||
[Workshop operator guide](../../workshop/operator#environment-variables).
|
||||
| `WORKSHOP_WEB_URL` | Public Workshop page opened from Homarr. | HTTP(S) URL | `HOMARR_WEBSITE_URL/workshop` |
|
||||
|
||||
## Authentication
|
||||
|
||||
@@ -58,12 +55,11 @@ A random secret can be generated by using the following command: `openssl rand -
|
||||
|
||||
## Docker
|
||||
|
||||
| Environment Variable | Description | Possible values | Default |
|
||||
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ------- |
|
||||
| `DOCKER_SOCKET_PATHS` | Comma-separated mounted Docker-compatible socket paths. Homarr discovers each path as a selectable Docker environment | For example `/var/run/docker.sock,/var/run/podman.sock` | - |
|
||||
| `DOCKER_HOSTNAMES` | Comma-separated plaintext TCP hostnames. Pair each hostname with the port in the same position in `DOCKER_PORTS` | For example `localhost,docker.example.com` | - |
|
||||
| `DOCKER_PORTS` | Comma-separated plaintext TCP ports paired with `DOCKER_HOSTNAMES` | For example `2375,2376` | - |
|
||||
| `DOCKER_ENDPOINTS` | Optional advanced JSON configuration for custom names, TLS, or restricted capabilities. It takes precedence over the simple variables above. See the [Docker and Podman connection guide](/docs/integrations/docker) | A JSON array of endpoint descriptors | - |
|
||||
| Environment Variable | Description | Possible values | Default |
|
||||
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ------- |
|
||||
| `DOCKER_HOSTNAMES` | Comma-separated list of hostnames to connect to | For example `localhost,docker.example.com` | - |
|
||||
| `DOCKER_PORTS` | Comma-separated list of ports to connect to | For example `2375,2376` | - |
|
||||
| `DOCKER_SOCKET_PATHS` | Comma-separated list of mounted Docker-compatible socket paths. See the [Docker and Podman integration guide](/docs/integrations/docker) | For example `/var/run/docker.sock,/var/run/podman.sock` | - |
|
||||
|
||||
## Database
|
||||
|
||||
@@ -113,9 +109,12 @@ External redis is currently not supported with Proxmox Community Scripts
|
||||
The advanced deployments environment variables should only be used if you know what you are doing.
|
||||
:::
|
||||
|
||||
| Environment Variable | Description |
|
||||
| ------------------------ | --------------------------------------------------- |
|
||||
| `DB_MIGRATIONS_DISABLED` | Disable db migrations. For example for helm charts. |
|
||||
| Environment Variable | Description |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `DB_MIGRATIONS_DISABLED` | Disable db migrations. For example for helm charts. |
|
||||
| `WORKSHOP_PUBLIC_ORIGIN` | Origin used in PocketBase-generated links and email. It must be set explicitly in production and must match the externally reachable Workshop origin. |
|
||||
| `PB_ALLOWED_ORIGINS` | Browser origins accepted by PocketBase. Use `*` for the central public community so arbitrary self-hosted Homarr instances can use the API and OAuth; private staging may use a comma-separated origin allowlist. |
|
||||
|
||||
All public URL variables reject non-HTTP protocols, embedded credentials, query strings, and fragments, and normalize
|
||||
trailing slashes when Homarr or Docusaurus starts.
|
||||
trailing slashes when Homarr or Docusaurus starts. The Docusaurus-only `WORKSHOP_URL` name is accepted as a deprecated alias for
|
||||
`WORKSHOP_API_URL` for one release. `WORKSHOP_API_URL` takes precedence.
|
||||
@@ -6,19 +6,14 @@ tags:
|
||||
|
||||
Homarr has the following keyboard shortcuts:
|
||||
|
||||
| Shortcut | Description |
|
||||
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `MOD` + `K` | Opens the search bar |
|
||||
| `MOD` + `J` | Toggles Dark Mode |
|
||||
| `MOD` + `E` | Toggles Edit Mode |
|
||||
| Hold `Shift` for 500 ms over a supported widget | Opens its temporary advanced view |
|
||||
| Hold `Shift` + `Ctrl`/`Cmd` over a supported widget | Keeps its advanced view open until it is closed |
|
||||
| `Esc` | Stops keyboard tile editing, cancels an active pointer or touch move/resize, or closes an open advanced view or board menu |
|
||||
|
||||
## Widget advanced view
|
||||
|
||||
Supported widgets open a temporary advanced view when you hold `Shift` over them for 500 ms. Hold `Shift` plus `Ctrl`
|
||||
on Windows/Linux or `Cmd` on macOS over the widget to keep the advanced view open, or use its menu.
|
||||
| Shortcut | Description |
|
||||
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `MOD` + `K` | Opens the search bar |
|
||||
| `MOD` + `J` | Toggles Dark Mode |
|
||||
| `MOD` + `E` | Toggles Edit Mode |
|
||||
| Hold `Shift` for 500 ms over a supported widget | Opens its temporary advanced view |
|
||||
| `Shift` + `Enter` on a supported widget | Opens its advanced view until it is closed |
|
||||
| `Esc` | Stops keyboard tile editing, cancels an active pointer or touch move/resize, or closes an open advanced view or board menu |
|
||||
|
||||
## Board editing
|
||||
|
||||
@@ -38,24 +33,13 @@ Commands at a grid boundary or at the tile's minimum size are ignored and announ
|
||||
|
||||
On touch screens, press and hold a tile before moving it; a quick tap does not start a drag. Resize handles accept direct touch input. During a pointer or touch move or resize, `Esc` cancels the interaction and restores the layout from before it started.
|
||||
|
||||
## Board editing
|
||||
|
||||
When a tile is focused in edit mode:
|
||||
|
||||
| Shortcut | Description |
|
||||
| -------------------- | --------------------------------------------------------------------------- |
|
||||
| `Enter` or `Space` | Starts or stops keyboard editing |
|
||||
| Arrow keys | Moves the focused tile within its current canvas, rail, or Container |
|
||||
| `Shift` + Arrow keys | Resizes from the tile's lower-right corner: Left/Up shrink; Right/Down grow |
|
||||
| `Esc` | Stops keyboard editing without undoing its changes |
|
||||
|
||||
Keyboard moves and resizes apply immediately. To move an item between a canvas, rail, or Container, drag it there or use **Move / resize item** for precise numeric placement.
|
||||
Commands at a grid boundary or at the tile's minimum size are ignored and announced instead of moving or shrinking the tile.
|
||||
|
||||
:::tip
|
||||
|
||||
Here `MOD` refers to the modifier key, which is `Ctrl` on Windows and `Cmd` on macOS.
|
||||
|
||||
Advanced view is available outside edit mode. Widgets without a distinct advanced experience adapt to their normal
|
||||
dimensions instead. A progress ring follows the pointer while the temporary advanced view is preparing to open.
|
||||
|
||||
:::
|
||||
|
||||
:::note
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"label": "Community",
|
||||
"position": 6,
|
||||
"position": 5,
|
||||
"collapsed": true,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
|
||||
@@ -6,6 +6,10 @@ tags:
|
||||
- Configuration
|
||||
---
|
||||
|
||||
import imageStartFromScratchStepServerSettings from "./img/onboarding-from-scratch-step-server-settings.png";
|
||||
import imageStartFromScratchStepAdminUser from "./img/onboarding-from-scratch-step-admin-user.png";
|
||||
import imageStartFromScratchStepExternalAdminGroup from "./img/onboarding-from-scratch-step-external-admin-group.png";
|
||||
import imageStartFromScratchStepFinish from "./img/onboarding-from-scratch-step-finish.png";
|
||||
import manageBoardNoApp from "./img/manage-board-no-app.png";
|
||||
import currentKeyboardTileEditing from "../management/boards/img/keyboard-tile-editing.png";
|
||||
import boardDesktopCurrent from "./img/onboarding-board-desktop-current.png";
|
||||
@@ -16,6 +20,10 @@ import editItemCurrent from "./img/onboarding-edit-item-current.png";
|
||||
import boardsHomeMobileMenu from "./img/onboarding-boards-home-mobile-menu.png";
|
||||
import moveResizeMenuCurrent from "./img/onboarding-move-resize-menu-current.png";
|
||||
import currentBoardMobileView from "./img/onboarding-board-mobile-current.png";
|
||||
import manageExportOldHomarr from "./img/old-homarr-export-data.png";
|
||||
import manageImportOldHomarrDropzone from "./img/old-homarr-import-dropzone.png";
|
||||
import manageImportOldHomarrChooseData from "./img/old-homarr-import-choose-imported-data.png";
|
||||
import manageImportDecryptionToken from "./img/old-homarr-import-decryption-token.png";
|
||||
|
||||
:::tip
|
||||
|
||||
@@ -27,50 +35,118 @@ Congratulations on installing Homarr. Now it's time to get yourself familiar wit
|
||||
|
||||
## Onboarding
|
||||
|
||||
Onboarding appears the first time Homarr starts. Its goal is to create a useful responsive board, not merely an
|
||||
administrator account. The setup uses one guided studio, so you can keep the safe defaults and finish quickly or review
|
||||
every detected service before anything is added to the board.
|
||||
The onboarding will show the first time you start Homarr.
|
||||
This process will help you with your first steps and adding some initial data, such as the administrator account.
|
||||
|
||||
:::note
|
||||
|
||||
If you see this screen repeatedly after restarting the Homarr container, your Docker mounting paths may be missing,
|
||||
If you see this screen repeatedly after you restart the Homarr container, your Docker mounting paths may be missing,
|
||||
incorrect or read-only. Read [our installation instructions](/docs/getting-started/installation/docker) again to fix this issue.
|
||||
|
||||
:::
|
||||
|
||||
### Restore or start fresh
|
||||
You can either import a previous dashboard using the ZIP archive or start from scratch:
|
||||
|
||||
SQLite installations can restore a complete Homarr database backup from the welcome screen. This restores current
|
||||
boards, people, apps, integrations, and settings. The pre-1.0 ZIP importer is no longer included.
|
||||

|
||||
|
||||
For a new installation, Homarr first creates either a credentials administrator or an administrator group matching your
|
||||
LDAP/OIDC provider. The browser that starts setup claims the first-run session; refreshes are safe, while another browser
|
||||
cannot change credentials or integrations during that setup.
|
||||
You may refresh anytime and continue from a different device - the onboarding is saved immediately on the server for your convenience.
|
||||
|
||||
### Setup studio
|
||||
### Starting from scratch
|
||||
|
||||
The studio keeps related choices together:
|
||||
Depending on your auth provider configuration you will either first see the admin user creation form or the external group creation form.
|
||||
If the credentials provider was selected, you'll be prompted to create an admin user for Homarr.
|
||||
This user cannot be deleted and has full control over Homarr and all of its boards.
|
||||
You must choose a username and password (both can later be changed).
|
||||
We enforce strict password requirements when you enter your password to protect your instance if you accidentally
|
||||
expose it to the internet.
|
||||
|
||||
1. **Essentials** sets the default language, color scheme, analytics preference, and the usual host, IP address, domain, or reverse-proxy path used by your services. URL suggestions can use separate ports, subdomains, or one path per service.
|
||||
2. **Discover** checks the actual database, Docker-compatible sockets, Kubernetes configuration, Workshop, and Assistant capabilities. Unavailable sources never block manual setup.
|
||||
3. **Connect** shows detected integrations and apps first, identifying whether each app came from an explicit [`homarr.*` or Homepage Docker label](/docs/integrations/docker-labels) or image matching. Select several integrations and apps at once, review every URL, and use the service-specific link to open its API-key settings when one is available. If a container has no published port or URL label, add its HTTP or HTTPS address before importing it.
|
||||
4. **Board** previews the Base layout, colors, widget radius, sidebars, selected apps, and widgets inferred from integrations or Docker labels. Choose 8 columns for a focused board, 10 for a balanced board, 12 for a wide board, or use the slider for a custom width up to 24 columns. Homarr still creates and reflows the protected Mobile layout automatically. A fast setup adds the integration-free Clock, Weather, and Bookmarks starter widgets.
|
||||
5. **Extend** explains and optionally configures Homarr Assistant, the community Workshop, and the separate Homarr MCP endpoint.
|
||||
6. **Review** tests the selected connections, then saves integrations, apps, widgets, settings, and the exact first board together. If any part fails, the database changes are rolled back and your form stays available to correct.
|
||||
<img src={imageStartFromScratchStepAdminUser} alt={"Screenshot form to create admin user"} width={300} />
|
||||
|
||||
Docker discovery reports each configured host separately. Results from reachable hosts remain selectable if another socket
|
||||
is unavailable, which is useful for remote Docker, rootless Docker, Synology, and mixed deployments. On Kubernetes or
|
||||
bare-metal installations without a Docker socket, enter services manually and use the same integration catalog.
|
||||
If you selected external providers (such as LDAP or OIDC), you'll be prompted to create an admin group.
|
||||
This group will be used to synchronize users from your provider to Homarr.
|
||||
When a user logs in through the provider, any of their groups that match groups in Homarr will be assigned to the user.
|
||||
The name of the group must match exactly the name of the group in the provider.
|
||||
|
||||
After setup, **Open my board** takes you directly to the configured board. The in-product tour then introduces edit mode,
|
||||
responsive layouts, widgets, and search without delaying completion.
|
||||
<img
|
||||
src={imageStartFromScratchStepExternalAdminGroup}
|
||||
alt={"Screenshot form to create external admin group"}
|
||||
width={300}
|
||||
/>
|
||||
|
||||
## Creating another board
|
||||
Next, you can configure some basic settings for Homarr. We recommend to leave all settings at default for most users.
|
||||
Here you can also disable analytics. Please note that our analytics are completely anonymous and fully compliant with GDPR and PECR.
|
||||
|
||||
<img
|
||||
src={imageStartFromScratchStepServerSettings}
|
||||
alt={"Screenshot of analytics and indexing settings in the onboarding"}
|
||||
width={300}
|
||||
/>
|
||||
|
||||
### Setting up integrations
|
||||
|
||||
After configuring server settings, the onboarding guides you through setting up your integrations.
|
||||
|
||||
#### Docker service discovery
|
||||
|
||||
If Docker is available (socket mounted), Homarr automatically detects running containers and matches them to known integration types.
|
||||
Matching integrations are pre-selected in the integration list, saving you from manually picking each one.
|
||||
|
||||
#### URL templating
|
||||
|
||||
You can define a base URL template to auto-fill integration URLs in bulk.
|
||||
Two modes are supported:
|
||||
|
||||
- **Subdomain mode** - e.g. `{app}.example.com` where `{app}` is replaced with the integration name
|
||||
- **Port mode** - e.g. `192.168.1.1:{port}` where `{port}` is replaced with the integration's default port
|
||||
|
||||
#### API key helper links
|
||||
|
||||
For integrations that require API keys (such as Sonarr, Radarr, SABnzbd, or Seerr), a helper link opens the relevant settings page in the target application so you can quickly copy your key.
|
||||
|
||||
#### Creating apps from Docker containers
|
||||
|
||||
You can optionally auto-create bookmark apps for all discovered Docker containers, making them immediately available on your board.
|
||||
|
||||
:::note
|
||||
|
||||
Release providers and Search.ch are configured directly in their widgets instead of the integrations page.
|
||||
|
||||
:::
|
||||
|
||||
After this step, you finished the onboarding.
|
||||
The onboarding can no longer be started again.
|
||||
From here you can click on one of the provided links to continue your journey.
|
||||
We recommend you to continue with the section [creating your first board on this page](#creating-your-first-board).
|
||||
|
||||
<img src={imageStartFromScratchStepFinish} alt={"Screenshot of the finish onboarding process with links"} width={300} />
|
||||
|
||||
### Importing a ZIP from version before 1.0.0
|
||||
|
||||
This is recommended when you used Homarr before and want to re-import your data.
|
||||
It is important that you first migrate correctly using [the list of breaking changes for 1.0](/blog/2024/09/23/version-1.0).
|
||||
|
||||
<img src={manageExportOldHomarr} alt="Export from old Homarr" style={{ maxWidth: 500 }} />
|
||||
|
||||
**How to export and import data**
|
||||
|
||||
1. Navigate to your old Homarr, open your management pages, open the tools submenu and open `Migrate to 1.0`.
|
||||
2. Check the contents that you want to export. We recommend you to export all data.
|
||||
3. Click on the export data button
|
||||
4. Save the downloaded ZIP file on your device. If you selected "integrations" or "users", you also get an encryption / decryption key.
|
||||
Copy it and keep it safe; you need it later in the import flow.
|
||||
5. Go to your new Homarr 1.0 or later instance and start the onboarding. Choose `Import from Homarr before 1.0`.
|
||||
6. Drag and drop your previously downloaded ZIP archive into the file upload: <br/><img src={manageImportOldHomarrDropzone} style={{ maxWidth: 500 }} />
|
||||
7. Next, choose the data that you want to import and how to import it. Old Homarr screen sizes are imported as responsive layouts on the same board: the narrowest becomes **Mobile**, the widest becomes **Base**, and layouts between them become custom layouts. Homarr normalizes duplicate breakpoints automatically, so you can review the imported board in Board Settings and adjust the layouts there.<br/><img src={manageImportOldHomarrChooseData} style={{ maxWidth: 500 }} />
|
||||
For example, legacy `sm`, `md`, and `lg` layouts at `0px`, `800px`, and `1400px` become Mobile, a custom layout, and Base. The imported board keeps each layout's positions, names, and column counts where possible so you can review the result before saving. If a board has one saved layout, Homarr keeps it as Base and generates Mobile from it; a board without saved layouts receives Mobile at `0px` with 3 columns and Base at `768px` with 10 columns.
|
||||
8. If you previously exported integrations or users, you will be asked to decrypt the data safely using your obtained encryption token. Paste into the modal and confirm your action.<br/><img src={manageImportDecryptionToken} style={{ maxWidth: 500 }} />
|
||||
9. Next, you can [follow the same setup as when starting from scratch](#starting-from-scratch). After you completed the onboarding, all of your data will be migrated and available.
|
||||
|
||||
## Creating your first board
|
||||
|
||||
In Homarr, you organize your apps and widgets on boards.
|
||||
Boards use fixed-size tiles so the same widget keeps the same proportions on every screen.
|
||||
Drag, resize, and section controls are available in edit mode.
|
||||
Onboarding already created the first board. To create another one, navigate to `Manage` > `Boards` > `New board`:
|
||||
Let's create your first board by navigating here: `Manage` > `Boards` > `New board`:
|
||||

|
||||
|
||||
A board must always have a name and can either be public or private.
|
||||
@@ -78,7 +154,7 @@ Public boards are also accessible for users, that are not logged in ([see access
|
||||
|
||||

|
||||
|
||||
Congratulations - you've just created another board. You can see the boards you have access to on this page:
|
||||
Congratulations - you've just created your first board. You can see the boards you have access to on this page:
|
||||
|
||||
<figure>
|
||||
<img
|
||||
@@ -132,8 +208,8 @@ Here you have the following options in the header:
|
||||
- **Search**: Here you can search for data. You can search for integrations, boards, actions, sites and much more. Some actions may be context dependent (eg. will only show when certain widgets are on your page)
|
||||
- **Edit board**: Clicking this button will toggle edit mode. In this mode you can edit the contents of your board.
|
||||
- **Settings**: Clicking this button will redirect you to a settings page where you can adjust the board settings (page title, colors, ...).
|
||||
- **Profile**: This avatar will be visible on every page. Its menu includes the board switcher; press `Shift+C` to open
|
||||
it directly. The current board is left out of the results.
|
||||
- **Board switcher**: Use this button to open another board that you can access.
|
||||
- **Profile**: This avatar will be visible on every page. Clicking it will show a menu with actions.
|
||||
|
||||
When entering the edit mode, you'll get some additional buttons to work with:
|
||||
|
||||
@@ -151,7 +227,7 @@ When entering the edit mode, you'll get some additional buttons to work with:
|
||||
|
||||
Outside edit mode, right-click a widget for its live status, refresh, settings, quick options, and supported actions.
|
||||
Hold `Shift` while hovering a widget that supports advanced view to expand it in place while the rest of the board
|
||||
stays visible but dimmed. Release `Shift` to close it. Hold `Shift` plus `Ctrl` on Windows/Linux or `Cmd` on macOS to
|
||||
stays visible but dimmed. Release `Shift` to close it. You can also focus that widget and press `Shift` + `Enter` to
|
||||
keep its advanced view open until you close it. Widgets without a distinct advanced experience adapt in place.
|
||||
|
||||
To continue, click **New item** in the add-content menu. New content is placed at the first available position on the main
|
||||
@@ -198,13 +274,9 @@ In the modal you can edit the app that you want to display:
|
||||
|
||||
<figure>
|
||||
<img src={editItemCurrent} alt="Current Edit item modal for an App tile" style={{ maxWidth: "620px" }} />
|
||||
<figcaption>
|
||||
The Edit item workspace previews option changes as you configure the tile. Save changes to apply them to the board.
|
||||
</figcaption>
|
||||
<figcaption>Use the Edit item modal to choose the app and configure how its tile is displayed.</figcaption>
|
||||
</figure>
|
||||
|
||||
When adding a widget, set its initial grid width and height directly in the preview. These size controls are only shown before the widget is added.
|
||||
|
||||
As soon as you save your changes, the tile on the board will refresh and display your app.
|
||||
Resize from any visible edge or corner handle. For a Container, activate its top-left grip before using its resize handles.
|
||||
Press and hold a handle, move the pointer, and release it when the tile has the size you want.
|
||||
@@ -373,7 +445,7 @@ Each page highlights the list view and the create action so you know where to fi
|
||||
|
||||
### Board tour
|
||||
|
||||
When you first open a board, a tour highlights the board header elements: search, edit mode, settings, and the user menu.
|
||||
When you first open a board, a tour highlights the board header elements: search, edit mode, settings, board switcher, and user menu.
|
||||
|
||||
### Tour controls
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 68 KiB |
@@ -1,95 +0,0 @@
|
||||
---
|
||||
title: "Docker labels"
|
||||
description: "Discover apps, integrations, and widgets from container labels during Homarr onboarding."
|
||||
---
|
||||
|
||||
# Docker label discovery
|
||||
|
||||
During onboarding, Homarr scans running containers on every reachable
|
||||
[Docker-compatible host](/docs/integrations/docker). Labels let you provide exact app metadata instead of relying on
|
||||
image-name matching. You can review and deselect every result before Homarr creates anything.
|
||||
|
||||
A container with valid Homarr labels takes priority over image-based discovery. Containers without valid labels can
|
||||
still be suggested from their image and published ports.
|
||||
|
||||
## Homarr labels
|
||||
|
||||
`homarr.name` and `homarr.href` are required for label discovery. All other labels are optional.
|
||||
|
||||
| Label | Purpose |
|
||||
| -------------------- | -------------------------------------------------------------------------- |
|
||||
| `homarr.name` | App or integration display name |
|
||||
| `homarr.href` | Address opened by the app and used as the suggested integration address |
|
||||
| `homarr.group` | Container section that receives the selected app and compatible widget |
|
||||
| `homarr.icon` | App icon URL |
|
||||
| `homarr.description` | App description |
|
||||
| `homarr.ping` | Address used for app status checks |
|
||||
| `homarr.id` | Stable source identifier; defaults to the Docker container ID |
|
||||
| `homarr.board` | Intended board name; see [Board targeting](#board-targeting) |
|
||||
| `homarr.integration` | Homarr integration kind, for example `sonarr` |
|
||||
| `homarr.widget` | Homarr widget kind to add when it supports the selected integration |
|
||||
| `homarr.hide` | Excludes the container when this label is present, regardless of its value |
|
||||
|
||||
Unknown integration and widget kinds are ignored. Do not put API keys, passwords, or other secrets in labels. Enter
|
||||
credentials in onboarding, where Homarr stores them using its normal encrypted integration-secret flow.
|
||||
|
||||
## Compose example
|
||||
|
||||
```yaml
|
||||
services:
|
||||
sonarr:
|
||||
image: lscr.io/linuxserver/sonarr:latest
|
||||
labels:
|
||||
homarr.name: "Sonarr"
|
||||
homarr.href: "http://server.local:8989"
|
||||
homarr.group: "Media"
|
||||
homarr.icon: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/sonarr.svg"
|
||||
homarr.description: "TV library"
|
||||
homarr.ping: "http://server.local:8989"
|
||||
homarr.integration: "sonarr"
|
||||
homarr.widget: "calendar"
|
||||
```
|
||||
|
||||
Use an address that is reachable where it is needed. A Docker service name can work for server-side integration
|
||||
requests, while an app link opened in a browser usually needs a LAN hostname, IP address, or reverse-proxy address.
|
||||
Onboarding accepts these self-hosted address formats directly and only requires the field to be non-empty.
|
||||
|
||||
## Homepage label fallback
|
||||
|
||||
When `homarr.name` is absent, onboarding also understands these Homepage labels:
|
||||
|
||||
- `homepage.name`
|
||||
- `homepage.href`
|
||||
- `homepage.group`
|
||||
- `homepage.icon`
|
||||
- `homepage.description`
|
||||
|
||||
`homepage.name` and `homepage.href` are required for the fallback. Homarr does not combine partial Homarr metadata with
|
||||
Homepage metadata: once `homarr.name` is present, use `homarr.*` labels for the remaining fields too.
|
||||
|
||||
## What onboarding creates
|
||||
|
||||
In **Connect**, label-discovered apps and integrations appear before image-based suggestions. You can import multiple
|
||||
services in one pass and edit every suggested address. For image-matched apps, Homarr uses the detected service type
|
||||
when building host, subdomain, or reverse-proxy path suggestions.
|
||||
|
||||
When setup completes, Homarr rechecks the selected container sources and then:
|
||||
|
||||
- creates an app with its name, address, icon, description, and ping address;
|
||||
- creates the requested integration and links its app after you provide required credentials;
|
||||
- places grouped apps in a board container named by `homarr.group`;
|
||||
- places ungrouped apps in the main board area;
|
||||
- adds `homarr.widget` only when the widget kind is valid and compatible with the selected integration;
|
||||
- keeps image-based discovery available for unlabeled containers.
|
||||
|
||||
If a selected container disappears, targets another board, or requests an incompatible widget, setup finishes with a
|
||||
warning instead of creating an invalid board item.
|
||||
|
||||
## Board targeting
|
||||
|
||||
On a fresh installation with one seeded `dashboard` board, one unique selected `homarr.board` value can name that first
|
||||
board. Otherwise, the label must match the board selected in onboarding. Services labeled for another board are skipped
|
||||
and reported in the completion warning.
|
||||
|
||||
Onboarding configures one board at a time. Run normal board management after setup if you want to distribute services
|
||||
across several boards.
|
||||
@@ -24,11 +24,12 @@ import { dockerContainersWidget } from "@site/docs/widgets/docker-containers";
|
||||
]}
|
||||
/>
|
||||
|
||||
## Connecting Docker to Homarr
|
||||
## Enabling the Docker integration
|
||||
|
||||
Docker connections come directly from Homarr's environment variables. They are automatically available in the Docker management table and Docker widget; you do not create a Docker integration under **Management → Integrations**.
|
||||
|
||||
If Homarr runs directly on a host with Docker installed, it uses `/var/run/docker.sock` by default. When Homarr runs in a container, mount the [Docker socket](https://docs.docker.com/engine/security/protect-access/) into it so Homarr can interact with Docker.
|
||||
This integration must first be enabled.
|
||||
If you directly run on the host system and you have docker installed, it will be enabled by default.
|
||||
Otherwise, you'll need to mount the [docker socket](https://docs.docker.com/engine/security/protect-access/) to the Homarr container,
|
||||
so Homarr can interact with the Docker service.
|
||||
|
||||
In Docker Compose, you can simply add this line:
|
||||
|
||||
@@ -121,89 +122,9 @@ services:
|
||||
DOCKER_SOCKET_PATHS: /var/run/docker.sock,/var/run/podman.sock
|
||||
```
|
||||
|
||||
For remote socket proxies, list the hostnames and matching ports in the same order:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
DOCKER_HOSTNAMES: docker-proxy-home,docker-proxy-lab
|
||||
DOCKER_PORTS: 2375,2375
|
||||
```
|
||||
|
||||
Homarr automatically discovers each comma-separated entry as a separate Docker environment. All environments are selected by default; the Docker widget lets you select one or more without any additional setup.
|
||||
|
||||
:::warning[Plaintext TCP]
|
||||
Plaintext Docker TCP can grant unauthenticated control of the Docker host. Prefer a local socket, a least-privilege socket proxy on a private Docker network, or TLS.
|
||||
:::
|
||||
|
||||
### Advanced endpoint configuration
|
||||
|
||||
Most users should use the comma-separated variables above. Use `DOCKER_ENDPOINTS` only when you need custom names, TLS, or restricted capabilities. It is a JSON array where every endpoint declares its identity, transport, and capabilities:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
homarr:
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./docker-ca.pem:/run/secrets/docker-ca.pem:ro
|
||||
environment:
|
||||
DOCKER_ENDPOINTS: >-
|
||||
[
|
||||
{
|
||||
"id": "local",
|
||||
"name": "Local Docker",
|
||||
"kind": "docker",
|
||||
"transport": { "type": "socket", "path": "/var/run/docker.sock" },
|
||||
"capabilities": ["inventory", "logs", "lifecycle", "remove"]
|
||||
},
|
||||
{
|
||||
"id": "remote-read-only",
|
||||
"name": "Production inventory",
|
||||
"kind": "docker",
|
||||
"transport": {
|
||||
"type": "tls",
|
||||
"host": "docker.example.com",
|
||||
"port": 2376,
|
||||
"caPath": "/run/secrets/docker-ca.pem"
|
||||
},
|
||||
"capabilities": ["inventory", "logs"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
The stable `id` keeps containers from different endpoints distinct. `name` is shown in the interface, and `kind` can be `docker` or `podman`. Every endpoint must include the `inventory` capability; optionally add `logs`, `lifecycle`, and `remove`. Homarr hides or disables actions the endpoint does not allow and also enforces the same capabilities on the server.
|
||||
|
||||
Supported transports are:
|
||||
|
||||
- `socket`: an absolute in-container `path`
|
||||
- `tls`: `host`, `port`, and an absolute `caPath`; add both `certPath` and `keyPath` for mutual TLS
|
||||
- `tcp`: `host`, `port`, and the explicit acknowledgement `allowInsecure: true`
|
||||
|
||||
The structured configuration refuses TCP unless `allowInsecure` is explicitly set to `true`. When `DOCKER_ENDPOINTS` is set, it takes precedence over `DOCKER_SOCKET_PATHS`, `DOCKER_HOSTNAMES`, and `DOCKER_PORTS`.
|
||||
|
||||
### Managing your Docker containers within Homarr
|
||||
|
||||
Homarr allows you to interact with Docker containers running on your system.
|
||||
The collapsed **Assisted Docker setup** helper optionally compares visible containers with your existing Homarr apps and integrations. Open it only when you want setup suggestions; it never blocks normal container management. Every service stays distinct by Docker endpoint and container ID, including multiple containers that use the same integration type.
|
||||
|
||||
Homarr can show a service as:
|
||||
|
||||
- **Ready to connect** when its image or name matches a supported integration
|
||||
- **New service** when it can be added as an app but no integration is recognized
|
||||
- **Already represented** or **Linked** when an app or integration already matches
|
||||
- **Address changed** when a service appears to match an integration but its reachable address has changed
|
||||
|
||||
Set the usual server address once, then choose whether services normally use different ports, subdomains, or paths. Homarr uses that pattern to prefill every detected service. Each suggestion has one editable URL field with the generated and Docker-reported addresses available as autocomplete options, so you can select a suggestion or type any address directly.
|
||||
|
||||
:::info[Review before changing]
|
||||
Service discovery is read-only. Homarr never creates or changes an app or integration automatically. **Set up integration** opens the standard prefilled integration form, while **Create app** opens the existing Docker app form, so you can review and confirm the final setup.
|
||||
:::
|
||||
|
||||
Suggestions appear as a compact task list. Expand an item only when you want to edit its URL or inspect relevant setup statuses. Use the filters to focus on services that need attention or those already represented in Homarr. Refresh runs discovery again. Dismissed suggestions are stored in the current browser using their endpoint-qualified service identity and can be restored from the same helper. If multiple existing items could match, Homarr marks the result as ambiguous and does not choose one automatically.
|
||||
|
||||
Compact status icons summarize the setup information Homarr can act on. Unknown and non-applicable runtime layers are omitted to keep the helper focused; use the integration form's connection test or the relevant board action for live diagnostics.
|
||||
|
||||
If one Docker endpoint is unavailable, Homarr reports that endpoint while continuing to show services from healthy endpoints. An unavailable endpoint is not treated as an empty Docker installation.
|
||||
|
||||
Each container row has an action menu with the following options:
|
||||
|
||||
- **View logs** - opens a dedicated logs page with a live terminal
|
||||
@@ -227,10 +148,6 @@ This opens a dedicated page with an xterm.js terminal that streams the container
|
||||
The Docker management table supports optional **CPU usage** and **Memory usage** columns.
|
||||
These columns are hidden by default and can be toggled on from the table column visibility settings.
|
||||
|
||||
#### Choosing Docker environments in the widget
|
||||
|
||||
The Docker widget can show one or more configured Docker environments. Leave **Docker environments** empty to include every endpoint, which is also the default for existing widgets. Selecting one or more entries limits container and statistics requests to those endpoints.
|
||||
|
||||
#### Add apps from containers
|
||||
|
||||
Homarr also lets you add apps from containers. You can simply select all containers you want to add an app for and then click the `Add to Homarr` button.
|
||||
@@ -243,13 +160,11 @@ They are then available in the app list and can be used as normally added apps.
|
||||
|
||||

|
||||
|
||||
### Container labels
|
||||
### Configuration
|
||||
|
||||
`homarr.hide` excludes a container from the Docker Containers widget, tools page, and onboarding discovery whenever the
|
||||
label is present.
|
||||
Currently Homarr supports one label for configuration of the containers:
|
||||
|
||||
During first-run onboarding, Homarr can also discover apps, integrations, groups, and compatible widgets from
|
||||
`homarr.*` labels, with Homepage labels as a fallback. See the complete [Docker label reference](/docs/integrations/docker-labels).
|
||||
- **homarr.hide:** If set to any value, the container will be hidden from the Docker Containers widget and tools page.
|
||||
|
||||
### Security
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ import { kubernetesIntegration } from ".";
|
||||
|
||||
Homarr provides a user-friendly Kubernetes dashboard that offers an overview of your cluster’s resources in a clear and structured interface. It allows users to view key Kubernetes objects, including `Pods`, `Services`, `Ingresses`, `Nodes`, `ConfigMaps`, `Secrets`, `Namespaces`, and `Volumes`, as well as basic metrics when the Kubernetes Metrics Server is enabled.
|
||||
|
||||
Docker service discovery does not convert Kubernetes resources into Homarr apps or integrations. Kubernetes remains a separate, read-only operational view, so a cluster resource is never changed or linked automatically.
|
||||
|
||||
:::warning[Read only]
|
||||
This integration is read-only, meaning Homarr does not modify or manage cluster resources but serves as an informational tool to enhance visibility into your Kubernetes environment.
|
||||
:::
|
||||
@@ -27,7 +25,7 @@ This integration is read-only, meaning Homarr does not modify or manage cluster
|
||||
Before deploying Homarr in Kubernetes, ensure the following prerequisites are met:
|
||||
|
||||
- Kubernetes (Version >= 1.24.0)
|
||||
- Metrics Server ([install from here](https://github.com/kubernetes-sigs/metrics-server)) is optional. Without it, Homarr still shows cluster inventory, node status, and reserved capacity, and identifies live CPU and memory usage as unavailable.
|
||||
- Metrics Server ([Install from here](https://github.com/kubernetes-sigs/metrics-server))
|
||||
- RBAC Enabled (Required for Homarr Helm chart)
|
||||
|
||||
### Configuring RBAC in Helm Chart
|
||||
@@ -47,25 +45,6 @@ Enabling RBAC in the Helm chart creates the necessary Kubernetes Role-Based Acce
|
||||
- ClusterRole & ClusterRoleBinding: Extends the same permissions cluster-wide for additional flexibility.
|
||||
:::
|
||||
|
||||
### Multiple Kubernetes contexts
|
||||
|
||||
When Homarr runs outside a cluster, set `KUBECONFIG` explicitly so Homarr loads the contexts from the referenced kubeconfig file or files. Mount the kubeconfig read-only and set the standard `KUBECONFIG` environment variable to its path inside the Homarr container:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
homarr:
|
||||
volumes:
|
||||
- ./kubeconfig:/app/config/kubeconfig:ro
|
||||
environment:
|
||||
KUBECONFIG: /app/config/kubeconfig
|
||||
```
|
||||
|
||||
The Kubernetes management pages include a context selector. The selected context is stored in the page URL, so each React Query cache entry and every API request remains context-specific. Resources with the same namespace and name in two contexts therefore remain separate.
|
||||
|
||||
Context status is checked independently. If one context is unavailable, the selector still lists healthy contexts. A context is shown as **Metrics unavailable** when its Kubernetes API is reachable but Metrics Server is not; inventory remains available without live CPU and memory usage.
|
||||
|
||||
When Homarr runs inside Kubernetes without `KUBECONFIG`, it keeps using the in-cluster service account as the single default context.
|
||||
|
||||
### Homarr Dashboard Overview
|
||||
|
||||
Once deployed, Homarr provides a dashboard displaying Kubernetes metrics and resource information. The main page includes:
|
||||
|
||||
@@ -72,34 +72,6 @@ API key authentication enables programmatic access for:
|
||||
- Custom dashboards and monitoring tools
|
||||
- Container orchestration systems that need to sync app definitions
|
||||
|
||||
## Permissions
|
||||
|
||||
API keys act as the user that created them, so every request is subject to that user's group
|
||||
permissions. A call that the user is not allowed to make returns `FORBIDDEN`, and list endpoints
|
||||
return only the records the user may see rather than failing.
|
||||
|
||||
### App endpoints require app management access
|
||||
|
||||
`GET /api/apps`, `GET /api/apps/paginated` and `GET /api/apps/search` (tRPC `app.all`,
|
||||
`app.getPaginated`, `app.search`) require the **modify all apps** permission. They previously
|
||||
returned the full app catalogue — including internal URLs — to any authenticated user.
|
||||
|
||||
If a script that used to work now returns `FORBIDDEN`, grant **modify all apps** to a group the key
|
||||
owner belongs to, or switch to `GET /api/apps/selectable`, which returns the reduced fields used by
|
||||
board pickers.
|
||||
|
||||
### Integration endpoints are filtered per key
|
||||
|
||||
`integration.all`, `integration.allThatSupportSearch`, `integration.allOfGivenCategory` and
|
||||
`integration.search` filter server side. Without a global integration permission, a key only
|
||||
receives the integrations its owner has been granted access to, so responses may contain fewer
|
||||
records than before rather than returning an error. `integration.search` applies its `limit` after
|
||||
filtering, so a scoped key still receives a full page of results.
|
||||
|
||||
Each returned integration carries a `permissions` object — check `hasUseAccess` before reading data
|
||||
and `hasInteractAccess` before performing actions; `false` means the key owner lacks that level for
|
||||
that integration, not that the call failed.
|
||||
|
||||
## Board Automation
|
||||
|
||||
The OpenAPI and MCP APIs include board management endpoints for common automation workflows:
|
||||
@@ -114,34 +86,6 @@ Board layout responses identify each layout with a `mobile`, `base`, or `custom`
|
||||
|
||||
Use the interactive API reference to inspect the exact request body for each endpoint under the `boards` and `settings` tags.
|
||||
|
||||
## Docker action payload migration
|
||||
|
||||
Docker lifecycle and removal actions now identify each container with both its Docker endpoint and container ID. This is a
|
||||
breaking change for tRPC and MCP clients that previously sent an `ids` array to the start, stop, restart, or remove action.
|
||||
|
||||
Replace the old payload:
|
||||
|
||||
```json
|
||||
{ "ids": ["container-id"] }
|
||||
```
|
||||
|
||||
with endpoint-qualified targets:
|
||||
|
||||
```json
|
||||
{
|
||||
"targets": [{ "endpointId": "local", "id": "container-id" }]
|
||||
}
|
||||
```
|
||||
|
||||
Read `endpointId` and `id` from `docker.getContainers` (MCP tool `docker_getContainers`) immediately before the action.
|
||||
Pass those pairs unchanged to `docker.startAll`, `docker.stopAll`, `docker.restartAll`, or `docker.removeAll`; their MCP tool
|
||||
names use underscores, such as `docker_restartAll`. Endpoint qualification prevents identical container IDs on different
|
||||
Docker or Podman endpoints from targeting the wrong service.
|
||||
|
||||
`docker.getContainers` also accepts an optional `endpointIds` array. Omit it or pass an empty array to query every configured
|
||||
Docker environment, or pass one or more IDs returned by `docker.getEndpoints` to query only those Dockerode clients. The
|
||||
Docker widget uses the same behavior for its environment selector.
|
||||
|
||||
## Partial integration failures
|
||||
|
||||
Multi-integration widget queries keep data from integrations that remain available. Calendar and media-server results include
|
||||
|
||||
@@ -51,17 +51,6 @@ Note that the ping URL only accepts `http://` and `https://` URLs — custom URI
|
||||
|
||||
## List of apps
|
||||
|
||||
On the apps management page you find all apps that exist in your Homarr instance. Search by name or URL, open an app
|
||||
directly, or use its row actions to edit and delete it. A filtered search can be cleared without leaving the page.
|
||||
On the apps management page you find all apps that exist in your Homarr instance. You can edit and delete them.
|
||||
|
||||
### Accessing the page
|
||||
|
||||
The `/manage/apps` page is reachable when you can manage all apps (`app-modify-all`) or create apps (`app-create`):
|
||||
|
||||
- **Manage all (`app-modify-all`)**: the full list of apps is shown and searchable, and each row can be edited.
|
||||
- **Create only (`app-create`)**: the page shows an empty "create an app" state instead of the list, so the names and
|
||||
URLs of existing apps are never disclosed. The create action is still available.
|
||||
- **Neither**: the page is not reachable and returns a not-found page.
|
||||
|
||||
Creating an app requires `app-create`, editing requires `app-modify-all`, and deleting requires `app-full-all`;
|
||||
board permissions are not required.
|
||||

|
||||
@@ -1,71 +1,362 @@
|
||||
---
|
||||
description: Configure and use Homarr Assistant.
|
||||
tags:
|
||||
- Assistant
|
||||
- AI
|
||||
- Administration
|
||||
- Security
|
||||
---
|
||||
|
||||
import assistantConfigImage from "./img/assistant-config.png";
|
||||
import assistantWidgetImage from "./img/assistant-widget.png";
|
||||
|
||||
# Assistant
|
||||
|
||||
Homarr Assistant can answer questions and manage Homarr for you. It uses the signed-in user's permissions and asks for
|
||||
approval before making changes.
|
||||
Administrators can configure the instance-wide AI assistant from **Management → Assistant**.
|
||||
It is available to signed-in users from the user menu, with `Shift + A`, and from the
|
||||
`Cmd/Ctrl + K` command menu. **Ask Homarr Assistant** is always available as a local result in the default
|
||||
search. Selecting it with an existing query sends that query in the background; selecting it without a
|
||||
query turns the command-menu search field into a prompt composer. Press
|
||||
`Enter` to send the prompt in the background without opening the conversation. The search field receives
|
||||
focus whenever the command menu opens, including when it is opened by an assistant tool. Sending from the
|
||||
command menu preserves any draft in the expanded conversation. The local result is temporarily disabled while
|
||||
the assistant is responding or waiting for an approval.
|
||||
|
||||

|
||||
Availability follows the setup state. Signed-out users do not get an assistant action in the user menu or from
|
||||
`Shift + A`; the always-visible command-menu result is disabled and says **Sign in**. If the provider connection or
|
||||
model is configured but **Enable Assistant** is off, the user-menu and `Shift + A` entry points do not open the
|
||||
assistant, while `Cmd/Ctrl + K` keeps its result visible but disabled with an explanation that an administrator must
|
||||
configure and enable it. Saving a working connection, model, and enabled state makes all three entry points
|
||||
available again.
|
||||
|
||||
## Set it up
|
||||
While a response is running, a compact activity bar appears at the bottom of the page and the user-menu
|
||||
avatar shows a thinking indicator. The activity bar can be opened to follow the response. Completed
|
||||
responses add an unread count to the user menu until they are opened or dismissed. The expanded
|
||||
conversation has a minimize action that returns it to the activity bar without interrupting a response.
|
||||
Its adjacent close action hides the panel without leaving an activity bar behind. Both window actions use
|
||||
subtle buttons with an icon, tooltip, and accessible label.
|
||||
The transcript follows streamed output while it is already at the bottom, including fast responses. Scrolling
|
||||
up releases that follow state until **Scroll to latest** is selected or the user returns to the bottom.
|
||||
|
||||
Open **Management → Assistant**.
|
||||
`Shift + A` opens the compact conversation panel directly. The shortcut is ignored while typing in text
|
||||
fields, selectors, and editable content.
|
||||
|
||||

|
||||
The **Assistant** board widget embeds the same persisted conversation and full interaction surface directly
|
||||
on a dashboard. By default it follows the current conversation. Widget settings can instead pin a specific
|
||||
conversation; the conversation picker stays hidden until **Pinned conversation** is selected.
|
||||
If the pinned conversation was deleted or cannot be opened, the widget shows an **Open conversation** recovery action
|
||||
and retry state instead of silently losing the widget content or falling back to another thread. **Open conversation**
|
||||
retries the same pinned thread after a failed switch.
|
||||
|
||||
1. Select a provider and check its API URL.
|
||||
2. Enter an API key when required, then save the connection.
|
||||
3. Refresh the model list and select a model.
|
||||
4. Enable the assistant and save.
|
||||
<img
|
||||
src={assistantWidgetImage}
|
||||
alt="Assistant widget embedded on a dashboard with its conversation header, automatic approvals control, model selector, and composer"
|
||||
style={{ maxWidth: "100%" }}
|
||||
/>
|
||||
|
||||
Choose a provider preset or use **Custom endpoint** with an OpenAI-compatible Chat Completions API. The
|
||||
[Homarr provider](../workshop/homarr-provider) is the default and needs no administrator API key. The Homarr team pays
|
||||
for a limited number of Assistant uses for each user every day; users sign in to the Community Workshop to use them.
|
||||
The current preview capture shows the board-embedded surface, including its conversation header, automatic-approval
|
||||
control, model and thinking controls, and composer. These controls operate on the same persisted thread as the
|
||||
floating assistant while keeping this surface's draft and focus independent.
|
||||
|
||||
:::info Local model servers
|
||||
The Shift+A panel is non-modal: opening it focuses its composer, but it does not capture focus from the board.
|
||||
Users can continue typing in an Assistant widget or interacting with other dashboard controls while the panel
|
||||
is open. Every visible assistant surface keeps its own draft, attachment list, selection, and focus while sharing
|
||||
the same persisted conversation. Switching conversations restores the draft that belongs to that surface and
|
||||
conversation instead of copying text into another widget or the floating panel.
|
||||
|
||||
Inside a container, `localhost` means the Homarr container. Use the model server's container name, local DNS name, or
|
||||
host gateway address.
|
||||
When the assistant is unavailable, its command-menu result remains visible but disabled and explains that
|
||||
an administrator must configure and enable it.
|
||||
|
||||
The assistant uses the assistant-ui runtime for **AI SDK v7**. Homarr tracks the newest mutually compatible
|
||||
package set permitted by its supply-chain release-age policy. Persisted message rows can still report the upstream
|
||||
codec name `ai-sdk/v6`; assistant-ui's current v7 adapter deliberately retains that identifier for its compatible
|
||||
UI-message wire format. It does not mean the application is running AI SDK v6.
|
||||
|
||||
## Configure a model provider
|
||||
|
||||
Homarr includes presets for OpenRouter, OpenAI, Anthropic Claude, Google Gemini, xAI, Groq, Mistral AI,
|
||||
DeepSeek, Together AI, Ollama, and LM Studio. Provider icons make the presets easy to identify. A custom
|
||||
preset connects any other service that implements the OpenAI Chat Completions API.
|
||||
|
||||
The provider list also shows **Homarr — Coming soon** as a disabled option.
|
||||
|
||||
The management page presents setup as three compact sections: provider and endpoint, credentials, then
|
||||
model and availability. A status summary at the top shows whether the connection is ready, whether a key
|
||||
is protected, and which model is selected. Redundant active-state badges are omitted when the setup is ready.
|
||||
|
||||
<img
|
||||
src={assistantConfigImage}
|
||||
alt="Assistant configuration page showing provider, credentials, model, and availability sections"
|
||||
style={{ maxWidth: "100%" }}
|
||||
/>
|
||||
|
||||
The screenshot shows the configuration surface with no provider key entered; the assistant remains disabled until a
|
||||
working provider, model, and enabled state are saved.
|
||||
|
||||
1. Choose a provider preset or **Custom endpoint**, then confirm the API base URL. It usually ends in `/v1`.
|
||||
2. Add an API key if the service requires bearer authentication. Additional authentication or routing
|
||||
headers are available in the collapsed advanced section.
|
||||
3. Save the provider connection.
|
||||
4. Refresh the model list and select a discovered model. Homarr displays the friendly model name but
|
||||
stores and sends the provider's exact model ID. Context-window sizes use compact labels such as `1M`.
|
||||
5. Enable the assistant and save the configuration.
|
||||
|
||||
Changing the provider or base URL immediately resets the unsaved credential/header draft, selected model, and
|
||||
enabled state, and puts the model section into a **save connection first** state. Saving that destination change also
|
||||
clears stored credentials for the previous destination. Changing the discovery path resets the selected model and
|
||||
enabled state; save the new path before relying on its discovery results. For an unsaved provider or base-URL change,
|
||||
the model controls, **Refresh models**, web-search switch, enable switch, and **Save assistant** remain unavailable.
|
||||
After the connection is saved, refresh the model list and select a model; if discovery is blank or returns no models,
|
||||
the manual model-ID field is the supported fallback. Only then enable the assistant and save the configuration.
|
||||
|
||||
The model discovery path defaults to `/models` and is appended to the API base URL. Leave the custom path blank when
|
||||
the service does not expose a usable discovery endpoint. A manual model ID can still be used when discovery is
|
||||
unavailable or returns no models.
|
||||
|
||||
API keys and additional header values are encrypted in the Homarr database. They are decrypted only on
|
||||
the server for discovery and model requests, and they are never returned to the browser. Changing the
|
||||
provider or base URL, then saving the connection, clears existing credentials. This prevents credentials for one
|
||||
service from being sent to another endpoint. Additional headers are write-only; saving them replaces the complete
|
||||
stored header set. Use the
|
||||
explicit clear/remove flow when you want to remove previously saved headers.
|
||||
|
||||
When a key is already stored, the page shows its protected state instead of an ambiguous empty password
|
||||
field. Removing the API key requires explicit confirmation. Save that removal before entering and saving a
|
||||
different key.
|
||||
|
||||
:::info Container networking
|
||||
|
||||
For a containerized Homarr installation, `localhost` refers to the Homarr container. Use a resolvable
|
||||
container name, internal DNS name, or host gateway address to reach Ollama, LM Studio, or another local
|
||||
model server.
|
||||
|
||||
:::
|
||||
|
||||
## Use it
|
||||
The configured endpoint must support streaming Chat Completions and structured tool calls for the full
|
||||
assistant experience. Model discovery alone does not guarantee tool support, except when OpenRouter
|
||||
explicitly advertises the model's `tools` capability.
|
||||
|
||||
- Select **Assistant** in the user menu or press `Shift + A`.
|
||||
- Ask from `Cmd/Ctrl + K`.
|
||||
- Add the [Assistant widget](../widgets/assistant) to a board.
|
||||
OpenRouter web search is an opt-in option in the model section. It registers OpenRouter's model-callable
|
||||
[`openrouter:web_search` server tool](https://openrouter.ai/docs/guides/features/server-tools/web-search)
|
||||
alongside Homarr's tools, so OpenRouter performs each search without
|
||||
exposing the stored credential to the browser. The model decides when live research is needed and can use the
|
||||
grounded results and source links in responses or dashboard notes. Homarr limits each assistant request to
|
||||
three searches with five results per search. OpenRouter charges web-search usage separately from model tokens.
|
||||
Completed responses show a compact web-search activity card when OpenRouter reports search usage. OpenRouter
|
||||
does not expose these server-side searches as ordinary client-executed tool calls, so Homarr shows the reported
|
||||
search count and the safe title and URL from each returned `url_citation` annotation instead. Citation excerpts
|
||||
stay in the provider response and are not persisted in the activity card.
|
||||
|
||||
The assistant can work with boards, apps, integrations, widgets, Custom Widgets, and connected services. Use `@` to
|
||||
add Homarr items as context, `/` for shortcuts, and the paperclip for supported files.
|
||||
The option is also available for custom endpoints that forward OpenRouter's server-tools request format. This
|
||||
supports OpenRouter proxies, including the future Homarr Router provider. Leave it disabled for ordinary
|
||||
OpenAI-compatible endpoints because the `openrouter:*` tool type is not part of the standard OpenAI API.
|
||||
|
||||
Use **Conversations** to search, switch, rename, export, or delete threads.
|
||||
The Claude preset uses Anthropic's OpenAI SDK compatibility layer. Anthropic describes that layer as
|
||||
suited to testing and comparison rather than as its full native API; unsupported OpenAI fields may be
|
||||
ignored. Streaming responses and tool calls are supported, which are the capabilities Homarr Assistant
|
||||
uses. Choose OpenRouter or another provider when you prefer a provider-neutral production endpoint.
|
||||
|
||||
Each request includes the user's name, current page and board, home board, server time, browser time zone, and a small
|
||||
summary of accessible Homarr resources. Live status is fetched only when needed.
|
||||
## Conversations and tools
|
||||
|
||||
## Permissions and privacy
|
||||
Conversations and messages are stored per user in the Homarr database. The assistant stays out of the
|
||||
page layout until it is opened. Users can start, revisit, and delete conversations from the
|
||||
conversation menu in the floating panel.
|
||||
|
||||
- Read-only actions can run automatically. Changes require approval by default. Automatic approvals apply only to the
|
||||
current conversation, turn off when you switch conversations, and never answer questions for you.
|
||||
- Homarr permissions apply to every tool call and mentioned item.
|
||||
- API keys and custom headers are encrypted and never returned to the browser.
|
||||
- Conversations are stored per user and can be exported or deleted.
|
||||
Messages support copy, edit-and-resend, response regeneration and branch navigation, Markdown export,
|
||||
and helpful/not-helpful feedback. The conversation menu also provides a compact export action beside delete for
|
||||
each conversation. Its debugging-oriented Markdown includes every stored user, assistant, reasoning, tool, error,
|
||||
branch-parent, and server-metadata record while redacting secret-looking fields and embedded attachment data.
|
||||
The per-response export uses the same tool-aware format instead of assistant-ui's text-only copy representation.
|
||||
Selecting text in a message exposes a quote action that carries the selected passage into the next prompt.
|
||||
Files returned by a compatible model can be downloaded directly from the message.
|
||||
Assistant responses render GitHub-flavored Markdown, including headings, links, lists, task lists, tables,
|
||||
quotes, inline code, and fenced code blocks. Line breaks are preserved, and Homarr repairs common streamed
|
||||
responses where a provider returns escaped line breaks or places a bold-label list on one line.
|
||||
Completed fenced code blocks use language-aware syntax highlighting; a streaming block stays plain until its
|
||||
content is stable. Safe same-origin links are prefetched and use client-side navigation, so opening a returned
|
||||
preview, editor, board, or other Homarr item does not unload the assistant. External links keep their normal
|
||||
external-browser behavior.
|
||||
Fenced `mermaid` blocks render as diagrams in the current color scheme using Mermaid's strict security mode;
|
||||
invalid diagrams fall back to their source instead of breaking the response.
|
||||
When Homarr's icon search returns an image URL, visual result tables render that actual icon in a compact
|
||||
image column instead of substituting an emoji. Assistant-provided Markdown images accept safe relative,
|
||||
HTTP, and HTTPS sources and load without forwarding the Homarr page as the referrer. Image message parts
|
||||
render through assistant-ui's image primitive in a bounded responsive preview with an accessible loading state,
|
||||
a filename caption when available, and an in-place retry when the source fails.
|
||||
Images up to 1 MB and text, Markdown, CSV, HTML, XML, or JSON documents up to 350 KB can be attached by
|
||||
using the paperclip, dropping files onto the composer, or pasting an image with `Cmd/Ctrl + V`. Attachment
|
||||
previews stay local to the composer where they were added and show upload state, file type, and a removable image
|
||||
thumbnail or document tile. A request can contain up to five attachments.
|
||||
Image understanding depends on the selected model; document text is extracted locally by Homarr and sent
|
||||
as part of the prompt.
|
||||
|
||||
Prompts, attachments, tool definitions, and tool results are sent to the selected model provider. Review that
|
||||
provider's privacy policy before enabling the assistant.
|
||||
Type `@` in the composer to add permission-checked context. The nested menu contains apps, integrations,
|
||||
boards, widgets, and tools available to the current user. Homarr validates every referenced ID again on
|
||||
the server before adding it to the model context. Selected references appear as compact, typed pills in the
|
||||
composer and sent message, while preserving assistant-ui's underlying directive syntax for the model. When the
|
||||
assistant cites one of those references in its own response, Homarr renders the same typed pill and uses the
|
||||
stored app or integration icon when one is available. Type `/`
|
||||
for common prompt shortcuts. Both menus support keyboard navigation and keep the active result visible while
|
||||
moving through long lists. The selector below the composer combines searchable, provider-grouped models with
|
||||
their description, context size and image-input capability, plus the thinking-level control. Model selection is
|
||||
saved to the current conversation when selected. For a persisted thread, changing the model updates that thread's
|
||||
preference without changing the administrator's configured default; switching threads restores each thread's saved
|
||||
model, while a new thread starts from the configured default until the user changes it. Thinking mode can be left on
|
||||
**Auto**, disabled, or set
|
||||
from minimal through high; explicit levels are sent using the AI SDK's provider-neutral reasoning setting and
|
||||
still depend on support from the chosen model and endpoint. Reasoning text is shown live when the provider streams
|
||||
it through assistant-ui's grouped-parts and chain-of-thought primitives. The compact **Thinking** trace groups
|
||||
adjacent reasoning with ordinary tool calls, shows each tool's status, duration and safe human-readable target,
|
||||
and expands raw input, output and result previews only on request. Approval and question tools remain full-width
|
||||
outside the trace. The disclosure renders Markdown, can be opened or closed without interrupting the response,
|
||||
and keeps the user's collapsed state when another message starts. A compact dot matrix distinguishes an initial
|
||||
thinking pass from response streaming and settles into waiting, success, or error states in the minimized activity
|
||||
bar. The state always keeps its text label, and the animation is disabled when the operating system requests
|
||||
reduced motion.
|
||||
|
||||
## Troubleshooting
|
||||
A circular context ring beside the model selector gives a quick glance at cumulative conversation usage without
|
||||
expanding the composer toolbar. Its accessible label includes cumulative tokens and cost when available, while
|
||||
its popover keeps the latest complete context reading as a new response starts and lists every request
|
||||
turn with its model, provider, input, output, cached-input and reasoning tokens, context use, duration, speed,
|
||||
agent steps, routing details, and cost when reported. Before usage is available, the control remains a compact
|
||||
ring instead of showing a redundant missing-data label. Closing and reopening the popover reads persisted
|
||||
conversation metadata instead of resetting the display to zero. A larger ring inside the popover breaks the latest
|
||||
reported context into used, remaining and total capacity plus input, cached-input, output and reasoning tokens.
|
||||
|
||||
- **No models found:** check the base URL, model discovery path, and API key. You can enter a model ID manually.
|
||||
- **Assistant is unavailable:** enable it and save the configuration.
|
||||
- **Tool calls stop:** select a model that supports streaming and tool calls.
|
||||
- **Local endpoint is unreachable:** check container networking and the server's listen address.
|
||||
Each assistant response places a small provider logo in the same stable action toolbar as copy, export,
|
||||
feedback, and regenerate. Hovering the logo shows the provider, exact model, timing, throughput, and cost without
|
||||
adding another metadata row to the transcript. Branch navigation occupies its own adjacent group, so switching
|
||||
between response versions never pushes the response actions off-screen.
|
||||
|
||||
See [MCP](./mcp) for external AI clients and [Custom Widget agent authoring](./custom-widgets/agent-authoring) for the
|
||||
widget authoring workflow.
|
||||
OpenRouter responses request inline usage and routing metadata. Opening detailed turn data also loads the
|
||||
server-side generation record for its model steps and briefly polls while OpenRouter finishes processing it.
|
||||
The details separate Homarr's end-to-end timing from OpenRouter's provider latency, generation time, and provider
|
||||
throughput, and can include normalized and native token counts, moderation latency, cache discount, fallback count,
|
||||
service tier, data region, BYOK status, routed provider, generation identifier, exact cost, and upstream inference
|
||||
cost. Provider credentials remain server-side; the browser can request only generation identifiers already stored
|
||||
on a conversation owned by the signed-in user and signed by the Homarr server for that user and conversation.
|
||||
Successful generation lookups are cached briefly so polling and multi-step requests do not repeatedly query
|
||||
OpenRouter for the same record.
|
||||
When a provider omits cost but model discovery includes prompt and completion pricing, Homarr labels the
|
||||
calculated value as estimated. Missing provider data is shown as not reported rather than inferred.
|
||||
|
||||
The assistant can use the same internal tool definitions as the [Homarr MCP server](./mcp).
|
||||
Each tool call runs as the signed-in user, so normal Homarr permission checks still apply. Read-only
|
||||
procedures can run during the agent loop. Mutating procedures require native approval unless automatic approvals are
|
||||
enabled for the conversation. Neutral choices and questions still wait for the user. The approval appears as a large
|
||||
**Approve and run** or **Deny** action;
|
||||
the assistant does not require a second typed confirmation first. Tool cards expose their status, timing,
|
||||
validated input, result, and error state without flooding the conversation with raw JSON by default.
|
||||
Common collection and widget results are summarized inline as compact responsive cards, while the full
|
||||
structured result remains available in the tool card details. Icon searches show the returned image variants,
|
||||
file formats, and repositories instead of the size of the complete icon catalog. Their trace uses a readable
|
||||
activity such as **Looking for “homarr” icons** instead of exposing the internal MCP procedure name.
|
||||
|
||||
Large tool-driven requests can use up to 32 agent steps and a larger response budget within one request, so
|
||||
multi-item dashboard changes normally finish without requiring a follow-up “Continue” message. OpenRouter calls
|
||||
Homarr tools sequentially to prevent routed models from interleaving or truncating parallel JSON arguments, while
|
||||
every mutation still follows its native approval policy, including conversation-scoped automatic approvals. A safely
|
||||
recoverable truncated icon-search object is repaired
|
||||
before validation; incomplete search strings and all mutation inputs are never guessed.
|
||||
Homarr also normalizes internal tool results to the model's JSON-compatible message format before continuing an
|
||||
agent loop. Rich server values such as dates cannot invalidate the next model step, and an interrupted tool call
|
||||
without a result is omitted when the conversation resumes instead of making the entire prompt fail.
|
||||
|
||||
The assistant can create a formatted dashboard note by adding a **Notebook** widget. Ask it for a note with
|
||||
headings, lists, tasks, links, emphasis, tables, or images; it generates compatible rich-text content, shows
|
||||
the native widget configuration form and standard mutation approval, then places the widget in the next
|
||||
available dashboard position. The generated content is retained even though the Notebook content field is
|
||||
normally hidden from widget settings.
|
||||
|
||||
Automatic placement checks every responsive layout and treats both widgets and complete container footprints
|
||||
as occupied. New assistant-created items use the first free position in the main canvas instead of overlapping
|
||||
existing content or being placed in a gutter.
|
||||
|
||||
Every assistant-created dashboard widget now passes through Homarr's native widget editor before it is added.
|
||||
The form fills widget defaults, validates widget-specific options, and only offers compatible integrations that
|
||||
the current user can use. Required integrations cannot be left empty, single-integration widgets cannot receive
|
||||
multiple integrations, and the reviewed values are passed unchanged into the final approval-gated mutation.
|
||||
|
||||
Custom-widget tools let an administrator list, inspect, create, and update definitions. The custom-widget
|
||||
form's **Design with AI** action sends a focused request into the current conversation, where the assistant
|
||||
can propose the matching mutation and use the same approval controls. Existing stored secret values are
|
||||
reported only as configured/not configured and are preserved on update unless replacements are explicitly
|
||||
provided. For Custom Widget requests, Homarr lazily adds the complete installed skill, all bundled schema,
|
||||
runtime and security references, and the current JSON Schema to the trusted authoring context. This avoids an
|
||||
extra provider round trip for the large `customWidget_getSkill` and `customWidget_schema` results. If the model
|
||||
discovers the Custom Widget workflow mid-response, Homarr promotes the same resources before its next step and
|
||||
removes the now-redundant resource calls from provider history. Multiline JSX uses `templateLines` to avoid fragile
|
||||
streamed JSON escaping. Custom Widget authoring tools are mandatory: the assistant validates the complete candidate,
|
||||
creates a preview from that exact version, executes every returned preview query, checks the actual HTTP status and
|
||||
response shape, and repeats the entire validation and preview sequence after any material JSX, request, source, option,
|
||||
or binding change. It only reports a widget as validated, working, created, or placed when the corresponding tool result
|
||||
proves that checkpoint. Large widgets are saved from the exact final tested preview session, so the approved create step
|
||||
does not stream the complete JSX a second time. When the request already names a board, that board is carried into the
|
||||
create result and placement continues without asking the user to select it again. Preview and saved-definition results
|
||||
include safe Homarr paths that open in the assistant without a page reload. When placement is still a choice, the
|
||||
assistant uses the structured question UI instead of ending with a prose question.
|
||||
|
||||
The authoring phase limits broad component-document retrieval and then moves into validation with the smallest useful
|
||||
reference set. Supplied samples and successful preview responses are treated as binding contracts: the assistant is
|
||||
instructed to render the requested fields, guard optional nested data, preserve every returned item, expose freshness
|
||||
when timestamps are available, and provide a refresh path for recoverable empty or error states.
|
||||
|
||||
The shield control in the conversation header can enable automatic approvals for the current conversation; they are
|
||||
off by default.
|
||||
While enabled, Homarr accepts every affirmative approval step as it arrives: protected mutations are approved,
|
||||
presented app details and board changes are used, and structured questions with an explicitly categorized
|
||||
**Confirm** answer choose that answer. Neutral choices such as selecting a board still wait for input because
|
||||
they do not have a safe affirmative default. Automatic approvals turn off when you switch conversations, and
|
||||
the shared approval tracker prevents the same call from being handled twice when both a board widget and the
|
||||
floating assistant are visible.
|
||||
|
||||
When the assistant genuinely needs missing information or a choice, it can pause with a structured
|
||||
question. Each proposed answer is visibly categorized as **Confirm**, **Decline**, or **Option**, so the effect
|
||||
of automatic approval is unambiguous. **Other** is shown only when the tool allows it (`allowOther` is not `false`);
|
||||
selecting it opens a freeform response and is always a manual alternative, not an automatic approval choice. While a
|
||||
question is unanswered, its controls stay in a dedicated area immediately
|
||||
above the composer instead of requiring the user to find the tool call in the transcript. The question temporarily
|
||||
uses the available conversation body, with its own scrolling when necessary, rather than squeezing a long option
|
||||
list into a short dock beside part of the transcript. The answered choice then remains in the conversation history.
|
||||
Submitting the answer resumes the same agent run automatically. The minimized activity bar shows the
|
||||
question or approval that needs attention and opens directly back to the request. While an answer, app
|
||||
form, or mutation approval is pending, the composer preserves its draft but does not replace the pending
|
||||
tool call with a new message. If the provider stops before an interactive tool supplies complete arguments,
|
||||
Homarr replaces the loading state with an explicit error and confirms that no change was made.
|
||||
|
||||
App creation uses Homarr's native app form inside the conversation. The form reuses the local icon picker,
|
||||
including automatic matching and searches through the `icon.findIcons` procedure, before the final
|
||||
`app.create` approval is requested. App fields are filled only after the complete tool input has arrived, so
|
||||
streaming partial arguments cannot replace the proposed values with empty fields. The model is instructed not
|
||||
to invent external icon CDN URLs.
|
||||
|
||||
Board appearance changes use a similar review flow. The assistant first reads the board's current settings,
|
||||
including its complete custom stylesheet, and then opens an editable form with Custom CSS, Appearance,
|
||||
Background, and General tabs. The form highlights how many fields were proposed, preserves settings the
|
||||
assistant did not change, and returns only values that differ from the current board. If nothing differs, the review
|
||||
cancels without creating a mutation or approval. Custom CSS can be
|
||||
copied or restored before continuing, and Homarr warns when it contains an imported stylesheet or remote
|
||||
resource URL. Continuing from the form opens the normal mutation approval before anything is saved.
|
||||
|
||||
The board-settings tool covers General, Background, Appearance, Custom CSS, and app-status settings. It does not edit
|
||||
responsive layouts, board access control, or Danger zone actions; those remain outside this review scope.
|
||||
|
||||
Ask for changes such as “make the cards denser on my Home board,” “add a mobile CSS rule without removing
|
||||
my existing styles,” or “change the board colors and disable app status checks.” The current user must have
|
||||
modify permission on the selected board. The empty conversation includes an **Improve a dashboard** starter,
|
||||
and `/style` prepares the same guided workflow from the composer.
|
||||
|
||||
Homarr also exposes a small set of browser-only actions to the assistant. These actions can navigate to
|
||||
safe internal routes, refresh the current view, and open Homarr interfaces such as the command menu and media request search.
|
||||
They never receive provider credentials and cannot navigate to arbitrary external URLs.
|
||||
|
||||
After an approved mutation completes, Homarr automatically refetches active page and dashboard data so
|
||||
board items, settings, custom CSS, apps, and other visible results update without a full browser reload.
|
||||
The refresh action in the conversation header performs the same safe refetch manually. It keeps the
|
||||
conversation open while refreshing both client queries and server-rendered route data.
|
||||
|
||||
:::warning
|
||||
|
||||
Assistant prompts and selected tool results are sent to the configured model endpoint. Review that
|
||||
service's privacy and retention terms before enabling this feature for an instance.
|
||||
|
||||
:::
|
||||
@@ -22,8 +22,6 @@ Navigate to `Management` → `Tools` → `Backup`. You must have **admin** permi
|
||||
|
||||
## Exporting a Backup
|
||||
|
||||

|
||||
|
||||
Click the **Export** button in the export card. You'll be prompted to type `I understand` to confirm. Once confirmed, a ZIP file downloads containing:
|
||||
|
||||
| File | Description |
|
||||
@@ -35,8 +33,6 @@ The backup contains the complete database. It includes Custom Widgets and all ot
|
||||
|
||||
## Restoring a Backup
|
||||
|
||||

|
||||
|
||||
### 1. Upload
|
||||
|
||||
Drag and drop or select a `.zip` backup file. The import card validates that the file contains a valid `db.sqlite` and metadata.
|
||||
@@ -53,9 +49,6 @@ Before restoring, Homarr uses **sql.js WASM** to analyze the backup directly in
|
||||
|
||||
Type `I understand` to acknowledge that restoring will replace your current database. This cannot be undone.
|
||||
|
||||
During first-run onboarding, the database is still empty, so the restore preview goes directly to **Restore Database**
|
||||
without the extra typed confirmation. After Homarr restarts, onboarding opens the restored default home board.
|
||||
|
||||
### 4. Restore
|
||||
|
||||
The backup ZIP is sent to the server, validated, and applied. After a successful restore:
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
@@ -15,6 +15,7 @@ import layoutSettingsImage from "./img/layout-settings.png";
|
||||
import layoutSettingsCustomRailsImage from "./img/layout-settings-custom-rails.png";
|
||||
import boardViewModeImage from "./img/board-view-mode.png";
|
||||
import boardEditModeImage from "./img/board-edit-mode.png";
|
||||
import addBoardContentMenuImage from "./img/add-board-content-menu.png";
|
||||
import keyboardTileEditingImage from "./img/keyboard-tile-editing.png";
|
||||
|
||||
Each dashboard is called a "board".
|
||||
@@ -22,16 +23,9 @@ You can create as many boards as you want.
|
||||
Boards can be restricted to user groups.
|
||||
A user may also have a "home board" which they will see at the root path (e.g. my-homarr.com or my-homarr.com/boards).
|
||||
Other boards can be viewed by direct links (e.g. my-homarr.com/boards/smart-home) or via the management pages.
|
||||
The management page includes a compact structural preview of each board's Base layout so you can recognize it before
|
||||
opening it. Containers include their visible widgets and app icons instead of appearing empty.
|
||||
|
||||
Names of boards must be unique because this will be used in the URL to load the desired board.
|
||||
|
||||
Press `Shift+C`, or select **Switch board** from the user menu, to open the board switcher. It shows structural
|
||||
previews of every board you can access, with your current board at the end. Home-board markers and creator avatars
|
||||
match the board management view. Use the search field at the top, or simply start typing, to filter by name. Use the
|
||||
arrow keys to move through the results and press `Enter` to open the selected board.
|
||||
|
||||
## Create a new board
|
||||
|
||||
When creating a board, the availability of the board name will be checked for you.
|
||||
@@ -130,9 +124,8 @@ The complete canvas always fits the available width and never adds horizontal bo
|
||||
- **Mobile** starts at `0px` and uses three columns on new boards.
|
||||
- **Base** is the widest layout and starts at `768px` on new boards.
|
||||
|
||||
Mobile and Base can be renamed and their column counts can be changed, but they cannot be removed. Mobile has no fixed
|
||||
rails; rail configuration is available only on Base and custom layouts. You can add removable custom layouts between
|
||||
their breakpoints.
|
||||
Mobile and Base can be renamed and their column counts and rails can be changed. You can add removable custom layouts
|
||||
between their breakpoints.
|
||||
|
||||
Each layout stores independent positions and sizes. Moving or resizing a widget while editing Mobile does not move it in
|
||||
Base or any custom layout. New custom layouts start from Base's current column count and rail widths; Homarr chooses a
|
||||
@@ -209,18 +202,15 @@ changes, Homarr asks whether to discard them. If the editor cannot load, the boa
|
||||
style={{ maxWidth: "100%" }}
|
||||
/>
|
||||
|
||||
While edit mode is active, **Add board content** opens a menu for adding a widget, app, integration, or Container. The
|
||||
available actions respect the current user's permissions. Widget setup offers the main canvas, enabled rails, and existing
|
||||
Containers as destinations before the widget is created, so the common flow does not require a second move step.
|
||||
While edit mode is active, **Add board content** creates a new item, app, integration, or Container on the main canvas.
|
||||
New content uses the first available position; clicking an empty cell does not place it there, and items cannot be added
|
||||
directly into a rail or Container from this menu.
|
||||
|
||||
Hold `Ctrl` on Windows or Linux, or `⌘` on macOS, while clicking board items to select several at once. The selection
|
||||
toolbar can move the complete selection to another canvas, rail, or Container, delete it with an undo option, or clear the
|
||||
selection. A short tip appears after entering edit mode as a reminder of the modifier key. A move is applied only when every
|
||||
selected item fits in the destination.
|
||||
|
||||
The widget editor also lists its selected integrations. Users with full access can choose **Edit connection** to test,
|
||||
diagnose, and repair an integration without leaving the board. A successful repair refreshes the affected widget; the
|
||||
saved integration changes apply everywhere that connection is used.
|
||||
<img
|
||||
src={addBoardContentMenuImage}
|
||||
alt="Board edit mode add-content menu showing New item, Add an app, New integration, and New container"
|
||||
style={{ maxWidth: "100%" }}
|
||||
/>
|
||||
|
||||
Pointer users can drag from the inert widget area; interactive controls are excluded. Containers use the grab button at their top-left corner.
|
||||
Widgets can be resized from any edge or corner handle. Activate a Container's chrome with its top-left grip before using
|
||||
@@ -335,9 +325,8 @@ This will disable the status check for apps.
|
||||
Widgets expose their routine controls without requiring edit mode:
|
||||
|
||||
- Hold `Shift` while hovering a widget that supports advanced view to expand it in place while the other widgets
|
||||
remain visible but dimmed. Release `Shift` to close it. Hold `Shift` plus `Ctrl` on Windows/Linux or `Cmd` on macOS to
|
||||
keep the view open until you close it. On touch devices, open it from the widget menu. Other widgets adapt in place
|
||||
instead.
|
||||
remain visible but dimmed. Release `Shift` to close it. Focus that widget and press `Shift` + `Enter` to keep the view
|
||||
open until you close it. On touch devices, open it from the widget menu. Other widgets adapt in place instead.
|
||||
- When signed in, outside edit mode, and with **Enable right click on widgets** enabled, right-click a widget to inspect
|
||||
its live query status, refresh its data, change quick options, open settings, or run supported actions. App tiles do
|
||||
not use this widget menu, and available controls follow your board and integration permissions.
|
||||
|
||||
@@ -74,7 +74,7 @@ These interfaces do not return an archive URL or archive hash.
|
||||
4. Build one definition, using `templateLines` for multiline JSX tool input.
|
||||
5. Validate the definition without saving it and repair every reported issue.
|
||||
6. Create a preview session.
|
||||
7. Run `customWidget_previewQuery` for every query returned by the preview and inspect its actual status and data shape. Assistant conversations retain a bounded response preview so large APIs cannot exhaust the model context. Use
|
||||
7. Run `customWidget_previewQuery` for every query returned by the preview and inspect its actual status and data shape. Use
|
||||
`{}` for load queries; manual queries require exactly the returned primitive `parameterNames`.
|
||||
8. Test relevant actions in simulation mode.
|
||||
9. Open the returned preview path with browser tools.
|
||||
@@ -131,33 +131,20 @@ To exercise the real Homarr Assistant lifecycle instead of accepting a returned
|
||||
OPENROUTER_API_KEY=... pnpm --filter @homarr/custom-widgets test:assistant-ai
|
||||
```
|
||||
|
||||
The same suites can target the Homarr provider with a Workshop PocketBase token instead of an OpenRouter key:
|
||||
|
||||
```sh
|
||||
AI_PROVIDER_BASE_URL=https://homarr.dev/api/ai/v1 \
|
||||
AI_PROVIDER_API_KEY=<workshop-token> \
|
||||
AI_PROVIDER_MODEL=homarr/model \
|
||||
pnpm --filter @homarr/custom-widgets test:assistant-ai
|
||||
```
|
||||
|
||||
`AI_PROVIDER_JUDGE_MODEL` can select a separate judge when the configured endpoint offers one. The Homarr provider
|
||||
offers exactly one model, so generation and review both use `homarr/model`.
|
||||
|
||||
The assistant test exposes the same lazy skill, live schema, component catalog, targeted component references,
|
||||
validation, preview-query, and create-from-preview tools as the product. It refuses to save a definition that was not
|
||||
validated exactly or whose preview queries were not all tested. Multi-request fixtures return a different payload for
|
||||
each documented path and require manual-query parameters. Use `--case=pokedex`, `--case=coinmarketcap-keyless`,
|
||||
`--case=bored-activity`, or `--case=agify-name` for a focused run.
|
||||
|
||||
Both suites use the configured provider models for generation and harsh review. They save tool traces, validated
|
||||
manifests, raw reviews, and detailed category scores under
|
||||
Both suites default generation and harsh review to OpenRouter's `~deepseek/deepseek-v4-flash-latest` route. They save
|
||||
tool traces, validated manifests, raw reviews, and detailed category scores under
|
||||
`packages/custom-widgets/.ai-evaluations/`. These live tests are maintainer-invoked rather than CI-required because
|
||||
they consume a configured provider account and exercise a nondeterministic model. The repository's ordinary tests
|
||||
cover the lifecycle enforcement, fixture routing, score calculation, and failure behavior without network access.
|
||||
|
||||
Each scenario stops as soon as it passes and has a hard limit of ten generation attempts. Override the endpoint,
|
||||
models, or per-scenario limit with `AI_PROVIDER_BASE_URL`, `AI_PROVIDER_API_KEY`, `AI_PROVIDER_MODEL`,
|
||||
`AI_PROVIDER_JUDGE_MODEL`, and
|
||||
Each scenario stops as soon as it passes and has a hard limit of ten generation attempts. Override the models or
|
||||
per-scenario limit with `OPENROUTER_GENERATOR_MODEL`, `OPENROUTER_JUDGE_MODEL`, and
|
||||
`CUSTOM_WIDGET_AI_MAX_LOOPS`. Generated artifacts are local and ignored by Git; promote a reviewed, validated result
|
||||
into the bundled-widget source deliberately.
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ to be saved before server requests run.
|
||||
|
||||
## Import and Workshop
|
||||
|
||||
The install flow always links to the corresponding public Workshop entry under the configured Workshop origin. Import confirmation shows origins, authentication modes, network scopes, methods, actions, and permissions. Every source has a suggested URL, but private and loopback sources must be confirmed or replaced for the current Homarr installation. The same screen accepts API keys, tokens, usernames, and passwords; credentials remain optional at install time and missing values stay visible on the Custom Widgets page.
|
||||
The install flow always links to the corresponding public Workshop entry using `WORKSHOP_WEB_URL`. Import confirmation shows origins, authentication modes, network scopes, methods, actions, and permissions. Every source has a suggested URL, but private and loopback sources must be confirmed or replaced for the current Homarr installation. The same screen accepts API keys, tokens, usernames, and passwords; credentials remain optional at install time and missing values stay visible on the Custom Widgets page.
|
||||
|
||||
For example, installing **Tautulli Activity** asks for both the user's Tautulli URL and API key. A stable public source such as PokéAPI keeps its published URL without creating unnecessary setup work.
|
||||
|
||||
|
||||
@@ -13,27 +13,9 @@ Check [the available integrations](/docs/category/integrations) for further info
|
||||
|
||||
## Create an integration
|
||||
|
||||
To create an integration, choose **New integration** and select the supported application from the searchable grid. The grid groups related services visually and shows which widgets each integration supports.
|
||||
To create an integration, you can choose the supported application from the dropdown at the top right:
|
||||
|
||||
The integrations management page uses a searchable list. Each row shows the integration type and URL, with the actions you have permission to use.
|
||||
|
||||
### Accessing the page
|
||||
|
||||
The `/manage/integrations` page is reachable when at least one of the following applies:
|
||||
|
||||
- you can manage all integrations (global `integration-full-all`),
|
||||
- you can create integrations (`integration-create`), or
|
||||
- you have been granted full access to at least one specific integration.
|
||||
|
||||
What the page shows depends on the case:
|
||||
|
||||
- **Manage all (`integration-full-all`)**: the full list of integrations is shown.
|
||||
- **Create only (`integration-create`)**: the page shows an empty "create an integration" state instead of the list, with the create action still available.
|
||||
- **Delegated (full access on one or more)**: only the integrations you have full access to are listed, above a note making clear that the view is scoped to what you can manage.
|
||||
- **None**: the page is not reachable and returns a not-found page.
|
||||
|
||||
Where you have a mix of access, the list combines the integrations you can manage. Integration lists are filtered
|
||||
server side, so a user never receives data about integrations they lack access to.
|
||||

|
||||
|
||||
Next, you'll need to fill out the required fields to configure the integration:
|
||||
|
||||
@@ -77,22 +59,6 @@ It is not allowed to create an integration without passing the test.
|
||||
This is to ensure that you minimize the amount of broken applications on your Homarr instance.
|
||||
The test and create button will perform a network request to the application and authenticate if required.
|
||||
|
||||
When you connect a service from a board, Homarr keeps you in the same setup flow. After the connection succeeds, it
|
||||
confirms whether an application shortcut was linked and offers the widgets that support the new integration. Selecting a
|
||||
widget returns to its settings with the new integration already selected.
|
||||
|
||||
Users with full access can also test and update a selected integration directly in the **Edit widget → Edit integration**
|
||||
tab on a board. Failed tests keep the editor open and show the same connection diagnostics as the management page.
|
||||
Successful changes apply to every app and widget that uses the integration.
|
||||
|
||||
When app creation is not permitted, the setup form offers linking an existing accessible app instead of attempting to
|
||||
create one.
|
||||
|
||||
When the connection unlocks useful widgets that are not on the current board yet, the confirmation also shows up to
|
||||
three explainable recommendations. A recommendation is based only on the services you can use and the widget kinds
|
||||
already present on that board. You can open the existing widget picker prefiltered to the recommendation, or dismiss
|
||||
recommendations for that service on that board.
|
||||
|
||||
## Managing permissions
|
||||
|
||||
On the edit page of an integration, you can manage which users / groups can
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
tags:
|
||||
- Administration
|
||||
- Logs
|
||||
- Troubleshooting
|
||||
---
|
||||
|
||||
# Logs
|
||||
|
||||
The logs page shows recent Homarr application logs and continues updating as new events occur. Use it to investigate
|
||||
widget, integration, and server errors without opening the container logs separately.
|
||||
|
||||
## Opening the logs page
|
||||
|
||||
Navigate to **Management** > **Tools** > **Logs**. The page and widget log links are only available to users with the
|
||||
**View logs** permission.
|
||||
|
||||
When a widget reports an error, **Check logs for more details** opens the logs page in a new tab and marks the time when
|
||||
the widget displayed the error. The related server error normally appears immediately above that marker. If those logs
|
||||
have already been removed from the retained history, the marker explains that they are no longer available.
|
||||
|
||||
## Retained and live logs
|
||||
|
||||
Homarr retains the latest 5,000 log events in Redis. Opening the page first loads this history and then continues with
|
||||
live events without a gap. The history is bounded, so older events are removed automatically.
|
||||
|
||||
The history survives normal application restarts when the configured Redis instance persists its data. For complete
|
||||
platform-level history, startup failures, or events older than the retained limit, use the logs provided by Docker or
|
||||
your deployment platform.
|
||||
|
||||
## Display controls
|
||||
|
||||
The severity selector applies to both retained and live events. Selecting a different level redraws the existing view
|
||||
using that threshold. The font-size control changes the terminal text size and is remembered in the browser.
|
||||
@@ -8,7 +8,8 @@ tags:
|
||||
# Search Engines
|
||||
|
||||
Using the search engines, terms can be quickly searched simply by opening the search bar at the top or using the keyboard shortcut `CTRL + K`.
|
||||
The management page provides a searchable list with the shortcut, URL template, source, and available row actions for each engine.
|
||||
|
||||

|
||||
|
||||
You can add any search engine that has a search URL that accepts a search term as a parameter.
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ tags:
|
||||
The settings page applies options to the whole Homarr application.
|
||||
Some changes need a full page reload before they take effect.
|
||||
|
||||
All settings sections remain visible in a single form. Click the **Save changes** button in the save bar at the bottom of the page to apply them. The save bar only appears when you have unsaved changes, and the **Discard** button reverts your changes.
|
||||
All changes are collected in a single form. Click the **Save changes** button in the save bar at the bottom of the page to apply them. The save bar only appears when you have unsaved changes, and the **Discard** button reverts your changes.
|
||||
|
||||
## Analytics
|
||||
|
||||
|
||||
@@ -163,77 +163,19 @@ Members can be removed from the group using the remove button at the end:
|
||||
#### Permissions tab
|
||||
|
||||
In this tab, you manage what permissions the members of this group will have.
|
||||
Permissions are shown as a matrix with one row per category (boards, apps, integrations, and so on). Each row has a
|
||||
segmented control that lets you pick a single **access level**, plus a checkbox for each **capability** the category
|
||||
has.
|
||||
Currently, the following permissions are available:
|
||||
|
||||
Levels in a category are cumulative: selecting a level grants that level and every level below it in the same
|
||||
category. Choosing **No access** grants no access level for that category.
|
||||
|
||||
##### Capabilities are separate from the access level
|
||||
|
||||
A capability is something you can grant on its own, independently of how much of everyone else's data the members
|
||||
can see. Creating is the usual example: you can let members create their own boards without letting them view or
|
||||
modify anyone else's boards. Apps additionally have **use all apps**, which lets members put any app on a board
|
||||
without being able to edit apps.
|
||||
|
||||
Some access levels already include a capability through permission inheritance (marked in the table below); the
|
||||
checkbox is then ticked, disabled, and explains which level includes it.
|
||||
|
||||
##### Administration
|
||||
|
||||
Selecting the **Administrator** level grants full access to everything. While it is selected, every other category
|
||||
is disabled and explains that administrators already have that access. Setting the administrator row back to
|
||||
**No access** re-enables the other categories with the selections they had before.
|
||||
|
||||
##### Access levels
|
||||
|
||||
The access levels below are cumulative, in ascending order, with the category's capabilities listed separately:
|
||||
|
||||
| Category | Access levels (ascending) | Capabilities | Already included at |
|
||||
| -------------- | ---------------------------------------------------------------------------------------- | ------------------------------- | ---------------------------------------------------- |
|
||||
| Boards | view all boards → modify all boards → full board access | create boards | full board access |
|
||||
| Apps | modify all apps → full app access | use all apps, create apps | create apps: modify all apps and full app access; use all apps: full app access |
|
||||
| Integrations | use all integrations → interact with any integration → full integration access | create integrations | full integration access |
|
||||
| Search engines | modify all search engines → full search engine access | create search engines | modify all search engines, full search engine access |
|
||||
| Medias | view all medias → full media access | upload medias | full media access |
|
||||
| Other | view logs | — | — |
|
||||
| Administrator | administrator | — | — |
|
||||
|
||||
Apps are the one category where "use" is not a level. Being able to modify every app does **not** imply being able
|
||||
to put any app on a board — only full app access grants both — so the two are kept independent rather than stacked,
|
||||
and picking **Modify** never silently grants use of every app.
|
||||
|
||||
The levels grant the following:
|
||||
|
||||
- **Boards**: view all boards; modify all boards (not access control and danger zone); full board access (view, modify and delete every board, including access control and the danger zone).
|
||||
- **Apps**: modify all apps; full app access (manage, use and delete any app).
|
||||
- **Integrations**: use all integrations; interact with any integration; full integration access (manage, use and interact with any integration).
|
||||
- **Search engines**: modify all search engines; full search engine access (manage and delete any search engine).
|
||||
- **Medias**: view all medias; full media access (manage and delete any media).
|
||||
- **Other**: view logs.
|
||||
|
||||
##### Presets
|
||||
|
||||
Above the rows you can apply a preset to fill in a common configuration in one click:
|
||||
|
||||
- **Viewer**: read-only - view all boards and medias, use any app on a board, and use existing integrations.
|
||||
- **Editor**: day-to-day management - modify all boards and apps, create boards and integrations, interact with any integration, modify search engines, and upload medias, without deleting resources or managing access.
|
||||
- **Admin**: grants the administrator permission (full access to everything).
|
||||
|
||||
When the current selection does not match any preset, a **Custom** badge is shown.
|
||||
|
||||
##### Also grants
|
||||
|
||||
Some levels include additional permissions automatically. When a level implies extra permissions (for example,
|
||||
modifying all apps also allows creating apps), a dimmed **Also grants: …** hint is shown below the category row,
|
||||
listing the implied permissions.
|
||||
|
||||
##### Effective permissions
|
||||
|
||||
Below the rows, the **Effective permissions** panel can be expanded to review everything members of this group will
|
||||
be able to do - the full, de-duplicated set of permissions, including everything granted indirectly through the
|
||||
levels - grouped by category, with a count for each.
|
||||
- Boards
|
||||
- **Create boards**: Allow members to create boards
|
||||
- **View all boards**: Allow members to view all boards
|
||||
- **Modify all boards**: Allow members to modify all boards (Does not include access control and danger zone)
|
||||
- **Full board access**: Allow members to view, modify, and delete all boards (Including access control and danger zone)
|
||||
- Integrations
|
||||
- **Create integrations**: Allow members to create integrations
|
||||
- **Use all integrations**: Allows members to add any integrations to their boards
|
||||
- **Interact with any integration**: Allow members to interact with any integration
|
||||
- **Full integration access**: Allow members to manage, use and interact with any integration
|
||||
- **Administrator**: Members with this permission have full access to all features and settings
|
||||
|
||||
Custom Widget authoring, import, migration, preview, secrets, Workshop access, and placement configuration are reserved
|
||||
for administrators and are not separate assignable group permissions. Board modifiers can still move, resize, change
|
||||
@@ -278,8 +220,7 @@ Clicking the `Copy & close` button will copy the URL to your clipboard.
|
||||
|
||||
### Self-onboarding
|
||||
|
||||
Clicking the invitation link opens a centered registration form that uses the same visual shell as Homarr's onboarding
|
||||
and sign-in pages, with fields for the username and password:
|
||||
Clicking the invitation link will bring up a form with fields for the username and password:
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
---
|
||||
tags:
|
||||
- Workshop
|
||||
- Custom Widgets
|
||||
- Custom CSS
|
||||
---
|
||||
|
||||
# Community Workshop
|
||||
|
||||
:::warning Experimental feature
|
||||
|
||||
This feature is experimental and will probably change. If you encounter bugs, need more capabilities, or find something confusing, tell us in the [official discussion thread](https://github.com/homarr-labs/homarr/discussions/categories/custom-widgets). Your feedback directly shapes what comes next. Happy hacking!
|
||||
|
||||
:::
|
||||
|
||||
The [Community Workshop](pathname:///workshop) is where Homarr users share and discover
|
||||
**custom CSS themes** and **custom widgets**. You can browse submissions, upvote the ones
|
||||
you like, report abuse, and install entries directly into your own Homarr instance.
|
||||
|
||||
The central `homarr.dev` Workshop is a public community. Browsing it on the website is public. Browsing, installing,
|
||||
publishing, or importing Workshop content **inside a Homarr instance is restricted to Homarr administrators**.
|
||||
|
||||
:::warning
|
||||
|
||||
The Workshop contains community-made content. Moderation is not a security boundary, and PocketBase does not interpret
|
||||
or validate submission content. Always review widget capabilities, network origins, actions, and CSS before installing.
|
||||
Homarr's installation-time validator is definitive and can reject content that the public service accepted earlier.
|
||||
Back up your instance before installing untrusted content.
|
||||
|
||||
:::
|
||||
|
||||
## Browsing and voting
|
||||
|
||||
- Filter by **Widgets** or **CSS**, show or hide outdated entries, and sort by popularity, creation date, recent updates, or discussion activity.
|
||||
- Sign in with **GitHub** to upvote/downvote, submit your own creations, or report a submission.
|
||||
- Each submission shows its title, description, linked GitHub author, revision, optional screenshots, comments, and full content. Widget pages also summarize API sources, actions, options, network access, and required credentials before installation.
|
||||
- A new submission receives one real upvote owned by its author. Votes remain unique per user and submission.
|
||||
- Open report counts are public on submission cards so visitors can make an informed installation decision. The submission author and Workshop moderators can read each report category and explanation; reporter identities remain moderator-only.
|
||||
- Signed-in moderators see a **Moderation** button in the Workshop header. The moderation queue is sorted by open report count and lets moderators inspect every report, dismiss it, or delete the submission. Reports do not automatically remove a submission.
|
||||
- The manual **Outdated** badge warns visitors that a submission may need an update.
|
||||
|
||||
## Comments
|
||||
|
||||
Comments are publicly readable and appear in chronological conversation bubbles with the author's avatar and GitHub profile. Signed-in users can post; authors can edit or delete their own comments, and Workshop moderators can delete any comment. Failed posts keep the text you typed. When SMTP and `WORKSHOP_PUBLIC_ORIGIN` are configured, the submission author receives one email for a comment from another user. Self-comments, edits, and deletions do not send mail.
|
||||
|
||||
## Submitting
|
||||
|
||||
1. Sign in with GitHub.
|
||||
2. Click **Share yours** and choose the type:
|
||||
- **Custom widget** — paste the JSON exported from **Manage → Custom Widgets → Export**.
|
||||
It must match the `homarr-custom-widget-v2` schema (validated before submission).
|
||||
- **Custom CSS** — paste your stylesheet (max 16384 characters).
|
||||
3. Review the widget JSON or CSS in the syntax-aware editor, then add a title, description, optional screenshots, and changelog.
|
||||
4. Submit the entry.
|
||||
|
||||
Custom widget JSON is normalized to readable multi-line formatting when it is published, and the Workshop detail page
|
||||
also formats older compact submissions before displaying them.
|
||||
|
||||
Community members can report submissions as outdated or no longer working, malicious, spam, copyright-infringing,
|
||||
inappropriate, or another concern. An outdated report does not automatically mark or remove the submission.
|
||||
|
||||
Submissions are published immediately. Reported entries are reviewed and may be removed.
|
||||
Authors can edit a submission later to publish a new revision. The edit form can keep or remove existing screenshots and add new PNG, JPG, or WebP images without changing the Workshop URL.
|
||||
|
||||
When SMTP and `WORKSHOP_PUBLIC_ORIGIN` are configured, every current Workshop moderator receives an email when a report is submitted. If a moderator deletes the reported submission, its author receives a separate removal email. Author self-deletion does not send a moderation email.
|
||||
|
||||
:::info Separate roles
|
||||
|
||||
A Workshop moderator is a user with PocketBase's internal `isAdmin` field. This role moderates the central community
|
||||
and is unrelated to a Homarr instance administrator.
|
||||
|
||||
:::
|
||||
|
||||
## Submission guidelines
|
||||
|
||||
Submissions must be useful to Homarr users and safe to install. Do not submit content that is
|
||||
malicious, deceptive, illegal, hateful, sexually explicit, gore, gambling-focused, spam, or content
|
||||
that primarily promotes unrelated products or services.
|
||||
|
||||
## Installing into Homarr
|
||||
|
||||
You can install workshop entries without leaving Homarr:
|
||||
|
||||
- **Custom widgets (Homarr administrators only)** — go to **Manage → Custom Widgets**, or use **Import from Workshop** in the
|
||||
board widget picker. Choose **Install widget**, review the complete source and capabilities, then confirm API sources and any local secrets in the second modal step.
|
||||
- **Custom CSS from Workshop (Homarr administrators only)** — open a board's **Settings → Custom CSS**, click **Import from Workshop**, choose a theme, review its full CSS in the second same-size modal step, and click **Import CSS** once. The board setting is still saved separately.
|
||||
|
||||
Alternatively, open a submission in the Workshop and use **Download widget JSON** or **Copy CSS**. The submission page shows the matching manual installation steps before the source code.
|
||||
|
||||
Custom Widget imports detect required bearer, basic, header, or query credentials. You can enter them during import or explicitly defer them; secret values are never stored in Workshop content. Missing credentials remain visible on the local Custom Widget afterward.
|
||||
|
||||
## Workshop deployment
|
||||
|
||||
Workshop is powered by [PocketBase](https://pocketbase.io). The production image compiles the current Docusaurus site and serves it together with the API from one origin. The deployment lives in `apps/workshop/` with a Dockerfile, Compose configuration, version-controlled migrations, hooks, a smoke test, and ACT-local workflow instructions.
|
||||
|
||||
Submission links include Open Graph and X/Twitter metadata for the submission title, description, and type. The first uploaded screenshot is used as the social preview image; submissions without screenshots use the Homarr logo.
|
||||
|
||||
Run `pnpm docker:workshop` for PocketBase on port 8090, then run
|
||||
`WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs` for Docusaurus on port 3003. `pnpm docker:docs` builds the
|
||||
combined production layout. Normal start and stop commands retain data; `down --volumes` is an intentional,
|
||||
irreversible development reset.
|
||||
|
||||
GitHub needs only the central callback `https://<workshop-host>/api/oauth2-redirect`. Keep PocketBase origins unrestricted
|
||||
unless every self-hosted Homarr caller is explicitly listed. Configure both `GITHUB_CLIENT_ID` and
|
||||
`GITHUB_CLIENT_SECRET`; supplying only one is a startup error. Set `WORKSHOP_PUBLIC_ORIGIN` in production so generated
|
||||
links and email use the public host. Workshop stores only the OAuth provider username in `users.name`, and
|
||||
users cannot rename it. Map **OAuth2 username** to `name`; leave OAuth2 id, full name, and avatar mappings
|
||||
empty. Profile and avatar URLs are derived from `https://github.com/<username>` and
|
||||
`https://github.com/<username>.png`, so PocketBase does not store duplicate profile metadata or avatar files. Configure
|
||||
SMTP for comment, report, and removal notices.
|
||||
|
||||
The public URL contract is:
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| ------------------------ | ------------------------------------------------- | ----------------------------- |
|
||||
| `HOMARR_WEBSITE_URL` | Documentation/site base used by Homarr links | `https://homarr.dev` |
|
||||
| `WORKSHOP_API_URL` | PocketBase API used by Homarr and standalone docs | `HOMARR_WEBSITE_URL` |
|
||||
| `WORKSHOP_WEB_URL` | Public Workshop page linked from Homarr | `HOMARR_WEBSITE_URL/workshop` |
|
||||
| `WORKSHOP_PUBLIC_ORIGIN` | PocketBase-generated links and email | Required in production |
|
||||
| `PB_ALLOWED_ORIGINS` | PocketBase browser origins | `*` for the central community |
|
||||
|
||||
The deprecated docs-only `WORKSHOP_URL` alias remains for one release; `WORKSHOP_API_URL` takes precedence. The
|
||||
combined Workshop image writes these public URLs into its browser runtime configuration whenever the container starts;
|
||||
the Docker build arguments remain fallbacks. Restart the container after changing these values, without rebuilding it.
|
||||
|
||||
### Staging and demo deployment
|
||||
|
||||
Use `apps/workshop/docker-compose.staging.yml` with the published `ghcr.io/homarr-labs/workshop:v2` image for a v2
|
||||
preview. Copy `apps/workshop/.env.staging.example` to `.env.staging`; its default public contract is:
|
||||
|
||||
```dotenv
|
||||
HOMARR_WEBSITE_URL=https://v2.preview.homarr.dev
|
||||
WORKSHOP_API_URL=https://v2.preview.homarr.dev
|
||||
WORKSHOP_WEB_URL=https://v2.preview.homarr.dev/workshop
|
||||
WORKSHOP_PUBLIC_ORIGIN=https://v2.preview.homarr.dev
|
||||
PB_ALLOWED_ORIGINS=*
|
||||
```
|
||||
|
||||
Start or update it from the repository root:
|
||||
|
||||
```sh
|
||||
docker compose --env-file apps/workshop/.env.staging \
|
||||
-f apps/workshop/docker-compose.staging.yml pull
|
||||
docker compose --env-file apps/workshop/.env.staging \
|
||||
-f apps/workshop/docker-compose.staging.yml up --detach
|
||||
curl --fail http://127.0.0.1:8090/api/health
|
||||
```
|
||||
|
||||
The service binds to `127.0.0.1:8090`. Terminate TLS at a reverse proxy and forward the **entire hostname**, including
|
||||
`/api`, `/_/`, and `/workshop-runtime-config.js`; forwarding only `/workshop` breaks PocketBase and runtime URL loading.
|
||||
This combined image serves both the documentation site and PocketBase, so use a dedicated Workshop hostname if the
|
||||
chosen hostname already serves Homarr itself. In that case, replace the hostname in all four URL values and keep
|
||||
`/workshop` only on `WORKSHOP_WEB_URL`.
|
||||
|
||||
The Compose volume preserves PocketBase data across pulls and container recreation. The mutable `v2` tag is suitable
|
||||
for previews; use the `sha-<commit>` tag for promotion. For the public central Workshop, `PB_ALLOWED_ORIGINS=*` allows
|
||||
self-hosted Homarr origins to use the API and OAuth. A private demo may use a comma-separated origin allowlist instead.
|
||||
GitHub OAuth requires both credentials and the callback
|
||||
`https://v2.preview.homarr.dev/api/oauth2-redirect`. Restart after changing environment values, then verify the health
|
||||
endpoint, `/workshop`, a public listing request, GitHub sign-in, and an installation from the Homarr preview.
|
||||
|
||||
Back up application and PocketBase data before deployment. Publish the combined image by immutable digest, test the
|
||||
exact digest in staging, rehearse application rollback and PocketBase restore, then promote that same digest.
|
||||
|
||||
## Client architecture
|
||||
|
||||
Workshop access is centralized in `@homarr/workshop/backend`. It exports the typed `WorkshopBackend`, its typed PocketBase instance, stable TanStack Query option factories, and the `useWorkshop…` query and mutation hooks used by Homarr. The documentation site uses the same backend rather than maintaining a second API contract.
|
||||
|
||||
Listing fields derived by the `workshop_listings` view—such as submission type, score, and counts—are normalized by the shared client. Healthy deployments keep filtering, sorting, and pagination in PocketBase. If a persistent development volume still has an older view that rejects those fields, the client retries without them and applies the same operations locally; it can also fall back to the base submissions collection. Production deployments should nevertheless apply the current migration so the view exposes the complete metadata.
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
title: "Air Quality & UV"
|
||||
description: "Displays air quality, ultraviolet index, pollutants and pollen for a location."
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
|
||||
|
||||
import { AddingWidget } from "@site/src/components/widgets/adding";
|
||||
import { WidgetConfig } from "@site/src/components/widgets/configuration";
|
||||
import { WidgetHeader } from "@site/src/components/widgets/header";
|
||||
import { airQualityWidget } from ".";
|
||||
|
||||
<WidgetHeader widget={airQualityWidget} categories={["Health", "Weather"]} />
|
||||
|
||||
The Air Quality & UV widget shows the current air quality index for a selected location. It adds the UV index,
|
||||
dominant pollutant, particulate matter and the highest available pollen reading as space allows. Open the advanced
|
||||
view for a 24-hour AQI and UV chart, all available pollutant and pollen readings, and seven-day daily maxima.
|
||||
|
||||
Select the European or US AQI scale, or use the automatic setting. Automatic uses the US scale for viewers whose
|
||||
locale region is the United States and the European scale everywhere else. The selected scale stays in widget options
|
||||
instead of being repeated beside every reading. Pollen values are individual measured concentrations; the widget does
|
||||
not combine them into a single pollen index.
|
||||
|
||||
The categories and colors summarize published index bands and are not medical advice. Pollutant concentrations use
|
||||
micrograms per cubic meter (`µg/m³`), and pollen uses grains per cubic meter.
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
<AddingWidget />
|
||||
|
||||
### Configuration
|
||||
|
||||
<WidgetConfig configuration={airQualityWidget.configuration} />
|
||||
|
||||
### Data source
|
||||
|
||||
Homarr uses the free Open-Meteo Air Quality API. The widget refreshes active data every 15 minutes and requests a
|
||||
seven-day forecast for European AQI, US AQI, UV, particulate matter, ozone, nitrogen dioxide, sulphur dioxide, carbon
|
||||
monoxide, and available alder, birch, grass, mugwort, olive and ragweed pollen. Identical location requests reuse the
|
||||
server response for 15 minutes. If a refresh fails, Homarr can keep a recent successful response visible for up to 24
|
||||
hours instead of replacing it with an empty state.
|
||||
|
||||
Set `NO_EXTERNAL_CONNECTION=true` to disable this and other internet-backed requests. If you need to allowlist the
|
||||
service, use `https://air-quality-api.open-meteo.com`.
|
||||
@@ -1,43 +0,0 @@
|
||||
import type { WidgetDefinition } from "@site/src/types";
|
||||
import { IconWind } from "@tabler/icons-react";
|
||||
|
||||
export const airQualityWidget: WidgetDefinition = {
|
||||
icon: IconWind,
|
||||
name: "Air Quality & UV",
|
||||
description: "Displays air quality, ultraviolet index, pollutants and pollen for a location.",
|
||||
path: "../../widgets/air-quality",
|
||||
configuration: {
|
||||
items: [
|
||||
{
|
||||
name: "Air quality location",
|
||||
description: "Location used for air quality, UV and pollen data",
|
||||
values: "Select a location through search or longitude/latitude",
|
||||
defaultValue: "Paris / 48.85341, 2.3488",
|
||||
},
|
||||
{
|
||||
name: "AQI standard",
|
||||
description: "Choose the European or US air quality scale, or select one from the viewer's locale",
|
||||
values: { type: "select", options: ["Automatic for locale", "European AQI", "US AQI"] },
|
||||
defaultValue: "Automatic for locale",
|
||||
},
|
||||
{
|
||||
name: "Show UV index",
|
||||
description: "Display the current UV index and its forecast",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "yes",
|
||||
},
|
||||
{
|
||||
name: "Show pollutant concentrations",
|
||||
description: "Display particulate matter and gaseous pollutant concentrations",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "yes",
|
||||
},
|
||||
{
|
||||
name: "Show pollen concentrations",
|
||||
description: "Display available pollen concentrations",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "yes",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -17,8 +17,6 @@ import { anchorIntegration } from "@site/docs/integrations/anchor";
|
||||
|
||||
The Anchor Note widget shows a selected note from your Anchor integration. If your note permissions allow it, you can also edit the note title and content directly in Homarr.
|
||||
|
||||
Advanced view always shows the note title and update time alongside the complete formatted document, permission, creation date, and pinned or archived state. Compact title and update-time switches do not hide that information in advanced view.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import anchorNoteWidgetScreenshot from "./img/widget.png";
|
||||
|
||||
@@ -14,7 +14,9 @@ import { archiveTeamWarriorWidget } from ".";
|
||||
|
||||
<WidgetHeader widget={archiveTeamWarriorWidget} categories={["Archiving", "Monitoring"]} />
|
||||
|
||||
Use this widget to see what your ArchiveTeam Warrior is preserving without leaving Homarr. The compact view keeps the current project, runner state, job counts, bandwidth, and optional broadcast message visible. Advanced view adds the complete active-job list, job start times, project and bandwidth-session identifiers, session transfer totals, exact update time, and the full broadcast message whenever one is available. A failed refresh keeps the last snapshot visible with a stale-data warning.
|
||||
Use this widget to see what your ArchiveTeam Warrior is preserving without leaving Homarr. The compact view prioritizes the
|
||||
selected project and Warrior status; shorter tiles can hide secondary counts, bandwidth, or the broadcast message. Advanced
|
||||
view adds the runner status, exact update time, and a scrollable list of reported work items.
|
||||
|
||||
### Supported integration
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ hide_title: true
|
||||
---
|
||||
|
||||
import { AddingWidget } from "@site/src/components/widgets/adding";
|
||||
import { WidgetConfig } from "@site/src/components/widgets/configuration";
|
||||
import { WidgetHeader } from "@site/src/components/widgets/header";
|
||||
import { assistantWidget } from ".";
|
||||
import assistantWidgetImage from "../../management/img/assistant-widget.png";
|
||||
@@ -39,8 +40,17 @@ touch devices. When a board layout does not provide enough height for conversati
|
||||
a compact **Open assistant** action instead of squeezing an unusable composer into the card. Increase its height
|
||||
in that layout to keep the complete conversation inline.
|
||||
|
||||
The widget follows whichever conversation is active in the user-menu panel, `Shift + A`, or the command menu. This
|
||||
keeps every assistant entry point synchronized without storing a user's private conversation details in board settings.
|
||||
### Configuration
|
||||
|
||||
<WidgetConfig configuration={assistantWidget.configuration} />
|
||||
|
||||
**Current conversation** follows whichever conversation is active in the user-menu panel, `Shift + A`, or
|
||||
the command menu. This is the default and keeps every assistant entry point synchronized.
|
||||
|
||||
**Pinned conversation** reveals a searchable conversation selector. A pinned widget never changes the
|
||||
active conversation silently: select **Open conversation** inside the widget before continuing that thread. If the pinned
|
||||
thread was deleted or cannot be opened, the widget keeps the pinned selection, shows the same recovery action and a
|
||||
retry state, and retries that thread when **Open conversation** is selected again.
|
||||
|
||||
### Conversation features
|
||||
|
||||
|
||||
@@ -36,9 +36,7 @@ import widgetOverview from "./img/widget-overview.png";
|
||||
]}
|
||||
/>
|
||||
|
||||
Advanced view shows every missing-subtitle and health summary regardless of compact visibility options, together with the already-fetched Sonarr and Radarr SignalR states and announcement count.
|
||||
|
||||
<WidgetInteractionGuide advancedView />
|
||||
<WidgetInteractionGuide advancedView={false} />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import beszelSystemGrid from "./img/beszel-system-grid.png";
|
||||
|
||||
<WidgetHeader widget={beszelSystemGridWidget} categories={["System Monitoring"]} />
|
||||
|
||||
This widget displays all your Beszel-monitored systems as a responsive card grid. Each card shows real-time metrics like CPU, memory, disk, network, temperature, and more. Resizing the widget adjusts its rows and columns to keep the cards balanced. If Beszel reports multiple filesystems, the disk bar exposes a hover breakdown for the root disk and each extra mount.
|
||||
This widget displays all your Beszel-monitored systems as a responsive card grid. Each card shows real-time metrics like CPU, memory, disk, network, temperature, and more. Cards adapt their layout based on available space. If Beszel reports multiple filesystems, the disk bar exposes a hover breakdown for the root disk and each extra mount.
|
||||
|
||||
Select a system card to open the complete statistics view, including system, filesystem, Docker, historical, and realtime charts.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "Bookmarks"
|
||||
description: "Keeps useful links together"
|
||||
description: "Displays multiple app links"
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
@@ -13,12 +13,9 @@ import { bookmarksWidget } from ".";
|
||||
|
||||
<WidgetHeader widget={bookmarksWidget} categories={["Links", "Apps"]} />
|
||||
|
||||
The bookmarks widget keeps direct URLs and Homarr Apps in one ordered list.
|
||||
Paste a URL and press Enter, paste several comma-separated or one-per-line URLs, or search for an existing App.
|
||||
URLs do not need an App record; Homarr uses the site's favicon and falls back to a link icon when it cannot load one.
|
||||
Bookmarks selected from Apps continue to use the App name, URL, icon, and description.
|
||||
|
||||
**Adaptive** layout is the practical default: it changes between icons, compact rows, and tiles as the widget changes shape. Every compact item exposes its destination on hover, while advanced view expands items into descriptive cards.
|
||||
The bookmarks widget allows you to display multiple app links in a single widget.
|
||||
You can add links to your favorite applications, websites, or any other resources you want quick access to.
|
||||
Its normal view adapts each configured layout to the available width and height: lower-priority hostnames are hidden before names or icons, and larger collections scroll instead of crushing cards.
|
||||
|
||||
### Screenshots
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { IconBookmark } from "@tabler/icons-react";
|
||||
export const bookmarksWidget: WidgetDefinition = {
|
||||
icon: IconBookmark,
|
||||
name: "Bookmarks",
|
||||
description: "Keeps useful links together",
|
||||
description: "Displays multiple app links",
|
||||
path: "../../widgets/bookmarks",
|
||||
configuration: {
|
||||
items: [
|
||||
@@ -16,21 +16,9 @@ export const bookmarksWidget: WidgetDefinition = {
|
||||
},
|
||||
{
|
||||
name: "Layout",
|
||||
description: "How bookmarks use the available widget space.",
|
||||
values: { type: "select", options: ["Adaptive", "Vertical", "Horizontal", "Grid", "Compact grid", "Icons"] },
|
||||
defaultValue: "Adaptive",
|
||||
},
|
||||
{
|
||||
name: "Appearance",
|
||||
description: "Card surface style.",
|
||||
values: { type: "select", options: ["Soft", "Filled", "Outline", "Plain"] },
|
||||
defaultValue: "Soft",
|
||||
},
|
||||
{
|
||||
name: "Card spacing",
|
||||
description: "Space between bookmark cards.",
|
||||
values: { type: "select", options: ["Extra small", "Small", "Medium", "Large", "Extra large"] },
|
||||
defaultValue: "Extra small",
|
||||
description: "Layout in which the bookmarks are displayed.",
|
||||
values: { type: "select", options: ["Vertical", "Horizontal", "Grid", "Grid horizontal"] },
|
||||
defaultValue: "Vertical",
|
||||
},
|
||||
{
|
||||
name: "Hide title",
|
||||
@@ -58,8 +46,8 @@ export const bookmarksWidget: WidgetDefinition = {
|
||||
},
|
||||
{
|
||||
name: "Bookmarks",
|
||||
description: "URLs and existing Apps shown by the widget.",
|
||||
values: "Paste one or many URLs, or find an existing App. Drag to reorder.",
|
||||
description: "List of bookmarks to display in the widget.",
|
||||
values: "Select of all your apps and order them as you like.",
|
||||
defaultValue: "No bookmarks selected",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -69,9 +69,8 @@ import advancedView from "./img/advanced-view.png";
|
||||
|
||||
### Interactions
|
||||
|
||||
- Colored dots directly below a date mark its events. They scale with the widget and are hidden when the calendar is too short.
|
||||
- Hover over a marked date to preview its events in a popover.
|
||||
- The advanced view replaces the month grid with a grouped event list, keeps every event attributed to its source integration, and expands titles, locations, and descriptions. Use its previous and next controls to change months.
|
||||
- The advanced view replaces the month grid with a grouped event list. Use its previous and next controls to change months.
|
||||
- If one selected integration is unavailable, its warning indicator remains visible while events from healthy integrations continue to load.
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
|
||||
@@ -15,26 +15,6 @@ import { clockWidget } from ".";
|
||||
|
||||
The clock widget displays the current date and time in a customizable format.
|
||||
|
||||
Advanced view turns it into a World Clock. The configured primary timezone stays prominent while New York, Paris, and
|
||||
Tokyo are included as secondary defaults. You can add, rename, remove, and reorder up to six cities in widget options;
|
||||
London, Sydney, and UTC are also available as quick presets.
|
||||
|
||||
The digital advanced view keeps the primary timezone prominent and presents every secondary city as a concise,
|
||||
comparable card. The primary clock includes a segmented 24-hour rail with a current-time marker, elapsed and remaining
|
||||
time, six-hour ticks, and a legend for night, dawn, day, and dusk. These phases are based on local clock time and are not
|
||||
astronomical sunrise or sunset calculations. Secondary cards stop after their localized date and timezone offsets.
|
||||
|
||||
The compact widget remains a single numeric clock. The time-format selector shows common Day.js presets with live
|
||||
examples and applies to the primary clock. Choosing a format with seconds enables second-level updates; 12-hour and
|
||||
24-hour display are selected through those same formats. The date-format selector likewise
|
||||
shows live Day.js examples and their tokens. Secondary cities always include a short localized date and a
|
||||
Yesterday, Today, or Tomorrow label relative to the primary timezone. UTC and relative offsets follow current timezone
|
||||
rules, including daylight-saving changes.
|
||||
|
||||
Weather remains optional and uses its own configured location rather than a World Clock city. Its icon is static by
|
||||
default and can be animated explicitly; reduced-motion preferences always disable animation. An unsupported saved
|
||||
timezone is identified without preventing valid clocks from rendering.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import localClock from "./img/local.png";
|
||||
|
||||
@@ -20,6 +20,18 @@ export const clockWidget: WidgetDefinition = {
|
||||
values: { type: "string" },
|
||||
defaultValue: "-",
|
||||
},
|
||||
{
|
||||
name: "24-hour format",
|
||||
description: "Use 24-hour format instead of 12-hour format.",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "yes",
|
||||
},
|
||||
{
|
||||
name: "Display seconds",
|
||||
description: "Whether to show seconds in the clock.",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "no",
|
||||
},
|
||||
{
|
||||
name: "Use fixed timezone",
|
||||
description: "Select custom timezone to display instead of the client timezone.",
|
||||
@@ -40,46 +52,21 @@ export const clockWidget: WidgetDefinition = {
|
||||
},
|
||||
{
|
||||
name: "Date format",
|
||||
description: "Choose a common Day.js date format with a live example and its tokens.",
|
||||
values: "YYYY-MM-DD, DD/MM/YYYY, MM/DD/YYYY, or common long and short date formats",
|
||||
description: "Format of the date displayed below the clock.",
|
||||
values: "A collection of date formats, e.g. 'DD/MM/YYYY', 'MM/DD/YYYY', etc.",
|
||||
defaultValue: "dddd, MMMM D",
|
||||
},
|
||||
{
|
||||
name: "Time format",
|
||||
description:
|
||||
"Choose a common Day.js time format with a live example. The format controls 12/24-hour display and seconds.",
|
||||
values: "HH:mm, HH:mm:ss, h:mm A, or h:mm:ss A",
|
||||
defaultValue: "HH:mm",
|
||||
name: "Custom time format",
|
||||
description: "Format of the time displayed in the clock.",
|
||||
values: 'ISO 8601 format, e.g. "HH:mm:ss", "hh:mm A", etc.',
|
||||
defaultValue: "-",
|
||||
},
|
||||
{
|
||||
name: "Show weather",
|
||||
description: "Show a compact weather summary from an independently configured location.",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "no",
|
||||
},
|
||||
{
|
||||
name: "Weather location",
|
||||
description: 'Location used by the weather summary. Only shown if "Show weather" is enabled.',
|
||||
values: "A searched city or custom latitude and longitude",
|
||||
defaultValue: "Brisbane",
|
||||
},
|
||||
{
|
||||
name: "Use Fahrenheit",
|
||||
description: 'Use Fahrenheit in the weather summary. Only shown if "Show weather" is enabled.',
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "no",
|
||||
},
|
||||
{
|
||||
name: "Animate weather icon",
|
||||
description: "Animate the weather icon unless the device requests reduced motion.",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "no",
|
||||
},
|
||||
{
|
||||
name: "World clock cities",
|
||||
description: "Add, label, and order up to six IANA timezones for advanced view.",
|
||||
values: "Up to six labeled IANA timezones",
|
||||
defaultValue: "New York, Paris, and Tokyo",
|
||||
name: "Custom date format",
|
||||
description: "Format of the date displayed below the clock.",
|
||||
values: 'ISO 8601 format, e.g. "YYYY-MM-DD", "DD/MM/YYYY", etc.',
|
||||
defaultValue: "-",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -27,11 +27,7 @@ The Coolify widget displays a compact overview of your Coolify instance. It can
|
||||
]}
|
||||
/>
|
||||
|
||||
Advanced view opens the Servers, Applications, and Services sections regardless of their compact visibility switches. It identifies each Coolify integration, shows server `is_reachable` and `is_usable` values, and lists applications nested in services when the API provides them. Healthy integrations remain visible if another source fails, with the failed source identified by a warning.
|
||||
|
||||
IP addresses remain masked in advanced view until you explicitly use the eye control. Public resource, Coolify dashboard, and log links retain the widget's safe new-tab handling.
|
||||
|
||||
<WidgetInteractionGuide advancedView />
|
||||
<WidgetInteractionGuide advancedView={false} />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
title: "Countdowns"
|
||||
description: "Tracks upcoming events and recurring anniversaries."
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
|
||||
|
||||
import { AddingWidget } from "@site/src/components/widgets/adding";
|
||||
import { WidgetConfig } from "@site/src/components/widgets/configuration";
|
||||
import { WidgetHeader } from "@site/src/components/widgets/header";
|
||||
import { countdownWidget } from ".";
|
||||
|
||||
<WidgetHeader widget={countdownWidget} categories={["Time"]} />
|
||||
|
||||
The Countdowns widget tracks up to 20 events without an integration. Add each event in widget options, choose its local
|
||||
date and time, and select an IANA timezone. Events can be reordered by dragging them.
|
||||
|
||||
The compact widget shows the first configured event, so dragging an event to the top selects the countdown displayed on
|
||||
the board. Advanced view uses that same event for its larger summary and preserves the configured order within its
|
||||
upcoming and completed groups. List cards keep their occurrence date at the bottom, below the optional progress bar.
|
||||
|
||||
One-time events remain completed after their target. A yearly event keeps the same local date and wall-clock time in its
|
||||
configured timezone, including when daylight-saving offsets change. A February 29 event waits until the next leap year.
|
||||
If a configured local time falls in a daylight-saving gap, the first valid time afterward is used; if it occurs twice,
|
||||
the earlier occurrence is used.
|
||||
|
||||
Progress is optional. One-time events need a start date before a percentage can be calculated. Yearly events show
|
||||
progress from the previous occurrence to the next. When enabled, seconds remain visible for every upcoming event.
|
||||
|
||||
Browser notifications are an optional local preference in advanced view and are disabled by default. Permission is
|
||||
requested only after you enable notifications, and alerts can fire once per occurrence while Homarr is open. Browser
|
||||
storage uses the board and widget identifiers to keep preferences separate and prevent duplicate alerts; clearing site
|
||||
data resets them. Widget previews do not persist alert preferences.
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
<AddingWidget />
|
||||
|
||||
### Configuration
|
||||
|
||||
<WidgetConfig configuration={countdownWidget.configuration} />
|
||||
@@ -1,31 +0,0 @@
|
||||
import { WidgetDefinition } from "@site/src/types";
|
||||
import { IconHourglass } from "@tabler/icons-react";
|
||||
|
||||
export const countdownWidget: WidgetDefinition = {
|
||||
icon: IconHourglass,
|
||||
name: "Countdowns",
|
||||
description: "Tracks upcoming events and recurring anniversaries.",
|
||||
path: "../../widgets/countdown",
|
||||
configuration: {
|
||||
items: [
|
||||
{
|
||||
name: "Events",
|
||||
description: "Add, edit, and order one-time or yearly events with a date, time, and IANA timezone.",
|
||||
values: "Up to 20 events",
|
||||
defaultValue: "No events",
|
||||
},
|
||||
{
|
||||
name: "Show progress",
|
||||
description: "Show progress from an optional start date, or between yearly occurrences.",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "yes",
|
||||
},
|
||||
{
|
||||
name: "Show seconds",
|
||||
description: "Keep seconds visible for every upcoming event, including events more than a day away.",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "no",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -20,9 +20,9 @@ import { technitiumDnsIntegration } from "@site/docs/integrations/technitium-dns
|
||||
|
||||
<WidgetHeader widget={dnsHoleControlsWidget} categories={["DNS Hole"]} />
|
||||
|
||||
When several DNS integrations are selected, Homarr keeps healthy controls available if one source fails and shows a warning identifying the unavailable integration.
|
||||
|
||||
This widget lets permitted users enable, disable, or temporarily pause one or more DNS services. It shares the summary query and cache with DNS Hole Summary. Every selected source stays available in the normal scrollable view, while cards and controls adapt to the widget's width and height. Bulk actions keep successful results visible and report the number of failures.
|
||||
This widget lets permitted users enable, disable, or temporarily pause one or more DNS services. It shares the summary query
|
||||
and cache with DNS Hole Summary. Cards and controls adapt to the widget's width and height. Sources that fail to load are
|
||||
omitted from the returned summaries; bulk actions keep successful results visible and report the number of failures.
|
||||
|
||||
### Screenshots
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ import { dnsHoleSummaryWidget } from ".";
|
||||
|
||||
<WidgetHeader widget={dnsHoleSummaryWidget} categories={["DNS Hole"]} />
|
||||
|
||||
When several DNS integrations are selected, Homarr keeps healthy sources visible if one source fails and shows a warning identifying the unavailable integration.
|
||||
|
||||
This widget combines statistics from every selected DNS service, including total queries, blocked queries, and blocklist size. Its data is cached under the same DNS query family as DNS Hole Controls, so placing both widgets on a board does not create a second integration request. When the widget has enough room, it adds a scrollable status row naming each selected source.
|
||||
|
||||
### Screenshots
|
||||
|
||||
@@ -7,15 +7,20 @@ hide_title: true
|
||||
import { WidgetHeader } from "@site/src/components/widgets/header";
|
||||
import { AddingWidget } from "@site/src/components/widgets/adding";
|
||||
import { WidgetConfig } from "@site/src/components/widgets/configuration";
|
||||
import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
|
||||
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
|
||||
import { IconAd } from "@tabler/icons-react";
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
import { dockerContainersWidget } from ".";
|
||||
import { dockerIntegration } from "@site/docs/integrations/docker";
|
||||
|
||||
<WidgetHeader widget={dockerContainersWidget} categories={["Containers"]} />
|
||||
|
||||
This widget displays the resource usage of your Docker containers and allows you to manage them.
|
||||
|
||||
:::info
|
||||
This widget reads the Docker environments configured through environment variables. You do not need to create a Docker integration under **Management → Integrations**. The widget can only be used with administrator privileges.
|
||||
This widget can only be used with administrator privileges.
|
||||
:::
|
||||
|
||||
### Screenshots
|
||||
@@ -24,6 +29,16 @@ import dockerTable from "./img/table.png";
|
||||
|
||||
<img src={dockerTable} alt="docker-stats table docker-containers" width={128 * 3} height={128 * 2} />
|
||||
|
||||
### Supported Integrations
|
||||
|
||||
<WidgetIntegrations
|
||||
items={[
|
||||
{
|
||||
integration: dockerIntegration,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
### Interactions
|
||||
|
||||
Hover a column header to reveal its grab handle, then drag it to reorder the table or drag the header's edge to resize it. The layout is saved automatically for each widget instance and restored after a reload. These controls are disabled while the board is in edit mode.
|
||||
|
||||
@@ -8,12 +8,6 @@ export const dockerContainersWidget: WidgetDefinition = {
|
||||
path: "../../widgets/docker-containers",
|
||||
configuration: {
|
||||
items: [
|
||||
{
|
||||
name: "Docker environments",
|
||||
description: "Select one or more automatically discovered Docker environments, or leave empty to show all",
|
||||
values: "Any Docker socket or host configured for Homarr",
|
||||
defaultValue: "All Docker environments",
|
||||
},
|
||||
{
|
||||
name: "Columns to show",
|
||||
description: "Select which columns are visible in the table",
|
||||
|
||||
@@ -135,8 +135,7 @@ are enabled, the shared menu provides widget-level actions such as advanced view
|
||||
- **Right-click** any download to copy its name or ID; pause, resume, and delete are available only with Interact access
|
||||
- **Click** any row to expand it and see full download details inline
|
||||
- **Hover** a download name for a quick stats tooltip
|
||||
- **Stats toggle** — show or hide the global stats bar in compact mode; advanced view always shows the stats bar without
|
||||
changing the compact choice
|
||||
- **Stats toggle** — show or hide the global stats bar in compact mode; advanced view always shows the stats bar
|
||||
- **Pause / Resume** actionable queues using the footer controls
|
||||
- **Filter** rows by integration or status using the filter button in the footer
|
||||
- **Sort** by clicking sortable column headers; the default sort is configured per widget, while header changes apply to the current view
|
||||
|
||||
@@ -23,8 +23,6 @@ import { glancesIntegration } from "@site/docs/integrations/glances";
|
||||
|
||||
This widget displays information about the health and status of your system(s), including CPU, memory, disk usage, and more.
|
||||
|
||||
Advanced view is a complete health workspace: it shows every available CPU, temperature, memory, GPU, filesystem, update, reboot, and system-information section even when compact visibility switches hide some of them on the board. Proxmox clusters likewise show uptime, CPU, memory, and every node, VM, container, and storage section. Storage-volume filters, units, and permissions remain in effect.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import systemView from "./img/system-view.png";
|
||||
|
||||
@@ -33,7 +33,7 @@ import screen from "./img/screen.png";
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
|
||||
Hover or focus the widget to pause its slideshow; touch devices keep the control available. A paused slideshow keeps its resume control visible. The advanced view displays the complete photo without cropping, groups previous, pause or resume, and next together, and includes every loaded photo in its thumbnail strip. If an Immich refresh fails, the last loaded album stays visible with a stale-data warning.
|
||||
Use the pause or resume control to pause its slideshow; touch devices keep the control available. A paused slideshow keeps its resume control visible. The advanced view displays the complete photo without cropping, groups previous, pause or resume, and next together, and includes every loaded photo in its thumbnail strip.
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@ import { sonarrIntegration } from "@site/docs/integrations/sonarr";
|
||||
|
||||
Monitor which movies and episodes are missing from your library, and track what is currently being downloaded. The widget pulls data from Radarr and Sonarr and groups results by type in two tabs.
|
||||
|
||||
Advanced view always shows both Missing and Queued panels side by side when space allows, requests at least 50 items per source, exposes queue status and time remaining, and keeps partial-integration warnings visible. Compact tab visibility and page-size choices continue to shape the board tile.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import widgetMissing from "./img/missing.png";
|
||||
|
||||
@@ -19,10 +19,6 @@ import { plexIntegration } from "@site/docs/integrations/plex";
|
||||
|
||||
Provides a list of newly added media or upcoming releases from your media server.
|
||||
|
||||
Releases retain the name and icon of the integration that supplied them. If one integration is unavailable, releases from healthy integrations remain visible and a warning identifies the failed source without exposing connection details. The widget reports an error only when every selected integration fails.
|
||||
|
||||
Advanced view displays every release in a roomy poster layout with its complete available metadata, description, tags, and visible source name, regardless of the compact layout and visibility options.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import backdrop from "./img/backdrop.png";
|
||||
|
||||
@@ -22,9 +22,9 @@ import { plexIntegration } from "@site/docs/integrations/plex";
|
||||
<WidgetHeader widget={mediaServerWidget} categories={["Media server"]} />
|
||||
|
||||
The media server widget shows active sessions across your media servers.
|
||||
It shows playback progress, pause state, time remaining, the streaming resolution (e.g. `1080p`), and whether each session is direct play, direct stream, or transcoding audio or video. Location and bitrate can be toggled independently.
|
||||
It shows playback progress, pause state, time remaining, and whether each session is direct play, direct stream, or transcoding audio or video. Location and bitrate can be toggled independently.
|
||||
|
||||
Select a session to inspect its media metadata in an anchored popover. Advanced mode adds search, sorting, location, bitrate, and a summary of playing and transcoding sessions while keeping separate integration instances identifiable. Compact visibility switches do not hide location or bitrate from the complete advanced session view.
|
||||
Select a session to inspect its media metadata in an anchored popover. Advanced mode adds search, sorting, and a summary of playing and transcoding sessions while keeping separate integration instances identifiable.
|
||||
If one selected server is unavailable, a warning indicator identifies the degraded result while sessions from healthy servers remain usable.
|
||||
Sessions without user metadata remain visible and are labeled as an unknown user.
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import { tdarrIntegration } from "@site/docs/integrations/tdarr";
|
||||
|
||||
The media transcoding widget provides you with an overview of your media transcoding status.
|
||||
It shows you the current queue, worker status, and statistics about your transcoding jobs.
|
||||
Advanced view presents all three panels together. If refreshes fail after data has loaded, Homarr keeps the last useful result visible and marks it as stale.
|
||||
|
||||
### Screenshots
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@ import { networkControllerStatusWidget } from ".";
|
||||
|
||||
Shows connected users and connected guest devices on wired or wireless networks.
|
||||
|
||||
Compact view aggregates the configured controllers into the selected Wi-Fi or wired total. Advanced view shows both totals separately for every controller, preserving each integration's name, type, and exact update time while keeping partial-source and stale-data warnings visible.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import wifiCard from "./img/wifi.png";
|
||||
|
||||
@@ -36,9 +36,7 @@ import summaryCard from "./img/summary.png";
|
||||
]}
|
||||
/>
|
||||
|
||||
In advanced view, each controller gets its own information matrix showing source, last update, WAN and web status, latency, ping, web uptime, separate Wi-Fi and LAN user and guest counts, and connected VPN users. The compact board view keeps its progressive aggregate layout.
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
<WidgetInteractionGuide advancedView={false} />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -34,9 +34,7 @@ import notificationsScreenshot from "./img/notifications.png";
|
||||
items={[{ integration: ntfyIntegration }, { integration: nextcloudIntegration }, { integration: gotifyIntegration }]}
|
||||
/>
|
||||
|
||||
Advanced view always shows available notification logos and sources and removes the compact body line limit. External notification links retain the same safe new-tab behavior.
|
||||
|
||||
<WidgetInteractionGuide advancedView />
|
||||
<WidgetInteractionGuide advancedView={false} />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -72,10 +72,6 @@ The widget adapts its layout based on the available space:
|
||||
|
||||
When the widget has enough space, a "last updated" timestamp appears at the bottom showing how recently PatchMon refreshed its data. Narrow layouts use a shortened timestamp such as `5m ago`; wider short layouts can show the full translated text.
|
||||
|
||||
### Advanced view
|
||||
|
||||
Advanced view is a complete PatchMon posture view. It always shows the compliance hero, all eight host and package statistics, the full OS distribution with versions, and the last-updated time. Compact visibility switches and OS limits still shape the board tile, but do not hide already-fetched security information in advanced view. Threshold and chart-style choices remain in effect.
|
||||
|
||||
### Supported Integrations
|
||||
|
||||
<WidgetIntegrations
|
||||
@@ -87,7 +83,7 @@ Advanced view is a complete PatchMon posture view. It always shows the complianc
|
||||
]}
|
||||
/>
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
<WidgetInteractionGuide advancedView={false} />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -35,9 +35,7 @@ GitHub, Docker Hub, GitLab, NPM, Codeberg, GitHub Container Registry, Quay, and
|
||||
For GitHub Container Registry, use `owner/package` identifiers such as `homarr-labs/homarr`.
|
||||
Full image references such as `ghcr.io/homarr-labs/homarr:latest` are also accepted.
|
||||
|
||||
Advanced view shows every configured repository even when compact view is limited to highlighted or top releases. Repository names and details remain visible, and selecting a repository still expands its description and links.
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
<WidgetInteractionGuide advancedView={false} />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -21,9 +21,7 @@ import screenshot from "./img/screenshot.png";
|
||||
|
||||
<img src={screenshot} alt="rss-feed noted-lol" width={128 * 4} />
|
||||
|
||||
Advanced view keeps the configured feeds and post limit unchanged while showing each fetched post's full description, image, source, relative time, and exact publication timestamp. Right-to-left text and safe external links continue to follow the widget configuration.
|
||||
|
||||
<WidgetInteractionGuide advancedView />
|
||||
<WidgetInteractionGuide advancedView={false} />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ The entity state enables you to display the current state of an entity.
|
||||
This works with most entities and enables you display almost any data.
|
||||
It is recommended that you transform data on the integration as much as possible since Homarr's capabilities in transformation are very limited.
|
||||
|
||||
Advanced view shows the safe Home Assistant metadata owned by Homarr: the friendly name, entity ID, unit, device class, icon, last-changed time, and last-updated time. Arbitrary attributes remain intentionally excluded.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import sun from "./img/sun.png";
|
||||
|
||||
@@ -20,8 +20,6 @@ import { homeAssistantIntegration } from "@site/docs/integrations/home-assistant
|
||||
|
||||
The execute automation widget allows you to trigger an automation with one click.
|
||||
|
||||
Advanced view shows the automation ID, interaction permission, last successful run in the current browser session, and the latest action error together. Session-only history is not presented as Home Assistant history.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import empty from "./img/empty.png";
|
||||
|
||||
@@ -18,10 +18,6 @@ import { speedtestTrackerIntegration } from "@site/docs/integrations/speedtest-t
|
||||
This widget displays internet speed test results from your [Speedtest Tracker](https://docs.speedtest-tracker.dev/) instance,
|
||||
including the latest result, historical averages, and a live chart of the last 12 hours.
|
||||
|
||||
Advanced view renders a source-labeled panel for every healthy integration. Each panel exposes that source's latest test, statistics, speed and ping charts, plus 12-hour, 24-hour, and truthful **Last 30** result ranges regardless of compact visibility switches. Compact mode keeps the responsive aggregate overview and names its healthy sources when multiple integrations are selected.
|
||||
|
||||
When multiple Speedtest Tracker integrations are selected, results from healthy sources remain visible if another source fails. A warning indicator identifies each unavailable integration without exposing private request details. If every selected integration fails, the widget reports an error; if cached data remains visible after a later total failure, a stale-data warning is shown.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import screenshot from "./img/screenshot.png";
|
||||
|
||||
@@ -16,8 +16,6 @@ import { stockPriceWidget } from ".";
|
||||
Using the Stocks widget, you can show the current stock prices of your favorite companies on your dashboard.
|
||||
The widget prioritizes the symbol, current price, and trend. Company name, change, time range, and a taller chart appear progressively when both widget dimensions can support them.
|
||||
|
||||
In advanced view, the widget always shows the returned symbol and company name, configured range and interval, current and previous close, absolute and percentage change, range minimum and maximum, and the full price-history chart. These values use the same configured Yahoo Finance query as the compact widget; advanced view does not change the symbol, range, or interval.
|
||||
|
||||
### Screenshots
|
||||
|
||||
import apple from "./img/apple.png";
|
||||
@@ -36,7 +34,7 @@ import nvidia from "./img/nvidia.png";
|
||||
<img src={nvidia} alt="stock-price nvidia" width={128 * 2 + 2} height={128 * 2 + 4} />
|
||||
</div>
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
<WidgetInteractionGuide advancedView={false} />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -44,9 +44,7 @@ This widget displays filesystem and SMART disk usage for supported server and NA
|
||||
]}
|
||||
/>
|
||||
|
||||
Advanced view keeps your configured volume filters while showing each integration name, usage percentage, used and total space, free-space percentage, SMART status, available temperature, and the usage background bar regardless of compact display options.
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
<WidgetInteractionGuide advancedView={false} />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
---
|
||||
title: "Timer / Pomodoro"
|
||||
description: "Runs a simple timer or a configurable Pomodoro sequence."
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
|
||||
|
||||
import { AddingWidget } from "@site/src/components/widgets/adding";
|
||||
import { WidgetConfig } from "@site/src/components/widgets/configuration";
|
||||
import { WidgetHeader } from "@site/src/components/widgets/header";
|
||||
import { timerWidget } from ".";
|
||||
|
||||
<WidgetHeader widget={timerWidget} categories={["Productivity", "Time"]} />
|
||||
|
||||
The Timer / Pomodoro widget runs entirely in your browser and does not require an integration. The compact widget
|
||||
always keeps its start or pause button and reset button visible. A running timer uses a saved deadline, so reopening
|
||||
the board or returning to a background tab restores the correct remaining time instead of restarting the countdown.
|
||||
|
||||
Timer mode uses one configured duration. Pomodoro mode starts with a focus session, follows most completed focus
|
||||
sessions with a short break, and uses a long break after the configured number of focus sessions. Automatic break and
|
||||
focus starts are separate options and are disabled by default. When automatic starts are enabled, elapsed phases are
|
||||
reconciled from their original deadlines after the board returns to the foreground.
|
||||
|
||||
Advanced view shows the current sequence, configured durations, and local alert preferences. Browser notifications are
|
||||
requested only when enabled. Notification and sound preferences, live progress, deadlines, and cycle position are
|
||||
stored locally using the board and widget identifiers. They are separate for each browser or device, are available to
|
||||
guest viewers, and are removed by clearing site data. Widget previews are ephemeral and do not write local timer state.
|
||||
|
||||
Timer sounds are enabled by default and use the same low-volume Tiks sound system as onboarding: start and pause use
|
||||
rising and falling soft-theme cues, reset uses a short click, and phase completion uses the glass theme's resonant
|
||||
notification ding. You can disable them in advanced view. The cues respect the device's reduced-motion preference and
|
||||
supplement the visible timer state rather than replacing it.
|
||||
|
||||
When automatic start is disabled, a completed phase labels the next step as ready and flashes the widget edges with the
|
||||
board's secondary color. The border glow and completion chime run together as a five-beat alarm burst at
|
||||
300-millisecond intervals, then stop. Reduced-motion and forced-colors modes replace the flash with a static border.
|
||||
The current phase stays pinned to the compact widget's top-left corner and wraps when needed. Timer text and controls
|
||||
use the dashboard theme's responsive size scale, with secondary details hidden when the widget preview is narrow or
|
||||
short.
|
||||
|
||||
Alerts can fire while Homarr is open. Browser and operating-system notification settings may still suppress them.
|
||||
The timer's mode, durations, session count, and automatic-start behavior are permanent board settings and are edited
|
||||
from the widget context menu.
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
<AddingWidget />
|
||||
|
||||
### Configuration
|
||||
|
||||
<WidgetConfig configuration={timerWidget.configuration} />
|
||||
@@ -1,61 +0,0 @@
|
||||
import type { WidgetDefinition } from "@site/src/types";
|
||||
import { IconClockPlay } from "@tabler/icons-react";
|
||||
|
||||
export const timerWidget: WidgetDefinition = {
|
||||
icon: IconClockPlay,
|
||||
name: "Timer / Pomodoro",
|
||||
description: "Runs a simple timer or a configurable Pomodoro sequence.",
|
||||
path: "../../widgets/timer",
|
||||
configuration: {
|
||||
items: [
|
||||
{
|
||||
name: "Mode",
|
||||
description: "Run one reusable timer or a repeating focus and break sequence",
|
||||
values: { type: "select", options: ["Timer", "Pomodoro"] },
|
||||
defaultValue: "Pomodoro",
|
||||
},
|
||||
{
|
||||
name: "Timer minutes",
|
||||
description: "Duration of the reusable timer",
|
||||
values: "1-1440 minutes",
|
||||
defaultValue: "10",
|
||||
},
|
||||
{
|
||||
name: "Focus minutes",
|
||||
description: "Duration of each Pomodoro focus session",
|
||||
values: "1-180 minutes",
|
||||
defaultValue: "25",
|
||||
},
|
||||
{
|
||||
name: "Short break minutes",
|
||||
description: "Duration of breaks between most focus sessions",
|
||||
values: "1-60 minutes",
|
||||
defaultValue: "5",
|
||||
},
|
||||
{
|
||||
name: "Long break minutes",
|
||||
description: "Duration of the break at the end of a Pomodoro cycle",
|
||||
values: "1-180 minutes",
|
||||
defaultValue: "15",
|
||||
},
|
||||
{
|
||||
name: "Focus sessions before a long break",
|
||||
description: "Number of completed focus sessions in one Pomodoro cycle",
|
||||
values: "1-12 sessions",
|
||||
defaultValue: "4",
|
||||
},
|
||||
{
|
||||
name: "Automatically start breaks",
|
||||
description: "Start the next short or long break when a focus session completes",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "no",
|
||||
},
|
||||
{
|
||||
name: "Automatically start focus sessions",
|
||||
description: "Start the next focus session when a break completes",
|
||||
values: { type: "boolean" },
|
||||
defaultValue: "no",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -17,8 +17,6 @@ import { tracearrIntegration } from "@site/docs/integrations/tracearr";
|
||||
|
||||
The Tracearr widget provides a real-time monitoring dashboard for your media server activity. It displays active streams, user statistics, recent watching history, and policy violations.
|
||||
|
||||
When multiple Tracearr integrations are selected, every stream, violation, and activity row keeps its source identity and an integration-scoped key. Data from healthy sources remains visible if another source fails, and a warning identifies each unavailable integration without exposing private request details. If every selected integration fails, the widget reports an error; if cached data remains visible after a later total failure, a stale-data warning is shown.
|
||||
|
||||
### Sections
|
||||
|
||||
| Section | Description |
|
||||
|
||||
@@ -17,10 +17,6 @@ import { traefikIntegration } from "@site/docs/integrations/traefik";
|
||||
|
||||
This widget displays a compact overview of your Traefik instance, including total routers, entry points, protocol-specific router/service/middleware counts, and warning or error totals when Traefik reports them.
|
||||
|
||||
Advanced view shows every protocol and entry point, identifies every healthy Traefik source and version, and labels each resource or failed endpoint with its owning integration. Compact mode keeps the responsive aggregate overview and names each healthy source when multiple integrations are selected.
|
||||
|
||||
When multiple Traefik integrations are selected, data from healthy sources remains visible if another source fails. A warning indicator identifies each unavailable integration without exposing private request details. If every selected integration fails, the widget reports an error; if cached data remains visible after a later total failure, a stale-data warning is shown.
|
||||
|
||||
### Supported Integrations
|
||||
|
||||
<WidgetIntegrations
|
||||
|
||||
@@ -19,10 +19,6 @@ This widget displays the status of the UPS devices reported by a Network UPS Too
|
||||
status (online, on battery, charging or low battery), battery charge and estimated runtime, output load, and input and
|
||||
output voltage.
|
||||
|
||||
Every device keeps an integration-scoped identity. Advanced view always labels each device with its owning integration, while compact layouts add the source label only when multiple healthy integrations are present.
|
||||
|
||||
When multiple UPS integrations are selected, devices from healthy sources remain visible if another source fails. A warning indicator identifies each unavailable integration without exposing private request details. If every selected integration fails, the widget reports an error; if cached data remains visible after a later total failure, a stale-data warning is shown.
|
||||
|
||||
### Layouts
|
||||
|
||||
import screenshotFull from "./img/screenshot.png";
|
||||
|
||||
@@ -17,10 +17,6 @@ import { uptimeKumaIntegration } from "@site/docs/integrations/uptime-kuma";
|
||||
|
||||
This widget displays uptime statistics from your Uptime Kuma status page, including a visual uptime ring, average uptime percentage, and per-status monitor counts.
|
||||
|
||||
Advanced view always shows the uptime ring, all status totals, and the complete monitor list with source identity, regardless of compact visibility switches.
|
||||
|
||||
When multiple Uptime Kuma integrations are selected, monitors from healthy sources remain visible if another source fails. A warning indicator identifies each unavailable integration without exposing private request details. If every selected integration fails, the widget reports an error; if cached data remains visible after a later total failure, a stale-data warning is shown.
|
||||
|
||||
### Supported Integrations
|
||||
|
||||
<WidgetIntegrations
|
||||
|
||||
@@ -13,21 +13,23 @@ import { weatherWidget } from ".";
|
||||
|
||||
<WidgetHeader widget={weatherWidget} categories={["Weather"]} />
|
||||
|
||||
The Weather Widget displays current conditions and a responsive forecast for a selected location. New widgets start
|
||||
with the city and a three-day forecast visible. As the widget gets smaller, it keeps the current condition and
|
||||
temperature readable before removing secondary details or forecast days.
|
||||
The Weather Widget uses your location to display the current, highest and lowest temperature.
|
||||
In the compact view, select a condition icon or forecast day to open details with a mouse, keyboard or touch.
|
||||
The compact forecast shows up to the configured number of days. The advanced view combines current conditions with the
|
||||
available daily forecast, including humidity, wind, gusts, sunrise and sunset. Both views reuse the same cached Open
|
||||
Meteo response.
|
||||
|
||||
Select a forecast day to inspect precipitation, apparent temperature, humidity, wind, UV index, sunrise, sunset and
|
||||
daylight duration with a mouse, keyboard or touch. Open the advanced view for a current-condition summary, a 24-hour
|
||||
temperature and precipitation chart, and the complete seven-day forecast. The chart has an equivalent data table for
|
||||
assistive technologies.
|
||||
### Screenshots
|
||||
|
||||
The **Show humidity** option controls humidity across the compact current-conditions view, advanced summary, hourly
|
||||
details, and daily forecast details. Forecast cards use a roomier adaptive grid in advanced view, with four columns on
|
||||
the standard desktop surface and fewer columns as the surface narrows.
|
||||
import forecastWeather from "./img/forecast.png";
|
||||
import currentWeather from "./img/current.png";
|
||||
import advancedWeather from "./img/advanced-view.png";
|
||||
|
||||
Weather icons are static by default. Animation can be enabled in the widget configuration, but the operating system's
|
||||
reduced-motion preference always disables it.
|
||||
<div className="flex flex-col gap-1">
|
||||
<img src={forecastWeather} alt="Weather compact forecast" width={238} height={112} />
|
||||
<img src={currentWeather} alt="Weather compact current conditions" width={198} height={93} />
|
||||
<img src={advancedWeather} alt="Weather advanced view with seven-day forecast details" width={800} height={484} />
|
||||
</div>
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
|
||||
@@ -41,10 +43,5 @@ reduced-motion preference always disables it.
|
||||
|
||||
### Data source
|
||||
|
||||
We use the Open Meteo API to fetch the weather and locations. Active weather data refreshes every ten minutes, while
|
||||
identical location requests can reuse a server response for five minutes. A manual refresh may therefore return the
|
||||
same still-current server response. If a refresh fails, Homarr keeps the last successful data visible and shows an
|
||||
error indicator.
|
||||
|
||||
Set `NO_EXTERNAL_CONNECTION=true` to disable this and other internet-backed requests.
|
||||
We use the Open Meteo API to fetch the weather and locations.
|
||||
If you need to whitelist the api, please use the following URL: `https://open-meteo.com`
|
||||
@@ -28,24 +28,8 @@ export const weatherWidget: WidgetDefinition = {
|
||||
values: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: "no",
|
||||
},
|
||||
{
|
||||
name: "Show humidity",
|
||||
description: "Display humidity in current conditions, hourly details, and daily forecasts",
|
||||
values: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: "yes",
|
||||
},
|
||||
{
|
||||
name: "Animate weather icons",
|
||||
description: "Animate condition icons unless the operating system requests reduced motion",
|
||||
values: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: "no",
|
||||
},
|
||||
{
|
||||
name: "Use imperial speed units",
|
||||
description: "Display wind speed in mph instead of km/h",
|
||||
@@ -75,7 +59,7 @@ export const weatherWidget: WidgetDefinition = {
|
||||
values: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: "yes",
|
||||
defaultValue: "no",
|
||||
},
|
||||
{
|
||||
name: "Has forecast",
|
||||
@@ -83,13 +67,13 @@ export const weatherWidget: WidgetDefinition = {
|
||||
values: {
|
||||
type: "boolean",
|
||||
},
|
||||
defaultValue: "yes",
|
||||
defaultValue: "no",
|
||||
},
|
||||
{
|
||||
name: "Amount of forecast days",
|
||||
description: "Number of days to show in the forecast",
|
||||
values: "1-7",
|
||||
defaultValue: "3",
|
||||
defaultValue: "5",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -27,9 +27,7 @@ This widget shows two stats from your WUD instance: the total number of monitore
|
||||
]}
|
||||
/>
|
||||
|
||||
<WidgetInteractionGuide />
|
||||
|
||||
In advanced view, the widget always shows its title, update ring, monitored-container summary, and the full list of available container updates. The configured horizontal or vertical layout still applies. Visibility options only affect the compact board view. A failed refresh keeps the last WUD snapshot visible with a stale-data warning.
|
||||
<WidgetInteractionGuide advancedView={false} />
|
||||
|
||||
### Adding the widget
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"label": "Workshop",
|
||||
"collapsed": true,
|
||||
"position": 4,
|
||||
"link": {
|
||||
"type": "doc",
|
||||
"id": "workshop/index"
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
title: Workshop development
|
||||
sidebar_position: 4
|
||||
tags:
|
||||
- Workshop
|
||||
- Developer
|
||||
- Docusaurus
|
||||
- PocketBase
|
||||
---
|
||||
|
||||
# Workshop development
|
||||
|
||||
Install the repository dependencies and make sure Docker is available before starting the Workshop services. See the
|
||||
[main development guide](../advanced/development/getting-started) for the repository prerequisites.
|
||||
|
||||
## Run PocketBase and documentation
|
||||
|
||||
Start PocketBase in one terminal:
|
||||
|
||||
```sh
|
||||
cp apps/workshop/.env.example apps/workshop/.env
|
||||
docker compose --env-file apps/workshop/.env -f apps/workshop/docker-compose.yml up --build workshop
|
||||
```
|
||||
|
||||
Then start Docusaurus in another terminal:
|
||||
|
||||
```sh
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
|
||||
```
|
||||
|
||||
PocketBase runs on port 8090 and Docusaurus on port 3003. Stopping the services preserves the PocketBase volume.
|
||||
|
||||
Open `http://127.0.0.1:8090/_/` and create the first PocketBase superuser. The Workshop UI is available at
|
||||
`http://127.0.0.1:3003/workshop`.
|
||||
|
||||
Point Homarr at the local services with:
|
||||
|
||||
```dotenv
|
||||
HOMARR_WEBSITE_URL=http://127.0.0.1:3003
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090
|
||||
```
|
||||
|
||||
Restart `pnpm dev:docs` after changing the Docusaurus URL variables. If Homarr is also running, restart its `pnpm dev`
|
||||
process after changing the Workshop URLs. GitHub OAuth is optional locally. See
|
||||
[`apps/workshop/README.md`](https://github.com/homarr-labs/homarr/blob/HEAD/apps/workshop/README.md) for OAuth,
|
||||
Workshop moderator promotion, logs, tests, backup, restore, stopping, and intentional volume reset.
|
||||
|
||||
## URL testing
|
||||
|
||||
Docusaurus reads `HOMARR_WEBSITE_URL`, `WORKSHOP_API_URL`, and `WORKSHOP_WEB_URL` when it starts or builds. Homarr uses
|
||||
`WORKSHOP_WEB_URL` for public Workshop links while API and documentation origins remain independent. These values do
|
||||
not rely on `NEXT_PUBLIC_*` build variables. Test separate origins before release:
|
||||
|
||||
```sh
|
||||
HOMARR_WEBSITE_URL=http://127.0.0.1:3003 \
|
||||
WORKSHOP_API_URL=http://127.0.0.1:8090 \
|
||||
WORKSHOP_WEB_URL=http://127.0.0.1:3003/workshop \
|
||||
pnpm dev:docs
|
||||
```
|
||||
|
||||
The [Workshop operator guide](./operator) covers production environment variables, staging, and service validation.
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
title: Homarr provider
|
||||
sidebar_position: 2
|
||||
tags:
|
||||
- Assistant
|
||||
- AI
|
||||
- Privacy
|
||||
- Community
|
||||
---
|
||||
|
||||
import { OpenCollectiveLink } from "@site/src/components/assistant-provider/open-collective-link";
|
||||
|
||||
# Homarr provider
|
||||
|
||||
The Homarr provider gives Community Workshop users access to the **Homarr model** without an administrator API key.
|
||||
The Homarr team chooses the upstream model based on quality, reliability, and cost.
|
||||
|
||||
## Set it up
|
||||
|
||||
1. Open **Management → Assistant**.
|
||||
2. Select **Homarr** as the provider.
|
||||
3. Save the provider connection.
|
||||
4. Select the available model, enable the assistant, and save.
|
||||
5. Open the assistant and sign in to the Workshop when asked.
|
||||
|
||||
## Daily allowance
|
||||
|
||||
- Each user gets **50 request units per UTC day**.
|
||||
- Each model request uses one unit. A tool loop can use several units.
|
||||
- Forwarded requests count even if the model later fails.
|
||||
- Allowances reset at 00:00 UTC. A shared daily limit also applies.
|
||||
|
||||
The battery beside the model selector shows the remaining allowance and reset time. Workshop enforces all limits on
|
||||
its server; changing the browser clock cannot bypass them.
|
||||
|
||||
## Privacy
|
||||
|
||||
Workshop keeps the OpenRouter API key on its server. Homarr and the browser never receive it.
|
||||
|
||||
Prompts, attachments, tools, and tool results pass through Workshop and OpenRouter. Workshop does not retain this
|
||||
content or the responses. It stores only the user, UTC day, and counters needed to enforce allowances. OpenRouter is
|
||||
configured for zero data retention with data collection disabled. Homarr does not use this content to train models.
|
||||
|
||||
We would rather become millionaires in a more ethical manner.
|
||||
|
||||
The community pays for these requests. Donations help cover the cost.
|
||||
|
||||
<OpenCollectiveLink />
|
||||
|
||||
Workshop operators can change the upstream model, per-user allowance, global allowance, and security limits in the
|
||||
[Workshop operator guide](./operator#homarr-provider-proxy).
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
title: Community Workshop
|
||||
sidebar_label: Overview
|
||||
tags:
|
||||
- Workshop
|
||||
- Custom Widgets
|
||||
- Custom CSS
|
||||
---
|
||||
|
||||
# Community Workshop
|
||||
|
||||
The [Community Workshop](pathname:///workshop) is where Homarr users share Custom Widgets and dashboard CSS.
|
||||
|
||||
:::warning Experimental
|
||||
|
||||
Workshop content is made by the community. Review it before installation and back up your Homarr instance.
|
||||
|
||||
:::
|
||||
|
||||
## Find and install content
|
||||
|
||||
Browse the Workshop by type, date, popularity, or recent activity. Each submission includes its source, screenshots,
|
||||
author, revision, comments, and reports.
|
||||
|
||||
Administrators can install content without leaving Homarr:
|
||||
|
||||
- **Custom Widgets:** open **Management → Custom Widgets**, then select **Import from Workshop**.
|
||||
- **Custom CSS:** open a board's **Settings → Custom CSS**, then select **Import from Workshop**.
|
||||
|
||||
Homarr shows the complete source and required permissions before installation. Widget credentials are entered in your
|
||||
Homarr instance and are never included in a Workshop submission.
|
||||
|
||||
## Share your work
|
||||
|
||||
For a saved Custom Widget, open **Management → Custom Widgets**, use its action menu, and select **Publish to
|
||||
Workshop**. Review the public listing details and source URLs, then publish the credential-free export. Custom CSS and
|
||||
manual submissions can be shared directly from the Workshop website with **Share yours**.
|
||||
|
||||
Publishing creates the listing immediately. To update or delete it, review reports, or mark it outdated, open the
|
||||
Workshop website, sign in, and select **Yours**. Editing a submission does not change its Workshop URL.
|
||||
|
||||
## Voting, comments, and reports
|
||||
|
||||
Signed-in users can vote, comment, and report submissions. Authors can edit their own comments and submissions.
|
||||
Workshop moderators can review reports, remove comments, and delete unsafe content. Reports do not remove a submission
|
||||
automatically.
|
||||
|
||||
A Workshop moderator manages the public community. This is separate from the administrator role on a Homarr instance.
|
||||
|
||||
Deployment and server configuration are in the [Workshop operator guide](./operator). Local setup is in
|
||||
[Workshop development](./development).
|
||||
@@ -1,144 +0,0 @@
|
||||
---
|
||||
title: Workshop operator guide
|
||||
sidebar_label: Operator guide
|
||||
sidebar_position: 3
|
||||
tags:
|
||||
- Workshop
|
||||
- Deployment
|
||||
- Environment variables
|
||||
- PocketBase
|
||||
---
|
||||
|
||||
# Workshop operator guide
|
||||
|
||||
Workshop is powered by [PocketBase](https://pocketbase.io). The production image compiles the current Docusaurus site
|
||||
and serves it together with the API from one origin. The deployment lives in `apps/workshop/` with a Dockerfile,
|
||||
Compose configuration, version-controlled migrations, hooks, a smoke test, and ACT-local workflow instructions.
|
||||
|
||||
Submission links include Open Graph and X/Twitter metadata for the submission title, description, and type. The first
|
||||
uploaded screenshot is used as the social preview image; submissions without screenshots use the Homarr logo.
|
||||
|
||||
Run `pnpm docker:workshop` for PocketBase on port 8090, then run
|
||||
`WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs` for Docusaurus on port 3003. `pnpm docker:docs` builds the
|
||||
combined production layout. Normal start and stop commands retain data; `down --volumes` is an intentional,
|
||||
irreversible development reset. See [Workshop development](./development) for the complete local setup.
|
||||
|
||||
## Authentication and notifications
|
||||
|
||||
GitHub needs only the central callback `https://<workshop-host>/api/oauth2-redirect`. Keep PocketBase origins
|
||||
unrestricted unless every self-hosted Homarr caller is explicitly listed. Configure both `GITHUB_CLIENT_ID` and
|
||||
`GITHUB_CLIENT_SECRET`; supplying only one is a startup error. Set `WORKSHOP_PUBLIC_ORIGIN` in production so generated
|
||||
links and email use the public host. Workshop stores only the OAuth provider username in `users.name`, and users cannot
|
||||
rename it. Map **OAuth2 username** to `name`; leave OAuth2 id, full name, and avatar mappings empty. Profile and avatar
|
||||
URLs are derived from `https://github.com/<username>` and `https://github.com/<username>.png`, so PocketBase does not
|
||||
store duplicate profile metadata or avatar files. Configure SMTP for comment, report, and removal notices.
|
||||
|
||||
## Environment variables
|
||||
|
||||
The public URL contract and Workshop-only server configuration are:
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| -------------------------------------- | --------------------------------------------- | ------------------------------ |
|
||||
| `HOMARR_WEBSITE_URL` | Documentation/site base used by Homarr links | `https://homarr.dev` |
|
||||
| `WORKSHOP_API_URL` | PocketBase API used by Homarr and docs | `HOMARR_WEBSITE_URL` |
|
||||
| `WORKSHOP_WEB_URL` | Public Workshop links in Homarr | `HOMARR_WEBSITE_URL/workshop` |
|
||||
| `WORKSHOP_PUBLIC_ORIGIN` | PocketBase-generated links and email | Required in production |
|
||||
| `PB_ALLOWED_ORIGINS` | PocketBase browser origins | `*` for the central community |
|
||||
| `OPENROUTER_API_KEY` | Server-only key for the Homarr provider proxy | Provider disabled when empty |
|
||||
| `HOMARR_AI_DAILY_REQUEST_LIMIT` | Per-user daily provider allowance | `50` |
|
||||
| `HOMARR_AI_GLOBAL_DAILY_REQUEST_LIMIT` | Shared daily ceiling across all users | `10000` |
|
||||
| `HOMARR_AI_OPENROUTER_BASE_URL` | OpenRouter-compatible proxy upstream | `https://openrouter.ai/api/v1` |
|
||||
| `HOMARR_AI_OPENROUTER_MODEL` | Server-selected upstream model | Selected by the Homarr team |
|
||||
|
||||
All public URL variables reject non-HTTP protocols, embedded credentials, query strings, and fragments, and normalize
|
||||
trailing slashes when Homarr or Docusaurus starts. The deprecated docs-only `WORKSHOP_URL` alias remains for one
|
||||
release; `WORKSHOP_API_URL` takes precedence for API calls and `WORKSHOP_WEB_URL` takes precedence for public Workshop
|
||||
links. The combined Workshop image writes the public URLs into its browser
|
||||
runtime configuration whenever the container starts; the Docker build arguments remain fallbacks. Restart the
|
||||
container after changing these values, without rebuilding it.
|
||||
|
||||
### Homarr provider proxy
|
||||
|
||||
The [Homarr provider](./homarr-provider) proxy is part of the Workshop image. `OPENROUTER_API_KEY` enables the proxy,
|
||||
`HOMARR_AI_OPENROUTER_BASE_URL` selects its OpenRouter-compatible upstream, `HOMARR_AI_OPENROUTER_MODEL` selects the
|
||||
private upstream model behind the stable `homarr/model` alias, and the two allowance variables provide per-user and
|
||||
shared request ceilings. Lowering either allowance takes effect for existing users. The upstream URL cannot contain
|
||||
embedded credentials, a query, or a fragment and must use HTTPS. Tests can explicitly opt into an insecure local mock
|
||||
with `HOMARR_AI_ALLOW_INSECURE_UPSTREAM=true`; do not use that setting with a real API key.
|
||||
|
||||
The proxy overwrites client routing and generation limits. It requires OpenRouter zero-data-retention routing, denies
|
||||
data-collecting providers, limits responses to 32,768 completion tokens, and does not retain prompts, responses, or
|
||||
per-request telemetry. It accepts only uploaded image data, conservatively bounds text to the advertised context, and
|
||||
allows at most two concurrent requests per user and 100 across the service. Keep an OpenRouter account-level credit
|
||||
limit as the final monetary backstop because request quotas are not dollar accounting.
|
||||
|
||||
Run `pnpm test:workshop` to test authentication, streaming, UTC reset, atomic per-user and global quota enforcement,
|
||||
failed-request accounting, identity attribution, server-selected model routing, function tools, input and output
|
||||
controls, privacy routing, spoofing resistance, absence of request logs, and migration rollback.
|
||||
|
||||
An operator can explicitly smoke-test a deployed Workshop against its real configured upstream with a dedicated,
|
||||
low-quota test user:
|
||||
|
||||
```sh
|
||||
HOMARR_AI_LIVE_SMOKE=1 \
|
||||
WORKSHOP_LIVE_TEST_URL=https://workshop.example.com \
|
||||
WORKSHOP_LIVE_TEST_TOKEN=<temporary-pocketbase-user-token> \
|
||||
pnpm test:workshop-live-provider
|
||||
```
|
||||
|
||||
This optional check consumes one provider request. The test client needs neither the OpenRouter key nor the private
|
||||
upstream model ID. Do not run it in CI or with a normal user's Workshop token.
|
||||
|
||||
The Custom Widget AI tests can use this proxy. Set `AI_PROVIDER_BASE_URL` to the `/api/ai/v1` endpoint and
|
||||
`AI_PROVIDER_API_KEY` to a temporary Workshop user token. See
|
||||
[Agent authoring](../management/custom-widgets/agent-authoring#evaluate-the-copied-prompt) for the command.
|
||||
|
||||
## Staging and demo deployment
|
||||
|
||||
Use `apps/workshop/docker-compose.staging.yml` with the published `ghcr.io/homarr-labs/workshop:v2` image for a v2
|
||||
preview. Copy `apps/workshop/.env.staging.example` to `.env.staging`; its default public contract is:
|
||||
|
||||
```dotenv
|
||||
HOMARR_WEBSITE_URL=https://v2.preview.homarr.dev
|
||||
WORKSHOP_API_URL=https://v2.preview.homarr.dev
|
||||
WORKSHOP_PUBLIC_ORIGIN=https://v2.preview.homarr.dev
|
||||
PB_ALLOWED_ORIGINS=*
|
||||
```
|
||||
|
||||
Start or update it from the repository root:
|
||||
|
||||
```sh
|
||||
docker compose --env-file apps/workshop/.env.staging \
|
||||
-f apps/workshop/docker-compose.staging.yml pull
|
||||
docker compose --env-file apps/workshop/.env.staging \
|
||||
-f apps/workshop/docker-compose.staging.yml up --detach
|
||||
curl --fail http://127.0.0.1:8090/api/health
|
||||
```
|
||||
|
||||
The service binds to `127.0.0.1:8090`. Terminate TLS at a reverse proxy and forward the **entire hostname**, including
|
||||
`/api`, `/_/`, and `/workshop-runtime-config.js`; forwarding only `/workshop` breaks PocketBase and runtime URL loading.
|
||||
This combined image serves both the documentation site and PocketBase, so use a dedicated Workshop hostname if the
|
||||
chosen hostname already serves Homarr itself. In that case, replace the hostname in all three URL values. Homarr
|
||||
derives the public Workshop page by appending `/workshop` to `HOMARR_WEBSITE_URL`.
|
||||
|
||||
The Compose volume preserves PocketBase data across pulls and container recreation. The mutable `v2` tag is suitable
|
||||
for previews; use the `sha-<commit>` tag for promotion. For the public central Workshop, `PB_ALLOWED_ORIGINS=*` allows
|
||||
self-hosted Homarr origins to use the API and OAuth. A private demo may use a comma-separated origin allowlist instead.
|
||||
GitHub OAuth requires both credentials and the callback `https://v2.preview.homarr.dev/api/oauth2-redirect`. Restart
|
||||
after changing environment values, then verify the health endpoint, `/workshop`, a public listing request, GitHub
|
||||
sign-in, and an installation from the Homarr preview.
|
||||
|
||||
Back up application and PocketBase data before deployment. Publish the combined image by immutable digest, test the
|
||||
exact digest in staging, rehearse application rollback and PocketBase restore, then promote that same digest.
|
||||
|
||||
## Client architecture
|
||||
|
||||
Workshop access is centralized in `@homarr/workshop/backend`. It exports the typed `WorkshopBackend`, its typed
|
||||
PocketBase instance, stable TanStack Query option factories, and the `useWorkshop…` query and mutation hooks used by
|
||||
Homarr. The documentation site uses the same backend rather than maintaining a second API contract.
|
||||
|
||||
Listing fields derived by the `workshop_listings` view—such as submission type, score, and counts—are normalized by the
|
||||
shared client. Healthy deployments keep filtering, sorting, and pagination in PocketBase. If a persistent development
|
||||
volume still has an older view that rejects those fields, the client retries without them and applies the same
|
||||
operations locally; it can also fall back to the base submissions collection. Production deployments should
|
||||
nevertheless apply the current migration so the view exposes the complete metadata.
|
||||
@@ -362,7 +362,7 @@ html[data-workshop-detail-loading] #workshop-detail-route-fallback {
|
||||
box-sizing: border-box;
|
||||
color: #4b5563;
|
||||
display: flex;
|
||||
font: 500 0.875rem/1.5 "Work Sans", system-ui, sans-serif;
|
||||
font: 500 0.875rem/1.5 Inter, system-ui, sans-serif;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
padding: 2rem;
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"postcss": "^8.5.16",
|
||||
"posthog-docusaurus": "^2.0.5",
|
||||
"prism-react-renderer": "^2.4.1",
|
||||
"random-word-slugs": "^0.1.7",
|
||||
"react": "catalog:",
|
||||
"react-day-picker": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
.link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.45rem 0.7rem;
|
||||
border: 1px solid var(--ifm-color-emphasis-300);
|
||||
border-radius: 0.5rem;
|
||||
background: var(--ifm-background-surface-color);
|
||||
color: var(--ifm-font-color-base);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background-color 150ms ease,
|
||||
border-color 150ms ease;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
border-color: var(--ifm-color-emphasis-500);
|
||||
background: var(--ifm-color-emphasis-100);
|
||||
color: var(--ifm-font-color-base);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link:focus-visible {
|
||||
outline: 2px solid var(--ifm-color-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.link {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import Translate from "@docusaurus/Translate";
|
||||
|
||||
import styles from "./open-collective-link.module.css";
|
||||
|
||||
export const OpenCollectiveLink = () => (
|
||||
<a className={styles.link} href="https://opencollective.com/homarr">
|
||||
<img className={styles.logo} src="/img/opencollective.svg" alt="" aria-hidden />
|
||||
<Translate id="workshop.provider.donate">Donate to Homarr on OpenCollective</Translate>
|
||||
</a>
|
||||
);
|
||||
Loaded 100 of 1300 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user