Compare commits

..
Author SHA1 Message Date
ajnart cebb5dc2bb fix(docs): restore dataflow backgrounds 2026-08-13 22:35:43 +02:00
ajnart 3318ddc8c3 docs: add dataflow preview 2026-08-13 22:24:24 +02:00
ajnart e656731729 fix(docs): cycle integration flow icons 2026-08-13 22:21:40 +02:00
ajnart 2257fe2a08 fix(docs): simplify integration nodes 2026-08-13 22:16:54 +02:00
ajnart 673589f6c8 fix(docs): restore homepage integration flow 2026-08-13 22:13:08 +02:00
ajnart c642f9a8d3 fix: terminate invalid provider streams 2026-08-12 13:35:40 +02:00
ajnart 83968be07e feat: complete the Homarr provider endpoint 2026-08-12 13:27:04 +02:00
ajnart 019bd11017 fix: clarify provider request accounting 2026-08-12 12:58:17 +02:00
ajnart c16e00f94c fix: address provider review feedback 2026-08-12 12:51:15 +02:00
ajnart bffa3e723c fix: harden the Homarr provider 2026-08-12 12:33:04 +02:00
ajnart 0774b02878 Harden Homarr provider limits 2026-08-11 22:53:49 +02:00
ajnart 47c349bcd4 docs: improve workshop and homepage 2026-08-11 22:38:43 +02:00
ajnart 874432becf Handle provider activity load failures 2026-08-11 22:16:51 +02:00
ajnart 958cc68cba Localize provider errors 2026-08-11 22:04:38 +02:00
ajnart abaf845e94 Address CodeRabbit provider feedback 2026-08-11 22:03:21 +02:00
ajnart ea9075bde5 Address Homarr provider review feedback 2026-08-11 21:50:40 +02:00
ajnart 4ebaea29fd Harden Homarr provider integration 2026-08-11 19:47:11 +02:00
ajnart 165201632d Complete Homarr provider integration 2026-08-11 19:19:17 +02:00
ajnart bc59355eab Add Homarr provider proxy foundation 2026-08-11 19:02:34 +02:00
Thomas Camlong 9143c15454 Merge branch 'feat/repair-stack-mobile-20260811' into feat/repair-stack-assistant-20260811 2026-08-11 17:03:22 +02:00
Thomas Camlong 3428e3682b Merge branch 'feat/repair-stack-widgets-20260811' into feat/repair-stack-mobile-20260811 2026-08-11 17:03:22 +02:00
Thomas Camlong 5b8f9187d5 Merge branch 'feat/repair-stack-dnd-20260811' into feat/repair-stack-widgets-20260811 2026-08-11 17:03:22 +02:00
Thomas Camlong a61e9d5efc Merge branch 'feat/repair-stack-v2-20260811' into feat/repair-stack-dnd-20260811 2026-08-11 17:03:21 +02:00
Thomas Camlong bf15e64b63 style: format v2 workshop files 2026-08-11 17:03:09 +02:00
Thomas Camlong e2dd68fdbc Merge branch 'feat/auto-mobile-boards' into feat/homarr-assistant 2026-08-11 16:59:12 +02:00
Thomas Camlong 16953fc019 Merge branch 'feat/widget-modernization' into feat/auto-mobile-boards 2026-08-11 16:57:29 +02:00
Thomas Camlong 545a5c1d96 Merge branch 'feat/dnd-kit-grid' into feat/widget-modernization 2026-08-11 16:57:05 +02:00
Thomas Camlong e55e709b72 Merge branch 'v2' into feat/dnd-kit-grid 2026-08-11 16:55:34 +02:00
Thomas Camlong 117eabd30b Merge branch 'dev' into v2 2026-08-11 16:55:16 +02:00
Thomas 38af22aba0 test: cover public custom widgets for guests (#6558) 2026-08-11 15:25:22 +02:00
Thomas Camlong cba20bf665 fix: simplify grid interaction previews 2026-08-11 15:09:53 +02:00
Thomas 827ba7cda4 feat: rewrite image picker (#6556)
* feat: rewrite image picker

* style: auto-fix lint and format

* fix: address image picker review

* fix: preserve unicode icon searches

* test: stabilize custom widget login

* fix: suggest image searches only on create
2026-08-11 14:51:31 +02:00
Thomas Camlong 513549784f Merge branch 'feat/auto-mobile-boards' into feat/homarr-assistant 2026-08-11 11:36:06 +02:00
Thomas Camlong a96105fb09 Merge branch 'feat/widget-modernization' into feat/auto-mobile-boards 2026-08-11 11:35:40 +02:00
Thomas Camlong f28d61faa1 Merge branch 'feat/dnd-kit-grid' into feat/widget-modernization 2026-08-11 11:35:14 +02:00
Thomas Camlong dc66493caa perf: isolate board grid previews 2026-08-11 11:27:58 +02:00
Thomas Camlong b7934d7e87 Merge branch 'feat/auto-mobile-boards' into feat/homarr-assistant 2026-08-11 11:06:18 +02:00
Thomas Camlong 4b5166dfed Merge branch 'feat/widget-modernization' into feat/auto-mobile-boards 2026-08-11 11:05:52 +02:00
Thomas Camlong afe17eefc8 Merge branch 'feat/dnd-kit-grid' into feat/widget-modernization 2026-08-11 11:05:26 +02:00
Thomas Camlong a7ce15eaea Merge branch 'v2' into feat/dnd-kit-grid 2026-08-11 11:05:01 +02:00
Thomas Camlong 44c22ccad3 ci: retry flaky container-backed unit tests in fast gate
Integration tests (nzbget, nextcloud, pi-hole, etc.) start Docker
containers and intermittently time out under shared-runner load, which
fails the fast gate and skips the container job that publishes the PR
preview image. Retry failing unit tests twice so transient container
startup failures do not block the image publish.
2026-08-11 11:03:33 +02:00
Thomas Camlong eae17877a1 ci: make e2e tests resilient to CI resource contention
E2E specs spin up Docker containers in parallel and intermittently time
out waiting for UI elements under shared-runner load. Limit parallel
workers to 2 and retry flaky failures twice so the container job can
complete and publish the PR preview image.
2026-08-11 11:03:12 +02:00
ajnart 6435955a51 test: wait for Home Assistant API readiness 2026-08-11 11:02:43 +02:00
Thomas Camlong 09bc5a16f4 test: align assistant credential flow 2026-08-10 20:43:03 +02:00
Thomas Camlong 223ef33bfd Merge branch 'feat/auto-mobile-boards' into feat/homarr-assistant 2026-08-10 20:13:05 +02:00
Thomas Camlong d8f71f46be Merge branch 'feat/widget-modernization' into feat/auto-mobile-boards 2026-08-10 20:13:00 +02:00
Thomas Camlong bc687b90ad Merge branch 'feat/dnd-kit-grid' into feat/widget-modernization 2026-08-10 20:13:00 +02:00
Thomas Camlong 3f9e83ab97 Merge branch 'feat/widget-modernization' into feat/auto-mobile-boards 2026-08-10 20:12:36 +02:00
Thomas Camlong 8fff3e413e Merge branch 'feat/dnd-kit-grid' into feat/widget-modernization 2026-08-10 20:12:33 +02:00
Thomas Camlong c2b7e43d7f Merge branch 'feat/dnd-kit-grid' into feat/widget-modernization 2026-08-10 20:12:05 +02:00
Thomas Camlong 16dd251276 fix: disable app context menu 2026-08-10 20:10:22 +02:00
Thomas Camlong b830479f09 Merge branch 'v2' into feat/dnd-kit-grid 2026-08-10 20:08:16 +02:00
Thomas Camlong 7a45b0d119 feat(workshop): add social previews 2026-08-10 20:07:51 +02:00
ajnart 8dd6cbbfdb fix assistant focus and settings 2026-08-10 19:49:33 +02:00
Thomas Camlong b7ddc028a5 Merge commit '1f17c77c9' into feat/homarr-assistant 2026-08-10 19:48:29 +02:00
Thomas Camlong 1f17c77c97 Merge commit '7d3625ecd' into feat/auto-mobile-boards 2026-08-10 19:48:20 +02:00
Thomas Camlong 7d3625ecdc Merge commit '6a08ae395' into feat/widget-modernization 2026-08-10 19:48:12 +02:00
Thomas Camlong 6a08ae3951 Merge commit '8b49982c3' into feat/dnd-kit-grid 2026-08-10 19:48:03 +02:00
Thomas Camlong 8b49982c3a fix: persist Workshop PocketBase data 2026-08-10 19:47:29 +02:00
Thomas Camlong 3c8fe54618 Merge commit '05b4f4dc5' into HEAD 2026-08-10 19:17:26 +02:00
Thomas Camlong 05b4f4dc52 Merge commit 'cae663407' into HEAD 2026-08-10 19:17:14 +02:00
Thomas Camlong cae6634074 Merge commit 'e5899521b' into HEAD 2026-08-10 19:16:56 +02:00
Thomas Camlong e5899521b9 Merge commit 'ad2fa1605' into HEAD 2026-08-10 19:16:39 +02:00
Thomas Camlong ad2fa1605e fix: defer Spotlight without loading feedback 2026-08-10 19:16:16 +02:00
Thomas Camlong b4bbaa2e12 Merge commit '55bc3353b' into HEAD 2026-08-10 19:14:07 +02:00
Thomas Camlong 55bc3353b6 Merge commit 'a60f15d36' into HEAD
# Conflicts:
#	apps/nextjs/src/app/[locale]/boards/[name]/settings/page.tsx
2026-08-10 19:13:51 +02:00
Thomas Camlong a60f15d366 Merge commit '9a2fe1324' into HEAD 2026-08-10 19:13:13 +02:00
Thomas Camlong 9a2fe13242 Merge remote-tracking branch 'origin/v2' into HEAD 2026-08-10 19:12:48 +02:00
Thomas Camlong 106fca2a61 fix: simplify assistant key management 2026-08-10 19:09:04 +02:00
Thomas Camlong 920631373c fix: refine responsive layout settings 2026-08-10 19:09:04 +02:00
ajnart 533003e49b test custom widget assistant authoring 2026-08-10 19:02:55 +02:00
Thomas Camlong a6061348c3 fix: polish settings and command menu 2026-08-10 19:00:43 +02:00
Thomas Camlong 8557f2107c Merge commit 'f81d66a16' into feat/homarr-assistant 2026-08-10 18:48:57 +02:00
Thomas Camlong f81d66a16f Merge commit 'd04eec39b' into feat/auto-mobile-boards 2026-08-10 18:48:37 +02:00
Thomas Camlong d04eec39b6 Merge commit '22e955529' into feat/widget-modernization 2026-08-10 18:48:22 +02:00
Thomas Camlong 22e955529b Merge commit 'e524515ca' into feat/dnd-kit-grid
# Conflicts:
#	apps/nextjs/src/components/workshop/workshop-css-import-button.tsx
2026-08-10 18:48:05 +02:00
Thomas Camlong e524515ca5 fix(workshop): streamline import review 2026-08-10 18:47:23 +02:00
Thomas Camlong 920c1a7a4c Merge commit '3fbd7ef04' into feat/propagate-workshop-install-assistant 2026-08-10 18:21:51 +02:00
Thomas Camlong 3fbd7ef04d Merge commit '333bab6d2' into feat/propagate-workshop-install-mobile 2026-08-10 18:21:31 +02:00
Thomas Camlong 333bab6d2a Merge commit 'ba582d04c' into feat/propagate-workshop-install-widgets 2026-08-10 18:21:18 +02:00
Thomas Camlong ba582d04ca Merge commit 'cf3c4b415' into feat/propagate-workshop-install-dnd 2026-08-10 18:21:00 +02:00
Thomas Camlong cf3c4b4152 fix(workshop): stack widget install flow 2026-08-10 18:19:31 +02:00
Thomas Camlong 3ea931d456 Merge commit '46b782847' into feat/propagate-workshop-admin-assistant
# Conflicts:
#	apps/nextjs/src/app/[locale]/manage/custom-widgets/_copy-ai-prompt-button.tsx
2026-08-10 17:40:40 +02:00
Thomas Camlong 46b782847c Merge commit 'cbb383959' into feat/propagate-workshop-admin-mobile 2026-08-10 17:38:05 +02:00
Thomas Camlong cbb383959e Merge commit '3ac76491c' into feat/propagate-workshop-admin-widgets 2026-08-10 17:37:51 +02:00
Thomas Camlong 3ac76491c6 Merge commit '378bc0cd2' into feat/propagate-workshop-admin-dnd 2026-08-10 17:37:29 +02:00
Thomas Camlong 378bc0cd28 fix(workshop): expose moderation 2026-08-10 17:37:13 +02:00
Thomas Camlong be93fb330a fix: simplify custom widget preview controls 2026-08-10 17:22:40 +02:00
ajnart 6d72f4ab83 fix assistant custom widget creation 2026-08-10 17:14:15 +02:00
Thomas Camlong 5125b46f03 Merge commit 'eebe3ab0f' into feat/homarr-assistant 2026-08-10 16:25:54 +02:00
Thomas Camlong eebe3ab0fd Merge commit '001bb9dc4' into feat/auto-mobile-boards 2026-08-10 16:25:36 +02:00
Thomas Camlong 001bb9dc4e Merge commit '4a96078bf' into feat/widget-modernization 2026-08-10 16:25:23 +02:00
Thomas Camlong 4a96078bf7 Merge commit '893fcd6ed' into feat/dnd-kit-grid 2026-08-10 16:25:10 +02:00
Thomas Camlong 893fcd6ed1 fix(workshop): use PocketBase name 2026-08-10 16:24:52 +02:00
Thomas Camlong 5387b3f92a Merge mobile Workshop consumer fix into assistant 2026-08-10 15:33:44 +02:00
Thomas Camlong aa8d68d224 Merge widget Workshop consumer fix into mobile 2026-08-10 15:33:30 +02:00
Thomas Camlong 2f12fd6fb3 Merge DnD Workshop consumer fix into widgets 2026-08-10 15:33:17 +02:00
Thomas Camlong 581e5105cc Merge v2 Workshop consumer fix into DnD 2026-08-10 15:33:03 +02:00
Thomas Camlong 0422675bc1 fix(workshop): use GitHub handles in Homarr 2026-08-10 15:32:32 +02:00
ajnart c7fd5eceae fix assistant tool result streaming 2026-08-10 15:24:34 +02:00
Thomas Camlong 414f5dfe53 Merge mobile docs typecheck fix into assistant 2026-08-10 15:22:55 +02:00
Thomas Camlong 34918352f1 Merge widget docs typecheck fix into mobile 2026-08-10 15:22:43 +02:00
Thomas Camlong 1450162d31 Merge DnD docs typecheck fix into widgets 2026-08-10 15:22:29 +02:00
Thomas Camlong 6e1b6d9ff3 Merge v2 docs typecheck fix into DnD 2026-08-10 15:22:17 +02:00
Thomas Camlong d610206896 fix(docs): exclude generated build from typecheck 2026-08-10 15:21:47 +02:00
Thomas Camlong a6dc90601e Merge mobile Workshop identity fix into assistant 2026-08-10 15:15:57 +02:00
Thomas Camlong efd3dae0db Merge widget Workshop identity fix into mobile 2026-08-10 15:15:36 +02:00
Thomas Camlong 2d594c22cc Merge DnD Workshop identity fix into widgets 2026-08-10 15:15:17 +02:00
Thomas Camlong 78d22c2514 Merge v2 Workshop identity fix into DnD 2026-08-10 15:14:56 +02:00
Thomas Camlong 64403079d8 fix(workshop): simplify GitHub identities 2026-08-10 15:14:32 +02:00
ajnart dc40ec2715 test assistant v7 request payload 2026-08-10 14:59:57 +02:00
ajnart b03849604e test assistant AI SDK v7 transport 2026-08-10 14:57:47 +02:00
ajnart 82bc4e21a9 chore upgrade assistant to AI SDK v7 2026-08-10 14:48:09 +02:00
Thomas Camlong 2495b4c4a2 Merge latest mobile boards into assistant 2026-08-10 14:24:50 +02:00
Thomas Camlong e5920b2f56 Merge latest widget modernization into mobile boards 2026-08-10 14:24:33 +02:00
Thomas Camlong 6ce08508fa Merge latest dnd-kit grid into widget modernization 2026-08-10 14:24:13 +02:00
Thomas Camlong 76a2aebb4a Merge latest v2 into dnd-kit grid 2026-08-10 14:23:52 +02:00
Thomas Camlong 015e0cbf63 Merge dev into v2 2026-08-10 14:23:19 +02:00
ajnart 305c9af2da fix assistant exports and tool calls 2026-08-10 14:09:44 +02:00
ajnart f7eed7ce8a refine assistant window controls 2026-08-10 14:09:44 +02:00
Thomas Camlong 6baac9298f Merge commit '9054dcad7' into feat/homarr-assistant 2026-08-10 13:22:00 +02:00
Thomas Camlong 9054dcad7a Merge commit 'f9a457982' into feat/auto-mobile-boards 2026-08-10 13:21:29 +02:00
Thomas Camlong f9a4579821 Merge commit '7cd56bdbd' into feat/widget-modernization 2026-08-10 13:20:49 +02:00
Thomas Camlong 7cd56bdbda perf: isolate grid interaction renders 2026-08-10 13:20:13 +02:00
Thomas Camlong 0f862de208 Merge feat/auto-mobile-boards into feat/homarr-assistant 2026-08-10 12:02:53 +02:00
Thomas Camlong 8ee8990f09 Merge feat/widget-modernization into feat/auto-mobile-boards 2026-08-10 12:02:35 +02:00
Thomas Camlong b6053a7b31 Merge feat/dnd-kit-grid into feat/widget-modernization 2026-08-10 12:02:06 +02:00
Thomas Camlong fe37673c95 fix: stabilize rail and resize interactions 2026-08-10 12:01:35 +02:00
ajnart 491b430b64 fix assistant dashboard placement 2026-08-10 11:58:32 +02:00
ajnart 12fe596a9e feat: add assistant window controls 2026-08-10 11:39:52 +02:00
ajnart 4118b6dd3b fix assistant tool continuation 2026-08-10 11:34:33 +02:00
Thomas Camlong ceec25af81 merge WUD OpenAPI schema 2026-08-10 10:54:58 +02:00
Thomas Camlong 6088061953 Merge commit 'b3e55eb20' into feat/schema-auto 2026-08-10 10:54:18 +02:00
Thomas Camlong b3e55eb20c Merge commit '1a349b9e9' into feat/schema-widget 2026-08-10 10:53:58 +02:00
Thomas Camlong 1a349b9e96 Merge commit '52266fc7f' into feat/schema-dnd 2026-08-10 10:53:46 +02:00
Thomas Camlong 52266fc7f6 docs: update WUD OpenAPI schema 2026-08-10 10:53:30 +02:00
Thomas Camlong 2aa8455cbf merge latest widget catalog updates 2026-08-10 10:46:00 +02:00
Thomas Camlong 3b1a979d4f Merge commit '2a4ed87c6' into feat/final-refresh-auto 2026-08-10 10:45:21 +02:00
Thomas Camlong 2a4ed87c6e Merge commit '58db9ddf6' into feat/final-refresh-widget 2026-08-10 10:45:03 +02:00
Thomas Camlong 58db9ddf65 Merge commit 'd53021a0f' into feat/final-refresh-dnd 2026-08-10 10:44:39 +02:00
Thomas Camlong d53021a0f3 Merge remote-tracking branch 'origin/dev' into feat/refresh-latest-dev-v2 2026-08-10 10:44:22 +02:00
Thomas Camlong b86cfaeb73 Merge commit '2372416fa' into feat/propagate-workshop-test-assistant 2026-08-10 10:17:02 +02:00
Thomas Camlong 2372416fac Merge commit '781c62a0f' into feat/propagate-workshop-test-auto 2026-08-10 10:16:47 +02:00
Thomas Camlong 781c62a0f5 Merge commit 'ae40fa032' into feat/propagate-workshop-test-widget 2026-08-10 10:16:32 +02:00
Thomas Camlong ae40fa0328 Merge commit 'c7e6ed894' into feat/propagate-workshop-test-dnd 2026-08-10 10:16:13 +02:00
Thomas Camlong c7e6ed8943 test: validate Workshop source locale 2026-08-10 10:15:58 +02:00
Thomas Camlong d1c5cfbcaf chore: leave en-GB to Crowdin 2026-08-10 10:09:30 +02:00
Thomas Camlong 69547495aa Merge commit '605439828' into feat/refresh-assistant-from-auto 2026-08-10 10:09:10 +02:00
Thomas Camlong 6054398285 Merge commit 'ff0b97dfb' into feat/refresh-auto-from-widget 2026-08-10 10:08:49 +02:00
Thomas Camlong ff0b97dfb4 chore: drop manual translations 2026-08-10 10:08:14 +02:00
Thomas Camlong 07422896aa Merge commit 'c1d93d4c7' into feat/refresh-widget-from-dnd 2026-08-10 10:07:53 +02:00
Thomas Camlong c1d93d4c7d chore: leave translations to Crowdin 2026-08-10 10:07:35 +02:00
Thomas Camlong 673fdf0027 Merge commit '5efe053c0' into feat/refresh-dnd-from-v2 2026-08-10 10:06:39 +02:00
Thomas Camlong 5efe053c0f merge dev into v2 2026-08-10 10:06:18 +02:00
Thomas Camlong 9344d4da18 fix: keep HTTP methods untranslated 2026-08-10 10:04:22 +02:00
Thomas Camlong 004eb94b95 Merge remote-tracking branch 'origin/feat/auto-mobile-boards' into feat/stack-agent-widget-fixes-assistant 2026-08-10 09:40:38 +02:00
Thomas Camlong 4822c2fa12 Merge remote-tracking branch 'origin/feat/widget-modernization' into feat/stack-agent-widget-fixes-mobile 2026-08-10 09:40:29 +02:00
Thomas Camlong 9c91ab0977 fix: rework media session details 2026-08-10 09:39:49 +02:00
Thomas Camlong 7e1edb844b fix: refine Beszel stats period controls 2026-08-10 09:39:49 +02:00
Thomas Camlong a5e0427fe7 Merge remote-tracking branch 'origin/feat/auto-mobile-boards' into feat/stack-immich-fit-assistant 2026-08-10 09:32:35 +02:00
Thomas Camlong 68070796a0 Merge remote-tracking branch 'origin/feat/widget-modernization' into feat/stack-immich-fit-mobile 2026-08-10 09:32:22 +02:00
Thomas Camlong b639ce8741 fix: show complete Immich photos in advanced mode 2026-08-10 09:31:54 +02:00
Thomas Camlong 34344ed57a Merge remote-tracking branch 'origin/feat/auto-mobile-boards' into feat/stack-downloads-tooltip-assistant 2026-08-10 09:27:24 +02:00
Thomas Camlong 58c0260e70 Merge remote-tracking branch 'origin/feat/widget-modernization' into feat/stack-downloads-tooltip-mobile 2026-08-10 09:27:17 +02:00
Thomas Camlong f5cde54705 fix: dismiss Downloads tooltips in advanced mode 2026-08-10 09:26:46 +02:00
Thomas Camlong bf4f93de4a Merge remote-tracking branch 'origin/feat/auto-mobile-boards' into feat/stack-beszel-assistant 2026-08-10 09:19:59 +02:00
Thomas Camlong ddff19c932 Merge remote-tracking branch 'origin/feat/widget-modernization' into feat/stack-beszel-mobile 2026-08-10 09:19:44 +02:00
Thomas Camlong 8307024abe fix: contain Beszel table status dots 2026-08-10 09:19:05 +02:00
Thomas Camlong 68125d8d30 Merge v2 CI repair through mobile boards 2026-08-09 22:28:01 +02:00
Thomas Camlong ff6a273cd1 Merge v2 CI repair through widget modernization 2026-08-09 22:27:57 +02:00
Thomas Camlong 7d560e0675 Merge v2 CI repair through dnd kit grid 2026-08-09 22:27:54 +02:00
Thomas Camlong aafd5f4c9c Merge v2 CI repair into dnd kit grid 2026-08-09 22:27:48 +02:00
Thomas Camlong 1b489cc2e0 fix: stabilize v2 CI 2026-08-09 22:27:28 +02:00
Thomas Camlong 955c232c20 Merge refreshed mobile boards into Homarr Assistant 2026-08-09 22:00:50 +02:00
Thomas Camlong fe71b18636 Merge refreshed widget modernization into mobile boards 2026-08-09 22:00:30 +02:00
Thomas Camlong a547e29607 Merge refreshed dnd kit grid into widget modernization 2026-08-09 22:00:05 +02:00
Thomas Camlong 8cbd53d362 Merge current v2 into dnd kit grid 2026-08-09 21:59:44 +02:00
Thomas Camlong 7cf27c3f39 feat: add Workshop staging deployment 2026-08-09 21:40:20 +02:00
Thomas Camlong 8a3f8d5c12 fix: allow Workshop runtime config writes 2026-08-09 21:21:01 +02:00
Thomas Camlong 8f75954ca9 fix: prevent horizontal rail auto-scroll 2026-08-09 21:17:41 +02:00
ajnart 19abd46414 feat assistant context and agent trace 2026-08-09 21:07:33 +02:00
Thomas Camlong 1028752cbc fix: restore Workshop runtime URLs 2026-08-09 20:11:13 +02:00
ajnart f495dc8be7 fix assistant reasoning mount lifecycle 2026-08-09 18:09:11 +02:00
ajnart a06d8b99d9 feat: improve assistant thinking states 2026-08-09 18:09:11 +02:00
ajnart 90066f8e37 fix: refine assistant controls 2026-08-09 18:09:11 +02:00
ajnart 112db000fd fix: adjust assistant response limits 2026-08-09 18:09:11 +02:00
ajnart 45c1f9e5a9 fix: stabilize custom widget authoring 2026-08-09 18:09:11 +02:00
ajnart edc7b4d55b fix: harden assistant authoring and UI 2026-08-09 18:09:11 +02:00
Thomas Camlong fa0e55cc93 fix: validate MCP v2 routes 2026-08-09 18:09:11 +02:00
Thomas Camlong d1042ce09b Merge custom option test fix into assistant 2026-08-09 18:09:11 +02:00
Thomas Camlong 3b82ee034d Merge custom option test fix into mobile boards 2026-08-09 18:09:07 +02:00
Thomas Camlong 2215fdacfb Merge custom option test fix into widget modernization 2026-08-09 18:09:07 +02:00
Thomas Camlong 7821377f59 Merge custom option test fix into dnd kit 2026-08-09 18:09:07 +02:00
Thomas Camlong 2c97eec1d0 Merge custom option test fix into mobile boards 2026-08-09 18:08:39 +02:00
Thomas Camlong b30f8bc6ea Merge custom option test fix into widget modernization 2026-08-09 18:06:57 +02:00
Thomas Camlong cd8a69fa01 Merge custom option test fix into dnd kit 2026-08-09 18:06:57 +02:00
Thomas Camlong b8bdbc2f3e fix: restore focus after preview exit 2026-08-09 18:06:26 +02:00
Thomas Camlong 889d732e03 test: preserve advanced preview hover ownership 2026-08-09 18:06:26 +02:00
Thomas Camlong 54bd8d6343 feat: improve advanced preview activation 2026-08-09 18:06:26 +02:00
Thomas Camlong 6f1c81bc04 fix: smooth advanced focus entrance 2026-08-09 18:06:26 +02:00
Thomas Camlong 00688d5c1a Remove Docker advanced focus 2026-08-09 18:06:26 +02:00
Thomas Camlong fc8b9178a3 Merge custom option test fix into widget modernization 2026-08-09 18:06:25 +02:00
Thomas Camlong fb7c7d35d9 Merge custom option test fix into dnd kit 2026-08-09 18:05:27 +02:00
Thomas Camlong 198fda7854 Revert "ci: use self-hosted runners for stack"
This reverts commit a1d80515b0e00240bb662180626a44bfb205656e.
2026-08-09 18:03:38 +02:00
Thomas Camlong 66a1d5b210 ci: use self-hosted runners for stack 2026-08-09 18:03:38 +02:00
Thomas Camlong 5e68aab970 fix: anchor dashboard rail labels 2026-08-09 18:03:38 +02:00
Thomas Camlong ca9d836fac Merge remote-tracking branch 'origin/feat/dnd-kit-grid' into HEAD 2026-08-09 18:03:38 +02:00
Thomas Camlong 82a50de403 fix: size dashboard rails to viewport 2026-08-09 18:03:37 +02:00
Thomas Camlong 41606cef86 fix: widen workshop CSS import 2026-08-09 18:03:37 +02:00
Thomas Camlong 7d9caf407c style: format board grid test 2026-08-09 18:03:37 +02:00
Thomas Camlong 59528ca1c3 test: allow dnd-kit DOM modifier import 2026-08-09 18:03:37 +02:00
Thomas Camlong 9373e3d69a fix: preserve narrow board interactions 2026-08-09 18:03:37 +02:00
Thomas Camlong 8acc134a36 fix: keep scaled board rails sticky 2026-08-09 18:03:37 +02:00
Thomas Camlong a4ff57ed85 test: assert clipped zoom overflow 2026-08-09 18:03:37 +02:00
Thomas Camlong c530190638 test: avoid physical zoom size assumption 2026-08-09 18:03:37 +02:00
Thomas Camlong d6b6b696b4 test: assert zoomed canvas fit invariant 2026-08-09 18:03:37 +02:00
Thomas Camlong 1473605445 style: format board grid coverage 2026-08-09 18:03:37 +02:00
Thomas Camlong 255b544a6c docs: align board geometry behavior 2026-08-09 18:03:37 +02:00
Thomas Camlong bcbde92c2e Merge sidebar stability fix into dnd kit 2026-08-09 18:03:37 +02:00
Thomas Camlong 63f82f1a35 Merge grid geometry fixes into dnd kit 2026-08-09 18:03:37 +02:00
Thomas Camlong 0a8e4fead0 fix: stabilize board rails 2026-08-09 18:03:37 +02:00
Thomas Camlong 03ecfef429 Merge layout compatibility fix into dnd kit 2026-08-09 18:03:37 +02:00
Thomas Camlong 7a97b7187b Fix board grid bounds and live growth 2026-08-09 18:03:37 +02:00
Thomas Camlong 899dfd7230 Merge custom option test fix into dnd kit 2026-08-09 18:03:37 +02:00
Thomas Camlong fc1dbd37f1 fix: accept legacy dynamic sections 2026-08-09 18:03:37 +02:00
Thomas Camlong b9c89c61a2 test: harden integration icon checks 2026-08-09 13:39:16 +02:00
Thomas Camlong b5dbeae864 test: fix custom option repair fixture 2026-08-09 09:21:55 +02:00
Thomas Camlong 5daa0570ac test: keep assistant widget compact 2026-08-09 09:20:51 +02:00
Thomas Camlong 9cd6bc90ed Merge widget option and advanced focus fixes into assistant 2026-08-09 09:20:18 +02:00
Thomas Camlong cfa104baf7 Merge advanced widget opt-in into mobile boards 2026-08-09 09:20:15 +02:00
Thomas Camlong f214c25e9c fix: require advanced widget focus opt-in 2026-08-09 09:19:54 +02:00
Thomas Camlong fcee8177af Merge custom widget option repair into widget modernization 2026-08-09 09:18:01 +02:00
Thomas Camlong 0d6f9337b4 Merge v2 custom widget option repair 2026-08-09 09:17:58 +02:00
Thomas Camlong d513c7fe46 fix: repair stale custom widget options 2026-08-09 09:17:32 +02:00
ajnart fa9d080eed feat: migrate MCP server to v2 2026-08-09 08:25:17 +02:00
Thomas Camlong 8fea2a2851 Merge migration renames into Homarr Assistant 2026-08-09 08:15:08 +02:00
Thomas Camlong 19a6e32d61 Merge migration renames into mobile boards 2026-08-09 08:14:40 +02:00
Thomas Camlong 0425c3630c Merge migration renames into widget modernization 2026-08-09 08:14:06 +02:00
Thomas Camlong 84db5ccfe7 Rename layout gutter migrations 2026-08-09 08:12:46 +02:00
Thomas Camlong d56e2b5d60 Rename Assistant migrations 2026-08-09 08:12:43 +02:00
Thomas Camlong ee7e090a1f Rename layout role migrations 2026-08-09 08:12:41 +02:00
Thomas Camlong 895ec9fad5 Merge branch 'feat/v2-stack-final-runtime-mobile' into feat/v2-stack-final-runtime-assistant 2026-08-09 02:56:50 +02:00
Thomas Camlong 9df91587b2 Merge branch 'feat/v2-stack-final-runtime-modernization' into feat/v2-stack-final-runtime-mobile 2026-08-09 02:56:39 +02:00
Thomas Camlong db7f1a0374 Merge branch 'feat/v2-stack-final-runtime-dnd' into feat/v2-stack-final-runtime-modernization 2026-08-09 02:56:26 +02:00
Thomas Camlong 1f045b2abf Merge branch 'feat/v2-stack-final-runtime-v2' into feat/v2-stack-final-runtime-dnd 2026-08-09 02:56:11 +02:00
Thomas Camlong 8dad83f720 test(e2e): serialize container specs 2026-08-09 02:55:51 +02:00
Thomas Camlong c237ceff9f Merge branch 'feat/v2-stack-final-runtime-mobile' into feat/v2-stack-final-runtime-assistant 2026-08-09 02:22:19 +02:00
Thomas Camlong cf6c362a4a Merge branch 'feat/v2-stack-final-runtime-modernization' into feat/v2-stack-final-runtime-mobile
# Conflicts:
#	apps/nextjs/src/app/[locale]/boards/[name]/settings/_settings-form.tsx
2026-08-09 02:22:03 +02:00
Thomas Camlong da99dc3080 Merge branch 'feat/v2-stack-final-runtime-dnd' into feat/v2-stack-final-runtime-modernization 2026-08-09 02:21:24 +02:00
Thomas Camlong 52ab119a1f Merge branch 'feat/v2-stack-final-runtime-v2' into feat/v2-stack-final-runtime-dnd 2026-08-09 02:21:13 +02:00
Thomas Camlong bbc23a6c65 fix(settings): avoid nested forms 2026-08-09 02:21:01 +02:00
Thomas Camlong 4e9b7a68ba Merge branch feat/v2-stack-final-runtime-mobile into feat/v2-stack-final-runtime-assistant 2026-08-09 01:47:41 +02:00
Thomas Camlong a75f6e1251 Merge branch feat/v2-stack-final-runtime-modernization into feat/v2-stack-final-runtime-mobile 2026-08-09 01:47:31 +02:00
Thomas Camlong 1db3b2c2cf Merge branch feat/v2-stack-final-runtime-dnd into feat/v2-stack-final-runtime-modernization
# Conflicts:
#	packages/widgets/src/clock/component.tsx
2026-08-09 01:47:20 +02:00
Thomas Camlong 1b663e56ee Merge branch feat/v2-stack-final-runtime-v2 into feat/v2-stack-final-runtime-dnd 2026-08-09 01:46:45 +02:00
Thomas Camlong 5ee14136ac fix(clock): defer live time until mount 2026-08-09 01:46:30 +02:00
Thomas Camlong 58342fabc5 Merge branch 'feat/v2-stack-final-runtime-mobile' into feat/v2-stack-final-runtime-assistant 2026-08-09 01:12:40 +02:00
Thomas Camlong 4da705b752 Merge branch 'feat/v2-stack-final-runtime-modernization' into feat/v2-stack-final-runtime-mobile 2026-08-09 01:12:40 +02:00
Thomas Camlong 4a47fc4551 Merge branch 'feat/v2-stack-final-runtime-dnd' into feat/v2-stack-final-runtime-modernization
# Conflicts:
#	packages/widgets/src/clock/component.tsx
2026-08-09 01:12:32 +02:00
Thomas Camlong 3abdae490e Merge branch 'feat/v2-stack-final-runtime-v2' into feat/v2-stack-final-runtime-dnd 2026-08-09 01:12:12 +02:00
Thomas Camlong 9d3d40d4bd fix(e2e): stabilize initial board hydration 2026-08-09 01:11:38 +02:00
Thomas Camlong b85719bc77 Merge branch 'feat/v2-stack-e2e-fix-mobile' into feat/v2-stack-e2e-fix-assistant 2026-08-09 00:40:17 +02:00
Thomas Camlong 77caf92e16 Merge branch 'feat/v2-stack-e2e-fix-modernization' into feat/v2-stack-e2e-fix-mobile 2026-08-09 00:40:16 +02:00
Thomas Camlong 1367e07f20 test(widgets): stabilize advanced focus hover 2026-08-09 00:39:38 +02:00
Thomas Camlong bbdbae636a Merge branch 'feat/v2-stack-e2e-fix-dnd' into feat/v2-stack-e2e-fix-modernization
# Conflicts:
#	packages/widgets/src/clock/component.tsx
2026-08-09 00:39:38 +02:00
Thomas Camlong 10719bc72e Merge branch 'feat/v2-stack-e2e-fix-v2' into feat/v2-stack-e2e-fix-dnd 2026-08-09 00:37:47 +02:00
Thomas Camlong 48d8517ed1 fix(clock): suppress live timestamp hydration noise 2026-08-09 00:37:33 +02:00
Thomas Camlong bd608f8985 Merge branch 'feat/v2-stack-e2e-fix-mobile' into feat/v2-stack-e2e-fix-assistant 2026-08-09 00:14:56 +02:00
Thomas Camlong d9f988a534 Merge branch 'feat/v2-stack-e2e-fix-modernization' into feat/v2-stack-e2e-fix-mobile 2026-08-09 00:14:44 +02:00
Thomas Camlong 074902ecc8 Merge branch 'feat/v2-stack-e2e-fix-dnd' into feat/v2-stack-e2e-fix-modernization 2026-08-09 00:14:32 +02:00
Thomas Camlong d17752188f Merge branch 'feat/v2-stack-e2e-fix-v2' into feat/v2-stack-e2e-fix-dnd 2026-08-09 00:14:22 +02:00
Thomas Camlong 7619492516 test(v2): make board tour dismissal optional 2026-08-09 00:14:10 +02:00
Thomas Camlong 89b9940c43 Merge branch 'feat/v2-stack-e2e-fix-mobile' into feat/v2-stack-e2e-fix-assistant 2026-08-09 00:11:59 +02:00
Thomas Camlong 60e0b537c1 Merge branch 'feat/v2-stack-e2e-fix-modernization' into feat/v2-stack-e2e-fix-mobile 2026-08-09 00:11:51 +02:00
Thomas Camlong 5e035e14ed Merge commit '9f36be028' into feat/v2-stack-e2e-fix-modernization 2026-08-09 00:11:41 +02:00
Thomas Camlong 9f36be0284 Merge commit 'ca6a019c1' into feat/v2-stack-e2e-fix-dnd
# Conflicts:
#	apps/nextjs/src/app/[locale]/boards/(content)/_board-add-menu.tsx
#	apps/nextjs/src/app/[locale]/boards/(content)/_header-actions.tsx
#	packages/translation/src/lang/en.json
2026-08-09 00:11:33 +02:00
Thomas Camlong ca6a019c1e test(v2): stabilize production E2E flows 2026-08-09 00:10:49 +02:00
Thomas Camlong b9f2054d35 Merge branch 'feat/v2-stack-unit-fix-mobile' into feat/v2-stack-unit-fix-assistant 2026-08-08 23:53:43 +02:00
Thomas Camlong 2182d8df92 test(mcp): review board reset layout tool 2026-08-08 23:53:33 +02:00
Thomas Camlong 50dc17484b Merge branch 'feat/v2-stack-unit-fix-mobile' into feat/v2-stack-unit-fix-assistant 2026-08-08 23:39:29 +02:00
Thomas Camlong 1d9f29b4f1 Merge commit '05eebc097' into feat/v2-stack-unit-fix-mobile 2026-08-08 23:39:29 +02:00
Thomas Camlong 05eebc097f test(mcp): review smart home entity details tool 2026-08-08 23:39:15 +02:00
Thomas Camlong 6b1870e813 Merge branch 'feat/v2-stack-unit-fix-mobile' into feat/v2-stack-unit-fix-assistant 2026-08-08 23:28:52 +02:00
Thomas Camlong 8ea4bc293d Merge commit '0bd9c6f29' into feat/v2-stack-unit-fix-mobile 2026-08-08 23:28:45 +02:00
Thomas Camlong 0bd9c6f290 style(widgets): split query matcher type imports 2026-08-08 23:28:33 +02:00
Thomas Camlong 87b2c0cdcc fix(assistant): preserve lazy board boundaries 2026-08-08 23:24:13 +02:00
Thomas Camlong d79774be1d Merge commit 'c3483a1ec' into feat/v2-stack-unit-fix-assistant 2026-08-08 23:22:00 +02:00
Thomas Camlong c3483a1ec9 test(boards): include layout roles in gutter fixtures 2026-08-08 23:21:55 +02:00
Thomas Camlong 406dc1f14f Merge commit 'bc896c0b0' into feat/v2-stack-unit-fix-mobile 2026-08-08 23:21:40 +02:00
Thomas Camlong bc896c0b03 fix(widgets): keep board graph imports scoped 2026-08-08 23:21:35 +02:00
Thomas Camlong 89d974d7ef Merge mobile boards into Homarr assistant
# Conflicts:
#	package.json
2026-08-08 23:07:39 +02:00
Thomas Camlong d8b46f239c Merge widget modernization into mobile boards 2026-08-08 23:07:10 +02:00
Thomas Camlong 14380e1cdf Merge dnd-kit grid into widget modernization 2026-08-08 23:07:00 +02:00
Thomas Camlong 0ece179558 Merge v2 unit-test project filter into dnd-kit grid 2026-08-08 23:06:54 +02:00
Thomas Camlong cae976b6d8 fix(ci): exclude e2e project from unit gate 2026-08-08 23:06:42 +02:00
Thomas Camlong 1259dd7bf7 Merge mobile boards into Homarr assistant
# Conflicts:
#	package.json
2026-08-08 22:52:10 +02:00
Thomas Camlong 6157a2504b Merge widget modernization into mobile boards 2026-08-08 22:51:24 +02:00
Thomas Camlong c2064faa95 Merge dnd-kit grid into widget modernization 2026-08-08 22:51:17 +02:00
Thomas Camlong 6d1538d3b1 Merge v2 unit-test gate into dnd-kit grid 2026-08-08 22:51:11 +02:00
Thomas Camlong 215f9a7d21 fix(ci): exclude e2e tests from unit gate 2026-08-08 22:50:57 +02:00
Thomas Camlong 882c0dcf07 Merge mobile boards into Homarr assistant 2026-08-08 22:39:29 +02:00
Thomas Camlong 06353862cf Merge widget modernization into mobile boards 2026-08-08 22:39:19 +02:00
Thomas Camlong eae55ce2f0 Merge dnd-kit grid into widget modernization 2026-08-08 22:39:10 +02:00
Thomas Camlong 12412947ee Merge v2 bundle checker into dnd-kit grid 2026-08-08 22:39:00 +02:00
Thomas Camlong dec46f3ee4 fix(ci): detect custom widget runtime chunks 2026-08-08 22:38:42 +02:00
Thomas Camlong 5a62b23cc2 docs: refresh assistant OpenAPI schema 2026-08-08 22:22:09 +02:00
Thomas Camlong adb4d7c6a2 Merge commit '360fd59771bd8e62247b4ea9b1b7cb4839b0105b' into feat/v2-stack-final-assistant 2026-08-08 22:21:07 +02:00
Thomas Camlong 360fd59771 style: format mobile migration metadata 2026-08-08 22:20:48 +02:00
Thomas Camlong e04db94631 Merge commit 'e65c98c29063d80cbfd39c48c699de7018892624' into feat/v2-stack-final-mobile 2026-08-08 22:20:17 +02:00
Thomas Camlong e65c98c290 fix(ci): make modernization independently installable 2026-08-08 22:19:20 +02:00
Thomas Camlong 1b6c4a9022 Merge commit '3246dc4c91656dec3c87267ae074436267ad77ac' into feat/v2-stack-final-modernization
# Conflicts:
#	apps/docs/docs/widgets/downloads/index.mdx
#	apps/docs/docs/widgets/media-server/index.mdx
2026-08-08 22:17:04 +02:00
Thomas Camlong 3246dc4c91 style: format dnd migration metadata 2026-08-08 22:16:16 +02:00
Thomas Camlong 97e89a936c Merge commit 'aaf86a9d1' into feat/v2-stack-final-dnd 2026-08-08 22:15:41 +02:00
Thomas Camlong aaf86a9d13 fix(ci): refresh v2 generated artifacts 2026-08-08 22:15:24 +02:00
Thomas Camlong b49cbb7791 Merge commit '254ed9afbabb957617fc17ebc0122063b0dde803' into feat/v2-stack-e2e-assistant 2026-08-08 22:07:09 +02:00
Thomas Camlong 254ed9afba Merge commit '298937b00ca522855e0ebcb95d8c3adccd34fef7' into feat/v2-stack-e2e-mobile 2026-08-08 22:06:53 +02:00
Thomas Camlong 298937b00c Merge commit '25ac7b6a9' into feat/v2-stack-e2e-modernization 2026-08-08 22:06:36 +02:00
Thomas Camlong 25ac7b6a9d test(e2e): wait for settled grid geometry 2026-08-08 22:06:19 +02:00
Thomas Camlong 84f5001bb2 merge: propagate mobile stack CI fixes 2026-08-08 21:53:28 +02:00
Thomas Camlong 83790f6875 merge: propagate modernization CI fixes 2026-08-08 21:53:09 +02:00
Thomas Camlong b937d4eb18 style: format modernization notes 2026-08-08 21:52:51 +02:00
Thomas Camlong 8d35c0c71f merge: propagate dnd stack CI fix 2026-08-08 21:52:32 +02:00
Thomas Camlong a5ce2e9b7a merge: propagate v2 CI fix 2026-08-08 21:52:16 +02:00
Thomas Camlong 2efc225617 fix(ci): align Workshop pnpm version 2026-08-08 21:52:01 +02:00
Thomas Camlong 0c7cbe17fb merge: propagate mobile stack fixes 2026-08-08 21:42:50 +02:00
Thomas Camlong 69359bb39d merge: propagate modernization fixes 2026-08-08 21:42:31 +02:00
Thomas Camlong f24d757ec8 fix: preserve app links and hover previews 2026-08-08 21:42:13 +02:00
Thomas Camlong 9fd7a8a05b merge: stack assistant on mobile boards 2026-08-08 21:27:56 +02:00
Thomas Camlong a039049084 merge: stack auto mobile boards 2026-08-08 20:59:56 +02:00
Thomas Camlong 9edeba5cd2 merge: stack widget modernization on dnd kit 2026-08-08 20:40:26 +02:00
Thomas Camlong 93276abc8c merge: stack v2 into dnd-kit grid 2026-08-08 20:16:04 +02:00
Thomas Camlong dd63d5600b merge dev into v2 2026-08-08 19:54:57 +02:00
ajnart 25be53244b ci(workshop): restore image publishing 2026-08-08 19:25:35 +02:00
ajnart 2657ff4384 fix: repair restored board layouts 2026-08-08 18:44:32 +02:00
ajnart bc41125b7f fix: guard recursive section rendering 2026-08-08 18:24:38 +02:00
ajnart a9fc3c6faa fix: stabilize responsive layout editing 2026-08-08 18:02:39 +02:00
Thomas 8ab3f136b8 fix(workshop): harden deployment and deep links (#6542) 2026-08-08 17:41:54 +02:00
ajnart 1877b4f9a4 fix: improve layout preview density 2026-08-08 16:45:38 +02:00
ajnart a33eb83655 fix: address responsive layout review 2026-08-08 16:19:26 +02:00
ajnart 991e79310a fix: avoid nested board settings forms 2026-08-08 16:02:40 +02:00
ajnart 43a2b4ac30 Merge remote-tracking branch 'origin/dev' into feat/auto-mobile-boards
# Conflicts:
#	apps/docs/docs/management/boards/index.mdx
#	apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx
2026-08-08 15:47:10 +02:00
ajnart 6728228387 feat: protect responsive board layouts 2026-08-08 15:41:14 +02:00
ajnart 74c7754890 fix(assistant): prevent panel focus capture 2026-08-08 14:26:04 +02:00
ajnart fa9444323b refactor: simplify mobile board layouts 2026-08-08 13:54:42 +02:00
ajnart 7e7b338d18 fix: refine mobile board preview 2026-08-08 13:09:20 +02:00
ajnart 0d1dd02184 fix: address mobile board review 2026-08-08 12:34:10 +02:00
ajnart bc90faf5f2 test: cover tablet mobile boards 2026-08-08 12:23:36 +02:00
ajnart 2c1e697f68 feat: add mobile layout preview 2026-08-08 12:21:53 +02:00
ajnart 5a21df86e2 merge: sync dev changes 2026-08-08 11:56:18 +02:00
Thomas 20fd49f082 fix(assistant): mysql message storage, migration metadata and per-page bundle cost (#6530) 2026-08-08 09:34:16 +02:00
Thomas Camlong 0e3461c6d7 fix: only auto-focus the assistant composer in the floating panel
The composer auto-focused in both the board widget and the floating
panel, so on page load the widget stole focus and scrolled the page to
it, and opening the panel made focus jump between the two composers.
Make auto-focus opt-in via an autoFocusComposer prop: the floating panel
enables it (explicit intent to compose), while the widget leaves it off.
2026-08-07 18:57:44 +02:00
Thomas Camlong 231ac14703 test(integrations): add missing timeout to nextcloud recurring-range test
The 'recurring events that go beyond the range' test was missing the
20s timeout used by every other test in the file, so it fell back to
vitest's default 5s and intermittently timed out against the slow
Nextcloud container. Add the explicit timeout.
2026-08-07 17:52:49 +02:00
Thomas Camlong aa1e6ebdc8 ci: retry flaky container-backed unit tests in fast gate
Integration tests (nzbget, nextcloud, pi-hole, etc.) start Docker
containers and intermittently time out under shared-runner load, which
fails the fast gate and skips the container job that publishes the PR
preview image. Retry failing unit tests twice so transient container
startup failures do not block the image publish.
2026-08-07 17:47:47 +02:00
Thomas Camlong da93adac14 ci: make e2e tests resilient to CI resource contention
E2E specs spin up Docker containers in parallel and intermittently time
out waiting for UI elements under shared-runner load. Limit parallel
workers to 2 and retry flaky failures twice so the container job can
complete and publish the PR preview image.
2026-08-07 16:28:33 +02:00
Thomas Camlong 926b7e2a5e test(e2e): wait for login form hydration before filling
The customJsx E2E test occasionally failed at login with the form
showing 'This field is required' on the username, because the submit
button was clicked while the form was still hydrating (it is disabled
until mounted) and the field values were reset by the re-mount. Wait
for the submit button to become enabled before filling, and assert the
username value stuck before submitting.
2026-08-07 15:49:21 +02:00
Thomas Camlong 17f08c6d14 ci: skip CI for autofix commits
The auto-fix workflow pushes a 'style: auto-fix lint and format' commit
after every user push, which triggers a new CI run that cancels the
in-progress run (concurrency cancel-in-progress) before the container
job can finish and publish the PR preview image. Mark the autofix commit
with [skip ci] so it does not restart CI, letting the user's run
complete and publish the image.
2026-08-07 15:25:03 +02:00
Thomas Camlong 32cad81e1c chore: remove unused assistant subtitle translation 2026-08-07 15:14:23 +02:00
Thomas Camlong eaacfdf7ce chore: simplify assistant header branding
Remove the 'Connected to this Homarr instance' subtitle (and the running
state text) from the assistant panel header, keeping only the robot icon
and title.
2026-08-07 15:07:26 +02:00
Thomas Camlong 96659fb543 chore: shrink demo assistant widget to 3x3 2026-08-07 15:04:24 +02:00
Thomas d625640051 feat: add assistant widget to demo dashboard (#6529) 2026-08-07 15:02:03 +02:00
Thomas b14756874e fix: handle missing message in assistant generation telemetry (#6528) 2026-08-07 14:59:59 +02:00
ajnart 6bbb786ede style: auto-fix lint and format 2026-08-07 12:54:24 +00:00
Thomas Camlong 31d9e8d582 fix: prevent infinite render loop in assistant telemetry
RequestTelemetry derived thread message metadata inside the useAuiState
selector with messages.map(), returning a new array on every render.
useSyncExternalStore treats the new reference as a changed snapshot,
causing an infinite re-render loop (React error #185) while an assistant
message streams. Select the stable messages reference and memoize the
metadata derivation instead.
2026-08-07 14:53:05 +02:00
Thomas bc12aba433 Merge branch 'dev' into feat/homarr-assistant 2026-08-06 21:53:57 +02:00
Thomas eca8b4286d feat: mock Homarr Assistant in demo mode and fix board page crash (#6524) 2026-08-06 21:33:03 +02:00
ajnart fce950d406 test: wait for Home Assistant API readiness 2026-08-04 18:31:23 +02:00
ajnart bcdd56f714 feat: polish assistant conversations 2026-08-04 18:22:18 +02:00
ajnart 8e557fb9fd feat: improve assistant interactions 2026-08-04 13:21:39 +02:00
Thomas Camlong 4bb862ee76 fix: stabilize advanced widget interactions 2026-08-03 21:45:00 +02:00
Thomas Camlong 0c5251a971 fix: finish widget modernization audit 2026-08-03 21:01:55 +02:00
Thomas Camlong 454752e46d fix: address widget review feedback 2026-08-03 20:04:56 +02:00
Thomas Camlong 596bfc317d fix: scope advanced preview controls 2026-08-03 19:42:43 +02:00
Thomas Camlong 97444d209a ci: limit turbo concurrency 2026-08-03 19:17:28 +02:00
Thomas Camlong ee51227a55 fix: restore notifications query scope 2026-08-03 18:53:26 +02:00
Thomas Camlong db7c850596 fix: remove stale notifications import 2026-08-03 18:52:02 +02:00
ajnart 30344bdaae style: auto-fix lint and format 2026-08-03 16:46:35 +00:00
Thomas Camlong 7847eea1a3 fix: refine widget interactions 2026-08-03 18:45:16 +02:00
ajnart 1642a862d9 stabilize hydrated login actions 2026-08-03 18:37:05 +02:00
ajnart 62f5c1cbc3 stabilize integration router tests 2026-08-03 18:14:25 +02:00
ajnart 5e857c0e0a fix Docker dependency fetch scripts 2026-08-03 17:55:03 +02:00
ajnart 5da70164a6 style: auto-fix lint and format 2026-08-03 15:40:15 +00:00
ajnart fbe381cbb5 style: auto-fix lint and format 2026-08-03 15:39:36 +00:00
Thomas af25e78120 Merge branch 'dev' into feat/auto-mobile-boards 2026-08-03 17:39:10 +02:00
Thomas 8d98408cfe Merge branch 'dev' into feat/dnd-kit-grid 2026-08-03 17:38:51 +02:00
Thomas 9234ca0f32 Merge branch 'dev' into feat/widget-modernization 2026-08-03 17:38:28 +02:00
ajnart f66d982ff3 fix duplicate release success flag 2026-08-03 17:35:28 +02:00
ajnart cd16ab8e84 Merge remote-tracking branch 'origin/dev' into feat/homarr-assistant 2026-08-03 17:34:33 +02:00
ajnart eaa7fd8bc3 clarify future Homarr provider 2026-08-03 17:25:14 +02:00
ajnart 4b62cbfb4b fix integration permission checks 2026-08-03 17:25:14 +02:00
Thomas Camlong 61a2f8cb6c feat: modernize widget experience 2026-08-03 17:18:22 +02:00
ajnart 5155d7b742 fix merged dependency lockfile 2026-08-03 17:14:29 +02:00
Thomas Camlong 596833f227 fix container header spacing 2026-08-03 17:13:02 +02:00
ajnart 56a6d4989a Merge remote-tracking branch 'origin/dev' into feat/homarr-assistant
# Conflicts:
#	pnpm-lock.yaml
2026-08-03 17:08:45 +02:00
ajnart c43ab2ee56 fix assistant provider icon contrast 2026-08-03 17:05:12 +02:00
ajnart f7b291b465 add OpenRouter web search option 2026-08-03 16:59:27 +02:00
Thomas Camlong cc71ab8cd9 fix: contain board canvas overflow 2026-08-03 16:46:43 +02:00
ajnart 35673f113e fix assistant widget configuration 2026-08-03 16:43:08 +02:00
ajnart bd3e4c33f3 enhance assistant batch responses 2026-08-03 15:48:41 +02:00
ajnart 6a98036c50 show assistant icon search previews 2026-08-03 15:36:10 +02:00
ajnart 59bed6b995 fix assistant streaming scroll lock 2026-08-03 15:30:10 +02:00
ajnart 5a3022a073 integrate assistant custom widget workflow 2026-08-03 15:22:08 +02:00
Thomas Camlong 49d623e5c2 fix: defer dependency scripts during container fetch 2026-08-03 15:18:07 +02:00
ajnart ab21a9fece style: auto-fix lint and format 2026-08-03 12:50:22 +00:00
Thomas Camlong 41fffbb79b Merge dev into feat/dnd-kit-grid 2026-08-03 14:48:31 +02:00
Thomas 36ac1b40ff Merge branch 'dev' into feat/auto-mobile-boards 2026-08-03 14:38:45 +02:00
Thomas Camlong 08da71d67f fix: contain collapsed layout and widget overflow 2026-08-03 14:27:45 +02:00
ajnart 65ff0ecb34 docs: add widget modernization audit 2026-08-03 14:27:30 +02:00
ajnart abc17e7c2a fix assistant prompt submission 2026-08-03 14:12:13 +02:00
ajnart c81a3b9432 refresh views after assistant changes 2026-08-03 13:38:20 +02:00
Thomas Camlong 80d46b0d85 restore context menu focus 2026-08-03 13:28:12 +02:00
Thomas Camlong 6e15f8bcf8 fix context menu keyboard focus 2026-08-03 13:24:13 +02:00
Thomas Camlong 294217fcba fix board E2E login race 2026-08-03 13:09:56 +02:00
ajnart 8dd1d70900 improve assistant question placement 2026-08-03 13:07:07 +02:00
Thomas Camlong 3c99c75ad7 fix SVG focus scrolling 2026-08-03 12:51:10 +02:00
ajnart 9e0a6a758f fix assistant automatic approvals 2026-08-03 12:34:54 +02:00
Thomas Camlong 20dbdcedf2 fix focused pane scrolling 2026-08-03 12:25:56 +02:00
Thomas Camlong a2f5668c44 fix focus scroll layering 2026-08-03 12:21:31 +02:00
Thomas Camlong 09e9cd4388 fix widget focus interactions 2026-08-03 12:15:12 +02:00
Thomas Camlong 5c9fd28ae1 test: wait for resized grid geometry 2026-08-02 19:27:09 +02:00
Thomas Camlong 8f0c9435ff test: stabilize board grid authentication 2026-08-02 19:14:40 +02:00
Thomas Camlong 96e92485a5 fix: align responsive widget actions 2026-08-02 18:10:25 +02:00
Thomas Camlong f9aae23f46 fix: keep session labels readable 2026-08-02 17:41:27 +02:00
Thomas Camlong fd024e32d2 fix: simplify widget feedback 2026-08-02 17:39:28 +02:00
Thomas Camlong 5010ecf188 fix: harden widget error feedback 2026-08-02 17:34:52 +02:00
Thomas Camlong f0d8e97f8d fix: address final widget review 2026-08-02 17:25:02 +02:00
ajnart 8f81de775e chore: remove unused ai sdk dependency 2026-08-02 16:57:11 +02:00
Thomas Camlong ed8ef15f12 fix: localize widget chart formatting 2026-08-02 16:48:34 +02:00
ajnart 8a2375b3ef fix: address assistant review feedback 2026-08-02 16:45:14 +02:00
Thomas Camlong d97f1bd34b fix: align widget accessibility and locales 2026-08-02 16:42:08 +02:00
Thomas Camlong d86293de0f fix: canonicalize widget date locales 2026-08-02 16:32:59 +02:00
Thomas Camlong 54e444d69d fix: close final widget review gaps 2026-08-02 16:26:04 +02:00
Thomas Camlong d5c3aebd26 fix: harden widget review follow-ups 2026-08-02 16:10:16 +02:00
ajnart 1777ca4446 style: auto-fix lint and format 2026-08-02 13:58:01 +00:00
ajnart cf6aefa48a fix: isolate assistant conversation state 2026-08-02 15:57:05 +02:00
Thomas Camlong 56e3bd1cf2 fix: smooth container collapse behavior 2026-08-02 15:57:05 +02:00
Thomas Camlong 7b7045202d fix: address widget review findings 2026-08-02 14:35:01 +02:00
ajnart d7969e1ec1 fix: harden assistant interactive tools 2026-08-02 14:20:40 +02:00
Thomas Camlong 1e0bf40040 feat: refine widget interactions 2026-08-02 14:12:18 +02:00
Thomas Camlong bdffb63ebc Merge remote-tracking branch 'origin/dev' into feat/widget-modernization 2026-08-02 14:06:03 +02:00
ajnart eff3168f1c feat: improve assistant tool workflows 2026-08-02 11:24:11 +02:00
ajnart 4db4d947db style: auto-fix lint and format 2026-08-02 09:04:11 +00:00
Thomas Camlong 0755fc1e26 fix: refine container interactions 2026-08-02 11:02:55 +02:00
ajnart 81a2f18c7c fix: map legacy media server options 2026-08-02 10:20:40 +02:00
ajnart 9eaadd83fb Merge remote-tracking branch 'origin/dev' into feat/homarr-assistant 2026-08-02 10:18:52 +02:00
Thomas Camlong d460ac8bbb fix: secure widget navigation links 2026-08-02 10:01:36 +02:00
Thomas Camlong 1d73513989 fix: polish widget failure feedback 2026-08-02 09:50:08 +02:00
Thomas Camlong e3f679237d fix: polish board grid interactions 2026-08-02 09:36:33 +02:00
Thomas Camlong 45ff97972c fix: address final widget review findings 2026-08-02 09:27:31 +02:00
ajnart 8bb0ddef42 fix: secure assistant generation telemetry 2026-08-02 09:09:01 +02:00
Thomas Camlong 36b75a071b fix: complete widget review cleanup 2026-08-02 09:05:02 +02:00
ajnart 04520cf67c feat: enrich assistant request telemetry 2026-08-02 08:51:48 +02:00
Thomas Camlong 3f7d934fa5 fix: address remaining widget review feedback 2026-08-02 08:44:00 +02:00
ajnart d562706c8b fix: sync assistant runtime preferences 2026-08-02 08:27:31 +02:00
ajnart 3245356f0f fix: align assistant management layout 2026-08-02 08:23:58 +02:00
Thomas Camlong 76974f676d fix: allow dynamic database schema access 2026-08-02 08:19:46 +02:00
ajnart 0d23bafa3d fix: hide inactive assistant scroll control 2026-08-02 08:18:05 +02:00
Thomas Camlong 5ddbf184fd ci: limit fast gate concurrency 2026-08-02 08:10:14 +02:00
Thomas Camlong c61695b142 fix: make integration warnings keyboard accessible 2026-08-02 08:09:55 +02:00
Thomas Camlong b9b938c658 fix: address board grid CI and review 2026-08-02 08:05:25 +02:00
Thomas Camlong 6f27ede8bf fix: complete widget modernization review 2026-08-02 08:05:04 +02:00
Thomas Camlong 79ae42f0c3 fix: sync widget modernization with dev 2026-08-02 01:37:01 +02:00
Thomas Camlong c754946089 fix: address widget review feedback 2026-08-01 23:44:17 +02:00
ajnart ab7d385a13 style: auto-fix lint and format 2026-08-01 21:38:41 +00:00
Thomas c53b38a738 Merge branch 'dev' into feat/dnd-kit-grid 2026-08-01 23:37:27 +02:00
Thomas Camlong d0c7ba401f fix: address board grid review 2026-08-01 23:26:02 +02:00
Thomas Camlong d94cadd3e7 feat: rebuild board grid with dnd-kit 2026-08-01 22:30:40 +02:00
ajnart cce3e455fa style: auto-fix lint and format 2026-08-01 19:47:19 +00:00
Thomas Camlong b28beec2c6 fix: refine widget modernization 2026-08-01 21:45:01 +02:00
Thomas Camlong d421046d15 feat: modernize dashboard widgets 2026-08-01 21:14:33 +02:00
ajnart b7034e59c1 fix: polish assistant widget interactions 2026-08-01 18:25:48 +02:00
ajnart d6c1a87a03 test: wait for grid reconciliation between drags 2026-08-01 17:56:09 +02:00
ajnart d111f7e981 fix: correct assistant widget docs metadata 2026-08-01 15:00:03 +02:00
ajnart 28b6321b1c style: auto-fix lint and format 2026-08-01 12:52:44 +00:00
ajnart 0a8e6ed194 feat: add assistant conversation widget 2026-08-01 14:51:20 +02:00
ajnart 50907c7289 fix: clarify board grid editing affordances 2026-08-01 14:13:07 +02:00
ajnart 6e28712ea7 fix: simplify assistant shortcut 2026-08-01 11:09:10 +02:00
ajnart 5458e7d647 feat: add assistant board styling tools 2026-08-01 10:39:41 +02:00
ajnart a5fa6d3afe fix: restore assistant approval flows 2026-08-01 09:54:11 +02:00
ajnart 9c8fa8158b fix: improve board grid interactions 2026-08-01 00:43:36 +02:00
Thomas Camlong e6e413bcb7 test: target visible layout preview 2026-08-01 00:43:36 +02:00
Thomas Camlong 81d14e39ab test: tolerate sticky rail subpixel rounding 2026-08-01 00:43:36 +02:00
Thomas Camlong f207587791 test: avoid grid drag rounding boundary 2026-08-01 00:43:36 +02:00
Thomas Camlong 5cc2df19ed fix: satisfy database namespace lint 2026-08-01 00:43:36 +02:00
ajnart 0a80d70730 style: auto-fix lint and format 2026-08-01 00:43:36 +02:00
ajnart 25c9955cc6 feat: rebuild board grid experience 2026-08-01 00:43:28 +02:00
Thomas d0e28a5fc9 Merge branch 'dev' into feat/homarr-assistant 2026-07-31 13:55:31 +02:00
Thomas 1b55514d98 Merge branch 'dev' into feat/homarr-assistant 2026-07-31 13:32:03 +02:00
Thomas 557ebe0a2f fix: harden v2 custom widgets and Workshop (#6480) 2026-07-31 13:28:53 +02:00
ajnart 9a2d6f692e feat: expose assistant global shortcuts 2026-07-31 06:58:39 +02:00
ajnart bf53a2a262 fix: keep assistant menu selection visible 2026-07-31 06:32:57 +02:00
ajnart 1d15b614f3 refactor: group assistant runtime controls 2026-07-31 06:00:49 +02:00
ajnart e6981a7f44 fix: invoke mutations before approval 2026-07-30 23:31:24 +02:00
ajnart 48dbdcd835 feat: redesign assistant management 2026-07-30 22:59:27 +02:00
ajnart 5fdf408378 docs: explain assistant search focus 2026-07-30 22:04:45 +02:00
ajnart 38e7d2c15f style: auto-fix lint and format 2026-07-30 20:02:54 +00:00
ajnart 82b146bdf6 feat: improve assistant confirmations 2026-07-30 22:01:21 +02:00
ajnart 2aefa23c36 feat: improve assistant background controls 2026-07-30 18:33:54 +02:00
ajnart 19cc115730 refactor: simplify assistant message controls 2026-07-30 17:33:43 +02:00
ajnart 429b587529 fix: complete assistant markdown lockfile 2026-07-30 17:03:14 +02:00
ajnart 93059c1e48 feat: render assistant content 2026-07-30 16:56:43 +02:00
ajnart af75bcd741 fix: send provider model ids 2026-07-30 16:00:09 +02:00
ajnart 6bd331bc73 fix: preserve assistant trigger focus 2026-07-30 14:33:29 +02:00
ajnart ee08870925 fix: address assistant capability review 2026-07-30 14:22:01 +02:00
ajnart 33e23021ae feat: expand assistant interaction capabilities 2026-07-30 14:09:15 +02:00
ajnart 9e6a0c7748 refactor: remove assistant chat archiving 2026-07-30 12:08:21 +02:00
ajnart 212ce64c04 fix: polish assistant mobile layout 2026-07-30 11:47:30 +02:00
ajnart 828f2ebecd fix: harden assistant floating panel 2026-07-30 11:17:08 +02:00
ajnart 0434f6ac10 feat: streamline assistant interactions 2026-07-30 11:06:16 +02:00
ajnart 7f946e45f4 fix: surface assistant stream errors 2026-07-30 10:01:36 +02:00
ajnart c10395fe5d fix: keep assistant in user menu 2026-07-30 09:37:52 +02:00
ajnart f3bdac4d23 fix: clarify assistant credential removal 2026-07-30 09:18:34 +02:00
ajnart be0496af5a fix: invalidate persisted assistant model state 2026-07-30 08:59:01 +02:00
ajnart 5da5433b58 fix: invalidate assistant models on discovery changes 2026-07-30 08:49:51 +02:00
ajnart 703c27c639 fix: gate assistant models on saved connection 2026-07-30 08:42:41 +02:00
ajnart 676ba6c885 fix: clear assistant credentials on destination changes 2026-07-30 08:34:44 +02:00
ajnart aa83fe4986 feat: promote assistant configuration 2026-07-30 08:25:00 +02:00
ajnart 1c3aac5e82 feat: expand assistant providers and polish experience 2026-07-30 00:39:22 +02:00
ajnart 7fec6c8a07 test: wait for Home Assistant restart 2026-07-29 21:18:35 +02:00
ajnart b025cc593c fix: label board edit mode control 2026-07-29 20:58:09 +02:00
ajnart 60cc1c1e29 fix: initialize tablet mobile boards consistently 2026-07-29 20:38:12 +02:00
ajnart b5ffefe707 fix: keep mobile board widgets unchanged 2026-07-29 20:21:59 +02:00
ajnart e2732dce2c Revert "feat: improve mobile board experience"
This reverts commit 7c0e98c55b.
2026-07-29 20:07:37 +02:00
ajnart c1819d6fdc Revert "fix: handle mobile logout failures"
This reverts commit 9f0969ec22.
2026-07-29 20:07:27 +02:00
ajnart 5c4cc81b05 Revert "feat: refine mobile dashboard experience"
This reverts commit 315520beca.
2026-07-29 20:07:17 +02:00
ajnart bbc560706a Revert "style: auto-fix lint and format"
This reverts commit 4bd0d8e8dd.
2026-07-29 20:06:59 +02:00
ajnart 84b334abab Revert "fix: safely extract notebook summaries"
This reverts commit fa3d0375c5.
2026-07-29 20:06:59 +02:00
ajnart 38a710efe3 Revert "fix: address mobile dashboard review findings"
This reverts commit 1039bb18b3.
2026-07-29 20:06:59 +02:00
ajnart 43e95ff185 Revert "fix: collect mobile board e2e test"
This reverts commit 6dbfbb1417.
2026-07-29 20:06:59 +02:00
ajnart d6bb6542c4 Revert "fix: preserve mobile section focus"
This reverts commit 7ce1081161.
2026-07-29 20:06:59 +02:00
ajnart 55ee5ef063 Revert "fix: layer mobile widget details above header"
This reverts commit a540f07920.
2026-07-29 20:06:59 +02:00
ajnart ee3b45b73a Revert "fix: keep interface layers server safe"
This reverts commit cf83e0d849.
2026-07-29 20:06:59 +02:00
ajnart b612fb9a89 Revert "feat: complete automatic mobile board UX"
This reverts commit 7959c20d88.
2026-07-29 20:06:47 +02:00
ajnart dba9a250ff Revert "fix: harden automatic mobile boards"
This reverts commit d0b9348b86.
2026-07-29 20:06:32 +02:00
ajnart f90fb4512b fix: derive assistant approval key at runtime 2026-07-28 23:47:07 +02:00
ajnart 92a2be2b65 merge: sync dev changes 2026-07-28 23:45:19 +02:00
ajnart 97fc9dda4c ci: limit turbo concurrency 2026-07-28 23:29:45 +02:00
ajnart 5c60489503 fix: hide closed assistant history 2026-07-28 23:19:31 +02:00
Thomas cfad8dd642 Merge branch 'dev' into feat/homarr-assistant 2026-07-28 23:13:43 +02:00
ajnart 36ae9f93ee fix: harden assistant integration 2026-07-28 22:56:28 +02:00
ajnart 82987d1f69 ci: limit fast gate concurrency 2026-07-28 22:48:00 +02:00
ajnart 00e8790ec2 feat: add Homarr assistant 2026-07-28 21:33:17 +02:00
ajnart d0b9348b86 fix: harden automatic mobile boards 2026-07-28 21:28:53 +02:00
ajnart 7959c20d88 feat: complete automatic mobile board UX 2026-07-28 19:39:13 +02:00
ajnart cf83e0d849 fix: keep interface layers server safe 2026-07-28 19:39:13 +02:00
ajnart a540f07920 fix: layer mobile widget details above header 2026-07-28 19:39:13 +02:00
ajnart 7ce1081161 fix: preserve mobile section focus 2026-07-28 19:39:13 +02:00
ajnart 6dbfbb1417 fix: collect mobile board e2e test 2026-07-28 19:39:12 +02:00
ajnart 1039bb18b3 fix: address mobile dashboard review findings 2026-07-28 19:39:12 +02:00
ajnart fa3d0375c5 fix: safely extract notebook summaries 2026-07-28 19:39:12 +02:00
ajnart 4bd0d8e8dd style: auto-fix lint and format 2026-07-28 19:39:12 +02:00
ajnart 315520beca feat: refine mobile dashboard experience 2026-07-28 19:39:12 +02:00
ajnart 9f0969ec22 fix: handle mobile logout failures 2026-07-28 19:39:12 +02:00
ajnart 7c0e98c55b feat: improve mobile board experience 2026-07-28 19:39:12 +02:00
ajnart bc57f9579d fix: align mobile root section ordering 2026-07-28 19:39:12 +02:00
ajnart e2f9fa871a fix: address board review and CI feedback 2026-07-28 19:39:12 +02:00
ajnart f133e09dc6 feat: add automatic mobile board layout 2026-07-28 19:39:12 +02:00
Thomas e8a2c87c2d Delete review.md 2026-07-23 02:13:20 +02:00
Thomas 4f6e335f72 fix(custom-widgets): address final v2 review feedback (#6451) 2026-07-23 02:11:32 +02:00
Thomas df967751f6 fix(custom-widgets): address v2 merge review (#6446) 2026-07-23 01:55:15 +02:00
Thomas 6aceb2fd1f feat(custom-widgets): complete Custom JSX v2 (#6390) 2026-07-22 20:47:19 +02:00
Thomas 60cbd7bb7a Merge branch 'dev' into v2 2026-07-18 23:00:30 +02:00
Thomas c0f1d6624c fix(custom-widgets): fix v2 editor crash and definition-not-found race (#6377) 2026-07-17 14:39:29 +02:00
Thomas Camlong 67d146220a test(integrations): wait for Home Assistant restart 2026-07-16 16:25:48 +02:00
Thomas Camlong 41db737905 ci: publish multi-platform preview images 2026-07-16 16:16:01 +02:00
Thomas Camlong 3b128585a3 ci: remove memory regression test 2026-07-16 15:01:27 +02:00
Thomas Camlong cdaf2a838c fix(ci): preserve memory-test encryption key 2026-07-16 14:52:40 +02:00
Thomas Camlong 926a6856fb test(integrations): allow recurring calendar setup 2026-07-16 14:34:07 +02:00
Thomas Camlong c6160da222 fix(ci): stabilize affected workspace gate 2026-07-16 14:22:01 +02:00
Thomas Camlong e729dfd891 fix(ci): prevent autofix race 2026-07-16 14:13:39 +02:00
Thomas Camlong eb71f936c6 fix(custom-widgets): remove stale form helpers 2026-07-16 14:13:39 +02:00
ajnart c10ebbc863 style: auto-fix lint and format [skip ci] 2026-07-16 08:22:51 +00:00
Thomas 106c3bfa90 Merge branch 'dev' into v2 2026-07-16 10:21:40 +02:00
Thomas Camlong 27a6467896 fix(ci): don't cancel CI on autofix pushes, guard format step 2026-07-15 16:47:28 +02:00
ajnart 8331a58471 style: auto-fix lint and format [skip ci] 2026-07-15 14:20:37 +00:00
Thomas Camlong ec74c36a6a fix(oxlint): remove unsupported option from docs child config 2026-07-15 16:19:12 +02:00
Thomas Camlong 487d98275c fix(ci): autofix commits skip CI to prevent race cancellation 2026-07-15 16:17:59 +02:00
Thomas Camlong 187a479bd5 fix(ci): scope format check to only PR-changed files 2026-07-15 15:57:42 +02:00
Thomas Camlong 0bfe6ddabe fix(ci): don't fail fast-gate on format check (auto-fix handles it) 2026-07-15 15:55:00 +02:00
ajnart 51d8a3cc53 style: auto-fix lint and format 2026-07-15 13:54:18 +00:00
Thomas Camlong 06db42129c fix(ci): remove coverage step, fix auto-fix to format all affected workspaces 2026-07-15 15:47:11 +02:00
Thomas Camlong a11583feb1 Revert "style: format all files and remove coverage report step"
This reverts commit 446931bc0a.
2026-07-15 15:44:49 +02:00
Thomas Camlong 446931bc0a style: format all files and remove coverage report step 2026-07-15 15:42:13 +02:00
Thomas Camlong 34bcc62415 fix: disable cpu-features native build (incompatible with Node 24 V8 API) 2026-07-15 15:35:43 +02:00
Thomas Camlong a2e3c5d3bc style: fix formatting in v2 files 2026-07-15 15:34:15 +02:00
Thomas Camlong ed9646b0c6 fix: regenerate pnpm-lock.yaml after dev merge 2026-07-15 15:25:52 +02:00
Thomas Camlong e0de370340 Merge branch 'dev' into v2 2026-07-15 15:17:18 +02:00
homarr-renovate[bot] 7e5453d47c chore(deps): update dependency @gfazioli/mantine-onboarding-tour to ^4.0.7 (#6343)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-15 12:16:38 +00:00
Thomas 3b2fc2527e feat(custom-widgets): Full Mantine v9 + SubFetch support for custom JSX (#6304) 2026-07-15 14:06:02 +02:00
Thomasandhomarr-renovate[bot] 0a0b3a12c9 bring dev changes to v2 (#6353)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-07-15 12:16:16 +02:00
Thomas ba263dbe47 Delete .opencode directory 2026-07-15 12:14:46 +02:00
Thomas Camlong 8a7c4875bf Revert "chore(merge): resolve v2 conflicts"
This reverts commit 377f2f6a20, reversing
changes made to 7e4cb0201b.
2026-07-15 12:12:33 +02:00
Thomas Camlong 377f2f6a20 chore(merge): resolve v2 conflicts 2026-07-15 12:09:51 +02:00
Thomasandcopilot-swe-agent[bot] c69e33ac8c feat: community workshop for custom widgets and CSS (#5984)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-15 11:53:15 +02:00
Thomas Camlong 121a83040d Setup v2 pre-release branch 2026-07-13 18:14:19 +02:00
963 changed files with 19263 additions and 45664 deletions

No files matched your search

+2
View File
@@ -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
+19 -9
View File
@@ -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
+8
View File
@@ -0,0 +1,8 @@
version = 1
[[analyzers]]
name = "javascript"
[analyzers.meta]
plugins = ["react"]
environment = ["nodejs"]
+1 -2
View File
@@ -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
View File
@@ -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'
+23 -89
View File
@@ -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 }}
-11
View File
@@ -84,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?
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+263
View File
@@ -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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

@@ -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.
![Export page](./img/old-homarr-export-data.png)
@@ -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
![Onboarding initial step](./img/onboarding-step-1.png)
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.
:::
![Onboarding import dropzone](./img/old-homarr-import-dropzone.png)
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.
![Onboarding import list](./img/old-homarr-import-choose-imported-data.png)
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.
![Onboarding import key input](./img/old-homarr-import-decryption-token.png)
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.
@@ -34,10 +34,8 @@ Images built by this CLI record their source checkout, revision, and optional pu
## Run images
```bash
homarr --pr <number> --env HOMARR_WEBSITE_URL=https://app-v2.preview.homarr.dev/
homarr run <name>
homarr run --pr <number>
```
The direct `homarr --pr <number>` form is shorthand for `homarr run --pr <number>`. Add `--detach` to run in the
background, `--demo` to enable demo data, or repeat `--env KEY=VALUE` to provide additional environment variables.
Add `--detach` to run in the background, `--demo` to enable demo data, or repeat `--env KEY=VALUE` to provide additional environment variables.
@@ -35,7 +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_WEB_URL` | Public Workshop page opened from Homarr. | 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).
@@ -58,12 +58,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
@@ -6,56 +6,22 @@ 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 |
| `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 |
## Widget advanced view
Supported widgets open a temporary advanced view when you hold `Shift` over them for 500 ms. Focus a widget and press
`Shift` + `Enter`, or use its menu, to keep the advanced view open.
## Board editing
`MOD` + `E` enters edit mode. Homarr keeps the current board visible while the editor prepares; movement and resizing become available when it is ready.
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.
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.
| 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` | Closes an open advanced view or board menu |
:::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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 39 KiB

+2 -2
View File
@@ -85,8 +85,8 @@ To do this, enter the board settings:
![](./img/enter-board-settings.png)
Scroll to the **Custom CSS** section on the board settings page. It is part of the unified settings form; enter your CSS
there and save the board settings to apply it to that board.
Scroll down a bit and click on custom CSS.
Here you can enter your CSS code, which will be applied on the respective board page.
![](./img/custom-css-setting.png)
@@ -6,16 +6,18 @@ 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 manageBoardHeaderEditMode from "./img/manage-board-header-edit-mode.png";
import manageBoardHeaderAddItem from "./img/manage-board-header-add-item-menu.png";
import manageBoardHeaderChooseItem from "./img/manage-board-header-choose-item.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";
import boardEditModeCurrent from "./img/onboarding-board-edit-mode-current.png";
import addContentMenuCurrent from "./img/onboarding-add-content-menu-current.png";
import chooseItemCurrent from "./img/onboarding-choose-item-current.png";
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 +29,117 @@ 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 onboard 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 repeatitly after you restarted 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.
![Screenshot of Step 1 onboarding](./img/onboarding-step-1.png)
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 completly anonymous and is 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 ZIP file, that is being downloaded, on your device. If you selected "integrations" or "users", you also get an encryption / decryption key.
Copy it and do not loose it!
5. Go to your new >1.0.0 Homarr 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. Since screen sizes were removed in 1.0, we create a new board for every screen size by default. We recommend you to only keep the best layout and adjust it later in 1.0.0 - but you can also import all of them and pick later.<br/><img src={manageImportOldHomarrChooseData} style={{ maxWidth: 500 }} />
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`:
![](./img/manage-create-board-button.png)
A board must always have a name and can either be public or private.
@@ -78,25 +147,9 @@ Public boards are also accessible for users, that are not logged in ([see access
![](./img/manage-create-board-modal.png)
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 boards, that you have access to, on this page:
<figure>
<img
src={boardsHomeMobileMenu}
alt="Boards management page showing a Home badge and separate desktop and mobile home-board menu entries"
style={{ maxWidth: "100%" }}
/>
<figcaption>The Boards page selects the board opened from Home separately for desktop and mobile.</figcaption>
</figure>
The board marked **Home** is the board opened from the desktop Home route. **Set as your mobile board** selects which board
opens on phones. This is separate from the **Mobile** layout: the home-board setting chooses a board, while **Mobile** and
**Base** are responsive layouts inside that board. A phone can therefore open a different home board and still use that
board's Mobile layout.
New boards start with **Mobile** and **Base** responsive layouts automatically, so you can edit the board for phones and
desktops without creating extra layouts first. Mobile starts at `0px` with 3 columns and Base at `768px` with 10 columns.
Each layout keeps its own tile positions and sizes.
![](./img/manage-view-board.png)
## Add your first app to Homarr
@@ -120,63 +173,37 @@ After you created the app, you'll see in on the apps page:
Let's add your freshly created app, from the previous step, to the board.
To do this, edit your previously created board by clicking on the big `Open board` button:
<figure>
<img src={boardDesktopCurrent} alt="Current Homarr board opened from the Boards page" style={{ maxWidth: "100%" }} />
<figcaption>Open a board to view its dashboard, then enter edit mode to arrange its content.</figcaption>
</figure>
![](./img/manage-view-board.png)
Here you have the following options in the header:
![](./img/manage-board-header.png)
- **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.
- **Edit mode**: Clicking this button will toggle the 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, ...).
- **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:
<figure>
<img
src={boardEditModeCurrent}
alt="Current Homarr board editor with tile controls visible"
style={{ maxWidth: "100%" }}
/>
<figcaption>The current editor keeps the board visible while its editing controls are available.</figcaption>
</figure>
<img src={manageBoardHeaderEditMode} alt={"Screenshot of buttons in the edit mode"} width={200} />
- **Add board content**: Click this button to create a new item, app, integration, or Container.
- **Save board**: Click this button to save your changes and leave edit mode. If you leave with unsaved changes, Homarr asks whether to discard them.
- **Add item**: Click this button to add new elements to your page.
- **Exit edit mode**: Click this button to save your changes and exit edit mode.
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. 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
canvas; it is not inserted at a clicked empty cell or directly into a rail or Container:
To continue, click the option "New Item":
<figure>
<img
src={addContentMenuCurrent}
alt="Current add board content menu with item, app, integration, and Container actions"
style={{ maxWidth: "100%" }}
/>
<figcaption>The add-content menu provides the four supported ways to place new content on the board.</figcaption>
</figure>
<img src={manageBoardHeaderAddItem} alt={"Screenshot of the add item menu"} width={200} />
Next, you can choose what item you'd like to add.
We will discuss the other options in the following sections - for now choose "App" to continue.
<figure>
<img
src={chooseItemCurrent}
alt="Current item picker showing the App card and other item types"
style={{ maxWidth: "100%" }}
/>
<figcaption>Choose the App item from the current picker to add an app tile.</figcaption>
</figure>
<img src={manageBoardHeaderChooseItem} alt={"Screenshot of choosing the app item in the modal"} width={300} />
As soon as you chose the app, the modal will close and you'll get the following item on your board:
@@ -185,52 +212,30 @@ As soon as you chose the app, the modal will close and you'll get the following
This is expected, because you haven't told Homarr what app it should show in this item.
To configure this, click on the small 3 dots at the top of the item in the edit mode and choose "Edit item" in the menu.
<figure>
<img
src={moveResizeMenuCurrent}
alt="Current board tile menu showing Edit item and Move or resize item"
style={{ maxWidth: "450px" }}
/>
<figcaption>The tile menu contains the item editor and the separate move-and-resize tools.</figcaption>
</figure>
![](./img/manage-board-edit-app.gif)
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>Use the Edit item modal to choose the app and configure how its tile is displayed.</figcaption>
</figure>
![](./img/manage-board-choose-app.png)
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.
Use the resize handle at the bottom right to resize the tile.
Press and hold the handle, move the pointer, and release it when the tile has the size you want.
To move a tile in edit mode, press and hold its inert content area, move it to the desired position, and release it.
![](./img/resize-item.gif)
To move a tile in edit mode, press and hold its content, move it to the desired position, and release it.
Widget content is disabled while editing so its controls do not intercept the drag; use the tile's settings button for edit actions.
The tile snaps to the grid. A single compatible collision may swap the two items when both resulting placements fit;
otherwise the collision chain reflows neighboring tiles automatically.
The tile snaps to the grid. Equally-sized tiles swap when one is dropped into the other's slot, while other neighboring tiles move out of the way automatically.
![](./img/move-item.gif)
Every `1 × 1` tile has a logical content size of `200 × 200` pixels.
Larger tiles use multiples of that fixed unit, with a consistent gap between cells.
Homarr scales the complete board uniformly, like zooming a page.
The complete canvas always fits the available width without horizontal board scrolling. Below 100%, Homarr preserves the
widget's readable typography and control density instead of shrinking each widget independently.
It stops shrinking at 75% and lets you scroll horizontally instead of making text and controls too small.
It does not squeeze or reflow the content of individual widgets, so a widget keeps the same design and proportions across screen sizes.
On a narrow screen, Homarr uses the **Mobile layout** for the selected board and stacks the board content vertically while
preserving each widget's fixed logical proportions:
<figure>
<img
src={currentBoardMobileView}
alt="Homarr's Mobile layout in a 390-pixel-wide viewport, with tiles scaled to fit and stacked vertically"
style={{ maxWidth: "390px" }}
/>
<figcaption>
At phone width, the selected board uses its Mobile layout and fits the complete canvas to the viewport.
</figcaption>
</figure>
:::note
Viewing a board initially uses the lightweight, non-interactive layout.
@@ -246,17 +251,16 @@ This helps you to explore the capabilities of Homarr before you build your board
:::
Keyboard users can focus a tile, press Enter or Space, then use arrow keys to move and Shift plus arrow keys to resize
within its current canvas, rail, or Container. Press Escape to stop keyboard editing. Touch dragging starts after a short
hold of about `200 ms`; resize handles accept direct touch input. During a pointer or touch move or resize, press Escape
to cancel and restore the previous layout. For cross-grid moves or precise numeric controls, choose **Move / resize item**
from the tile's edit menu.
Keyboard commands at a grid boundary or minimum tile size are ignored and announced; they do not move or shrink the tile.
Keyboard users can focus a tile, press Enter or Space, then use arrow keys to move and Shift plus arrow keys to resize.
Press Escape to stop.
For precise numeric controls or touch input, choose **Move / resize item** from the tile's edit menu.
The dialog can also move the tile to another section or fixed sidebar:
<img src={currentKeyboardTileEditing} alt="A board tile focused for keyboard editing" style={{ maxWidth: "100%" }} />
![](./img/manage-board-move-resize-item-menu.png)
Open **Move / resize item** from the tile menu to move the tile to another section or rail and enter precise numeric
coordinates and dimensions. After making changes, click **Save board** to persist them and leave edit mode.
Be sure to save your changes by exiting edit mode!
![](./img/exit-edit-mode.png)
## Adding your first integration
@@ -291,14 +295,7 @@ Let's add your integration, that you have configured in the section above, as a
First, navigate back to your board that you want to add it to.
Next, enter edit mode again and add another item to your board:
<figure>
<img
src={addContentMenuCurrent}
alt="Current add board content menu with item, app, integration, and Container actions"
style={{ maxWidth: "100%" }}
/>
<figcaption>Use the same add-content menu to place an integration widget on the board.</figcaption>
</figure>
![](./img/manage-board-header-add-item-menu.png)
Instead of choosing an app, let's choose `System Health Monitoring` this time.
Click on "Add to board":
@@ -325,7 +322,9 @@ Editing a widget will trigger a data update and data will soon appear:
![](./img/widget-system-health.png)
As with every change, click **Save board** to persist your board changes and leave edit mode.
As with every change, don't forget to save your changes using the exit edit mode button:
![](./img/exit-edit-mode.png)
## Organizing items with Containers
@@ -338,7 +337,7 @@ Each Container can configure:
- Show or hide **Open all in new tabs**.
- Change its border color and custom CSS classes.
Open the add-content menu and choose **New container**.
Open the add-content menu and choose **New Container**.
In edit mode, move and resize it like any other item, then drag items into or out of it.
Nested content stays within the Container's bounds.
While resizing, the solid outline follows the pointer continuously and the dashed grid preview shows the size that will be saved on release.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 561 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 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.
+8 -93
View File
@@ -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.
![Apps added from containers](img/add-to-homarr-apps.png)
### 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:
-60
View File
@@ -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
@@ -149,10 +93,6 @@ an optional `error` on a failed integration; media-request responses expose a `f
requests endpoint returns its items in `requests`. When every selected integration fails, the query returns an error instead
of a partial response.
Other widgets use the same settlement pattern with endpoint-specific shapes: a failed integration may appear as a named
fallback row, in a failure array, or be omitted while healthy results remain. Widget pages should identify the shape they
render rather than assuming every query exposes an `error` field.
### Saving dashboard layouts
The dashboard editor saves item positions and sections as one board update.
+2 -13
View File
@@ -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.
![Apps list](./img/app-list.png)
+52 -38
View File
@@ -8,64 +8,78 @@ tags:
# 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.
Homarr Assistant can answer questions about your Homarr instance and use Homarr tools on your behalf. It follows the
permissions of the signed-in user and asks for approval before it changes anything.
![Homarr Assistant conversation with suggested prompts](/img/assistant/conversation.png)
## Set it up
## Open the assistant
- Select **Assistant** in the user menu.
- Press `Shift + A` from anywhere in Homarr.
- Type a question in `Cmd/Ctrl + K`, then select **Ask Homarr Assistant**.
- Add the **Assistant** widget to a board for a permanent conversation view.
You can minimize a conversation while it works. The activity bar shows its progress and the user menu shows unread
responses.
## What it can do
- Find and update boards, apps, integrations, widgets, and dashboard settings.
- Check downloads, media, Docker containers, service health, and other connected integrations.
- Create formatted notes and Custom Widgets.
- Search the web when OpenRouter web search is enabled.
- Show tool activity, reasoning summaries, usage, time, and cost when the provider reports them.
Use `@` to add an app, board, integration, widget, or tool as context. Use `/` for common prompts. You can also paste or
attach images and text files when the selected model supports them.
## Configure the assistant
Open **Management → Assistant**.
![Assistant provider, credentials, and model configuration](/img/assistant/configuration.png)
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.
1. Select a provider.
2. Check the API base URL and add an API key if required.
3. Select **Save provider connection**.
4. Refresh the model list and choose a model.
5. Enable the assistant, then select **Save assistant**.
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.
Homarr includes presets for OpenRouter, OpenAI, Claude, Gemini, xAI, Groq, Mistral, DeepSeek, Together AI, Ollama, and
LM Studio. Use **Custom endpoint** for another OpenAI-compatible Chat Completions service.
The [Homarr provider](../workshop/homarr-provider) does not require an administrator API key. Each user signs in to the
Community Workshop and receives a separate daily allowance. A shared Workshop daily quota also applies, so requests
can be rejected after that shared quota is exhausted.
:::info Local model servers
Inside a container, `localhost` means the Homarr container. Use the model server's container name, local DNS name, or
host gateway address.
host gateway address instead.
:::
## Use it
## Approvals and data
- Select **Assistant** in the user menu or press `Shift + A`.
- Ask from `Cmd/Ctrl + K`.
- Add the [Assistant widget](../widgets/assistant) to a board.
Read-only tools can run automatically. Changes show an approval card with the exact action before Homarr runs it.
Normal Homarr permissions still apply.
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.
API keys and custom headers are encrypted in the Homarr database and are never returned to the browser. Prompts,
attachments, tool definitions, and tool results are sent to the configured model provider. Check that provider's data
policy before enabling the assistant.
Use **Conversations** to search, switch, rename, export, or delete threads.
Conversations are stored per user. Users can edit or retry messages, copy responses, export a conversation as Markdown,
and delete conversations.
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.
## Permissions and privacy
- 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.
Prompts, attachments, tool definitions, and tool results are sent to the selected model provider. Review that
provider's privacy policy before enabling the assistant.
## Troubleshooting
## If setup does not work
- **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.
- **The assistant is unavailable:** enable it and save the configuration.
- **A model stops during a tool call:** use a model that supports streaming and structured tool calls.
- **A local endpoint cannot be reached:** check container networking and the endpoint's listen address.
See [MCP](./mcp) for external AI clients and [Custom Widget agent authoring](./custom-widgets/agent-authoring) for the
widget authoring workflow.
Homarr exposes its MCP-enabled procedures to external clients through the [MCP server](./mcp). MCP omits
`customWidget_*` tools for non-admin API keys, while the built-in assistant adds browser actions and optional
OpenRouter web search. Custom Widget authors can find the full workflow in
[Agent authoring](./custom-widgets/agent-authoring).
-3
View File
@@ -53,9 +53,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.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 36 KiB

+35 -109
View File
@@ -11,19 +11,15 @@ tags:
import iconColorImage from "./img/icon-color.png";
import itemRadiusImage from "./img/item-radius.png";
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 keyboardTileEditingImage from "./img/keyboard-tile-editing.png";
import layoutLargeImage from "./img/layout-large.png";
import layoutMediumImage from "./img/layout-medium.png";
import layoutSmallImage from "./img/layout-small.png";
Each dashboard is called a "board".
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.
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/board).
Other boards can be viewed by direct links (e.g. my-homarr.com/board/smart-home) or via the management pages.
Names of boards must be unique because this will be used in the URL to load the desired board.
@@ -96,23 +92,9 @@ Homarr will fall back to the Homarr logo if nothing is set.
In this section you can configure the different responsive layouts. Homarr selects the layout with the highest breakpoint that is not wider than the current screen, then scales its complete fixed-size canvas uniformly.
<img
src={layoutSettingsImage}
alt="Board settings showing protected Mobile and Base responsive layouts with rail controls"
style={{ maxWidth: "100%" }}
/>
![](./img/layout-settings.png)
The layout cards show the representative geometry used for planning. This current example also shows a custom layout
with both fixed rails enabled; the rails reserve columns and the remaining canvas reflows automatically.
<img
src={layoutSettingsCustomRailsImage}
alt="Board layout settings with a custom layout and both fixed rails enabled"
style={{ maxWidth: "100%" }}
/>
Every board has two protected layouts. Creating a board adds **Mobile** at `0px` with three columns and **Base** at
`768px` with the column count you choose. Both layouts can be edited, but neither can be removed.
Every board has two protected layouts:
Every `1 × 1` tile has a `200 × 200` logical-pixel content area.
The `24` logical-pixel gap between tiles is separate from that size.
@@ -125,18 +107,9 @@ 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, but they cannot be removed. 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
free breakpoint between Mobile and Base, and you can then change the breakpoint, columns, and rails.
Older or incomplete boards are repaired automatically. A board with no layouts receives Mobile at `0px` with three
columns and Base at `768px` with ten columns. A board with one layout keeps it as Base and receives a generated Mobile
layout. With multiple layouts, the narrowest becomes Mobile, the widest becomes Base, and intermediate layouts remain
custom. Duplicate or out-of-order breakpoints are normalized without discarding saved positions.
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 are generated from Base and scaled to their selected column count.
Use **Reset from Base** on Mobile or a custom layout to replace all of its positions and sizes with a newly generated version of Base. The layout's name, breakpoint, and column count are kept.
@@ -162,7 +135,6 @@ The preview shows how much space remains for the main canvas.
Rail columns are reserved from the layout's total column count.
For example, a 12-column layout with a two-column left rail leaves ten columns for the canvas.
The combined rail widths cannot consume the final main-canvas column.
Enabling or widening a rail reflows canvas items into the remaining columns.
Disabling a rail moves its contents back into the next available canvas positions.
@@ -171,75 +143,39 @@ Apps, widgets, and Containers inside them still use the same fixed `200 × 200`
#### Breakpoint
The minimum screen width where the layout is used; a layout is eligible at its breakpoint and above. Mobile is fixed at
`0px`. Custom breakpoints must be unique and sit between Mobile and Base. Base must remain the highest breakpoint.
When the initial viewport is not available, Homarr uses its remembered viewport information, client hints, or User-Agent
fallback to select a layout.
The minimum screen width where the layout is used. Mobile is fixed at `0px`. Custom breakpoints must be unique and sit between Mobile and Base. Base must remain the highest breakpoint.
#### View and edit modes
View mode renders a static board without executing the drag-and-drop editor.
After the board is interactive, Homarr prepares the editor in the background for users who can modify it.
Entering edit mode keeps the static board visible until that preparation finishes, then enables collision-aware movement and resizing.
Collapsed Containers are expanded while editing so their contents and controls are reachable.
<img
src={boardViewModeImage}
alt="A Homarr board in view mode with the dashboard canvas and static widgets visible"
style={{ maxWidth: "100%" }}
/>
Widget content is inert in edit mode, so dragging from the inert widget area moves the tile instead of activating the widget;
settings and other controls remain usable in the compact header above the tile.
A single compatible collision can swap two items when both resulting placements fit, including different-sized or lateral moves.
For other collisions, or when a tile grows into occupied space, nearby tiles are compacted out of the way and collision chains reflow.
Entering edit mode keeps the static board visible until that preparation finishes, then enables collision-aware movement and resizing without changing the board geometry.
Widget content is inert in edit mode, so dragging anywhere on the content moves the tile instead of activating the widget; its settings button remains visible in the compact header above the tile.
Dropping a tile into another tile's exact slot swaps their positions when both resulting placements fit without overlapping other content.
For other collisions, or when a tile grows into occupied space, nearby tiles are compacted out of the way.
The visible corner controls provide a 44-pixel resize target, and edge controls appear on hover for horizontal or vertical resizing.
Resize handles stop at the tile's minimum allowed size while the pointer is still moving.
Use the pencil button in the board header to enter edit mode; click it again to save. If you leave the page with unsaved
changes, Homarr asks whether to discard them. If the editor cannot load, the board remains in view mode.
Exit edit mode to save the resulting positions.
<img
src={boardEditModeImage}
alt="Board in edit mode with the board header controls and editable dashboard tiles visible"
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.
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 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.
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
its resize handles.
On a touch screen, press and hold a tile for about `200 ms` before moving it; a quick tap does not start a drag. Resize
handles support direct touch input.
Pointer users can drag anywhere on a widget tile. Containers use the grab button at their top-left corner.
Resize tiles from any edge or corner handle.
On a touch screen, press and hold the tile before moving it; resize handles support direct touch input.
Keyboard users can focus a tile, press Enter or Space to start editing, use the arrow keys to move it, and use Shift plus the arrow keys to resize it.
Press Enter, Space, or Escape to stop keyboard editing.
Keyboard moves and resizes apply immediately within the current canvas, rail, or Container; they cannot move an item
between grids. Use pointer drag or **Move / resize item** for that.
Escape stops keyboard editing without undoing those changes.
Keyboard moves and resizes apply immediately; Escape stops keyboard editing without undoing those changes.
During a pointer or touch interaction, Escape cancels the move or resize and restores the layout from before that interaction.
Invalid, outside, or otherwise forbidden drops are cancelled in the same way.
The **Move / resize item** action in the edit menu remains available for precise numeric input.
<img src={keyboardTileEditingImage} alt="A focused board tile in keyboard editing mode" style={{ maxWidth: "100%" }} />
#### Preview
Layout Settings shows a lightweight structural preview for every layout, including saved spans, Containers, widget labels, and app icons.
It uses a representative viewport width to project the saved geometry; it does not execute widget queries or represent every
pixel of the screen range where the layout is active.
Layout Settings shows a lightweight structural preview for every layout, including saved spans, Containers, widget labels, and app icons. The canvas width represents the screen range where that layout is active.
Choose **Edit layout** to open the real board editor at that representative width on desktop. Save returns to Layout Settings. Cancel discards the local changes. On phones and tablets, the normal board editor continues to use the layout selected by the device width.
<img src={layoutSmallImage} height={256} />
<img src={layoutMediumImage} height={256} />
<img src={layoutLargeImage} height={256} />
### Containers and rails
Containers can hold apps, widgets, and nested Containers.
@@ -311,10 +247,9 @@ Change the radius of your items and Containers.
This is the field where you can enter custom CSS.
The styling will only be applied on the respective board page.
When Homarr Assistant is enabled, users with modify permission can also ask it to edit the board's General, Background,
Appearance, Custom CSS, and app-status settings. The assistant reads the current stylesheet first and presents an
editable review form before requesting approval to save the change. It does not edit responsive layouts, access control,
or Danger zone actions.
When Homarr Assistant is enabled, users with modify permission can also ask it to edit the board's Custom
CSS and other visual or behavior settings. The assistant reads the current stylesheet first and presents an
editable review form before requesting approval to save the change.
[See this guide on how to work with custom CSS](/docs/advanced/styling/).
@@ -331,12 +266,12 @@ 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. 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.
- Right-click a widget to inspect its live query status, refresh its data, change quick options, open settings, or run
supported actions. Available controls follow your board and integration permissions.
- If you can modify the board, right-click or touch and hold an empty grid cell to add an item there. Focus a board
section and press `Shift` + `F10` for the keyboard equivalent.
The widget context menu follows the **Enable right click on widgets** preference in your user settings. On touch devices,
use the widget menu where your browser exposes it.
Widget and empty-space context menus follow the **Enable right click on widgets** preference in your user settings.
### Access control
@@ -395,8 +330,7 @@ Duplicating a board will not copy the users and groups that have access to the b
nor the integrations the user does not have access to.
:::
Duplicating a board will create a new board with the same settings, all responsive layouts, and independent positions as the original board.
Duplicating a board that contains Custom Widgets also requires an administrator.
Duplicating a board will create a new board with the same settings and layout as the original board.
You can duplicate a board by clicking on the three dots in the list of boards and selecting "Duplicate board".
![](./img/duplicate-board-modal.png)
@@ -454,13 +388,5 @@ For the global home board you can only select boards that are public.
### Mobile home board
The mobile home board controls which board opens for devices that Homarr detects as mobile from the User-Agent header.
This is separate from a board's **Mobile layout**, which controls the layout selected inside that board.
You can set a mobile home board for yourself in your user preferences, for a user or group through management, or for the
whole server with **Global mobile board** under `Management` > `Settings` > `Boards`.
For signed-in users, Homarr checks the user's mobile home board, then the user's desktop home board, then the mobile and
desktop defaults for the lowest-position matching group, followed by the mobile and desktop defaults for the `Everyone`
group, and finally the server-wide mobile and desktop defaults. Anonymous visitors use the server-wide mobile default and
then the server-wide desktop default. Server-wide fallback boards must be public. See the
[global board defaults](/docs/management/settings#boards) for the complete precedence and the required public-board rule.
Same as the home board, you can also set a mobile home board.
It will be used for devices that are detected as mobile devices. (Using User-Agent header)
@@ -67,23 +67,17 @@ These interfaces do not return an archive URL or archive hash.
1. Load `customWidget_getSkill`, then get the prompt and live schema.
2. Call `customWidget_getComponentCatalog`, followed by `customWidget_getComponent` for only the relevant installed
components and `customWidget_getExample` when one catalog example matches the interaction pattern. Fetch at most
eight component documents before the first validation (or four after a complete example already matches). If shared
props are needed, collect their catalog names and call `customWidget_getSharedProps` once for that set.
components and `customWidget_getExample` when one catalog example matches the interaction pattern. If shared props
are needed, collect their catalog names and call `customWidget_getSharedProps` once for that set.
3. Read the external API documentation.
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
`{}` for load queries; manual queries require exactly the returned primitive `parameterNames`.
7. Run `customWidget_previewQuery` for every query returned by the preview and inspect its actual status and data shape.
8. Test relevant actions in simulation mode.
9. Open the returned preview path with browser tools.
10. Inspect the redacted journal and revise the local candidate until validation, data, and rendering all work. Keep the
exact tested preview candidate locally. Any definition, template, or source change requires a new validation and preview;
earlier query evidence belongs to the previous preview revision.
11. Create the widget from that final tested preview with `customWidget_createFromPreview`. The preview session already carries
the source URLs and encrypted credentials supplied during preview; the create call accepts only its session ID plus an
optional target board and persists that tested preview.
10. Inspect the redacted journal and revise the local candidate until validation, data, and rendering all work.
11. Create the widget, including any source URLs and credentials the user already supplied.
12. Open its returned management path, and use a structured question when the user must choose whether to place it on a board.
13. Otherwise, ask the user to configure the saved definition with the ten-minute Homarr source-setup URL.
14. Create a final preview with the saved definition ID so Homarr loads its stored credentials, then test it again.
@@ -1,37 +1,8 @@
# Create a Custom Widget
import { CanonicalCustomWidgetExample } from "@site/src/components/custom-widget-code";
import workbenchImage from "./img/workbench.png";
import workbenchMobileImage from "./img/workbench-mobile.png";
import workbenchValidationImage from "./img/workbench-validation.png";
The workbench exposes **General**, **API sources**, **Requests**, **Options**, **JSX**, and **Preview** sections. Advanced JSON is
available in the manifest and request/option editors. Code and preview sit side by side on desktop; on smaller screens the
**Configure**/**Preview** switch shows one pane at a time and the section navigation scrolls horizontally. Preview sizes display
as **S/M/L** (Compact/Standard/Wide), and fixtures are **Live**, **Loading**, **Empty**, or **Error**.
For multiline JSX, prefer `templateLines` in agent or MCP input and keep the final tested preview as the source of truth before saving.
<img
src={workbenchImage}
alt="Custom Widget workbench with configuration sections and a live preview"
style={{ maxWidth: "100%" }}
/>
<img
src={workbenchMobileImage}
alt="Mobile Custom Widget workbench with Configure and Preview switch and horizontally scrollable section navigation"
style={{ maxWidth: "100%" }}
/>
The workbench keeps the configuration and preview together so you can inspect validation diagnostics, the redacted request
journal, fixture data, and preview size before saving. Invalid fields stay marked in the form while the preview explains why it
needs attention.
<img
src={workbenchValidationImage}
alt="Custom Widget workbench showing a Base URL validation error with Test, preview, and Create actions disabled"
style={{ maxWidth: "100%" }}
/>
The workbench is visual-first: configure the default API connection, add request cards, add option rows, then write JSX. Additional sources and raw manifest JSON remain under Advanced. Code and preview sit side by side on desktop and use tabs on mobile.
## Manual workflow
@@ -50,9 +21,7 @@ needs attention.
<CanonicalCustomWidgetExample id="widget-options" label="Widget options" example="options" height="360px" />
Every option has a default. Supported controls include text, textarea, number, switch, select, multi-select, slider, date, time, color, icon, URL, duration, time zone, and JSON. Dynamic choices use `choicesFrom: { request, itemsPath?, valuePath, labelPath }`; the request must be a load query without invocation parameters, and you cannot provide both static `choices` and `choicesFrom`.
Preview actions are simulated by default and recorded in the journal without contacting the service. Enable **Run actions against the configured API** only when you intend a real mutation; live actions still require configured confirmation.
Every option has a default. Supported controls include text, textarea, number, switch, select, multi-select, slider, date, time, color, icon, URL, duration, time zone, and JSON. Dynamic choices use `choicesFrom` with a query ID and value/label paths.
## ChatGPT or Claude
@@ -62,8 +31,7 @@ The model must return one fenced `json` block containing the complete widget, in
field. Copy that single block and select **Paste**. Homarr fills the visual builders and the JSX editor
without any placeholder replacement or second paste.
Homarr shows all actionable schema or JSX errors inline. Enter credentials separately. The copied prompt does not include the
latest preview response; use **Preview** > **Data** to inspect it and mention important response fields in your request. The prompt deliberately
Homarr shows all actionable schema or JSX errors inline. Enter credentials separately. The prompt deliberately
recommends a focused Mantine component set, visual hierarchy, responsive layout, theme compatibility, and loading,
empty, failure, and success states.
@@ -7,21 +7,7 @@ Templates are JSX expressions interpreted inside a widget-local safety and rende
- `options.name` — saved widget configuration
- `inputs.name` — temporary values from declarative `bind`
The v2 manifest requires a `default` source and supports up to eight sources, 64 requests, and 64 options. Source IDs are
case-insensitively unique. Each request allows up to 32 static headers and 32 invalidation targets; a widget can contain
up to 24 secrets, each up to 8,192 characters, and templates are limited to 50,000 characters. Validation reports the
exact field when a limit is exceeded.
Authoring input accepts either `template` or `templateLines`, not both. `templateLines` accepts 1–2,000 lines, each up to
10,000 characters; after joining lines with newlines, the complete template must still fit the 50,000-character limit.
The safe interpreter also bounds JSX evaluation: AST depth 64, 25,000 operations, 4,000 collection items, 10,000 rendered
nodes, and 200,000 characters in one string or rendered value. API responses are bounded separately before they reach the
template.
Use normal Mantine layouts, typography, feedback, dates, and charts. Official compound names such as `Card.Section`, `Tabs.List`, and `Radio.Group` are supported. Ordinary safe props pass through; a dangerous capability produces an explicit error.
Unsafe URLs, callback props, and unapproved style properties are stripped. Response data is deep-sanitized and budgeted
before it reaches `data`, so treat it as untrusted input even when the source is private.
## Temporary inputs
@@ -29,7 +15,7 @@ before it reaches `data`, so treat it as untrusted input even when the source is
<Stack>
<TextInput bind="search" defaultValue="" label="Search" />
<SubFetch requestId="search" params={{ query: inputs.search }}>
{(results, meta) => (
{(results) => (
<Stack>
{(results ?? []).map((item) => (
<Text key={item.id}>{item.name}</Text>
@@ -46,23 +32,14 @@ Bound values live only in React memory while the widget is mounted. They are not
Use expressions, conditionals, optional chaining, arrays, objects, template strings, safe standard helpers, and expression callbacks for `map`, `filter`, `sort`, and trusted runtime slots. Callback blocks, authored `const`, IIFEs, authored recursion, arbitrary functions, imports, hooks, refs, raw events, browser requests, `eval`, and bigint are blocked.
Bounded regex literals are supported for string matching, splitting, search, and replacement. Regex literals are limited to
128 characters and flags `gimsu`; lookarounds, backreferences, repeated alternatives/groups, unsupported flags, and more
than one variable quantifier are rejected.
Bounded regex literals are supported for string matching, splitting, search, and replacement. Patterns with lookbehind, backreferences, nested quantifiers, excessive length, or unsupported flags are rejected.
## Runtime components
- `SubFetch` runs a named query. Automatic queries run when the widget loads; set `trigger="manual"` to defer one and pass
primitive `params` for its invocation references. With `triggerContent`, any content node (commonly a card or image) becomes
the accessible click and keyboard trigger instead of showing the default load button.
- `SubFetch` with `trigger="manual"` keeps its result local to that instance; it does not populate the shared
`data.requestId` or `status.requestId` roots. The child callback receives `(result, meta)`, while loading, errors, and
retry are handled by `SubFetch`. Provide both `triggerContent` and `triggerAriaLabel` when custom content launches it.
- `ActionButton` and `ToggleSwitch` run actions only when the widget is mounted outside edit mode. A toggle needs both
enabled and disabled request parameters and rolls back its visual state if the action fails.
- `RefreshButton` renders a compact, accessible icon control that invalidates the client&apos;s load-query cache so active load
queries can refetch; it does not execute a request itself. It is disabled when the widget has no active item or preview
session, or while the board is in edit mode.
- `SubFetch` runs a manual query with its `params`. Set `triggerContent` to a card or image to make that content the accessible click and keyboard trigger instead of showing the default load button.
- `ActionButton` and `ToggleSwitch` run actions.
- `RefreshButton` renders a compact, accessible icon control that refreshes load queries for authenticated viewers. It
is disabled for anonymous public-board viewers because refreshing invalidates shared server cache state.
- `Icon`/`TablerIcon` resolves installed safe icons.
Always include responsive loading, empty, error, and success states. Runtime failures remain inside the widget tile.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

@@ -42,14 +42,9 @@ interface HomarrCustomWidgetV2 {
### Design with AI
The **Design with AI** action is available while creating or editing a custom widget. Describe what the widget should show and,
when [Homarr Assistant](../assistant) is enabled, send the request directly from the form. The generated prompt includes the
widget schema and current non-secret configuration, but not the latest preview response. It proposes the matching create or
update tool for your approval.
The **Design with AI** action is available while creating or editing a custom widget. Describe what the widget should show and, when [Homarr Assistant](../assistant) is enabled, send the request directly from the form. The assistant receives the widget schema, current non-secret configuration, and latest test response, then proposes the matching create or update tool for your approval.
Use the preview's **Data** tab to inspect the endpoint's real response shape and mention important fields in your request; the
copied prompt does not automatically include the latest preview response. Saved secrets are never added to the prompt. **Copy to
clipboard** remains available when the Assistant is disabled or when you prefer another AI service.
Run **Test** first when possible so the prompt includes the endpoint's real response shape. Saved secrets are never added to the prompt. **Copy to clipboard** remains available when the Assistant is disabled or when you prefer another AI service.
<CustomWidgetCodeExample
id="complete-widget-manifest"
@@ -67,9 +62,9 @@ preview, publish, install, configure, or write secrets, including through HTTP a
only lists Custom Widget MCP tools, prompts, and resources when its owner is an administrator.
Adding or configuring a Custom Widget placement requires both administrator access and permission to modify that
board. Duplicating a board that contains Custom Widgets also requires an administrator. A non-administrator with
board-modify access can move, resize, change presentation settings, or remove an existing placement, but cannot add one,
change its definition, refresh interval, or Custom Widget options.
board. A non-administrator with board-modify access can move, resize, change presentation settings, or remove an
existing placement, but cannot add one, change its definition, refresh interval, or Custom Widget options. Duplicating
a board that contains Custom Widgets also requires an administrator.
Once an administrator has placed a widget, ordinary board permissions apply to rendering and requests. Authorized
non-administrators can use it on private boards, and queries can render for anonymous viewers on public boards.
@@ -104,12 +99,14 @@ logs, or exports.
For administrators adding widgets while editing a board, the Custom Widgets section includes **Import from
Workshop**. Non-administrators do not see the Custom Widget picker or Workshop actions. Selecting **Install widget**
opens a two-step modal flow: first review the Workshop entry and its complete source in the read-only editor, then
confirm API sources and credentials. After import, the new definition appears in the picker. Its tile waits for the board
to be saved before server requests run.
confirm API sources and credentials. The steps keep the same dimensions and the previous step remains available when
you go back. After import, the new definition appears in the picker. Its tile
waits for the board to be saved before server requests run and loads automatically after a successful save; no page
reload is required.
## 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.
@@ -134,7 +131,6 @@ replace the v1 backup.
## Bundled examples
Homarr seeds five disabled examples without credentials and never overwrites an edited seed. They use the same public
contract. Tautulli Activity is one of the bundled examples preserved from Homarr's AI authoring evaluation:
Homarr seeds five disabled examples without credentials and never overwrites an edited seed. They use the same public contract. The Tautulli example is the highest-scoring widget preserved from Homarr's AI authoring evaluation:
<BundledCustomWidgetGallery />
@@ -1,11 +1,6 @@
# Configure requests and security
Most widgets use a single source named `default`; the manifest must contain it. Extra keyed sources are available for widgets
that combine APIs. Source IDs are case-insensitively unique. Base URLs must use HTTP(S), contain no embedded credentials,
query string, or fragment, and sources can be `public`, `private`, or `loopback`. Authentication is `none`, `bearer`,
`basic`, an API-key header, or an API-key query parameter. Credentials belong to the widget but remain outside its manifest.
Source origins are fixed by the definition and source-setup flow; widget options and invocation parameters cannot redirect a
request to a different origin.
Most widgets use a single source named `default`. Extra keyed sources are available under Advanced for widgets that combine APIs. Authentication is `none`, `bearer`, `basic`, an API-key header, or an API-key query parameter. Credentials belong to the widget but remain outside its manifest.
## Named requests
@@ -40,28 +35,19 @@ Bind values directly where they are used:
`{option:name}` and `$option` read saved widget options. `{param:name}` and `$param` read invocation-time `params`. Load queries cannot use invocation parameters. Homarr infers the exact parameter names and primitive values from these references, rejecting missing or extra invocation params at the request boundary.
Path placeholders and query values must resolve to text, numbers, or booleans. `$option` and `$param` references may appear
recursively in JSON bodies; invocation parameters remain primitive, and load queries cannot use `$param`. JSON bodies can
also use structured `json` and `multiSelect` option values, for example `{ "entity_ids": { "$option": "entities" } }`.
Paths and query-string values must resolve to text, numbers, or booleans. JSON bodies can also use structured `json` and `multiSelect` option values, for example `{ "entity_ids": { "$option": "entities" } }`.
## Protection
Every request uses Homarr's existing server executor: same-origin source enforcement, network scope, DNS and redirect validation, SSRF protection, board permissions, rate and request limits, body and response limits, timeouts, and encrypted credential injection. Static headers cannot override credentials, cookies, routing, proxy, forwarding, or hop-by-hop headers.
The executor caps request bodies at 10 KiB and response bodies at 1 MiB. Parsed JSON is limited to depth 32 and 50,000 nodes;
each network hop has a ten-second timeout, queries follow at most three same-origin redirects, actions do not follow redirects,
and the total request deadline is 45 seconds.
The limiter allows four concurrent requests per user/item and eight per definition. Per minute, a user/item can make up to 60
queries, 10 actions, or 3 DELETE actions; a definition can receive up to 240, 40, or 12 of those request categories.
Queries default to view permission and actions to modify. DELETE always requires full permission and confirmation. Actions are never cached, retried, or run on load.
Management and runtime are intentionally separate. A Homarr administrator with board-modify permission places and
configures the widget. After placement, query and action authorization follows the request's board permission. Public
boards may execute queries for anonymous viewers, but mutations stay authenticated. This includes shared-cache
invalidation: `RefreshButton` is disabled only without an active item or preview session, or in edit mode, while ordinary
scheduled query rendering remains available to anonymous viewers.
invalidation: `RefreshButton` is disabled for anonymous viewers, while ordinary scheduled query rendering remains
available.
Responses use bounded, versioned shared-Redis cache keys. Action invalidation advances the shared version so every
Homarr replica stops serving the previous response without scanning or deleting a key prefix.
@@ -41,8 +41,7 @@ The workbench marks the invalid field while you type. An invalid URL does not re
An invalid definition disables widget preview and Save/Create. The Data, Options, Actions, and Diagnostics tabs remain available so you can correct it.
The Diagnostics tab shows current schema and JSX errors, accessibility checks, and (after a preview) the redacted request
journal. It does not expose credentials or raw secret values.
The Diagnostics tab shows the current schema errors.
## Workshop unavailable
Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

@@ -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.
![](./img/create-integration-dropdown.png)
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
-34
View File
@@ -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.
+2 -2
View File
@@ -23,9 +23,9 @@ approval before running mutating procedures.
## Getting Started
The MCP endpoint lives under the **API** page at `Management` → `Tools` → `API` → **AI / MCP** tab.
The MCP endpoint lives under the **API** page at `Management` → `Tools` → `API` → **MCP** tab.
![AI / MCP tab showing the endpoint, API-key warning, and client configuration](./api/img/mcp-instructions.png)
![MCP Instructions Page](./api/img/mcp-instructions.png)
### 1. Create an API Key
@@ -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.
![](./img/search-engines.png)
You can add any search engine that has a search URL that accepts a search term as a parameter.
+1 -1
View File
@@ -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
+13 -72
View File
@@ -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 &rarr; modify all boards &rarr; full board access | create boards | full board access |
| Apps | modify all apps &rarr; 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 &rarr; interact with any integration &rarr; full integration access | create integrations | full integration access |
| Search engines | modify all search engines &rarr; full search engine access | create search engines | modify all search engines, full search engine access |
| Medias | view all medias &rarr; 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:
![](./img/invitation-self-onboarding.png)
@@ -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";
+1 -2
View File
@@ -28,8 +28,7 @@ import piHoleApp from "./img/pi-hole-ping-icon.png";
### Interactions
App tiles stay compact and open their configured destination directly. They do not use the shared widget context menu;
configure the app and its status check from the tile's edit actions.
App tiles stay compact and open their configured destination directly. Right-click a tile to inspect its status or change its settings when the board and your preferences allow it.
### Adding the widget
@@ -14,7 +14,7 @@ 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 keeps the current project, runner state, job counts, bandwidth, and broadcast message visible. Advanced view adds the complete active-job list and exact update time.
### Supported integration
+12 -22
View File
@@ -5,9 +5,9 @@ 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";
<WidgetHeader widget={assistantWidget} categories={["Productivity"]} />
@@ -16,18 +16,7 @@ board. Messages, conversations, tool calls, approvals, files, mentions, model co
usage details, and response actions remain synchronized with the app-wide assistant.
The assistant must be enabled by an administrator in **Management → Assistant**. Signed-out users and users
on an instance without an enabled model see an unavailable state instead of a broken composer. Signed-out users see
**Sign in**; a configured provider without a saved model or with **Enable Assistant** off remains unavailable until
the administrator saves a working connection, selects a model, and enables the assistant.
<img
src={assistantWidgetImage}
alt="Assistant widget embedded on a dashboard with its conversation header, automatic approvals control, model selector, and composer"
style={{ maxWidth: "100%" }}
/>
The current preview capture labels the board-embedded surface: its header exposes conversation history, refresh,
automatic approvals, and new-conversation controls, while the footer exposes the model, thinking mode, and composer.
on an instance without an enabled model see an unavailable state instead of a broken composer.
### Adding the widget
@@ -39,8 +28,15 @@ 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.
### Conversation features
@@ -54,16 +50,10 @@ keeps every assistant entry point synchronized without storing a user's private
- Edit user messages and copy, regenerate, branch, quote, export, or rate responses.
- Inspect syntax-highlighted Markdown, sources, generated files, tool results, streaming chain-of-thought,
cumulative conversation usage, per-turn token and context use, timing, speed, provider routing, and cost.
- Change the model and reasoning effort for the active conversation. A model choice is saved per persisted thread,
does not change the administrator's default, and is restored when that thread is selected again; a new thread
starts from the configured default.
- Change the model and reasoning effort for the active conversation.
- Refresh the current board from the conversation header. Approved mutations also refresh visible board data automatically.
The expanded assistant and board widget use the same persisted thread runtime. A response continues in the
background when the widget leaves the viewport or the expanded panel is minimized. Each visible surface keeps an
independent draft, attachments, selection, and focus, so typing in a board widget cannot move or overwrite the
floating panel composer.
Automatic approvals are off by default, reset when switching conversations, and never select a neutral choice. A
structured question shows **Other** only when the tool allows it (`allowOther` is not `false`); selecting **Other**
opens a manual freeform response even when automatic approvals are enabled.
+1 -3
View File
@@ -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
@@ -34,7 +34,7 @@ This widget displays your Beszel alert configurations separated by status (trigg
]}
/>
<WidgetInteractionGuide advancedView={false} />
<WidgetInteractionGuide />
### Adding the widget
@@ -8,7 +8,6 @@ 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 { beszelSystemGridWidget } from ".";
import { beszelIntegration } from "@site/docs/integrations/beszel";
@@ -16,7 +15,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.
@@ -35,8 +34,6 @@ Select a system card to open the complete statistics view, including system, fil
]}
/>
<WidgetInteractionGuide advancedView={false} />
### Adding the widget
<AddingWidget />
@@ -17,7 +17,7 @@ import beszelSystemStats from "./img/beszel-system-stats.png";
<WidgetHeader widget={beszelSystemStatsWidget} categories={["System Monitoring", "Charts"]} />
This widget displays time-series charts for a selected Beszel system, including CPU, memory, disk, network, and Docker container metrics. The Disk Usage chart combines the root disk and every extra filesystem reported by Beszel. Use the widget controls to switch between the realtime Live view and historical periods ranging from 1 hour to 30 days. Live view keeps a one-minute window and pauses while the browser tab is hidden. If the live stream drops, the widget now shows the connection failure state immediately and can switch back to historical data.
This widget displays time-series charts for a selected Beszel system, including CPU, memory, disk, network, and Docker container metrics. The Disk Usage chart combines the root disk and every extra filesystem reported by Beszel. Hover over or focus the widget controls to switch between the realtime Live view and historical periods ranging from 1 hour to 30 days. Live view keeps a one-minute window and pauses while the browser tab is hidden. If the live stream drops, the widget now shows the connection failure state immediately and can switch back to historical data.
### Screenshots
@@ -8,7 +8,6 @@ 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 { beszelSystemTableWidget } from ".";
import { beszelIntegration } from "@site/docs/integrations/beszel";
@@ -36,8 +35,6 @@ Select a system row to open the complete statistics view.
]}
/>
<WidgetInteractionGuide advancedView={false} />
### Adding the widget
<AddingWidget />
+2 -4
View File
@@ -13,11 +13,9 @@ import { bookmarksWidget } from ".";
<WidgetHeader widget={bookmarksWidget} categories={["Links", "Apps"]} />
The bookmarks widget allows you to display multiple links in a single widget.
Select one or more existing Apps directly in the widget settings; these bookmarks keep a reference to the App, so changes to its name, icon, or URL are reflected automatically.
For links that do not need an App, add their URLs under **Custom links** instead.
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.
Advanced view shows every bookmark as a roomy card with its full title, hostname, icon, and complete description regardless of the compact layout and visibility choices.
### Screenshots
+1 -7
View File
@@ -47,15 +47,9 @@ export const bookmarksWidget: WidgetDefinition = {
{
name: "Bookmarks",
description: "List of bookmarks to display in the widget.",
values: "Select one or more Apps directly, then order them as you like.",
values: "Select of all your apps and order them as you like.",
defaultValue: "No bookmarks selected",
},
{
name: "Custom links",
description: "URLs to display without first creating an App.",
values: "List of URLs.",
defaultValue: "No custom links",
},
],
},
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

+10 -6
View File
@@ -31,12 +31,16 @@ the same data is requested through Homarr's MCP or Assistant tools.
### Screenshots
import currentView from "./img/current-view.png";
import advancedView from "./img/advanced-view.png";
import popover from "./img/popover.png";
import withIntegrations from "./img/with-integrations.png";
import withoutIntegrations from "./img/without-integrations.png";
<div className="flex flex-col gap-1">
<img src={currentView} alt="Calendar month view with marked events" width={513} height={303} />
<img src={advancedView} alt="Calendar advanced view with grouped events" width={800} height={484} />
<div className="flex gap-1">
<img src={withIntegrations} alt="calendar with-integrations big-size" width={256 + 4} height={256 + 4} />
<div className="flex flex-col gap-1">
<img src={popover} alt="calendar popover-details the-mandalorian" width={128 * 3} height={128} />
<img src={withoutIntegrations} alt="calendar without-integrations small-size" width={256} height={128} />
</div>
</div>
### Supported Integrations
@@ -70,7 +74,7 @@ import advancedView from "./img/advanced-view.png";
### Interactions
- 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 />
-20
View File
@@ -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";
+22 -35
View File
@@ -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: "-",
},
],
},
+1 -5
View File
@@ -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} />
-31
View File
@@ -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",
},
],
},
};
+1 -2
View File
@@ -32,8 +32,7 @@ Credentials remain on the server.
:::note
Custom API widgets must first be created in **Management → Custom Widgets** before they appear in the widget picker.
Action buttons require the request's minimum board permission, which is `modify` by default. Anonymous viewers cannot run
mutations. DELETE additionally requires full permission and confirmation.
Action buttons can only be executed by administrators.
:::
### Permissions and actions
@@ -20,9 +20,7 @@ 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&apos;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. Every selected source stays available in the normal scrollable view, while cards and controls adapt to the widget&apos;s width and height.
### 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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 39 KiB

@@ -7,15 +7,19 @@ 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 { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
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 +28,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.
@@ -35,8 +49,6 @@ Each container row has an action menu, accessible via the three-dot icon or by r
- **Remove** - permanently remove the container after a second confirmation click
- **Add to Homarr** - create a bookmark app from the container
<WidgetInteractionGuide advancedView={false} />
### Adding the widget
<AddingWidget />
@@ -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",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

+15 -59
View File
@@ -21,79 +21,36 @@ import { downloadsWidget } from ".";
<WidgetHeader widget={downloadsWidget} categories={["Torrent client", "Usenet client", "Downloads"]} />
This widget displays torrent, Usenet, and miscellaneous download jobs, along with their progress and other relevant information. It supports 7 download client integrations and provides a rich, responsive table interface.
This widget displays a list of torrents or usenet-articles that are currently being downloaded, along with their progress and other relevant information. It supports 7 download client integrations and provides a rich, responsive table interface.
### Screenshots
import downloadsCard from "./img/screenshot.png";
import contextMenuImg from "./img/context-menu.png";
import advancedViewImg from "./img/advanced-view.png";
import filterPanelImg from "./img/filter-panel.png";
<div>
<img src={downloadsCard} alt="Downloads widget showing torrent and usenet items" width={128 * 4} />
</div>
#### Download-row context menu
#### Right-click context menu
<div>
<img src={contextMenuImg} alt="Downloads row context menu with pause, delete, and copy actions" width={128 * 4} />
</div>
#### Advanced view
<div>
<img
src={advancedViewImg}
alt="Downloads widget in advanced view with stats, sortable columns, and expanded details"
width={128 * 6}
/>
</div>
#### Advanced filter panel
<div>
<img src={filterPanelImg} alt="Downloads advanced view with the status filter panel open" width={128 * 6} />
<img src={contextMenuImg} alt="Downloads widget context menu with pause, delete, and copy actions" width={128 * 4} />
</div>
### Features
- **Responsive compact table** — in compact mode, selected columns adapt to both widget width and height; narrow or short tiles hide speed/state columns, and the shortest tiles also hide the ETA column
- **Sortable columns** — click any sortable column header to sort the current view; the configured default controls the initial column and direction
- **Responsive table** — columns automatically adapt to widget width, hiding less important columns at smaller sizes
- **Sortable columns** — click any column header to sort ascending/descending
- **Column customization** — choose which columns to display, then hover a header to reveal its grab handle for reordering or drag its edge to resize it; layouts save automatically per widget
- **Download-row context menu** — copy a download name or ID from any row; pause, resume, and delete actions appear when you have Interact access to that integration
- **Right-click context menu** — pause, resume, delete downloads, or copy download names directly from the context menu
- **Row expansion** — click any row to see full details inline without opening a separate modal
- **Hover tooltips** — hover a download name to see size, speeds, ETA, ratio, and category; open tooltips close when switching between compact and advanced mode
- **Global stats bar** — show queue progress, downloaded/total size, transfer speeds, global torrent ratio, and per-state counts in compact mode; advanced view always includes it
- **Global stats bar** — toggle a summary bar showing queue progress, total size, speeds, ratio, and per-state counts
- **Client filtering** — filter downloads by integration or status using the footer filter panel
- **Global controls** — pause/resume actionable queues from the footer bar, limited to selected integrations where you have Interact access
- **Global controls** — pause/resume all downloads across all clients from the footer bar
- **Status indicators** — color-coded badges show download state at a glance
- **Speed overview** — total download and upload speeds for the selected clients are displayed in the footer
:::note
Compact layouts use both dimensions: below 500px wide, speed columns are hidden; below 300px wide, the ETA and state
columns are hidden too. A tile shorter than 180px hides speed and state columns, and a tile shorter than 120px also
hides ETA. The footer is hidden below 96px in compact mode. Advanced view bypasses these compact size reductions,
keeps the selected column set, and always includes the stats bar and footer controls, with the filter panel available
from the footer.
The filter panel changes the visible rows. Its client/status filters also change the stats bar's progress, size, and
state counts, while transfer-speed totals summarize the selected clients and the global torrent ratio summarizes the
selected torrent clients. The configured category/label filter can optionally be applied to the global torrent ratio
through **Use category filter for global torrent ratio**; footer client/status filters do not change that ratio.
The per-item **Ratio** is sent bytes divided by the item's total size. **Global Ratio** is total uploaded bytes divided
by total downloaded bytes.
Mutation controls depend on the integration's Interact permission. Users without that permission can still view rows and
use the row menu's copy actions. `slskd` is list-only: its pause, resume, and delete operations are no-ops, so those
controls have no effect for that client even when Interact permission exposes them.
The screenshot above is a Downloads row menu, separate from the shared widget context menu. When board right-click menus
are enabled, the shared menu provides widget-level actions such as advanced view, widget options, refresh, and settings.
:::
- **Speed overview** — total download and upload speeds displayed in the footer
### Supported Integrations
@@ -125,21 +82,20 @@ are enabled, the shared menu provides widget-level actions such as advanced view
},
{
integration: slskdIntegration,
note: "Soulseek client (list-only; mutation controls are no-ops)",
note: "Soulseek client (read-only)",
},
]}
/>
### Interactions
- **Right-click** any download to copy its name or ID; pause, resume, and delete are available only with Interact access
- **Right-click** any download to open a context menu with pause/resume, delete, and copy actions
- **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
- **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
- **Stats toggle** — show or hide the global stats bar from the footer
- **Pause / Resume** all downloads using the footer controls
- **Filter** by integration or status using the filter button in the footer
- **Sort** by clicking column headers (with persistent sort direction)
<WidgetInteractionGuide />
+8 -9
View File
@@ -30,20 +30,19 @@ export const downloadsWidget: WidgetDefinition = {
{
name: "Columns to show",
description:
"Select the columns you want to display in the widget. Compact layouts automatically hide/show selected columns based on widget width and height; advanced view keeps the selected columns visible.",
"Select the columns you want to display in the widget. Columns automatically hide/show based on widget width.",
values: `List of columns: ${columnsList.join(", ")}`,
defaultValue: ["name", "progress", "downSpeed", "time", "state"].join(", "),
},
{
name: "Default sort column",
description:
"The column used for initial sorting when the widget loads. Clicking a column header changes the current view only.",
description: "The column used for initial sorting when the widget loads.",
defaultValue: "progress",
values: `List of columns: ${sortColumns.join(", ")}`,
},
{
name: "Invert sorting",
description: "This sets the initial sorting direction.",
description: "This will invert the default sorting order.",
values: { type: "boolean" },
defaultValue: "no",
},
@@ -67,7 +66,8 @@ export const downloadsWidget: WidgetDefinition = {
},
{
name: "Hide completed torrent under this threshold (in kiB/s)",
description: "This will hide completed torrent entries whose upload speed is below the specified threshold.",
description:
"This will hide completed torrent entries that have a download speed below the specified threshold.",
values: "Any number above 0, 0 to disable",
defaultValue: "0",
},
@@ -85,16 +85,15 @@ export const downloadsWidget: WidgetDefinition = {
defaultValue: "no",
},
{
name: "Use category filter for global torrent ratio",
description:
"When enabled, the configured category/label filter is applied to the global torrent ratio. Footer client/status filters do not change this ratio.",
name: "Use filter to calculate Ratio",
description: "This will use the category filter when calculating the global torrent ratio.",
values: { type: "boolean" },
defaultValue: "yes",
},
{
name: "Limit items per integration",
description: "This will limit the number of items shown per integration, not globally.",
values: "Any whole number from 1 upward",
values: "Any number above 1",
defaultValue: "50",
},
],
@@ -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.
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.
### Adding the widget
Loaded 100 of 963 files, more files were not shown because too many files have changed in this diff. Show more