Compare commits

...
Author SHA1 Message Date
Thomas Camlong d2b21d0881 fix: position calendar error indicator 2026-08-22 16:56:12 +02:00
Thomas 19760e5722 fix: restore translucent dashboard styling (#6664)
* fix: restore translucent dashboard styling

* fix: restore dashboard widget transparency

* fix: address dashboard review feedback

* fix: apply board opacity to containers

* fix: flatten demo board container

* fix: default board roundness to sm

* fix: restore assistant widget transparency

* Revert "fix: default board roundness to sm"

This reverts commit c235cbdf2b.

* fix: address remaining dashboard review comments

* test: align grid geometry expectations
2026-08-22 15:51:43 +02:00
Thomas 2570de25a6 fix: hide default weather details (#6665) 2026-08-22 15:48:02 +02:00
Thomas f46ccadbe4 feat: redesign demo dashboard (#6663) 2026-08-22 13:08:27 +02:00
Thomas 6297ca4b16 fix: keep media stream status at table edge (#6661) 2026-08-22 11:48:33 +02:00
Thomas 2accdc2fad feat: rework board switcher (#6660)
* feat: rework board switcher

* fix: address board switcher review
2026-08-22 11:15:40 +02:00
Thomas 8a08293016 fix: restore bookmark grid fill (#6659)
* fix: restore bookmark grid fill

* fix: use scroll area viewport class
2026-08-22 11:14:41 +02:00
Bergasha 7591f7e313 feat(v2 Containers): scrollable containers (#6629) 2026-08-22 09:24:52 +02:00
Thomas 011ce8e5a3 fix: preserve board drafts and widget scroll state (#6637) 2026-08-22 09:13:32 +02:00
Thomas b2fa46c760 fix: restore release translation checks (#6658) 2026-08-22 08:49:14 +02:00
Thomas 9e14a07e64 refactor: consolidate English translations (#6640) 2026-08-21 19:00:27 +02:00
Thomas 81aa704537 feat(cli): rework developer CLI with non-blocking tasks and GitHub CI checks (#6651) 2026-08-21 18:59:11 +02:00
Thomas f0f9539466 feat(ui): add situational tips system (#6648) 2026-08-21 18:58:52 +02:00
Thomas 3c2f95d529 perf(board): stabilize edit mode layout (#6650) 2026-08-21 16:17:42 +02:00
Thomas 64e3d04517 fix(calendar): improve event indicators (#6652) 2026-08-21 15:55:38 +02:00
Bergashaandajnart 002381d294 feat(v2 docker widget): Fixes column-resize squashing (#6646)
* fix(dataTable): resizing a column no longer squashes the others

mantine-datatable's built-in column resize keeps the table's total
width fixed by shrinking every other column to compensate, which
collapses columns toward zero in a narrow widget (open upstream
issue, no fix: icflorescu/mantine-datatable#discussions-757).

Replaced it with our own resize handle that only ever changes the
width of the column being dragged - the table grows and scrolls
horizontally instead. Shared via usePersistedTableLayout, so docker,
downloads, and beszel-system-table all get it.

* feat(docker): enable hold-Shift advanced focus preview

The widget already renders differently for displayMode="advanced",
but was missing supportsAdvancedFocus: true, so the board never
offered the hold-Shift preview for it - matching downloads and
media-server, which both set this flag.

* style(docker): tighten row density, decouple icon size from row height

Circular avatar, denser row/footer padding, and action icons rendered
larger than their button via absolute positioning so they don't
inflate row height (the button stays small and fixed - it repeats
once per row, so any growth there multiplies across every visible
container).

* fix(data-table): harden shared column resizing

* fix(data-table): account for board scaling

* fix(data-table): compact shared headers

* fix(data-table): fit columns to widget

* fix(downloads): simplify advanced details

* test(widgets): align Docker advanced focus

* fix(downloads): widen expanded details

---------

Co-authored-by: ajnart <thomas@ajnart.dev>
2026-08-21 15:12:31 +02:00
Thomas 9b406d5d83 fix(beszel): restore balanced system grid layout (#6647) 2026-08-21 13:11:40 +02:00
Thomas Camlong 4c475b8c0a ci: publish v2 images reliably 2026-08-21 12:49:03 +02:00
Thomas f3014fe271 refactor(board): modernize item and application select modals with SelectableCard (#6642)
Modernize board item, application, and integration selection flows with shared Mantine components, batch board actions, improved accessibility, and hardened persistence.
2026-08-21 03:19:18 +02:00
Afonso Jorge Ramos bd4f6a92c6 fix(onboarding): keep the account submit disabled until the step advances (#6641) 2026-08-20 18:48:33 +02:00
Thomas Camlong 7e8c139590 test(e2e): stabilize lazy widget login 2026-08-20 18:33:43 +02:00
Thomas d241bac55d feat: improve assistant conversation experience (#6636) 2026-08-20 16:26:23 +02:00
Thomas 58a24569ce feat: expand utility widgets (#6638) 2026-08-20 16:26:04 +02:00
Thomas 53b518960c feat: retain application log history (#6633)
* feat: retain application log history

* fix: harden retained log streaming

* fix: harden contextual log focus

* fix: retain multiline log output
2026-08-20 12:57:30 +02:00
Thomas ddf8ba9948 feat: rework management settings layout (#6634) 2026-08-20 12:33:18 +02:00
Bergasha a2219b72f6 Merge pull request #6632 from homarr-labs/feat/v2-stocks-widget
feat (v2 stocks widget):Add a "Show details" toggle to the stock widget's compact view
2026-08-20 12:59:52 +10:00
Bergasha 7150cdbbab Add a "Show details" toggle to the stock widget's compact view
The compact view auto-reveals name/change/range as the widget gets
bigger, which can overlap badly depending on how the widget's actual
proportions land relative to those breakpoints. Add a switch (on by
default, matching current behavior) to force the minimal symbol+price
look regardless of size. Scoped to the compact layout only - the
Shift-triggered advanced view is untouched.
2026-08-20 12:32:42 +10:00
Thomas ff14613f95 feat: recalculate permission scope & permission matrix UI (#6612) 2026-08-19 17:22:31 +02:00
ajnart 78b3dc1922 fix: restore workshop create icon 2026-08-19 16:34:21 +02:00
ajnart 2e97b337d2 fix: remove universal create search 2026-08-19 16:29:30 +02:00
Thomas 369a2660b1 Embed integration editing in widget tab (#6630) 2026-08-19 14:01:11 +02:00
Bergasha 60aec370d6 Merge pull request #6627 from homarr-labs/fix/v2-media-server-widget
fix(v2 media widget): updated media server widget
2026-08-19 16:56:30 +10:00
Bergasha 6a76b9eafe Merge branch 'release/v2' into fix/v2-media-server-widget 2026-08-19 16:55:15 +10:00
Bergasha 76ba13fbad Address Codex review feedback on PR #6627
- Classify IPv4/IPv6 link-local addresses (169.254.0.0/16, fe80::/10) as
  LAN in Jellyfin location parsing - they can't traverse a router, so a
  false WAN reading was possible for common autoconfig/link-local sessions.
- Fix portrait video resolution mislabeling - a 1080x1920 clip was
  classified off its long-axis height (1920) and showed as 1440p. Classify
  by the short axis instead so orientation doesn't flip the result.
- Raise the compact-mode status column breakpoint from 420 to 540px - at
  420 the fixed-width user (200px) and status (190px) columns left almost
  no room for the currentlyPlaying column, the widget's primary content.
- Document the new resolution indicator in the media-server widget docs.
2026-08-19 16:45:09 +10:00
Thomas 150d38be62 feat(docker): add endpoint filtering and streamline setup assistant (#6625)
* feat(docker): add endpoint filtering and setup assistant

* fix(docker): simplify environment setup

* test(docker): cover demo endpoint filtering

* refactor(docker): use accordion for setup suggestions

* fix(docker): use docker icon for setup assistant

* refactor(docker): simplify assisted service setup

* refactor(docker): reuse shared translations

* fix(docker): address review feedback
2026-08-19 08:45:07 +02:00
Bergasha a4bbed0cb8 Fixed resolution showing the raw cropped height (e.g. 804p) instead of the nominal class Plex/Jellyfin display (1080p) - letterboxed/cinemascope sources are shorter than their resolution tier, so classify off the width-derived 16:9 height instead 2026-08-19 14:54:48 +10:00
Bergasha 90fa9e8741 Added tests for the LAN/WAN parsing and fixed IPv4-mapped IPv6 addresses being misclassified as WAN - a dual-stack server can report a LAN client as ::ffff:192.168.x.x, which the private-range check was missing since it only matched literal IPv6 ranges 2026-08-19 13:52:14 +10:00
Bergasha 4b20d6ecd0 Widened the status column from 140 to 190px - the Np resolution format is shorter so line 2 had room, but Transcoding video/audio badges are noticeably longer than Direct play and needed the space on line 1 2026-08-19 13:33:21 +10:00
Bergasha 6cac34e968 Changed resolution to show the actual streaming resolution (transcode target) instead of the source file's, formatted as e.g. 1080p instead of 1920x1080 2026-08-19 13:33:14 +10:00
Bergasha c6f77cec2e Added resolution to the status column, restructured into two lines: status + LAN/WAN on top, resolution + bitrate underneath 2026-08-19 13:33:08 +10:00
Bergasha acc97da84b Added LAN/WAN detection to Jellyfin, matching what Plex already reports - Jellyfin's sessions endpoint only gives a client IP (RemoteEndPoint), so classify it against the standard private IP ranges ourselves 2026-08-19 13:32:40 +10:00
Bergasha 6b10ac2ab5 Gave User a fixed 200px width so Currently playing (the busiest column) gets the largest share of the row instead of splitting it evenly with User 2026-08-19 13:32:34 +10:00
Bergasha c7563f2c39 Fixed Jellyfin bitrate showing null on direct play - the sessions endpoint only reliably fills in MediaSources bitrate while transcoding, so sum the individual media streams' own bitrates as a fallback 2026-08-19 13:32:28 +10:00
Bergasha 96676182f4 Gave the status column a fixed 140px width - it had no explicit width so it shared an equal, unconstrained share of the row with the other columns despite only holding a small badge 2026-08-19 13:32:21 +10:00
Bergasha 3769d88ebb Dimmed the column header labels and added a bottom border under the sticky header, so it reads as its own band instead of the same weight as the row data 2026-08-19 13:32:15 +10:00
Bergasha d9c35744f6 Only show a server's badge in the footer when it actually has an active session - selecting both Plex and Jellyfin always showed both badges even if only one was streaming. Also gave the footer badges a pill style and added a separator between the stream count and bitrate 2026-08-19 13:31:58 +10:00
Bergasha 33991f4e22 Added flexShrink to the session avatar so it stops getting squished in a tight row 2026-08-19 13:31:33 +10:00
Bergasha df300919fc Gated season/episode fields on episode type - was showing S/E labels on movies and music too, using the episode number as season 2026-08-19 13:31:27 +10:00
Thomas Camlong 315245e048 fix(media-server): close Stack element in session table cell 2026-08-18 10:28:44 +02:00
BergashaandThomas ebdfeb724b fix(media widget): text size fix and added season + Episode info (#6605)
Co-authored-by: Thomas <thomas@ajnart.fr>
2026-08-18 09:44:12 +02:00
Thomas e842177d9b feat: port release-v2 experience improvements from #6582 (audited subset) (#6609) 2026-08-18 09:40:02 +02:00
Thomas Camlong e3a713d2df Revert "fix: use stable selfhst icons for flaky integrations"
This reverts commit e7879ed3a2.
2026-08-18 09:24:00 +02:00
ajnart e7879ed3a2 fix: use stable selfhst icons for flaky integrations 2026-08-17 18:12:02 +02:00
ajnart 19e72fc757 fix: use stable readarr icon URL 2026-08-17 17:41:00 +02:00
Thomas Camlong 38fc5dd582 feat(custom-widgets): configure missing credentials 2026-08-17 15:47:42 +02:00
Thomas Camlong 6d284adb98 fix(media-server): stack user details reliably 2026-08-17 15:40:49 +02:00
Thomas Camlong bd171bf73b fix(custom-widgets): reduce action menu file below architecture line limit 2026-08-17 08:39:29 +02:00
Bergasha 1ee519bb7a fix(beszel): stop small text from vanishing under board zoom (#6616) 2026-08-17 07:14:29 +02:00
Thomas Camlong c53852fdb6 refactor(ui): adopt Mantine Marquee and drop mount guards
Rebuild the integration marquee on Mantine's Marquee, SimpleGrid and
ThemeIcon instead of the hand-rolled grid, track and keyframes, and
reduce the stylesheet to the mask and reduced-motion rule.

Drop the useMounted guards from the login form and let the buttons rely
on the pending state alone, and use Group for the restore flow's file
summary.
2026-08-17 01:15:45 +02:00
Thomas Camlong 206905ea2d feat(workshop): rebuild install and publish as pages
Replace the stacked install modals (browse -> details -> import review)
with page-based flows so the primary action is always reachable:

- /manage/custom-widgets/workshop browses submissions as a card grid
  with large previews, sitting in a tab next to the installed widgets
- /manage/custom-widgets/workshop/[id] merges the listing, security
  review and source setup onto one page with a sticky install footer
- /manage/custom-widgets/publish/[id] replaces the publish modal
- /manage/workshop redirects; its navbar entry is gone
- the Custom CSS picker collapses from three modals to one

Extract useCustomWidgetImport and ImportReviewContent so the Workshop
page and the file/clipboard dialog share one import pipeline.

Votes now show the viewer's own choice filled Reddit-style, backed by a
new user-votes query, and screenshots get a zoomable lightbox.

Rework the installed list onto ManageCollectionItem: show the widget
description, replace the cryptic "JSX - 2" badge with plain request and
origin facts, and swap the toggle and bare pencil for a status badge,
a labelled Edit button and a menu entry for enable/disable.

Fixes found while reviewing:

- importing could run twice and duplicate a widget; guard on pending and
  succeeded, and return to the installed list instead of the heavy edit
  form
- a background refetch of the submission (window focus, or the
  invalidation a vote triggers) reset the source setup and silently wiped
  part-typed credentials; key the reset on the sources, not the object
- breadcrumbs raised MISSING_MESSAGE for unmapped dynamic segments; fall
  back to the raw segment via t.has
- publishing had no in-flight guard and could post duplicate listings
- tooltips on disabled vote and report controls never fired, making the
  sign-in hint unreachable
2026-08-17 01:14:38 +02:00
Thomas Camlong 50537d077c feat(custom-widgets): export legacy definitions 2026-08-16 23:33:07 +02:00
Thomas Camlong 229a559aff fix: replace Workshop detail with import review 2026-08-16 23:16:29 +02:00
Thomas Camlong 4e514ab151 fix: show Workshop voting as net score 2026-08-16 23:15:09 +02:00
Thomas Camlong 960551f573 feat: simplify Workshop widget installation 2026-08-16 23:11:52 +02:00
Thomas Camlong 304ea089a2 fix: resolve backup migrations in development 2026-08-16 22:32:05 +02:00
Thomas Camlong 199f6ffc3b fix: run custom migrations on backup restore 2026-08-16 22:25:15 +02:00
Thomas Camlong 7e569bff4c fix: skip restore confirmation during onboarding 2026-08-16 22:10:12 +02:00
Thomas Camlong 0ec5f36050 ci: publish release v2 test image 2026-08-16 21:25:08 +02:00
Thomas e22be4dca6 Merge pull request #6607 from homarr-labs/fix/dnd-kit-widget-options-overlap
fix(board): render widget options above grid resize handles
2026-08-16 15:12:20 +02:00
ajnart 3237d5953f fix(board): render widget options above grid resize handles
The widget options ActionIcon rendered inside the widget Card, whose
containerType: size creates a stacking context, trapping its zIndex and
letting the dnd-kit resize handles paint over and block it.

Hoist BoardItemMenu out of the Card to the item root (same layer as the
title badge) and raise it to zIndex 30, above the handles (20). Error
recovery after edits is preserved via resetKeys=[item], which replaces
the resetErrorBoundary prop the in-card menu used to receive.
2026-08-16 14:15:14 +02:00
Thomas 1325418300 fix: complete location input test options (#6602)
* fix: complete location input test options

* fix: run CI for slash-named branches
2026-08-15 13:50:46 +02:00
Thomas 4505ce96c9 fix: refine dashboard interactions and widget options (#6597)
* fix: refine dashboard item interactions

* fix board interactions and widget options

* feat: improve bookmark selection

* fix: address dashboard review feedback
2026-08-15 10:44:37 +02:00
Thomas Camlong b491d45fdc feat: improve workshop publishing flow 2026-08-14 23:16:39 +02:00
Thomas Camlong 168091134d fix: bound preview query context 2026-08-14 22:46:15 +02:00
Thomas Camlong 28e58a5a5c fix: expose assistant provider errors 2026-08-14 22:37:28 +02:00
Thomas Camlong 3c533f2d96 fix: isolate downloads stats state 2026-08-14 22:07:12 +02:00
Thomas Camlong 2e3e654748 fix: improve auth form semantics 2026-08-14 21:22:14 +02:00
Thomas Camlong 409e8ed71a feat: unify authentication pages 2026-08-14 21:18:06 +02:00
Thomas Camlong 95489b692c fix: derive workshop web url 2026-08-14 21:10:34 +02:00
Thomas Camlong b8c06a8622 feat(dev): support direct environment overrides 2026-08-14 21:09:41 +02:00
Thomas Camlong f62add8ae4 Remove obsolete DeepSource config and Workshop critiques 2026-08-14 20:47:21 +02:00
Thomas Camlong 498d781e19 fix: isolate node instrumentation 2026-08-14 20:16:34 +02:00
Thomas Camlong 0afed2c3f6 fix: contain media server streams 2026-08-14 20:09:57 +02:00
ajnart efc0ee470b test: remove obsolete advanced interactions e2e 2026-08-14 15:14:42 +02:00
ajnart fe41382074 test: remove obsolete advanced focus assertion 2026-08-14 14:47:02 +02:00
Thomas Camlong c851609041 fix: stabilize media streams and edit hover 2026-08-14 14:33:02 +02:00
ajnart 03338b1755 revert: remove advanced mode from compact widgets 2026-08-14 13:30:10 +02:00
ajnart 8a44451c72 docs: simplify advanced view guidance 2026-08-14 12:11:33 +02:00
ajnart d434953478 test: skip removed universal create flow 2026-08-14 12:11:33 +02:00
ajnart 7aaa646b82 test: restore release advanced focus flows 2026-08-14 12:11:33 +02:00
ajnart c4f6d98300 fix: stabilize release e2e flows 2026-08-14 12:11:33 +02:00
ajnart 81fdc5856b test: preserve release advanced focus 2026-08-14 12:11:33 +02:00
ajnart 4e193da3a4 fix: preserve release advanced widget support 2026-08-14 12:11:33 +02:00
Thomas Camlong 29f7c35fc1 test: align widget modernization fixtures 2026-08-14 12:11:33 +02:00
Thomas Camlong 8775597d5a feat: expand advanced widget experiences 2026-08-14 12:11:33 +02:00
ajnart 339a0d8e2b docs: simplify assistant guide 2026-08-14 12:11:33 +02:00
ajnart 4ddc7266e9 fix(workshop): publish release v2 image 2026-08-14 12:11:33 +02:00
Thomas 42bf55641b docs: align v2 stack documentation (#6564) 2026-08-14 12:11:33 +02:00
ajnart c92e074a6f chore: update Next.js to 16.3.1 2026-08-14 12:11:21 +02:00
ajnart 741e641bd2 fix: address onboarding review findings 2026-08-14 11:32:13 +02:00
ajnart 9b7f189b02 test: skip obsolete backup restore flow 2026-08-14 11:32:13 +02:00
ajnart 0070538857 style: format board preview query 2026-08-14 11:32:13 +02:00
Thomas Camlong a11c53ebf7 fix(boards): preview container contents 2026-08-14 11:32:13 +02:00
ajnart e55def9b60 style: format restored board creator 2026-08-14 11:32:13 +02:00
Thomas Camlong fc66211dd2 fix(onboarding): preserve restored board login target 2026-08-14 11:32:13 +02:00
Thomas Camlong 174e0a7606 fix(onboarding): streamline backup restore 2026-08-14 11:32:13 +02:00
ajnart 30cb066b7f style: format onboarding setup studio 2026-08-14 11:32:13 +02:00
Thomas Camlong f1ce0fffad fix(onboarding): accept self-hosted addresses 2026-08-14 11:32:13 +02:00
Thomas Camlong 9c5ab62b3a fix(onboarding): simplify detected app cards 2026-08-14 11:32:13 +02:00
ajnart 6adb49eedf test: skip unavailable advanced focus flows 2026-08-14 11:32:13 +02:00
ajnart 6482ea6ae8 test: align onboarding focus contract 2026-08-14 11:32:13 +02:00
ajnart f7d5fde8a9 style(docs): format Docker labels guide 2026-08-14 11:32:13 +02:00
ajnart de368a6969 style(onboarding): format app URL 2026-08-14 11:32:13 +02:00
ajnart e1280f311c fix(onboarding): restore e2e flow 2026-08-14 11:32:13 +02:00
Thomas Camlong 517dcfa64e fix(onboarding): guard detected app type 2026-08-14 11:32:13 +02:00
Thomas Camlong 6faba5f772 fix(onboarding): align detected app fields 2026-08-14 11:32:12 +02:00
Thomas Camlong cca6de2b86 fix(onboarding): align detected app setup 2026-08-14 11:32:12 +02:00
Thomas Camlong c24ccde404 fix(onboarding): highlight detected apps 2026-08-14 11:32:12 +02:00
Thomas Camlong d28dd380e5 docs(onboarding): document Docker label discovery 2026-08-14 11:32:12 +02:00
Thomas Camlong 1de167c98d fix(onboarding): show integration picker 2026-08-14 11:32:12 +02:00
Thomas Camlong 7b1a801ab8 feat: consolidate onboarding 2026-08-14 11:32:12 +02:00
Thomas Camlong 8239a631f2 feat: consolidate onboarding 2026-08-14 11:32:12 +02:00
Thomas Camlong 775ae0147c feat: enhance onboarding 2026-08-14 11:32:12 +02:00
Thomas Camlong ece6757979 test(onboarding): cover integration checks 2026-08-14 11:32:12 +02:00
Thomas Camlong dd707bed7e feat(onboarding): test integration connections 2026-08-14 11:32:12 +02:00
Thomas Camlong 276431ef4c fix(onboarding): skip unfinished integrations 2026-08-14 11:32:12 +02:00
Thomas Camlong c25c1dbac7 feat(onboarding): polish setup experience 2026-08-14 11:32:12 +02:00
Thomas Camlong 66ad01e7ce fix(onboarding): use preset board colors 2026-08-14 11:32:12 +02:00
Thomas Camlong d4ffd19f01 fix(onboarding): collapse manual integrations 2026-08-14 11:32:12 +02:00
Thomas Camlong 4f693b0465 fix(onboarding): harden release acceptance 2026-08-14 11:32:12 +02:00
Thomas Camlong 6437e57565 style: format stacked changes 2026-08-14 11:32:12 +02:00
Thomas Camlong 2566be2838 fix: address onboarding review feedback 2026-08-14 11:32:12 +02:00
Thomas Camlong dc71084323 fix: harden onboarding completion 2026-08-14 11:32:12 +02:00
Thomas Camlong e41605640b feat: rebuild onboarding experience 2026-08-14 11:32:11 +02:00
ajnart d091362db2 style: format Coolify widgets 2026-08-14 11:32:11 +02:00
ajnart 653dadaeff chore: reconcile performance layer after stack rebase 2026-08-14 11:32:11 +02:00
ajnart af6fb75e6b perf: trim widget loading work 2026-08-14 11:32:11 +02:00
ajnart b2fbd10965 perf: reduce widget render overhead 2026-08-14 11:32:11 +02:00
ajnart 63dca7d1b6 perf: simplify widget optimizations 2026-08-14 11:32:11 +02:00
ajnart 22d5770977 perf: remove global V8 memory cap 2026-08-14 11:32:11 +02:00
ajnart dcef36ffa7 perf: optimize widget integration requests 2026-08-14 11:32:11 +02:00
ajnart f65969053a fix: refresh docker modem patch hash 2026-08-14 11:32:11 +02:00
ajnart 8eb6735387 perf: reduce widget and runtime overhead 2026-08-14 11:32:11 +02:00
ajnart 7841359917 chore: scope performance harness ignores 2026-08-14 11:32:11 +02:00
ajnart a45b077b7c chore: ignore local performance harness 2026-08-14 11:32:11 +02:00
ajnart 111a72c283 fix: retain assistant translation options 2026-08-14 11:32:11 +02:00
ajnart b988e431e8 fix: address assistant review findings 2026-08-14 11:32:11 +02:00
ajnart 87f264a286 chore: reconcile assistant layer after stack rebase 2026-08-14 11:32:11 +02:00
Thomas Camlong 83f80eb9a7 chore: refresh stack checks 2026-08-14 11:32:11 +02:00
ajnart 3c6b2a5d49 docs: simplify assistant guides 2026-08-14 11:32:11 +02:00
Thomas Camlong 1b658b631c chore: refresh stack merge state 2026-08-14 11:32:11 +02:00
ajnart 384dc46f26 feat: improve assistant panel context 2026-08-14 11:32:11 +02:00
ajnart 48abdb52af fix: default Homarr provider URL in CI 2026-08-14 11:32:11 +02:00
Thomas 45d8bc9443 feat: add the community-funded Homarr assistant provider (#6563)
* Add Homarr provider proxy foundation

* Complete Homarr provider integration

* Harden Homarr provider integration

* Address Homarr provider review feedback

* Address CodeRabbit provider feedback

* Localize provider errors

* Handle provider activity load failures

* docs: improve workshop and homepage

* Harden Homarr provider limits

* fix: harden the Homarr provider

* fix: address provider review feedback

* fix: clarify provider request accounting

* feat: complete the Homarr provider endpoint

* fix: terminate invalid provider streams
2026-08-14 11:32:11 +02:00
Thomas Camlong b8f360c04e test: align assistant credential flow 2026-08-14 11:32:11 +02:00
ajnart fc5ba7d888 fix assistant focus and settings 2026-08-14 11:32:11 +02:00
Thomas Camlong b7fdff9f7f fix: simplify assistant key management 2026-08-14 11:32:11 +02:00
ajnart dfafad15bf test custom widget assistant authoring 2026-08-14 11:32:11 +02:00
ajnart ceed0979a1 fix assistant custom widget creation 2026-08-14 11:32:11 +02:00
ajnart 474d9968b5 fix assistant tool result streaming 2026-08-14 11:32:11 +02:00
ajnart 295eb836bb test assistant v7 request payload 2026-08-14 11:32:11 +02:00
ajnart 7b11d6673b test assistant AI SDK v7 transport 2026-08-14 11:32:11 +02:00
ajnart ce48cabb36 chore upgrade assistant to AI SDK v7 2026-08-14 11:32:11 +02:00
ajnart 443fbe0c36 fix assistant exports and tool calls 2026-08-14 11:32:11 +02:00
ajnart 9c64f8772d refine assistant window controls 2026-08-14 11:32:11 +02:00
ajnart c0596d1b5e fix assistant dashboard placement 2026-08-14 11:32:11 +02:00
ajnart 517d9320dc feat: add assistant window controls 2026-08-14 11:32:11 +02:00
ajnart bb6e8e5c5a fix assistant tool continuation 2026-08-14 11:32:11 +02:00
ajnart f4f6773f26 feat assistant context and agent trace 2026-08-14 11:32:11 +02:00
ajnart aea2832370 fix assistant reasoning mount lifecycle 2026-08-14 11:32:11 +02:00
ajnart 59cb3a4d99 feat: improve assistant thinking states 2026-08-14 11:32:11 +02:00
ajnart ebd63bea74 fix: refine assistant controls 2026-08-14 11:32:11 +02:00
ajnart ef9c37d5ec fix: adjust assistant response limits 2026-08-14 11:32:11 +02:00
ajnart a1c2559541 fix: stabilize custom widget authoring 2026-08-14 11:32:10 +02:00
ajnart d80e883479 fix: harden assistant authoring and UI 2026-08-14 11:32:10 +02:00
Thomas Camlong 3cef3cbdbd fix: validate MCP v2 routes 2026-08-14 11:32:10 +02:00
Thomas Camlong ae68d599be test: keep assistant widget compact 2026-08-14 11:32:10 +02:00
ajnart e4bf2aa13d feat: migrate MCP server to v2 2026-08-14 11:32:10 +02:00
Thomas Camlong 4ed1eb5d59 Rename Assistant migrations 2026-08-14 11:32:10 +02:00
Thomas Camlong 66043eb78d fix(assistant): preserve lazy board boundaries 2026-08-14 11:32:10 +02:00
Thomas Camlong 8a1b2a5cd6 docs: refresh assistant OpenAPI schema 2026-08-14 11:32:10 +02:00
ajnart 0298256b62 fix(assistant): prevent panel focus capture 2026-08-14 11:32:10 +02:00
Thomas a9b0c2ae47 fix(assistant): mysql message storage, migration metadata and per-page bundle cost (#6530) 2026-08-14 11:32:10 +02:00
Thomas Camlong f6f755d5c9 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-14 11:32:10 +02:00
Thomas Camlong b3e75df7fa 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-14 11:32:10 +02:00
Thomas Camlong bcd3d901d2 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-14 11:32:10 +02:00
Thomas Camlong 30ca26cb1a chore: shrink demo assistant widget to 3x3 2026-08-14 11:32:10 +02:00
Thomas bb5a9562d5 feat: add assistant widget to demo dashboard (#6529) 2026-08-14 11:32:10 +02:00
Thomas 5501eb555e fix: handle missing message in assistant generation telemetry (#6528) 2026-08-14 11:32:10 +02:00
ajnart 28a65d61f9 style: auto-fix lint and format 2026-08-14 11:32:10 +02:00
Thomas Camlong f98766873f 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-14 11:32:10 +02:00
Thomas 7bf22639ba feat: mock Homarr Assistant in demo mode and fix board page crash (#6524) 2026-08-14 11:32:10 +02:00
ajnart f991dc6334 feat: polish assistant conversations 2026-08-14 11:32:10 +02:00
ajnart 1ad1439828 feat: improve assistant interactions 2026-08-14 11:32:10 +02:00
ajnart 9918386257 stabilize hydrated login actions 2026-08-14 11:32:10 +02:00
ajnart 97a399d0fa stabilize integration router tests 2026-08-14 11:32:10 +02:00
ajnart f357eb4da6 clarify future Homarr provider 2026-08-14 11:32:10 +02:00
ajnart d6a259bdbe fix integration permission checks 2026-08-14 11:32:10 +02:00
ajnart 9588c3860d fix assistant provider icon contrast 2026-08-14 11:32:10 +02:00
ajnart c0cdedc039 add OpenRouter web search option 2026-08-14 11:32:10 +02:00
ajnart 1f60b4b4be fix assistant widget configuration 2026-08-14 11:32:10 +02:00
ajnart 8d06201b41 enhance assistant batch responses 2026-08-14 11:32:10 +02:00
ajnart edf3f6cd84 show assistant icon search previews 2026-08-14 11:32:10 +02:00
ajnart 9f2b52dd7f fix assistant streaming scroll lock 2026-08-14 11:32:10 +02:00
ajnart 87a42f09be integrate assistant custom widget workflow 2026-08-14 11:32:10 +02:00
ajnart d40e6852e6 fix assistant prompt submission 2026-08-14 11:32:10 +02:00
ajnart 37f5473054 refresh views after assistant changes 2026-08-14 11:32:10 +02:00
ajnart 3d7c95ddcc improve assistant question placement 2026-08-14 11:32:10 +02:00
ajnart 454a35d1a9 fix assistant automatic approvals 2026-08-14 11:32:10 +02:00
ajnart a979e8d78a chore: remove unused ai sdk dependency 2026-08-14 11:32:10 +02:00
ajnart 5ceef06b4b fix: address assistant review feedback 2026-08-14 11:32:10 +02:00
ajnart 33695753dd fix: isolate assistant conversation state 2026-08-14 11:32:10 +02:00
ajnart 3faac7a96d fix: harden assistant interactive tools 2026-08-14 11:32:10 +02:00
ajnart 38440a2e57 feat: improve assistant tool workflows 2026-08-14 11:32:10 +02:00
ajnart c5800785c9 fix: secure assistant generation telemetry 2026-08-14 11:32:10 +02:00
ajnart b66c73141f feat: enrich assistant request telemetry 2026-08-14 11:32:10 +02:00
ajnart 0a88db1296 fix: sync assistant runtime preferences 2026-08-14 11:32:10 +02:00
ajnart 23e4fae275 fix: align assistant management layout 2026-08-14 11:32:10 +02:00
ajnart 1e058c6ed1 fix: hide inactive assistant scroll control 2026-08-14 11:32:10 +02:00
ajnart ebf6c0d97b fix: polish assistant widget interactions 2026-08-14 11:32:10 +02:00
ajnart 5ede7ef4b9 fix: correct assistant widget docs metadata 2026-08-14 11:32:10 +02:00
ajnart 165d148cfc style: auto-fix lint and format 2026-08-14 11:32:10 +02:00
ajnart 8e0a52fcd0 feat: add assistant conversation widget 2026-08-14 11:32:10 +02:00
ajnart f3420a4149 fix: simplify assistant shortcut 2026-08-14 11:32:10 +02:00
ajnart 6dc9a565b0 feat: add assistant board styling tools 2026-08-14 11:32:10 +02:00
ajnart 07547a462c fix: restore assistant approval flows 2026-08-14 11:32:10 +02:00
ajnart 73e4b80a26 feat: expose assistant global shortcuts 2026-08-14 11:32:10 +02:00
ajnart 4b647ec2c9 fix: keep assistant menu selection visible 2026-08-14 11:32:10 +02:00
ajnart 03c448f1f8 refactor: group assistant runtime controls 2026-08-14 11:32:10 +02:00
ajnart e77174ff15 fix: invoke mutations before approval 2026-08-14 11:32:10 +02:00
ajnart 18ed16d07e feat: redesign assistant management 2026-08-14 11:32:10 +02:00
ajnart 9c57e0cff9 docs: explain assistant search focus 2026-08-14 11:32:10 +02:00
ajnart c45a9d047d style: auto-fix lint and format 2026-08-14 11:32:10 +02:00
ajnart efe477e5b5 feat: improve assistant confirmations 2026-08-14 11:32:10 +02:00
ajnart 41389b61e2 feat: improve assistant background controls 2026-08-14 11:32:10 +02:00
ajnart 0c5fc1d331 refactor: simplify assistant message controls 2026-08-14 11:32:10 +02:00
ajnart 6eb75ca559 feat: render assistant content 2026-08-14 11:32:10 +02:00
ajnart 1656890b77 fix: send provider model ids 2026-08-14 11:32:10 +02:00
ajnart 3ed839334b fix: preserve assistant trigger focus 2026-08-14 11:32:10 +02:00
ajnart b5162a9b95 fix: address assistant capability review 2026-08-14 11:32:10 +02:00
ajnart 40c7b1523b feat: expand assistant interaction capabilities 2026-08-14 11:32:10 +02:00
ajnart d37a2ba963 refactor: remove assistant chat archiving 2026-08-14 11:32:10 +02:00
ajnart 26a9752206 fix: polish assistant mobile layout 2026-08-14 11:32:10 +02:00
ajnart 3e1257bafc fix: harden assistant floating panel 2026-08-14 11:32:10 +02:00
ajnart 63699b1610 feat: streamline assistant interactions 2026-08-14 11:32:10 +02:00
ajnart 27448868dd fix: surface assistant stream errors 2026-08-14 11:32:10 +02:00
ajnart 8a8e222da3 fix: keep assistant in user menu 2026-08-14 11:32:10 +02:00
ajnart f3360e0d97 fix: clarify assistant credential removal 2026-08-14 11:32:10 +02:00
ajnart 8d9d2fe802 fix: invalidate persisted assistant model state 2026-08-14 11:32:10 +02:00
ajnart 96d2c78879 fix: invalidate assistant models on discovery changes 2026-08-14 11:32:10 +02:00
ajnart e9e3a523dc fix: gate assistant models on saved connection 2026-08-14 11:32:10 +02:00
ajnart 0ef0e32e18 fix: clear assistant credentials on destination changes 2026-08-14 11:32:10 +02:00
ajnart cba2df72e2 feat: promote assistant configuration 2026-08-14 11:32:10 +02:00
ajnart c0445cdca2 feat: expand assistant providers and polish experience 2026-08-14 11:32:10 +02:00
ajnart 5f3d676af0 fix: derive assistant approval key at runtime 2026-08-14 11:32:10 +02:00
ajnart e9b24f1242 fix: hide closed assistant history 2026-08-14 11:32:09 +02:00
ajnart 5db95f2def fix: harden assistant integration 2026-08-14 11:32:09 +02:00
ajnart 10ef871777 feat: add Homarr assistant 2026-08-14 11:32:09 +02:00
ajnart 854b59ede4 fix: honor requested board layout edits 2026-08-14 11:32:09 +02:00
ajnart eb918b805a chore: reconcile mobile layer after stack rebase 2026-08-14 11:32:09 +02:00
Thomas 4a4b04d471 fix(boards): disable mobile sidebars (#6565) 2026-08-14 11:32:09 +02:00
Thomas Camlong 3943741838 fix: refine responsive layout settings 2026-08-14 11:32:09 +02:00
Thomas Camlong e425e060d9 Rename layout role migrations 2026-08-14 11:32:09 +02:00
Thomas Camlong 6754e53f56 test(boards): include layout roles in gutter fixtures 2026-08-14 11:32:09 +02:00
Thomas Camlong 357adffb30 style: format mobile migration metadata 2026-08-14 11:32:09 +02:00
ajnart d24eca12ef fix: repair restored board layouts 2026-08-14 11:32:09 +02:00
ajnart dfcd1dbb64 fix: guard recursive section rendering 2026-08-14 11:32:09 +02:00
ajnart 252ce998ae fix: stabilize responsive layout editing 2026-08-14 11:32:09 +02:00
ajnart a940a8223d fix: improve layout preview density 2026-08-14 11:32:09 +02:00
ajnart bbd1abd4f0 fix: address responsive layout review 2026-08-14 11:32:09 +02:00
ajnart 7adc18b1ce fix: avoid nested board settings forms 2026-08-14 11:32:09 +02:00
ajnart 6a5162d615 feat: protect responsive board layouts 2026-08-14 11:32:09 +02:00
ajnart 02fe99d5aa refactor: simplify mobile board layouts 2026-08-14 11:32:09 +02:00
ajnart 7bbca3bd40 fix: refine mobile board preview 2026-08-14 11:32:09 +02:00
ajnart 923a528038 fix: address mobile board review 2026-08-14 11:32:09 +02:00
ajnart bb599e7fae test: cover tablet mobile boards 2026-08-14 11:32:09 +02:00
ajnart bb5df3a2e6 feat: add mobile layout preview 2026-08-14 11:32:09 +02:00
ajnart c805d5c631 fix: label board edit mode control 2026-08-14 11:32:09 +02:00
ajnart 747234c040 fix: keep mobile board widgets unchanged 2026-08-14 11:32:09 +02:00
ajnart b828f59568 Revert "feat: improve mobile board experience"
This reverts commit 7c0e98c55b.
2026-08-14 11:32:09 +02:00
ajnart 60ea03e72e Revert "feat: refine mobile dashboard experience"
This reverts commit 315520beca.
2026-08-14 11:32:09 +02:00
ajnart e9853f1fb1 Revert "style: auto-fix lint and format"
This reverts commit 4bd0d8e8dd.
2026-08-14 11:32:09 +02:00
ajnart f8b364dce2 Revert "fix: safely extract notebook summaries"
This reverts commit fa3d0375c5.
2026-08-14 11:32:09 +02:00
ajnart cd8773681f Revert "fix: address mobile dashboard review findings"
This reverts commit 1039bb18b3.
2026-08-14 11:32:09 +02:00
ajnart 2f99f1f34b Revert "fix: collect mobile board e2e test"
This reverts commit 6dbfbb1417.
2026-08-14 11:32:09 +02:00
ajnart 6fc851f054 Revert "fix: layer mobile widget details above header"
This reverts commit a540f07920.
2026-08-14 11:32:09 +02:00
ajnart 7bedf4a13d Revert "fix: keep interface layers server safe"
This reverts commit cf83e0d849.
2026-08-14 11:32:09 +02:00
ajnart a9c8b65c14 Revert "feat: complete automatic mobile board UX"
This reverts commit 7959c20d88.
2026-08-14 11:32:09 +02:00
ajnart e336059079 Revert "fix: harden automatic mobile boards"
This reverts commit d0b9348b86.
2026-08-14 11:32:09 +02:00
ajnart 708105ab3f fix: harden automatic mobile boards 2026-08-14 11:32:09 +02:00
ajnart e6bd1adfc2 feat: complete automatic mobile board UX 2026-08-14 11:32:09 +02:00
ajnart b217d8e026 fix: keep interface layers server safe 2026-08-14 11:32:09 +02:00
ajnart 35b7c4fc2d fix: layer mobile widget details above header 2026-08-14 11:32:09 +02:00
ajnart 251a4c8ae8 fix: collect mobile board e2e test 2026-08-14 11:32:09 +02:00
ajnart b4404113c4 fix: address mobile dashboard review findings 2026-08-14 11:32:09 +02:00
ajnart c1efaabd36 fix: safely extract notebook summaries 2026-08-14 11:32:09 +02:00
ajnart b75483982f style: auto-fix lint and format 2026-08-14 11:32:09 +02:00
ajnart c1da19c9b2 feat: refine mobile dashboard experience 2026-08-14 11:32:09 +02:00
ajnart 46898cd0ea feat: improve mobile board experience 2026-08-14 11:32:09 +02:00
ajnart 6f4cd7522f fix: align mobile root section ordering 2026-08-14 11:32:09 +02:00
ajnart eaaeaaccdb fix: address board review and CI feedback 2026-08-14 11:32:09 +02:00
ajnart d63653142d feat: add automatic mobile board layout 2026-08-14 11:32:09 +02:00
ajnart 08588da0d1 test: stabilize advanced view keyboard flow 2026-08-14 11:31:55 +02:00
ajnart 464f30799a fix: refine advanced widget access 2026-08-14 11:07:09 +02:00
ajnart bd4bbcaca2 chore: reconcile widget layer after stack rebase 2026-08-14 11:07:09 +02:00
Thomas Camlong 09b82ba171 fix: limit advanced widget focus 2026-08-14 11:07:09 +02:00
Thomas Camlong f38eb034da fix: address widget review findings 2026-08-14 11:07:09 +02:00
Thomas Camlong a3a89db727 test: align widget modernization fixtures 2026-08-14 11:07:09 +02:00
Thomas Camlong c099a8eef6 feat: expand advanced widget experiences 2026-08-14 11:07:09 +02:00
Thomas Camlong 9437d211c8 chore: drop manual translations 2026-08-14 11:07:09 +02:00
Thomas Camlong 096cf56ac9 fix: rework media session details 2026-08-14 11:07:08 +02:00
Thomas Camlong 851129e62e fix: refine Beszel stats period controls 2026-08-14 11:07:08 +02:00
Thomas Camlong cc1b22f052 fix: show complete Immich photos in advanced mode 2026-08-14 11:07:08 +02:00
Thomas Camlong 5df4358f0d test: preserve advanced preview hover ownership 2026-08-14 11:07:08 +02:00
Thomas Camlong 821b5a555d feat: improve advanced preview activation 2026-08-14 11:07:08 +02:00
Thomas Camlong a0f4a58386 fix: smooth advanced focus entrance 2026-08-14 11:07:08 +02:00
Thomas Camlong 7c840bcfa0 Remove Docker advanced focus 2026-08-14 11:07:08 +02:00
Thomas Camlong 38d30acb87 fix: require advanced widget focus opt-in 2026-08-14 11:07:08 +02:00
Thomas Camlong ae515584cc test(widgets): stabilize advanced focus hover 2026-08-14 11:07:08 +02:00
Thomas Camlong 9338d6d4ec test(mcp): review smart home entity details tool 2026-08-14 11:07:08 +02:00
Thomas Camlong d98b7797c1 style(widgets): split query matcher type imports 2026-08-14 11:07:08 +02:00
Thomas Camlong 3c739dc636 fix(widgets): keep board graph imports scoped 2026-08-14 11:07:08 +02:00
Thomas Camlong 639cb8d35f fix(ci): make modernization independently installable 2026-08-14 11:07:08 +02:00
Thomas Camlong f1ac482684 style: format modernization notes 2026-08-14 11:07:08 +02:00
Thomas Camlong 9cc7fd2051 fix: preserve app links and hover previews 2026-08-14 11:07:08 +02:00
Thomas Camlong d4d951b99c fix: stabilize advanced widget interactions 2026-08-14 11:07:08 +02:00
Thomas Camlong a4f115064e fix: finish widget modernization audit 2026-08-14 11:07:08 +02:00
Thomas Camlong 07e0642192 fix: address widget review feedback 2026-08-14 11:07:08 +02:00
Thomas Camlong 965ccb32d6 fix: scope advanced preview controls 2026-08-14 11:07:08 +02:00
Thomas Camlong be08f70c8d fix: restore notifications query scope 2026-08-14 11:07:08 +02:00
Thomas Camlong 4892c399ba fix: remove stale notifications import 2026-08-14 11:07:08 +02:00
ajnart 687fb323f7 style: auto-fix lint and format 2026-08-14 11:07:08 +02:00
Thomas Camlong 56cc021f98 fix: refine widget interactions 2026-08-14 11:07:08 +02:00
Thomas Camlong 836a4cd46b feat: modernize widget experience 2026-08-14 11:07:08 +02:00
ajnart a607dff50a docs: add widget modernization audit 2026-08-14 11:07:08 +02:00
Thomas Camlong 68751d85dc restore context menu focus 2026-08-14 11:07:08 +02:00
Thomas Camlong 869dd45de5 fix context menu keyboard focus 2026-08-14 11:07:08 +02:00
Thomas Camlong 352aa7dd64 fix board E2E login race 2026-08-14 11:07:08 +02:00
Thomas Camlong 801221d2da fix SVG focus scrolling 2026-08-14 11:07:08 +02:00
Thomas Camlong 861538e9a0 fix focused pane scrolling 2026-08-14 11:07:08 +02:00
Thomas Camlong cb7b49b3e7 fix focus scroll layering 2026-08-14 11:07:08 +02:00
Thomas Camlong f5cced4080 fix widget focus interactions 2026-08-14 11:07:08 +02:00
Thomas Camlong e429d7540a fix: align responsive widget actions 2026-08-14 11:07:08 +02:00
Thomas Camlong 3bd80dcc58 fix: simplify widget feedback 2026-08-14 11:07:08 +02:00
Thomas Camlong 0e01ac67df fix: harden widget error feedback 2026-08-14 11:07:08 +02:00
Thomas Camlong 8259df58f6 fix: address final widget review 2026-08-14 11:07:08 +02:00
Thomas Camlong 3485ef0877 fix: localize widget chart formatting 2026-08-14 11:07:08 +02:00
Thomas Camlong 2214acd17e fix: align widget accessibility and locales 2026-08-14 11:07:08 +02:00
Thomas Camlong 25b835dca9 fix: canonicalize widget date locales 2026-08-14 11:07:08 +02:00
Thomas Camlong 2efa6bcd4c fix: close final widget review gaps 2026-08-14 11:07:08 +02:00
Thomas Camlong c67c893f8f fix: harden widget review follow-ups 2026-08-14 11:07:08 +02:00
Thomas Camlong e7e44375ef fix: address widget review findings 2026-08-14 11:07:08 +02:00
Thomas Camlong 842e86012d feat: refine widget interactions 2026-08-14 11:07:08 +02:00
Thomas Camlong a8a7545f40 fix: secure widget navigation links 2026-08-14 11:07:08 +02:00
Thomas Camlong ed7b30dc5f fix: polish widget failure feedback 2026-08-14 11:07:08 +02:00
Thomas Camlong c09ed96978 fix: address final widget review findings 2026-08-14 11:07:08 +02:00
Thomas Camlong 44b55a401c fix: complete widget review cleanup 2026-08-14 11:07:08 +02:00
Thomas Camlong f00f5b83b5 fix: address remaining widget review feedback 2026-08-14 11:07:08 +02:00
Thomas Camlong 5553c4e0c5 fix: allow dynamic database schema access 2026-08-14 11:07:08 +02:00
Thomas Camlong 6cdda54e10 fix: make integration warnings keyboard accessible 2026-08-14 11:07:08 +02:00
Thomas Camlong 0f9d038aaf fix: complete widget modernization review 2026-08-14 11:07:08 +02:00
Thomas Camlong a38e731209 fix: address widget review feedback 2026-08-14 11:07:08 +02:00
ajnart b1b17ad69f style: auto-fix lint and format 2026-08-14 11:07:08 +02:00
Thomas Camlong add40370aa fix: refine widget modernization 2026-08-14 11:07:07 +02:00
Thomas Camlong a2763d1f6f feat: modernize dashboard widgets 2026-08-14 11:07:07 +02:00
ajnart f3048f4804 docs: correct board interaction guidance 2026-08-14 11:07:07 +02:00
ajnart 6bfa9c33d0 chore: reconcile grid layer after stack rebase 2026-08-14 11:07:07 +02:00
Thomas Camlong 16982aa178 fix: simplify grid interaction previews 2026-08-14 11:07:07 +02:00
Thomas Camlong a2400f8412 perf: isolate board grid previews 2026-08-14 11:07:07 +02:00
Thomas Camlong 6e455760a3 fix: disable app context menu 2026-08-14 11:07:07 +02:00
Thomas Camlong 0b9b77c632 perf: isolate grid interaction renders 2026-08-14 11:07:07 +02:00
Thomas Camlong 346bff540f fix: stabilize rail and resize interactions 2026-08-14 11:07:07 +02:00
Thomas Camlong f8771c371f chore: leave translations to Crowdin 2026-08-14 11:07:07 +02:00
Thomas Camlong 71cc3abbe9 fix: prevent horizontal rail auto-scroll 2026-08-14 11:07:07 +02:00
Thomas Camlong a098c42013 Revert "ci: use self-hosted runners for stack"
This reverts commit a1d80515b0e00240bb662180626a44bfb205656e.
2026-08-14 11:07:07 +02:00
Thomas Camlong 4cf83443fd ci: use self-hosted runners for stack 2026-08-14 11:07:07 +02:00
Thomas Camlong f5b4889663 fix: anchor dashboard rail labels 2026-08-14 11:07:07 +02:00
Thomas Camlong 06aa5caf0b fix: size dashboard rails to viewport 2026-08-14 11:07:07 +02:00
Thomas Camlong d3534dfcf8 style: format board grid test 2026-08-14 11:07:07 +02:00
Thomas Camlong f7591dd196 test: allow dnd-kit DOM modifier import 2026-08-14 11:07:07 +02:00
Thomas Camlong 0c5f7a45bb fix: preserve narrow board interactions 2026-08-14 11:07:07 +02:00
Thomas Camlong cdbba92f31 fix: keep scaled board rails sticky 2026-08-14 11:07:07 +02:00
Thomas Camlong 3b0633f6a8 test: assert clipped zoom overflow 2026-08-14 11:07:07 +02:00
Thomas Camlong d0880aa04c test: avoid physical zoom size assumption 2026-08-14 11:07:07 +02:00
Thomas Camlong 7cf6a5d5ab test: assert zoomed canvas fit invariant 2026-08-14 11:07:07 +02:00
Thomas Camlong d5763737c3 style: format board grid coverage 2026-08-14 11:07:07 +02:00
Thomas Camlong 00b0b215b0 fix: stabilize board rails 2026-08-14 11:07:07 +02:00
Thomas Camlong 5f08327c5c Fix board grid bounds and live growth 2026-08-14 11:07:07 +02:00
Thomas Camlong 2645cb426a fix: accept legacy dynamic sections 2026-08-14 11:07:07 +02:00
Thomas Camlong 65e866064e Rename layout gutter migrations 2026-08-14 11:07:07 +02:00
Thomas Camlong 392df12d8a style: format dnd migration metadata 2026-08-14 11:07:07 +02:00
Thomas Camlong 6209980b27 test(e2e): wait for settled grid geometry 2026-08-14 11:07:07 +02:00
Thomas Camlong 3f04cf83a2 fix container header spacing 2026-08-14 11:07:07 +02:00
Thomas Camlong e5a8194c03 fix: contain board canvas overflow 2026-08-14 11:07:07 +02:00
Thomas Camlong 90cf0e1127 fix: contain collapsed layout and widget overflow 2026-08-14 11:07:07 +02:00
Thomas Camlong 498481d539 test: wait for resized grid geometry 2026-08-14 11:07:07 +02:00
Thomas Camlong 65da74858b test: stabilize board grid authentication 2026-08-14 11:07:07 +02:00
ajnart af2b7b3157 style: auto-fix lint and format 2026-08-14 11:07:07 +02:00
Thomas Camlong db40779492 fix: smooth container collapse behavior 2026-08-14 11:07:07 +02:00
ajnart 91aeac8377 style: auto-fix lint and format 2026-08-14 11:07:06 +02:00
Thomas Camlong dfe2ddf34b fix: refine container interactions 2026-08-14 11:07:06 +02:00
Thomas Camlong 34f2a0bd24 fix: polish board grid interactions 2026-08-14 11:07:06 +02:00
Thomas Camlong 4227e392b5 fix: address board grid CI and review 2026-08-14 11:07:06 +02:00
ajnart 6893205ee0 style: auto-fix lint and format 2026-08-14 11:07:06 +02:00
Thomas Camlong 28ac23e66b fix: address board grid review 2026-08-14 11:07:06 +02:00
Thomas Camlong cddae6719e feat: rebuild board grid with dnd-kit 2026-08-14 11:07:06 +02:00
ajnart 0cb75982fc test: wait for grid reconciliation between drags 2026-08-14 11:07:06 +02:00
ajnart 36de8131c8 fix: clarify board grid editing affordances 2026-08-14 11:07:06 +02:00
ajnart 2f28f45ff1 fix: improve board grid interactions 2026-08-14 11:07:06 +02:00
Thomas Camlong 915af9ae96 test: target visible layout preview 2026-08-14 11:07:06 +02:00
Thomas Camlong db5d088330 test: tolerate sticky rail subpixel rounding 2026-08-14 11:07:06 +02:00
Thomas Camlong d1a2ed8fd7 test: avoid grid drag rounding boundary 2026-08-14 11:07:06 +02:00
ajnart d6b114e423 feat: rebuild board grid experience 2026-08-14 11:07:06 +02:00
1735 changed files with 142226 additions and 28000 deletions

No files matched your search

+9 -1
View File
@@ -7,11 +7,15 @@ description: Author safe Homarr Custom JSX v2 widgets from API documentation.
Author one widget at a time. When connected to Homarr, retrieve `homarr://custom-widgets/schema` and only the component resources needed for the design. The live resources are authoritative for the installed release.
Keep documentation retrieval targeted: fetch at most eight named component documents before the first validation, or at most four after loading a complete example. Fetch another component only when a validation issue or a concrete missing interaction requires it; never load the catalog component-by-component.
1. Read the API documentation.
2. Create one credential-free widget with keyed `sources`, keyed `requests`, optional keyed `options`, and safe JSX `template`.
3. Validate, preview, test queries and simulated actions, visually inspect, and iterate.
4. Configure deployment-specific source URLs and request credentials through Homarr; never repeat plaintext.
5. Save only after narrow/wide, light/dark, loading, empty, error, and success states work.
5. Save only after narrow/wide, light/dark, loading, empty, error, and success states work. When connected through MCP, persist the exact final tested preview with `customWidget_createFromPreview`; do not resend a large template through `customWidget_create` unless no preview session is available.
Treat a supplied sample or successful preview response as the binding contract. Render every core requested field, guard optional arrays and nested values before indexing, and do not silently drop returned items. Show concise freshness context when the API supplies a timestamp. Give recoverable load errors and empty states a clear refresh or retry path.
Use `{option:name}` or `$option` for saved options. Use `{param:name}` or `$param` for values supplied by `SubFetch`, `ActionButton`, or `ToggleSwitch`. Load queries cannot use invocation parameters. Templates read `data`, `status`, `options`, and temporary `inputs`.
@@ -19,6 +23,10 @@ Use `{option:name}` or `$option` for saved options. Use `{param:name}` or `$para
Use standard Mantine compound names and deliberate visual hierarchy, spacing, restrained semantic color, and responsive layouts. Avoid excessive nested cards.
Aim for distinctive polish without decorative gradients or fragile custom styling: use one semantic accent, clear surface contrast, purposeful typography, and compact rows that remain scannable in narrow tiles. Give standalone icons an accessible label or pair them with visible text.
Make initial states actionable with an example, useful hint, or clear next step. Use wrapping groups for variable-length labels and values on narrow tiles. Do not use an unlabeled decorative icon as an empty state.
Do not use imports, hooks, refs, raw HTML, raw event callbacks, browser requests, arbitrary functions, eval, bigint, npm packages, authored statement blocks, IIFEs, or recursion. Do not pretend MCP tools exist in an offline chat session.
To install an existing widget, use `customWidget_workshopSearch`, inspect it with `customWidget_workshopGet`, then use `customWidget_workshopInstall`. Configure each self-hosted source afterward with `customWidget_sourceConfigure`, or call `customWidget_configurationRequestUser` so the user can enter the server URL and credentials directly in Homarr.
@@ -0,0 +1,60 @@
---
name: homarr-local-harness
description: Build and inspect a writable Docker demo harness from the current Homarr checkout when validating a local branch in a browser.
---
# Homarr Local Harness
Use this skill when a Homarr change needs a real production image and browser validation. Work from the repository root and keep the harness name Docker-safe, such as `feature-board`.
## Set up the harness
Run the TypeScript operations CLI; this workflow does not use the interactive developer TUI:
```sh
pnpm cli harness setup feature-board
```
`setup` builds the current checkout as `homarr:feature-board`, replaces the named harness container, and starts it with a persistent data volume. It enables demo mode, mock integrations, and writable demo mutations by default:
- `DEMO_MODE=true`
- `DEMO_READ_ONLY=false`
- `UNSAFE_ENABLE_MOCK_INTEGRATION=true`
Pass repeatable environment overrides when the feature needs them:
```sh
pnpm cli harness setup feature-board \
--env WORKSHOP_WEB_URL=https://example.test \
--env FEATURE_FLAG=true
```
Use `pnpm cli harness build <name>` when only the image is needed, and `pnpm cli harness run <name>` to start an already-built image. `--port <number>` pins a host port; otherwise Docker assigns one.
Always use the URL printed by the command. If the output is no longer available, query it directly:
```sh
pnpm cli harness port feature-board
```
Do not assume port `7575`; the container listens on `7575`, but the host port is dynamic by default.
## Browser validation
Open the printed URL with the environment's `browser-harness` tool. In this workspace, the `agent-browser` CLI is the supported browser automation fallback. Load its current workflow before invoking it:
```sh
agent-browser skills get core
```
Navigate to the exact harness URL, wait for the app to become usable, exercise the changed flow, and capture screenshots or other browser evidence. Keep browser state in a named session when the workflow needs login persistence. Report the exact URL, viewport/session used, and screenshot paths; a Docker process or HTTP health response alone is not browser acceptance.
## Cleanup
Stop the named container when finished:
```sh
pnpm cli harness stop feature-board
```
Stopping keeps the data volume so the same harness can be restarted. A later `setup` rebuilds the image and replaces only the container. Preserve unrelated containers, images, volumes, and working-tree changes.
@@ -0,0 +1,4 @@
interface:
display_name: "Homarr Local Harness"
short_description: "Build and browse a writable local Homarr branch harness"
default_prompt: "Use $homarr-local-harness to set up a writable Homarr harness for the current branch and validate it in the browser."
-2
View File
@@ -66,8 +66,6 @@ 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
+9 -19
View File
@@ -263,15 +263,6 @@ 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:
@@ -610,13 +601,12 @@ 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 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
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
-8
View File
@@ -1,8 +0,0 @@
version = 1
[[analyzers]]
name = "javascript"
[analyzers.meta]
plugins = ["react"]
environment = ["nodejs"]
+2 -1
View File
@@ -67,7 +67,8 @@ 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 page opened from Homarr. Defaults to HOMARR_WEBSITE_URL/workshop.
# Public Workshop website used by links in Homarr, Custom Widgets, onboarding,
# and assistant context. Defaults to HOMARR_WEBSITE_URL/workshop.
# WORKSHOP_WEB_URL=https://homarr.dev/workshop
# Documentation and PocketBase deployment settings.
+84 -43
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]
branches: [main, dev, beta, next, v2, release/v2]
merge_group:
workflow_dispatch:
inputs:
@@ -105,18 +105,20 @@ jobs:
container:
name: Container and E2E
needs: fast-gate
needs: [fast-gate, preview-amd64]
if: |
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'))
!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'))
)
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
packages: write
outputs:
digest: ${{ steps.preview-build.outputs.digest }}
packages: read
steps:
- uses: actions/checkout@v7
if: "!env.ACT"
@@ -131,8 +133,18 @@ 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: "!env.ACT"
if: needs.preview-amd64.outputs.digest == '' && !env.ACT
uses: docker/build-push-action@v7
with:
context: .
@@ -147,8 +159,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-linux-amd64
cache-to: type=gha,scope=homarr-linux-amd64,mode=max
cache-from: type=gha,scope=homarr-e2e
cache-to: type=gha,scope=homarr-e2e,mode=max
secrets: |
TURBO_API=${{ secrets.TURBO_API }}
TURBO_TEAM=${{ secrets.TURBO_TEAM }}
@@ -189,7 +201,10 @@ jobs:
run: pnpm exec playwright install-deps chromium
- name: Run E2E tests
run: pnpm test:e2e
env:
NODE_ENV: development
CI: "true"
run: pnpm exec vitest e2e --retry 1 --maxWorkers 4
- name: Upload E2E artifacts
if: failure() && !env.ACT
@@ -199,27 +214,44 @@ jobs:
path: e2e/test-results/
retention-days: 7
- 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')
)
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"
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push amd64 preview image by digest
- name: Set up Docker Buildx
if: "!env.ACT"
uses: docker/setup-buildx-action@v4
- name: Build and push amd64 preview image by digest
id: preview-build
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')
)
if: "!env.ACT"
uses: docker/build-push-action@v7
with:
context: .
@@ -227,22 +259,20 @@ 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
@@ -290,10 +320,11 @@ jobs:
publish-preview:
name: Publish multi-platform preview image
needs: [container, preview-arm64]
needs: [preview-amd64, 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
@@ -303,7 +334,7 @@ jobs:
packages: write
pull-requests: read
env:
PREVIEW_TAG: pr-${{ github.event.pull_request.number || inputs.pr_number }}
PREVIEW_TAG: ${{ github.event_name == 'push' && 'v2' || format('pr-{0}', github.event.pull_request.number || inputs.pr_number) }}
steps:
- name: Log in to GHCR
if: "!env.ACT"
@@ -340,24 +371,34 @@ jobs:
- name: Publish preview manifest
if: "!env.ACT"
env:
AMD64_DIGEST: ${{ needs.container.outputs.digest }}
AMD64_DIGEST: ${{ needs.preview-amd64.outputs.digest }}
ARM64_DIGEST: ${{ needs.preview-arm64.outputs.digest }}
run: |
docker buildx imagetools create -t "${PREVIEW_IMAGE}:${PREVIEW_TAG}" \
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[@]}" \
"${PREVIEW_IMAGE}@${AMD64_DIGEST}" \
"${PREVIEW_IMAGE}@${ARM64_DIGEST}"
- name: Verify preview manifest platforms
if: "!env.ACT"
run: |
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
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"
done
docker buildx imagetools inspect "$image"
cleanup:
name: Clean up preview image
@@ -1,5 +1,6 @@
permissions:
statuses: write
contents: read
on:
pull_request:
@@ -16,12 +17,20 @@ jobs:
env:
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
run: |
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'
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
+83 -17
View File
@@ -3,7 +3,7 @@ name: Workshop
on:
pull_request:
push:
branches: [dev, v2, main]
branches: [dev, v2, release/v2, main]
permissions:
contents: read
@@ -27,37 +27,103 @@ jobs:
- run: pnpm test:workshop
- run: pnpm test:workshop-image
publish:
if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'main')
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: 30
timeout-minutes: 20
permissions:
contents: read
packages: write
outputs:
digest: ${{ steps.build.outputs.digest }}
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 }}
- 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
id: build
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 }}
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]
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
packages: write
env:
WORKSHOP_IMAGE: ghcr.io/homarr-labs/workshop
steps:
- 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"
+13
View File
@@ -25,6 +25,8 @@ packages/db/migrations/*/migrate.cjs
# production
build
tools/homarr-dev/homarr
tools/homarr-dev/homarr-dev
# misc
.DS_Store
@@ -84,3 +86,14 @@ 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/
@@ -1,116 +0,0 @@
---
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?
@@ -1,64 +0,0 @@
---
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.
@@ -1,106 +0,0 @@
---
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,8 +40,6 @@ 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
@@ -1,96 +0,0 @@
# 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`.
+475
View File
@@ -0,0 +1,475 @@
# Mantine UI - LLM Documentation
This index lists Mantine documentation pages formatted for LLMs.
Each link points to a standalone Markdown file under the /llms path.
For a single consolidated file with all content, use:
- https://mantine.dev/llms-full.txt
## FAQ
- [Are Mantine components accessible?](https://mantine.dev/llms/q-are-mantine-components-accessible.md): Learn about Mantine components accessibility features
- [Browser zooms in when input is focused. What should I do?](https://mantine.dev/llms/q-browser-zooms-on-focus.md): Use meta tag to disable browser scaling or increase input size
- [Can I have color schemes other than light and dark?](https://mantine.dev/llms/q-light-dark-is-not-enough.md): Learn about the difference between color scheme and theme
- [Can I have different primary color for light and dark color schemes?](https://mantine.dev/llms/q-primary-virtual-color.md): Learn how to use virtual color with primary color in theme object
- [Can I remove MultiSelect placeholder when the component has selected values?](https://mantine.dev/llms/q-multiselect-value-placeholder.md): Learn why MultiSelect placeholder is not removed when values are selected and how to remove it with CSS
- [Can I use an array of strings as a list in use-form?](https://mantine.dev/llms/q-list-of-strings-in-use-form.md): Learn about use-form lists limitations
- [Can I use Mantine components as server components?](https://mantine.dev/llms/q-server-components.md): Learn about use client directive and server components usage
- [Can I use Mantine with Astro?](https://mantine.dev/llms/q-can-i-use-mantine-with-astro.md): No, Astro does not support React context
- [Can I use Mantine with Create React App (CRA)?](https://mantine.dev/llms/q-can-i-use-mantine-with-cra.md): Learn how to use Mantine without postcss-preset-mantine or how to eject CRA and customize webpack.config.js and use postcss-preset-mantine
- [Can I use Mantine with Emotion/styled-components/tailwindcss?](https://mantine.dev/llms/q-third-party-styles.md): Learn about limitations of third-party styles
- [Can I use Mantine with Vue/Svelte/Angular/etc.?](https://mantine.dev/llms/q-vue-svelte-angular.md): No, Mantine is a React library and does not support other frameworks/libraries
- [Can I use nested inline styles with Mantine components?](https://mantine.dev/llms/q-nested-inline-styles.md): Nested styles are supported only in CSS files
- [Can I use PostCSS function in inline styles?](https://mantine.dev/llms/q-postcss-fns-inline.md): Learn where PostCSS functions can be used in Mantine
- [Can I use private CSS variables to style components?](https://mantine.dev/llms/q-private-css-variables.md): No, it is not safe and will not work with future versions of Mantine.
- [Can I use SegmentedControl with empty value?](https://mantine.dev/llms/q-segmented-control-no-value.md): SegmentedControl cannot be used without a value
- [How can I add dynamic CSS styles?](https://mantine.dev/llms/q-dynamic-css-styles.md): Use data attributes, CSS variables or inline styles
- [How can I add fuzzy search to Select component?](https://mantine.dev/llms/q-select-fuzzy.md): Learn how to integrate third-party fuzzy search libraries with Mantine Select component
- [How can I add hover styles to an element?](https://mantine.dev/llms/q-how-to-add-hover-styles.md): Learn how to add hover classes to an element with CSS modules, &:hover or @mixin hover
- [How can I apply styles to all Mantine components?](https://mantine.dev/llms/q-apply-styles-to-all.md): Learn how to use attribute selector to apply styles to all Mantine components
- [How can I change body background color?](https://mantine.dev/llms/q-body-background.md): Use CSS to change body background color
- [How can I change component color prop value depending on the color scheme?](https://mantine.dev/llms/q-color-scheme-color.md): Learn how to use CSS variables resolver to change color value depending on the color scheme
- [How can I change input focus styles?](https://mantine.dev/llms/q-input-focus-styles.md): Learn how to use Styles API with Mantine inputs
- [How can I change Tabs border color?](https://mantine.dev/llms/q-tabs-border-color.md): Learn how to use Styles API with Tabs component
- [How can I contribute to the library?](https://mantine.dev/llms/q-how-can-i-contribute.md): There are multiple ways to contribute even without writing code
- [How can I disable all inputs/input groups inside a form?](https://mantine.dev/llms/q-disable-all-inputs-in-form.md): Learn how to disable all inputs/input groups inside a form with the unstyled Fieldset component
- [How can I display different elements in light and dark color schemes?](https://mantine.dev/llms/q-light-dark-elements.md): Learn how to hide/show elements based on color scheme
- [How can I focus the first input with error with use-form?](https://mantine.dev/llms/q-focus-first-input-with-error.md): Learn how to handle focus with use-form hook
- [How can I get current color scheme value in JavaScript?](https://mantine.dev/llms/q-how-to-get-color-scheme-value-in-js.md): How to use useMantineColorScheme and useComputedColorScheme hooks to get current color scheme value in JavaScript
- [How can I lint CSS files?](https://mantine.dev/llms/q-how-to-setup-stylelint.md): Learn how to set up Stylelint for Mantine postcss syntax
- [How can I load fonts in Next.js?](https://mantine.dev/llms/q-next-load-fonts.md): A guide to load custom fonts in Next.js with CSS and next/font package
- [How can I load fonts in React Router?](https://mantine.dev/llms/q-react-router-load-fonts.md): A guide to load custom fonts in React Router
- [How can I load fonts in Vite?](https://mantine.dev/llms/q-vite-load-fonts.md): A guide to load custom fonts in Vite
- [How can I lock scroll in my application?](https://mantine.dev/llms/q-how-to-lock-scroll.md): Use react-remove-scroll library to lock scroll in your application
- [How can I submit a template to Mantine documentation?](https://mantine.dev/llms/q-submit-template.md): Learn how to create and submit a template to Mantine documentation
- [How can I test Modal/Drawer/Popover components?](https://mantine.dev/llms/q-portals-testing.md): Learn how to use react-testing-library to test components that use portals and transitions.
- [How can I test Select/MultiSelect components?](https://mantine.dev/llms/q-combobox-testing.md): Learn how to use react-testing-library to test Select and MultiSelect components.
- [How can I upload files from Dropzone component?](https://mantine.dev/llms/q-dropzone-upload.md): Learn how to process files dropped into Dropzone component
- [How is that thing done on the mantine.dev website?](https://mantine.dev/llms/q-how-that-thing-is-done.md): Learn how various elements are implemented on the mantine.dev website
- [How to align input with a button in a flex container?](https://mantine.dev/llms/q-align-input-button.md): Learn how to align Mantine inputs with buttons using Group component
- [How to call a function when Modal/Drawer closes and animation completes?](https://mantine.dev/llms/q-how-to-call-function-when-modal-closes.md): How to use transitionProps in Modal/Drawer components
- [How to change background color for FiveM?](https://mantine.dev/llms/q-fivem-background-color.md): Unset body background color and color-scheme styles for FiveM
- [How to change input placeholder color?](https://mantine.dev/llms/q-inputs-placeholder-color.md): Learn how to change placeholder color with Styles API
- [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 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
- [Is there a comparison with other libraries?](https://mantine.dev/llms/q-other-libs.md): Comparison with other libraries is not provided, you are trusted to make your own decision based on documentation examples
- [Is there a floating action button component?](https://mantine.dev/llms/q-floating-action-button.md): No, but you can build it with Affix and ActionIcon components
- [Is there DataGrid component that I can use with Mantine?](https://mantine.dev/llms/q-data-grid-i-need.md): A list of community packages that provide DataGrid component that can be used with Mantine.
- [It is not possible to pinch to zoom when a Modal is opened. What should I do?](https://mantine.dev/llms/q-pinch-to-zoom-modal.md): Use removeScrollProps to configure react-remove-scroll options
- [MantineProvider was not found in component tree. What should I do?](https://mantine.dev/llms/q-mantine-provider-missing.md): Learn how to resolve MantineProvider issues
- [My buttons are transparent and the background is visible only on hover, what is wrong?](https://mantine.dev/llms/q-transparent-buttons.md): You have installed a third-party library that overrides Mantine styles
- [My Popover dropdown closes when I click on the dropdown of a nested Popover](https://mantine.dev/llms/q-nested-popover-closes.md): Popover dropdown is closed when it detects click-outside events
- [My styles are broken with disabled JavaScript. What should I do?](https://mantine.dev/llms/q-disabled-js.md): Learn how to use Mantine without JavaScript enabled
- [My styles are overridden by Mantine components styles, what should I do?](https://mantine.dev/llms/q-styles-order.md): Learn how to use CSS layers to control styles order and prevent Mantine components from overriding your styles
- [Native browser validation does not work in some components, what should I do?](https://mantine.dev/llms/q-native-required.md): Learn why native browser validation does not work in some components
- [The use-local-storage hook returns the real value only after mounting. Is it a bug?](https://mantine.dev/llms/q-local-storage-effect.md): Learn how Mantine retrieves local storage value
- [What is the difference between searchable Select and Autocomplete?](https://mantine.dev/llms/q-select-autocomplete-difference.md): Searchable Select and Autocomplete are similar components, but they serve different purposes.
- [Where can I find the roadmap?](https://mantine.dev/llms/q-roadmap.md): The roadmap is private and only available to the team.
- [Why are my Carousel slides in vertical orientation?](https://mantine.dev/llms/q-carousel-missing-styles.md): You forgot to import carousel styles
- [Why are my notifications displayed at the wrong position?](https://mantine.dev/llms/q-notifications-missing-styles.md): Because you did not import styles
- [Why can I not use one polymorphic component in the component prop of another polymorphic component?](https://mantine.dev/llms/q-polymorphic-in-polymorphic.md): Learn how polymorphic components types work
- [Why can I not use value/label data structure with Autocomplete/TagsInput?](https://mantine.dev/llms/q-autocomplete-value-label.md): Learn the difference between Autocomplete and Select components
- [Why can VSCode not autoimport the Text component?](https://mantine.dev/llms/q-why-vscode-cannot-autoimport-text.md): It is confused by the native Text constructor
- [Why do I see color scheme flickering on page load?](https://mantine.dev/llms/q-color-scheme-flickering.md): Color scheme flickering is caused by incorrect usage of ColorSchemeScript
- [Why do my dates components look broken?](https://mantine.dev/llms/q-dates-missing-styles.md): Because you did not import styles
- [Why does FileButton not work in Menu?](https://mantine.dev/llms/q-file-button-in-menu.md): Learn how to use FileButton in Menu component
- [Why is it required to have 10 shades per color?](https://mantine.dev/llms/q-ten-shades-per-color.md): Learn how Mantine uses theme colors
- [Why is my screen completely empty after I](https://mantine.dev/llms/q-notifications-empty-screen.md): You have used Notifications component incorrectly
## Core
- [Accordion](https://mantine.dev/llms/core-accordion.md): Divide content into collapsible sections
- [ActionIcon](https://mantine.dev/llms/core-action-icon.md): Icon button
- [Affix](https://mantine.dev/llms/core-affix.md): Renders children inside portal at fixed position
- [Alert](https://mantine.dev/llms/core-alert.md): Attract user attention with important static message
- [AlphaSlider](https://mantine.dev/llms/core-alpha-slider.md): Slider component for selecting alpha channel in color pickers (0 – 1)
- [Anchor](https://mantine.dev/llms/core-anchor.md): Display link with theme styles
- [AngleSlider](https://mantine.dev/llms/core-angle-slider.md): Pick angle value between 0 and 360
- [AppShell](https://mantine.dev/llms/core-app-shell.md): Responsive shell for your application with header, navbar, aside and footer
- [AspectRatio](https://mantine.dev/llms/core-aspect-ratio.md): Maintain responsive consistent width/height ratio
- [Autocomplete](https://mantine.dev/llms/core-autocomplete.md): Autocomplete user input with any list of options
- [Avatar](https://mantine.dev/llms/core-avatar.md): Display user profile image, initials or fallback icon
- [BackgroundImage](https://mantine.dev/llms/core-background-image.md): Displays image as background
- [Badge](https://mantine.dev/llms/core-badge.md): Display badge, pill or tag
- [Blockquote](https://mantine.dev/llms/core-blockquote.md): Blockquote with optional cite
- [Box](https://mantine.dev/llms/core-box.md): Base component for all Mantine components
- [Breadcrumbs](https://mantine.dev/llms/core-breadcrumbs.md): Separates list of react nodes with given separator
- [Burger](https://mantine.dev/llms/core-burger.md): Open/close navigation button
- [Button](https://mantine.dev/llms/core-button.md): Button component to render button or link
- [Card](https://mantine.dev/llms/core-card.md): Card with sections
- [Cascader](https://mantine.dev/llms/core-cascader.md): Select a path from hierarchical data with cascading columns
- [Center](https://mantine.dev/llms/core-center.md): Centers content vertically and horizontally
- [Checkbox](https://mantine.dev/llms/core-checkbox.md): Capture boolean input from user
- [Chip](https://mantine.dev/llms/core-chip.md): Pick one or multiple values with inline controls
- [CloseButton](https://mantine.dev/llms/core-close-button.md): Button with close icon
- [Code](https://mantine.dev/llms/core-code.md): Inline and block code
- [Collapse](https://mantine.dev/llms/core-collapse.md): Animate presence with slide down/up transition
- [ColorInput](https://mantine.dev/llms/core-color-input.md): Capture color from user
- [ColorPicker](https://mantine.dev/llms/core-color-picker.md): Pick colors in hex(a), rgb(a), hsl(a) and hsv(a) formats
- [ColorSwatch](https://mantine.dev/llms/core-color-swatch.md): Displays color
- [Combobox](https://mantine.dev/llms/core-combobox.md): Create custom select, autocomplete or multiselect inputs
- [ComboboxPopover](https://mantine.dev/llms/core-combobox-popover.md): Add combobox dropdown to any button element
- [Container](https://mantine.dev/llms/core-container.md): Center content with padding and max-width
- [CopyButton](https://mantine.dev/llms/core-copy-button.md): Copies given text to clipboard
- [CorePackage](https://mantine.dev/llms/core-package.md)
- [DataList](https://mantine.dev/llms/core-data-list.md): Display label-value pairs as a description list
- [Dialog](https://mantine.dev/llms/core-dialog.md): Display a fixed overlay dialog at any side of the screen
- [Divider](https://mantine.dev/llms/core-divider.md): Horizontal line with optional label or vertical divider
- [Drawer](https://mantine.dev/llms/core-drawer.md): Display overlay area at any side of the screen
- [EmptyState](https://mantine.dev/llms/core-empty-state.md): Display a placeholder for no data, empty results and first-run states
- [Fieldset](https://mantine.dev/llms/core-fieldset.md): Group related elements in a form
- [FileButton](https://mantine.dev/llms/core-file-button.md): Open file picker with a button click
- [FileInput](https://mantine.dev/llms/core-file-input.md): Capture files from user
- [Flex](https://mantine.dev/llms/core-flex.md): Compose elements in a flex container
- [FloatingIndicator](https://mantine.dev/llms/core-floating-indicator.md): Display a floating indicator over a group of elements
- [FloatingWindow](https://mantine.dev/llms/core-floating-window.md): Draggable floating area
- [FocusTrap](https://mantine.dev/llms/core-focus-trap.md): Trap focus at child node
- [Grid](https://mantine.dev/llms/core-grid.md): Responsive 12 columns grid system
- [Group](https://mantine.dev/llms/core-group.md): Compose elements and components in a horizontal flex container
- [Highlight](https://mantine.dev/llms/core-highlight.md): Highlight given part of a string with mark
- [HoverCard](https://mantine.dev/llms/core-hover-card.md): Display popover section when target element is hovered
- [HueSlider](https://mantine.dev/llms/core-hue-slider.md): Slider component for selecting hue channel in color pickers (0 – 360 degrees)
- [Image](https://mantine.dev/llms/core-image.md): Image with optional fallback
- [Indicator](https://mantine.dev/llms/core-indicator.md): Display element at the corner of another element
- [Input](https://mantine.dev/llms/core-input.md): Base component to create custom inputs
- [JsonInput](https://mantine.dev/llms/core-json-input.md): Capture json data from user
- [Kbd](https://mantine.dev/llms/core-kbd.md): Display keyboard key
- [List](https://mantine.dev/llms/core-list.md): Display ordered or unordered list
- [Loader](https://mantine.dev/llms/core-loader.md): Indicate loading state
- [LoadingOverlay](https://mantine.dev/llms/core-loading-overlay.md): An overlay with centered loader
- [Mark](https://mantine.dev/llms/core-mark.md): Highlight part of the text
- [Marquee](https://mantine.dev/llms/core-marquee.md): Create continuous scrolling animation for content
- [MaskInput](https://mantine.dev/llms/core-mask-input.md): Input with mask pattern for formatted text entry
- [Menu](https://mantine.dev/llms/core-menu.md): Combine a list of secondary actions into single interactive area
- [Menubar](https://mantine.dev/llms/core-menubar.md): Desktop application style menubar with a row of menu triggers
- [Modal](https://mantine.dev/llms/core-modal.md): An accessible overlay dialog
- [MultiSelect](https://mantine.dev/llms/core-multi-select.md): Custom searchable multi select
- [NativeSelect](https://mantine.dev/llms/core-native-select.md): Native select element based on Input
- [NavLink](https://mantine.dev/llms/core-nav-link.md): Navigation link
- [Notification](https://mantine.dev/llms/core-notification.md): Show dynamic notifications and alerts to user, part of notifications system
- [NumberFormatter](https://mantine.dev/llms/core-number-formatter.md): Format number with thousands/decimal separators and suffix/prefix
- [NumberInput](https://mantine.dev/llms/core-number-input.md): Capture number from user
- [OverflowList](https://mantine.dev/llms/core-overflow-list.md): Hide items that do not fit in a container and display them as a single collapsed item
- [Overlay](https://mantine.dev/llms/core-overlay.md): Overlays parent element with div element with any color and opacity
- [Pagination](https://mantine.dev/llms/core-pagination.md): Display active page and navigate between multiple pages
- [Paper](https://mantine.dev/llms/core-paper.md): Renders white or dark background depending on color scheme
- [PasswordInput](https://mantine.dev/llms/core-password-input.md): Capture password data from user
- [Pill](https://mantine.dev/llms/core-pill.md): Removable and non-removable tags
- [PillsInput](https://mantine.dev/llms/core-pills-input.md): Base component for custom tags inputs and multi selects
- [PinInput](https://mantine.dev/llms/core-pin-input.md): Capture pin code or one time password from the user
- [Popover](https://mantine.dev/llms/core-popover.md): Display popover section relative to given target element
- [Portal](https://mantine.dev/llms/core-portal.md): Renders component outside of parent element tree
- [Progress](https://mantine.dev/llms/core-progress.md): Give user feedback for status of the task
- [Radio](https://mantine.dev/llms/core-radio.md): Wrapper for input type radio
- [RangeSlider](https://mantine.dev/llms/core-range-slider.md): RangeSlider component
- [Rating](https://mantine.dev/llms/core-rating.md): Pick and display rating
- [RingProgress](https://mantine.dev/llms/core-ring-progress.md): Give user feedback for status of the task with circle diagram
- [RollingNumber](https://mantine.dev/llms/core-rolling-number.md): Animate value changes with rolling digits
- [ScrollArea](https://mantine.dev/llms/core-scroll-area.md): Area with custom scrollbars
- [Scroller](https://mantine.dev/llms/core-scroller.md): Horizontal scroll container with navigation controls
- [SegmentedControl](https://mantine.dev/llms/core-segmented-control.md): A linear set of two or more segments
- [Select](https://mantine.dev/llms/core-select.md): Custom searchable select
- [SemiCircleProgress](https://mantine.dev/llms/core-semi-circle-progress.md): Represent progress with semi circle diagram
- [SimpleGrid](https://mantine.dev/llms/core-simple-grid.md): Responsive grid in which each item takes equal amount of space
- [Skeleton](https://mantine.dev/llms/core-skeleton.md): Indicate content loading state
- [Slider](https://mantine.dev/llms/core-slider.md): Slider component
- [Space](https://mantine.dev/llms/core-space.md): Add horizontal or vertical spacing from theme
- [Splitter](https://mantine.dev/llms/core-splitter.md): Resizable split pane layout
- [Spoiler](https://mantine.dev/llms/core-spoiler.md): Hide long sections of content under a spoiler
- [Stack](https://mantine.dev/llms/core-stack.md): Compose elements and components in a vertical flex container
- [Stepper](https://mantine.dev/llms/core-stepper.md): Display content divided into a steps sequence
- [Switch](https://mantine.dev/llms/core-switch.md): Capture boolean input from user
- [Table](https://mantine.dev/llms/core-table.md): Render table with theme styles
- [TableOfContents](https://mantine.dev/llms/core-table-of-contents.md): Renders a list of headings on the page and tracks current heading visible in the viewport
- [Tabs](https://mantine.dev/llms/core-tabs.md): Switch between different views
- [TagsInput](https://mantine.dev/llms/core-tags-input.md): Capture a list of values from user with free input and suggestions
- [Text](https://mantine.dev/llms/core-text.md): Display text
- [Textarea](https://mantine.dev/llms/core-textarea.md): Autosize or regular textarea
- [TextInput](https://mantine.dev/llms/core-text-input.md): Capture string input from user
- [ThemeIcon](https://mantine.dev/llms/core-theme-icon.md): Render icon inside element with theme colors
- [Timeline](https://mantine.dev/llms/core-timeline.md): Display list of events in chronological order
- [Title](https://mantine.dev/llms/core-title.md): h1-h6 heading
- [Tooltip](https://mantine.dev/llms/core-tooltip.md): Renders tooltip at given element on mouse over or other event
- [Transition](https://mantine.dev/llms/core-transition.md): Animate presence of component with pre-made animations
- [Tree](https://mantine.dev/llms/core-tree.md): Display a Tree structure
- [TreeSelect](https://mantine.dev/llms/core-tree-select.md): Select with hierarchical tree data
- [Typography](https://mantine.dev/llms/core-typography.md): Styles provider for html content
- [UnstyledButton](https://mantine.dev/llms/core-unstyled-button.md): Unstyled polymorphic button
- [VisuallyHidden](https://mantine.dev/llms/core-visually-hidden.md): Hide element visually but keep it accessible for screen readers
## Hooks
- [HooksPackage](https://mantine.dev/llms/hooks-package.md)
- [use-element-size](https://mantine.dev/llms/hooks-use-element-size.md): Returns element width and height and observes changes with ResizeObserver
- [useClickOutside](https://mantine.dev/llms/hooks-use-click-outside.md)
- [useClipboard](https://mantine.dev/llms/hooks-use-clipboard.md)
- [useCollapse](https://mantine.dev/llms/hooks-use-collapse.md)
- [useColorScheme](https://mantine.dev/llms/hooks-use-color-scheme.md)
- [useCounter](https://mantine.dev/llms/hooks-use-counter.md)
- [useDebouncedCallback](https://mantine.dev/llms/hooks-use-debounced-callback.md)
- [useDebouncedState](https://mantine.dev/llms/hooks-use-debounced-state.md)
- [useDebouncedValue](https://mantine.dev/llms/hooks-use-debounced-value.md)
- [useDidUpdate](https://mantine.dev/llms/hooks-use-did-update.md)
- [useDisclosure](https://mantine.dev/llms/hooks-use-disclosure.md)
- [useDocumentTitle](https://mantine.dev/llms/hooks-use-document-title.md)
- [useDocumentVisibility](https://mantine.dev/llms/hooks-use-document-visibility.md)
- [useDrag](https://mantine.dev/llms/hooks-use-drag.md)
- [useEventListener](https://mantine.dev/llms/hooks-use-event-listener.md)
- [useEyeDropper](https://mantine.dev/llms/hooks-use-eye-dropper.md)
- [useFavicon](https://mantine.dev/llms/hooks-use-favicon.md)
- [useFetch](https://mantine.dev/llms/hooks-use-fetch.md)
- [useFileDialog](https://mantine.dev/llms/hooks-use-file-dialog.md)
- [useFloatingWindow](https://mantine.dev/llms/hooks-use-floating-window.md)
- [useFocusReturn](https://mantine.dev/llms/hooks-use-focus-return.md)
- [useFocusTrap](https://mantine.dev/llms/hooks-use-focus-trap.md)
- [useFocusWithin](https://mantine.dev/llms/hooks-use-focus-within.md)
- [useForceUpdate](https://mantine.dev/llms/hooks-use-force-update.md)
- [useFullscreen](https://mantine.dev/llms/hooks-use-fullscreen.md)
- [useHash](https://mantine.dev/llms/hooks-use-hash.md)
- [useHeadroom](https://mantine.dev/llms/hooks-use-headroom.md)
- [useHotkeys](https://mantine.dev/llms/hooks-use-hotkeys.md)
- [useHover](https://mantine.dev/llms/hooks-use-hover.md)
- [useId](https://mantine.dev/llms/hooks-use-id.md)
- [useIdle](https://mantine.dev/llms/hooks-use-idle.md)
- [useInputState](https://mantine.dev/llms/hooks-use-input-state.md)
- [useIntersection](https://mantine.dev/llms/hooks-use-intersection.md)
- [useInterval](https://mantine.dev/llms/hooks-use-interval.md)
- [useInViewport](https://mantine.dev/llms/hooks-use-in-viewport.md)
- [useIsFirstRender](https://mantine.dev/llms/hooks-use-is-first-render.md)
- [useIsomorphicEffect](https://mantine.dev/llms/hooks-use-isomorphic-effect.md)
- [useListState](https://mantine.dev/llms/hooks-use-list-state.md)
- [useLocalStorage](https://mantine.dev/llms/hooks-use-local-storage.md)
- [useLogger](https://mantine.dev/llms/hooks-use-logger.md)
- [useLongPress](https://mantine.dev/llms/hooks-use-long-press.md)
- [useMap](https://mantine.dev/llms/hooks-use-map.md)
- [useMask](https://mantine.dev/llms/hooks-use-mask.md)
- [useMediaQuery](https://mantine.dev/llms/hooks-use-media-query.md)
- [useMergedRef](https://mantine.dev/llms/hooks-use-merged-ref.md)
- [useMounted](https://mantine.dev/llms/hooks-use-mounted.md)
- [useMouse](https://mantine.dev/llms/hooks-use-mouse.md)
- [useMove](https://mantine.dev/llms/hooks-use-move.md)
- [useMutationObserver](https://mantine.dev/llms/hooks-use-mutation-observer.md)
- [useNetwork](https://mantine.dev/llms/hooks-use-network.md)
- [useOrientation](https://mantine.dev/llms/hooks-use-orientation.md)
- [useOs](https://mantine.dev/llms/hooks-use-os.md)
- [usePageLeave](https://mantine.dev/llms/hooks-use-page-leave.md)
- [usePagination](https://mantine.dev/llms/hooks-use-pagination.md)
- [usePrevious](https://mantine.dev/llms/hooks-use-previous.md)
- [useQueue](https://mantine.dev/llms/hooks-use-queue.md)
- [useRadialMove](https://mantine.dev/llms/hooks-use-radial-move.md)
- [useReducedMotion](https://mantine.dev/llms/hooks-use-reduced-motion.md)
- [useResizeObserver](https://mantine.dev/llms/hooks-use-resize-observer.md)
- [useRovingIndex](https://mantine.dev/llms/hooks-use-roving-index.md)
- [useScrollDirection](https://mantine.dev/llms/hooks-use-scroll-direction.md)
- [useScroller](https://mantine.dev/llms/hooks-use-scroller.md)
- [useScrollIntoView](https://mantine.dev/llms/hooks-use-scroll-into-view.md)
- [useScrollSpy](https://mantine.dev/llms/hooks-use-scroll-spy.md)
- [useSelection](https://mantine.dev/llms/hooks-use-selection.md)
- [useSet](https://mantine.dev/llms/hooks-use-set.md)
- [useSetState](https://mantine.dev/llms/hooks-use-set-state.md)
- [useShallowEffect](https://mantine.dev/llms/hooks-use-shallow-effect.md)
- [useSplitter](https://mantine.dev/llms/hooks-use-splitter.md)
- [useStateHistory](https://mantine.dev/llms/hooks-use-state-history.md)
- [useTextSelection](https://mantine.dev/llms/hooks-use-text-selection.md)
- [useThrottledCallback](https://mantine.dev/llms/hooks-use-throttled-callback.md)
- [useThrottledState](https://mantine.dev/llms/hooks-use-throttled-state.md)
- [useThrottledValue](https://mantine.dev/llms/hooks-use-throttled-value.md)
- [useTimeout](https://mantine.dev/llms/hooks-use-timeout.md)
- [useToggle](https://mantine.dev/llms/hooks-use-toggle.md)
- [useUncontrolled](https://mantine.dev/llms/hooks-use-uncontrolled.md)
- [useValidatedState](https://mantine.dev/llms/hooks-use-validated-state.md)
- [useViewportSize](https://mantine.dev/llms/hooks-use-viewport-size.md)
- [useWindowEvent](https://mantine.dev/llms/hooks-use-window-event.md)
- [useWindowScroll](https://mantine.dev/llms/hooks-use-window-scroll.md)
## Form
- [Form context](https://mantine.dev/llms/form-create-form-context.md): Add context support to use-form with createFormContext
- [formActions](https://mantine.dev/llms/form-actions.md)
- [formAllInputs](https://mantine.dev/llms/form-all-inputs.md)
- [formErrors](https://mantine.dev/llms/form-errors.md)
- [formGetInputProps](https://mantine.dev/llms/form-get-input-props.md)
- [formNested](https://mantine.dev/llms/form-nested.md)
- [FormPackage](https://mantine.dev/llms/form-package.md)
- [formRecipes](https://mantine.dev/llms/form-recipes.md)
- [formSchemaValidation](https://mantine.dev/llms/form-schema-validation.md)
- [formStatus](https://mantine.dev/llms/form-status.md)
- [formUncontrolled](https://mantine.dev/llms/form-uncontrolled.md)
- [formValidation](https://mantine.dev/llms/form-validation.md)
- [formValidators](https://mantine.dev/llms/form-validators.md)
- [formValues](https://mantine.dev/llms/form-values.md)
- [use-field](https://mantine.dev/llms/form-use-field.md): use-field hook – manage single field state
- [use-form](https://mantine.dev/llms/form-use-form.md): Manage form state
## Dates
- [Calendar](https://mantine.dev/llms/dates-calendar.md): Base component for custom date pickers
- [DateInput](https://mantine.dev/llms/dates-date-input.md): Free form date input
- [DatePicker](https://mantine.dev/llms/dates-date-picker.md): Inline date, multiple dates and dates range picker
- [DatePickerInput](https://mantine.dev/llms/dates-date-picker-input.md): Date, multiple dates and dates range picker input
- [DateTimePicker](https://mantine.dev/llms/dates-date-time-picker.md): Capture datetime from the user
- [GettingStartedDates](https://mantine.dev/llms/dates-getting-started.md)
- [InlineDateTimePicker](https://mantine.dev/llms/dates-inline-date-time-picker.md): Inline date and time picker with range support
- [MiniCalendar](https://mantine.dev/llms/dates-mini-calendar.md): Compact calendar to display a small number of days in a row
- [MonthPicker](https://mantine.dev/llms/dates-month-picker.md): Inline month, multiple months and months range picker
- [MonthPickerInput](https://mantine.dev/llms/dates-month-picker-input.md): Month, multiple months and months range picker input
- [TimeGrid](https://mantine.dev/llms/dates-time-grid.md): Captures time value from the user with a predefined set of options
- [TimeInput](https://mantine.dev/llms/dates-time-input.md): Capture time from the user
- [TimePicker](https://mantine.dev/llms/dates-time-picker.md): Captures time value from the user
- [TimeValue](https://mantine.dev/llms/dates-time-value.md): Display a formatted time value
- [YearPicker](https://mantine.dev/llms/dates-year-picker.md): Inline year, multiple years and years range picker
- [YearPickerInput](https://mantine.dev/llms/dates-year-picker-input.md): Inline year, multiple years and years range picker
## Charts
- [AreaChart](https://mantine.dev/llms/charts-area-chart.md): Area chart component with stacked, percent and split variants
- [BarChart](https://mantine.dev/llms/charts-bar-chart.md): Bar chart component with stacked and percent variants
- [BarsList](https://mantine.dev/llms/charts-bars-list.md): Display a list of bars with names and values
- [BubbleChart](https://mantine.dev/llms/charts-bubble-chart.md): Bubble chart component
- [BulletChart](https://mantine.dev/llms/charts-bullet-chart.md): Compact KPI chart with value, target, and qualitative ranges
- [CompositeChart](https://mantine.dev/llms/charts-composite-chart.md): Composed chart with support for Area, Bar and Line charts
- [DonutChart](https://mantine.dev/llms/charts-donut-chart.md): Donut chart component
- [FunnelChart](https://mantine.dev/llms/charts-funnel-chart.md): Funnel chart component
- [GettingStartedCharts](https://mantine.dev/llms/charts-getting-started.md)
- [Heatmap](https://mantine.dev/llms/charts-heatmap.md): Heatmap chart component
- [LineChart](https://mantine.dev/llms/charts-line-chart.md): Line chart component
- [PieChart](https://mantine.dev/llms/charts-pie-chart.md): Pie chart component
- [RadarChart](https://mantine.dev/llms/charts-radar-chart.md): Radar chart component
- [RadialBarChart](https://mantine.dev/llms/charts-radial-bar-chart.md): Radial bar chart component
- [SankeyChart](https://mantine.dev/llms/charts-sankey-chart.md): Sankey diagram component
- [ScatterChart](https://mantine.dev/llms/charts-scatter-chart.md): Scatter chart component
- [Sparkline](https://mantine.dev/llms/charts-sparkline.md): Simplified area chart to show trends
- [SunburstChart](https://mantine.dev/llms/charts-sunburst-chart.md): Sunburst chart component for hierarchical data
- [Treemap](https://mantine.dev/llms/charts-treemap.md): Treemap chart component
## Schedule
- [AgendaView](https://mantine.dev/llms/schedule-agenda-view.md): Vertical list view of events grouped by date
- [DayView](https://mantine.dev/llms/schedule-day-view.md): Standalone schedule day view component
- [GettingStartedSchedule](https://mantine.dev/llms/schedule-getting-started.md)
- [MobileMonthView](https://mantine.dev/llms/schedule-mobile-month-view.md): Mobile-optimized schedule month view component
- [MonthView](https://mantine.dev/llms/schedule-month-view.md): Standalone schedule month view component
- [RecurringEventsSchedule](https://mantine.dev/llms/schedule-recurring-events.md)
- [ResourcesDayView](https://mantine.dev/llms/schedule-resources-day-view.md): Day view with resource columns for scheduling across resources
- [ResourcesMonthView](https://mantine.dev/llms/schedule-resources-month-view.md): Month view with resource lanes for scheduling across resources
- [ResourcesSchedule](https://mantine.dev/llms/schedule-resources-schedule.md): Schedule wrapper component that combines resource day, week and month views
- [ResourcesWeekView](https://mantine.dev/llms/schedule-resources-week-view.md): Week view with resource rows for scheduling across resources
- [Schedule](https://mantine.dev/llms/schedule-schedule.md): Schedule component with day, week, month and year views
- [ScheduleEventsData](https://mantine.dev/llms/schedule-events-data.md)
- [WeekView](https://mantine.dev/llms/schedule-week-view.md): Standalone schedule week view component
- [YearView](https://mantine.dev/llms/schedule-year-view.md): Standalone schedule year view component
## Guides
- [ControlledVsUncontrolled](https://mantine.dev/llms/guides-controlled-vs-uncontrolled.md)
- [CustomComponents](https://mantine.dev/llms/guides-custom-components.md)
- [EightToNine](https://mantine.dev/llms/guides-8x-to-9x.md)
- [FunctionsReference](https://mantine.dev/llms/guides-functions-reference.md)
- [Gatsby](https://mantine.dev/llms/guides-gatsby.md)
- [Icons](https://mantine.dev/llms/guides-icons.md)
- [JavaScript](https://mantine.dev/llms/guides-javascript.md)
- [Jest](https://mantine.dev/llms/guides-jest.md)
- [LLMDocumentation](https://mantine.dev/llms/guides-llms.md)
- [NextJs](https://mantine.dev/llms/guides-next.md)
- [Polymorphic](https://mantine.dev/llms/guides-polymorphic.md)
- [ReactRouter](https://mantine.dev/llms/guides-react-router.md)
- [Redwood](https://mantine.dev/llms/guides-redwood.md)
- [SevenToEight](https://mantine.dev/llms/guides-7x-to-8x.md)
- [SixToSeven](https://mantine.dev/llms/guides-6x-to-7x.md)
- [Storybook](https://mantine.dev/llms/guides-storybook.md)
- [Tiptap3Migration](https://mantine.dev/llms/guides-tiptap-3-migration.md)
- [TypeScript](https://mantine.dev/llms/guides-typescript.md)
- [Vite](https://mantine.dev/llms/guides-vite.md)
- [Vitest](https://mantine.dev/llms/guides-vitest.md)
## Theming
- [Colors](https://mantine.dev/llms/theming-colors.md)
- [ColorSchemes](https://mantine.dev/llms/theming-color-schemes.md)
- [DefaultProps](https://mantine.dev/llms/theming-default-props.md)
- [MantineProvider](https://mantine.dev/llms/theming-mantine-provider.md)
- [ThemeObject](https://mantine.dev/llms/theming-theme-object.md)
- [Typography](https://mantine.dev/llms/theming-typography.md): Styles provider for html content
## Styles
- [ColorFunctions](https://mantine.dev/llms/styles-color-functions.md)
- [CSSFilesList](https://mantine.dev/llms/styles-css-files-list.md)
- [CSSModules](https://mantine.dev/llms/styles-css-modules.md)
- [CssVariables](https://mantine.dev/llms/styles-css-variables.md)
- [CSSVariablesList](https://mantine.dev/llms/styles-css-variables-list.md)
- [DataAttributes](https://mantine.dev/llms/styles-data-attributes.md)
- [Emotion](https://mantine.dev/llms/styles-emotion.md)
- [GlobalStyles](https://mantine.dev/llms/styles-global-styles.md)
- [MantineStyles](https://mantine.dev/llms/styles-mantine-styles.md)
- [PostCSSPreset](https://mantine.dev/llms/styles-postcss-preset.md)
- [Rem](https://mantine.dev/llms/styles-rem.md)
- [ResponsiveStyles](https://mantine.dev/llms/styles-responsive.md)
- [Rtl](https://mantine.dev/llms/styles-rtl.md)
- [StyleProp](https://mantine.dev/llms/styles-style.md)
- [StyleProps](https://mantine.dev/llms/styles-style-props.md)
- [StylesApi](https://mantine.dev/llms/styles-styles-api.md)
- [StylesOverview](https://mantine.dev/llms/styles-styles-overview.md)
- [StylesPerformance](https://mantine.dev/llms/styles-styles-performance.md)
- [UnstyledComponents](https://mantine.dev/llms/styles-unstyled.md)
- [UsageWithSass](https://mantine.dev/llms/styles-sass.md)
- [VanillaExtract](https://mantine.dev/llms/styles-vanilla-extract.md)
- [VariantsAndSizes](https://mantine.dev/llms/styles-variants-sizes.md)
## X
- [Carousel](https://mantine.dev/llms/x-carousel.md): Embla based carousel component
- [CodeHighlight](https://mantine.dev/llms/x-code-highlight.md): Highlight code with shiki or highlight.js
- [Dropzone](https://mantine.dev/llms/x-dropzone.md): Capture files from user with drag and drop
- [ExtensionsPackage](https://mantine.dev/llms/x-extensions.md)
- [Modals manager](https://mantine.dev/llms/x-modals.md): Centralized modals manager with option to handle state of multi-step modals
- [NavigationProgress](https://mantine.dev/llms/x-nprogress.md): Navigation progress bar
- [Notifications system](https://mantine.dev/llms/x-notifications.md): Mantine notifications system
- [Rich text editor](https://mantine.dev/llms/x-tiptap.md): Tiptap based rich text editor
- [Spotlight](https://mantine.dev/llms/x-spotlight.md): Command center for your application
## Other
- [About](https://mantine.dev/llms/about.md)
- [AllReleases](https://mantine.dev/llms/changelog-all-releases.md)
- [BrowserSupport](https://mantine.dev/llms/browser-support.md)
- [Changelog700](https://mantine.dev/llms/changelog-7-0-0.md)
- [Changelog710](https://mantine.dev/llms/changelog-7-1-0.md)
- [Changelog7100](https://mantine.dev/llms/changelog-7-10-0.md)
- [Changelog7110](https://mantine.dev/llms/changelog-7-11-0.md)
- [Changelog7120](https://mantine.dev/llms/changelog-7-12-0.md)
- [Changelog7130](https://mantine.dev/llms/changelog-7-13-0.md)
- [Changelog7140](https://mantine.dev/llms/changelog-7-14-0.md)
- [Changelog7150](https://mantine.dev/llms/changelog-7-15-0.md)
- [Changelog7160](https://mantine.dev/llms/changelog-7-16-0.md)
- [Changelog7170](https://mantine.dev/llms/changelog-7-17-0.md)
- [Changelog720](https://mantine.dev/llms/changelog-7-2-0.md)
- [Changelog730](https://mantine.dev/llms/changelog-7-3-0.md)
- [Changelog740](https://mantine.dev/llms/changelog-7-4-0.md)
- [Changelog750](https://mantine.dev/llms/changelog-7-5-0.md)
- [Changelog760](https://mantine.dev/llms/changelog-7-6-0.md)
- [Changelog770](https://mantine.dev/llms/changelog-7-7-0.md)
- [Changelog780](https://mantine.dev/llms/changelog-7-8-0.md)
- [Changelog790](https://mantine.dev/llms/changelog-7-9-0.md)
- [Changelog800](https://mantine.dev/llms/changelog-8-0-0.md)
- [Changelog810](https://mantine.dev/llms/changelog-8-1-0.md)
- [Changelog820](https://mantine.dev/llms/changelog-8-2-0.md)
- [Changelog830](https://mantine.dev/llms/changelog-8-3-0.md)
- [Changelog900](https://mantine.dev/llms/changelog-9-0-0.md)
- [Changelog910](https://mantine.dev/llms/changelog-9-1-0.md)
- [Changelog920](https://mantine.dev/llms/changelog-9-2-0.md)
- [Changelog930](https://mantine.dev/llms/changelog-9-3-0.md)
- [Changelog940](https://mantine.dev/llms/changelog-9-4-0.md)
- [Changelog950](https://mantine.dev/llms/changelog-9-5-0.md)
- [Contribute](https://mantine.dev/llms/contribute.md)
- [GettingStarted](https://mantine.dev/llms/getting-started.md)
- [NotFound](https://mantine.dev/llms/404.md)
- [OxcConfig](https://mantine.dev/llms/oxc-config-mantine.md)
- [PreviousChangelogs](https://mantine.dev/llms/changelog-previous-versions.md)
- [Support](https://mantine.dev/llms/support.md)
-33
View File
@@ -1,33 +0,0 @@
# Product
## Register
product
## Users
Homarr is used by self-hosters and administrators who assemble dashboards for services running across home labs and private infrastructure. They need dense, reliable controls and status at a glance, with 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 a user can build a useful dashboard quickly, understand its state, recover from configuration errors, and extend it without requiring a bespoke Homarr feature for every service.
## Brand Personality
Direct, capable, and approachable. Copy should be concise and technical when precision matters, while the interface should remain familiar to users who are not developers.
## Anti-references
Avoid decorative admin dashboards, wizard flows that fight ordinary scrolling, hidden configuration magic, inconsistent controls, excessive modal usage, and interfaces that expose implementation complexity before the user needs it.
## Design Principles
1. Keep the user in control by making configuration and consequences visible.
2. Prefer familiar product patterns and progressive disclosure over custom interaction concepts.
3. Make simple tasks short without limiting advanced use cases.
4. Isolate failures so one service or widget cannot disrupt the dashboard.
5. Use the existing Mantine design language consistently across management and board surfaces.
## Accessibility & Inclusion
Target WCAG 2.2 AA. Support keyboard navigation, screen readers, visible focus, sufficient contrast, reduced motion, non-color status cues, responsive layouts, and actionable error messages.
@@ -13,8 +13,6 @@ 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,12 +10,11 @@ If you are still on a version before 0.15.10, please update to 0.15.10 where we
<!-- truncate -->
## Export your data
## Preserve your old installation
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.
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.
![Export page](./img/old-homarr-export-data.png)
@@ -28,28 +27,15 @@ 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`.
![Onboarding initial step](./img/onboarding-step-1.png)
:::warning Legacy guide
After that you can upload the zip file you downloaded in the first step.
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.
![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.
+1 -1
View File
@@ -1,7 +1,7 @@
{
"label": "Advanced",
"collapsed": true,
"position": 4,
"position": 5,
"link": {
"type": "generated-index",
"title": "Advanced Concepts or features"
@@ -1,41 +1,175 @@
# Developer Docker CLI
The host-side developer CLI builds and runs local Homarr images and pull-request previews. Run it directly from a checkout with `pnpm dev:cli -- ...`; no shell-specific setup script is required. It is separate from the recovery CLI included in production images.
The host-side developer CLI builds and runs local Homarr images and pull-request previews with non-blocking background operations. Run it directly from a checkout with `pnpm dev:cli -- ...`; no shell-specific setup script is required. It is separate from the recovery CLI included in production images.
To install an optional `homarr` binary, run `pnpm dev:cli:install` and make your Go bin directory available on `PATH`. GitHub CLI authentication is only needed for pull-request features.
To install an optional `homarr` binary, run `pnpm dev:cli:install` and make your Go bin directory available on `PATH`. GitHub CLI authentication (`gh auth login`) is only needed for pull-request features.
## Browse development images
## Requirements
- Docker or OrbStack
- Go 1.25+ (when building the CLI from source)
- GitHub CLI authenticated with `gh auth login` (for pull-request and CI features)
- A [Nerd Font](https://www.nerdfonts.com/) enabled in your terminal (recommended for icons, status indicators, and TUI glyphs)
## Interactive Development Browser
```bash
pnpm dev:cli --
# or with global binary:
homarr
```
The main screen combines local `homarr:*` images with open pull requests and their GHCR preview status. Select an image to start or stop it, use `R` to build a remote pull request locally in the background or rebuild a local image, or use `p` to force a remote preview pull. Press `d` to switch to the instance dashboard and `d` again to return without losing the browser state. The explicit `dev` and `dash` subcommands start on either screen.
The main browser combines local `homarr:*` images with open pull requests and their GHCR preview status. Select an image to start or stop it, use `R` to build a remote pull request locally in the background or rebuild a local image, or use `p` to force a remote preview pull. Press `d` (or `1`/`2`) to toggle between the image browser and the instance dashboard.
A locally built PR image is folded into its remote PR row only when its recorded revision exactly matches the current GitHub head SHA. The row is marked `◆ local`, and starting it prefers that local image without a registry pull. Outdated local builds remain separate rows. Navigation and dashboard switching remain available during background builds.
A locally built PR image is folded into its remote PR row only when its recorded revision matches the current GitHub head SHA (marked `◆ local`). When the matching GHCR image is also available, pressing `Enter` opens a local/remote selector with local preselected. Outdated local builds remain separate rows.
## Build images
### Multi-Source Sidebar
Press `Tab` to cycle between sidebar tabs:
- **`[logs]`**: Live container output streams (press `f` to follow/pause, `L` to pin to the selected container, `Page Up`/`Page Down` to scroll).
- **`[build]`**: Real-time BuildKit step streaming for active local builds.
- **`[ci]`**: GitHub Actions CI check statuses, run duration, and workflow links for pull requests.
### Background Task Overlay
Pulls and builds run asynchronously in the background. Press `t` to open the tasks overlay, showing:
- Layer-by-layer download progress and transfer speeds (via Docker Engine API).
- Active build stages and logs.
- Task cancellation and status.
### Browser Shortcuts
| Key | Action |
| ---------------------- | --------------------------------------------------------------------------------------------- |
| `/` | Filter rows by PR number, title, author, branch, CI, image, state, or port |
| `↑` / `↓` or `j` / `k` | Navigate rows |
| `Enter` or `Space` | Start the available image; choose local or remote when both exist; stop if running |
| `R` | Build locally in background (or rebuild local image) |
| `p` | Pull remote PR image in background and start container |
| `m` | Manage mode (`b` rebuild, `i` delete image, `d` delete data, `c` remove container, `p` prune) |
| `t` | Toggle background tasks overlay |
| `Tab` | Cycle sidebar tabs (`[logs]`, `[build]`, `[ci]`) |
| `l` / `L` | Toggle sidebar visibility / Pin logs to selected instance |
| `f` | Follow or pause logs |
| `M` | Toggle demo mode for next launch |
| `b` | Toggle bot pull requests |
| `o` / `a` | Open PR on GitHub / Open running instance in browser |
| `c` | Copy instance URL to clipboard |
| `r` | Refresh data |
| `1` / `2` / `d` | Switch screen (development browser / instance dashboard) |
| `?` / `q` | Help overlay / Quit (cancels active background tasks) |
## Instance Dashboard
```bash
pnpm dev:cli -- build <name>
pnpm dev:cli -- build --pr <number> [name]
homarr dash
# or
pnpm dev:cli -- dash
```
The first command builds the current checkout as `homarr:<name>`. The pull-request form uses a depth-one clone in a temporary directory and defaults to `homarr:pr-<number>` when `name` is omitted.
The instance dashboard displays all running and stopped Homarr containers, published ports, health states, and resource volumes.
## Rebuild images
| Key | Action |
| ------------------ | ---------------------------------------- |
| `Enter` or `Space` | Start or stop selected instance |
| `s` / `S` | Stop / Restart instance |
| `x` | Remove container (preserves data volume) |
| `m` | Manage mode for selected instance |
| `R` | Rebuild from recorded build provenance |
## CLI Commands
The commands below use the installed `homarr` binary. Without global installation, prefix each command with `pnpm dev:cli --` (for example, `pnpm dev:cli -- list`).
### Launch Instances
```bash
pnpm dev:cli -- rebuild <name>
homarr run dev
homarr run --detach dev
homarr run --pr 6441
homarr run --pr 6441 --demo
homarr --pr 6441 --env WORKSHOP_WEB_URL=https://app-v2.preview.homarr.dev/
```
Images built by this CLI record their source checkout, revision, and optional pull-request number. Rebuild uses that provenance to select the original checkout or create a fresh temporary pull-request checkout.
- `--pr <number>`: Pull and run a PR image from GHCR (shorthand: `homarr --pr <number>`).
- `--demo` (`-m`): Start with mock integrations and sample data.
- `--detach`: Start container in background.
- `--env` (`-e`): Pass additional environment variables (repeatable: `--env KEY=VALUE`).
## Run images
### Build & Rebuild
```bash
homarr run <name>
homarr run --pr <number>
# Build current checkout as homarr:<name>
homarr build feature
# Build a pull request locally in a temporary checkout
homarr build --pr 6441
# Rebuild an existing image from recorded provenance
homarr rebuild feature
```
Add `--detach` to run in the background, `--demo` to enable demo data, or repeat `--env KEY=VALUE` to provide additional environment variables.
Images built with the CLI record their source checkout, revision SHA, and optional PR number. `homarr rebuild` reads this provenance to reconstruct the build without manual configuration.
### GitHub CI Checks
```bash
# View CI checks for a specific PR
homarr ci 6441
# View CI checks for the current branch
homarr ci
# Watch CI checks until completion
homarr ci 6441 --watch
```
### Interactive Shell
```bash
# Open interactive shell in running container
homarr shell homarr-dev
# Run specific command in container
homarr shell homarr-dev pnpm db:migration:sqlite:run
```
### Image & Volume Management
```bash
# List local images with build provenance (source, revision, PR)
homarr images
# Delete local image
homarr images rm feature
# List data volumes with owner PR/tag and in-use status
homarr data
# Delete data volume (safely stops and removes owning container first)
homarr data rm homarr_feature_data
# Remove stopped containers (prompts without --force)
homarr prune --force
```
### Instance Operations & Diagnostics
```bash
# List instances in a script-friendly table
homarr list
# Follow container logs
homarr logs homarr-dev
# Stop, restart, remove, or open container in browser
homarr stop homarr-dev
homarr restart homarr-dev
homarr remove homarr-dev
homarr open homarr-dev
# Verify Docker daemon, Engine API, GHCR access, and gh CLI authentication
homarr doctor
```
@@ -25,3 +25,27 @@ You should also create a backup of the database, in case you break something.
4. Click "Execute" in the top right of the Window. ![](img/334116411-df1482f9-3aa9-4686-b77f-e0dca09e60b2.png)
5. Select /bin/bash and click Execute ![](img/334116518-e1ddb515-99c7-4159-81bf-76e889d1e808.png)
6. Run the Homarr command and press enter (eg. homarr reset-owner-password) ![](img/334116642-08c4f204-5cd0-4e4e-a785-0142e1b279ca.png)
## Build a local branch harness
The host-side operations CLI can build the current checkout and run it as a named, browser-ready Docker harness. This is separate from the recovery CLI described above and does not open the interactive developer TUI.
```sh
pnpm cli harness setup feature-board
```
The command builds `homarr:feature-board`, starts a container with a persistent data volume, enables demo mode, and keeps demo mutations writable (`DEMO_READ_ONLY=false`). Docker assigns a free host port by default; use the URL printed by the command or query it later:
```sh
pnpm cli harness port feature-board
```
Pass environment overrides by repeating `--env`:
```sh
pnpm cli harness setup feature-board \
--env WORKSHOP_WEB_URL=https://example.test \
--env FEATURE_FLAG=true
```
Other commands are `harness build <name>`, `harness run <name>`, and `harness stop <name>`. Use the returned URL with a browser automation harness for feature validation; do not assume the host port is `7575`.
@@ -57,50 +57,8 @@ Homarr becomes the instance administrator.
Use `pnpm docker:dev:up` instead when you only need Redis in the background. Running the migration also seeds the
default data; use `pnpm db:seed` to seed it explicitly later.
### Run PocketBase and documentation
Start PocketBase in another terminal:
```sh
cp apps/workshop/.env.example apps/workshop/.env
docker compose --env-file apps/workshop/.env -f apps/workshop/docker-compose.yml up --build workshop
```
Then start Docusaurus in a third terminal:
```sh
WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
```
PocketBase runs on port 8090 and Docusaurus on port 3003. Stopping the services preserves the PocketBase volume.
Open `http://127.0.0.1:8090/_/` and create the first PocketBase superuser. The Workshop UI is available at
`http://127.0.0.1:3003/workshop`.
Point Homarr at the local services with:
```dotenv
HOMARR_WEBSITE_URL=http://127.0.0.1:3003
WORKSHOP_API_URL=http://127.0.0.1:8090
WORKSHOP_WEB_URL=http://127.0.0.1:3003/workshop
```
Restart `pnpm dev` after changing `.env`. GitHub OAuth is optional locally. See
[`apps/workshop/README.md`](https://github.com/homarr-labs/homarr/blob/HEAD/apps/workshop/README.md) for OAuth,
Workshop moderator promotion, logs, tests, backup, restore, stopping, and intentional volume reset.
### URL testing
Docusaurus reads `HOMARR_WEBSITE_URL` and `WORKSHOP_API_URL` when it starts or builds. Homarr also reads
`WORKSHOP_WEB_URL` when its server starts, so these values do not rely on `NEXT_PUBLIC_*` build variables. Test a
separate API origin before release:
```sh
HOMARR_WEBSITE_URL=http://127.0.0.1:3003 \
WORKSHOP_API_URL=http://127.0.0.1:8090 \
WORKSHOP_WEB_URL=http://127.0.0.1:3003/workshop \
pnpm dev:docs
```
Workshop contributors can set up PocketBase and Docusaurus with the dedicated
[Workshop development guide](../../workshop/development).
### Useful commands
@@ -112,7 +70,6 @@ pnpm dev:docs
- `pnpm dev:cli -- rebuild <name>` — rebuild an image from its recorded checkout or pull request.
- `pnpm dev:cli:install` — optionally install the developer CLI as a `homarr` binary.
- `pnpm dev:docs` — Docusaurus only on port 3003.
- `pnpm docker:workshop` — local PocketBase through Docker Compose.
- `pnpm docker:dev:up` — Redis development service in the background.
- `pnpm cli` — Homarr operations CLI.
- `pnpm db:migration:sqlite:run` — create or update a SQLite database.
@@ -122,7 +79,6 @@ pnpm dev:docs
- `pnpm lint` / `pnpm lint:fix` — check or apply oxlint.
- `pnpm typecheck` — typecheck all workspaces.
- `pnpm test` — unit and contract suites.
- `pnpm test:workshop` — disposable Workshop integration suite.
- `pnpm build` — production monorepo build.
- `docker build -t homarr .` — build a production Docker image.
- `docker run -p 7575:7575 -e SECRET_ENCRYPTION_KEY='your_64_character_hex_string' homarr:<name>` — run it.
@@ -35,7 +35,10 @@ 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 page opened from Homarr. | HTTP(S) URL | `HOMARR_WEBSITE_URL/workshop` |
| `WORKSHOP_WEB_URL` | Public Workshop website used by links in Homarr, Custom Widgets, onboarding, and assistant context. | HTTP(S) URL | `HOMARR_WEBSITE_URL/workshop` |
Workshop service operators can find its server-only configuration in the
[Workshop operator guide](../../workshop/operator#environment-variables).
## Authentication
@@ -55,11 +58,12 @@ A random secret can be generated by using the following command: `openssl rand -
## Docker
| 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` | - |
| 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 | - |
## Database
@@ -109,12 +113,9 @@ External redis is currently not supported with Proxmox Community Scripts
The advanced deployments environment variables should only be used if you know what you are doing.
:::
| Environment Variable | Description |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DB_MIGRATIONS_DISABLED` | Disable db migrations. For example for helm charts. |
| `WORKSHOP_PUBLIC_ORIGIN` | Origin used in PocketBase-generated links and email. It must be set explicitly in production and must match the externally reachable Workshop origin. |
| `PB_ALLOWED_ORIGINS` | Browser origins accepted by PocketBase. Use `*` for the central public community so arbitrary self-hosted Homarr instances can use the API and OAuth; private staging may use a comma-separated origin allowlist. |
| Environment Variable | Description |
| ------------------------ | --------------------------------------------------- |
| `DB_MIGRATIONS_DISABLED` | Disable db migrations. For example for helm charts. |
All public URL variables reject non-HTTP protocols, embedded credentials, query strings, and fragments, and normalize
trailing slashes when Homarr or Docusaurus starts. The Docusaurus-only `WORKSHOP_URL` name is accepted as a deprecated alias for
`WORKSHOP_API_URL` for one release. `WORKSHOP_API_URL` takes precedence.
trailing slashes when Homarr or Docusaurus starts.
@@ -6,11 +6,51 @@ 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 |
| Shortcut | Description |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `MOD` + `K` | Opens the search bar |
| `MOD` + `J` | Toggles Dark Mode |
| `MOD` + `E` | Toggles Edit Mode |
| Hold `Shift` for 500 ms over a supported widget | Opens its temporary advanced view |
| Hold `Shift` + `Ctrl`/`Cmd` over a supported widget | Keeps its advanced view open until it is closed |
| `Esc` | Stops keyboard tile editing, cancels an active pointer or touch move/resize, or closes an open advanced view or board menu |
## Widget advanced view
Supported widgets open a temporary advanced view when you hold `Shift` over them for 500 ms. Hold `Shift` plus `Ctrl`
on Windows/Linux or `Cmd` on macOS over the widget to keep the advanced view open, or use its menu.
## 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.
:::tip
Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 38 KiB

+2 -2
View File
@@ -85,8 +85,8 @@ To do this, enter the board settings:
![](./img/enter-board-settings.png)
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.
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.
![](./img/custom-css-setting.png)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"label": "Community",
"position": 5,
"position": 6,
"collapsed": true,
"link": {
"type": "generated-index"
@@ -6,18 +6,16 @@ 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 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";
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";
:::tip
@@ -29,116 +27,50 @@ Congratulations on installing Homarr. Now it's time to get yourself familiar wit
## Onboarding
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.
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.
:::note
If you see this screen repeatitly after you restarted the Homarr container, your Docker mounting paths may be missing,
If you see this screen repeatedly after restarting 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.
:::
You can either import a previous dashboard using the ZIP archive or start from scratch:
### Restore or start fresh
![Screenshot of Step 1 onboarding](./img/onboarding-step-1.png)
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.
You may refresh anytime and continue from a different device - the onboarding is saved immediately on the server for your convenience.
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.
### Starting from scratch
### Setup studio
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.
The studio keeps related choices together:
<img src={imageStartFromScratchStepAdminUser} alt={"Screenshot form to create admin user"} width={300} />
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.
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.
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.
<img
src={imageStartFromScratchStepExternalAdminGroup}
alt={"Screenshot form to create external admin group"}
width={300}
/>
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.
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
## Creating another board
In Homarr, you organize your apps and widgets on boards.
Boards can be organized using the interactive drag and drop.
Let's create your first board by navigating here: `Manage` > `Boards` > `New board`:
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`:
![](./img/manage-create-board-button.png)
A board must always have a name and can either be public or private.
@@ -146,9 +78,25 @@ 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 your first board. You can see boards, that you have access to, on this page:
Congratulations - you've just created another board. You can see the boards you have access to on this page:
![](./img/manage-view-board.png)
<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.
## Add your first app to Homarr
@@ -172,32 +120,63 @@ 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:
![](./img/manage-view-board.png)
<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>
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 mode**: Clicking this button will toggle the edit mode. In this mode you can edit the contents of your board.
- **Edit board**: Clicking this button will toggle edit mode. In this mode you can edit the contents of your board.
- **Settings**: Clicking this button will redirect you to a settings page where you can adjust the board settings (page title, colors, ...).
- **Profile**: This avatar will be visible on every page. Clicking it will show a menu with actions.
- **Profile**: This avatar will be visible on every page. Its menu includes the board switcher; press `Shift+C` to open
it directly. The current board is left out of the results.
When entering the edit mode, you'll get some additional buttons to work with:
<img src={manageBoardHeaderEditMode} alt={"Screenshot of buttons in the edit mode"} width={200} />
<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>
- **Add item**: Click this button to add new elements to your page.
- **Exit edit mode**: Click this btuton to save your changes and exit edit mode.
- **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.
To continue, click the option "New Item":
Outside edit mode, right-click a widget for its live status, refresh, settings, quick options, and supported actions.
Hold `Shift` while hovering a widget that supports advanced view to expand it in place while the rest of the board
stays visible but dimmed. Release `Shift` to close it. Hold `Shift` plus `Ctrl` on Windows/Linux or `Cmd` on macOS to
keep its advanced view open until you close it. Widgets without a distinct advanced experience adapt in place.
<img src={manageBoardHeaderAddItem} alt={"Screenshot of the add item menu"} width={200} />
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:
<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>
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.
<img src={manageBoardHeaderChooseItem} alt={"Screenshot of choosing the app item in the modal"} width={300} />
<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>
As soon as you chose the app, the modal will close and you'll get the following item on your board:
@@ -206,28 +185,57 @@ 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.
![](./img/manage-board-edit-app.gif)
<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>
In the modal you can edit the app that you want to display:
![](./img/manage-board-choose-app.png)
<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>
As soon as you save your changes, the tile on the board will refresh and display your app.
Using the small bidirectional arrow at the bottom right you can resize the element.
Start pressing with your mouse button down, move your mouse to resize and release your finger as soon your you're happy with the size.
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.
![](./img/resize-item.gif)
To move a tile in edit mode, press and hold its inert content area, 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.
To move your item to a different position on the board, simply hold the mouse button anywhere on the item and move with the mouse to the desired position.
As soon as you release your finger, the item will snap in place in the grid.
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 does not squeeze or reflow the content of individual widgets, so a widget keeps the same design and proportions across screen sizes.
![](./img/move-item.gif)
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
You cannot move items freely on the vertical axis.
We always align all items to the top of the page to simply organization.
To move items to the bottom, you must add more items or resize items.
Viewing a board initially uses the lightweight, non-interactive layout.
For users with edit access, Homarr prepares drag-and-drop and resize controls in the background after the board becomes interactive.
If you enter edit mode before that finishes, the existing board stays visible until the editor is ready.
:::
@@ -238,14 +246,17 @@ This helps you to explore the capabilities of Homarr before you build your board
:::
Users on touch or mobile devices may not be able to drag and drop or move items.
They can use the "Move / resize item" option in the edit menu of items instead:
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.
![](./img/manage-board-move-resize-item-menu.png)
<img src={currentKeyboardTileEditing} alt="A board tile focused for keyboard editing" style={{ maxWidth: "100%" }} />
Be sure to save your changes by exiting edit mode!
![](./img/exit-edit-mode.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.
## Adding your first integration
@@ -280,7 +291,14 @@ 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:
![](./img/manage-board-header-add-item-menu.png)
<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>
Instead of choosing an app, let's choose `System Health Monitoring` this time.
Click on "Add to board":
@@ -307,47 +325,38 @@ Editing a widget will trigger a data update and data will soon appear:
![](./img/widget-system-health.png)
As with every change, don't forget to save your changes using the exit edit mode button:
As with every change, click **Save board** to persist your board changes and leave edit mode.
![](./img/exit-edit-mode.png)
## Organizing items with Containers
## Grouping your items
Containers are resizable areas for apps, widgets, and nested Containers.
The same Container works on the main canvas, inside a fixed rail, or inside another Container.
Each Container can configure:
As the last step, let's organize your freshly created items using two different options.
You can choose either or both of them on your boards.
- Its label and whether that label is visible.
- Allow or disable collapsing.
- Show or hide **Open all in new tabs**.
- Change its border color and custom CSS classes.
### Using dynamic sections
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.
Dynamic sections are a simple, resizable visual group to group items.
### Collapsing and opening apps
![](./img/create-dynamic-section.png)
Users can collapse an eligible Container like an accordion in view mode without changing its saved expanded size.
The full-width header becomes the compact expansion bar, so the half-row state has no empty pill-shaped spacer.
Expanding it restores its bounds and makes room for its contents.
If **Open all in new tabs** is enabled, the Container menu can open every app link it contains.
After adding a dynamic section, you can simply drag and drop items into the section:
### Fixed rails
![](./img/dynamic-section.gif)
### Using categories
Categories are more advanced and complicated than dynamic sections.
To create a category, open the add item modal and choose "new category":
![](./img/add-category.png)
Every category must have a name. After you click "Add category", you will get a new item on your board:
![](./img/category.gif)
You can drag and drop items in to this category in the edit mode.
Categories will have a big space before and after them in the edit mode to
enable you to drag and drop items outside the category.
You can also collapse or open categories in edit mode to facilitate your editing process.
:::note
A current limitation of categories is, that they will always have a width of 100%.
You cannot reduce their horizontal width.
:::
Open the board's **Layout** settings to enable the left or right rail for each responsive layout.
Choose one, two, or three columns with the slider and use the preview to check the remaining canvas width.
The rail reserves those columns from the dashboard, so Homarr moves canvas items into the next available positions.
Rail content stays visible while the page scrolls and remains editable.
Each responsive layout keeps its own rail widths, item positions, and item sizes.
## Guided tours
@@ -360,7 +369,7 @@ Each page highlights the list view and the create action so you know where to fi
### Board tour
When you first open a board, a tour highlights the board header elements: search, edit mode, settings, board switcher, and user menu.
When you first open a board, a tour highlights the board header elements: search, edit mode, settings, and the user menu.
### Tour controls
+10 -10
View File
@@ -1,13 +1,13 @@
# Glossary
In the glossary, you'll learn the different components and their names of Homarr.
This glossary explains Homarr's main components and the terms used for them.
| Term | Definition |
| --------------- | -------------------------------------------------------------------------------------------------------------- |
| Apps | An piece of software that you or a third party hosts (eg. Google) |
| Integration | A connection to a service outside of Homarr |
| Dynamic section | Resizeable area where items on your board can be placed and grouped |
| Widget | A tile that uses your integrations to display data or enables you to interact with with them |
| Edit mode | Temporary state that you can enter where you can drag and drop, edit or customize your board |
| Board | Customizable page which can be edited using the edit mode. This is where apps, sections and widgets are placed |
| Tasks | Workloads that are performed in the background - even if there is no user present |
| Term | Definition |
| ----------- | --------------------------------------------------------------------------------------- |
| Apps | A piece of software that you or a third party hosts (for example, Google) |
| Integration | A connection to a service outside of Homarr |
| Container | A resizable area that can hold apps, widgets, and nested Containers |
| Widget | A tile that uses your integrations to display data or enables you to interact with them |
| Edit mode | A temporary state where you can drag, resize, edit, or customize your board |
| Board | A customizable page where apps, widgets, and Containers are arranged |
| Tasks | Workloads that are performed in the background, even if no user is present |
Binary file not shown.

After

Width:  |  Height:  |  Size: 561 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

@@ -0,0 +1,95 @@
---
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.
+93 -8
View File
@@ -24,12 +24,11 @@ import { dockerContainersWidget } from "@site/docs/widgets/docker-containers";
]}
/>
## Enabling the Docker integration
## Connecting Docker to Homarr
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.
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.
In Docker Compose, you can simply add this line:
@@ -122,9 +121,89 @@ 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
@@ -148,6 +227,10 @@ 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.
@@ -160,11 +243,13 @@ 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)
### Configuration
### Container labels
Currently Homarr supports one label for configuration of the containers:
`homarr.hide` excludes a container from the Docker Containers widget, tools page, and onboarding discovery whenever the
label is present.
- **homarr.hide:** If set to any value, the container will be hidden from the Docker Containers widget and tools page.
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).
### Security
@@ -14,6 +14,8 @@ 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.
:::
@@ -25,7 +27,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))
- 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.
- RBAC Enabled (Required for Homarr Helm chart)
### Configuring RBAC in Helm Chart
@@ -45,6 +47,25 @@ 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:
+76
View File
@@ -72,6 +72,34 @@ 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:
@@ -82,8 +110,56 @@ The OpenAPI and MCP APIs include board management endpoints for common automatio
- Set desktop and mobile home boards for the current user
- Configure global board defaults such as public home boards and status behavior
Board layout responses identify each layout with a `mobile`, `base`, or `custom` role. Mobile and Base are protected. Layout-saving clients must preserve existing roles, keep Mobile at breakpoint `0`, keep Base as the highest breakpoint, and use unique custom breakpoints between them. The layout save operation returns the canonical saved layouts, including server-generated IDs for new custom layouts. A non-Base layout can be regenerated from Base with the protected reset operation.
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
an optional `error` on a failed integration; media-request responses expose a `failedIntegrations` array, and the latest
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.
Canvas height follows its content automatically and does not require a separate API setting.
Responsive layouts store optional left and right gutter widths from zero to three columns.
Gutter columns are reserved from the layout's total column count, and changing them reflows affected items.
## Get Custom Widget authoring resources
Custom JSX v2 provides four HTTP discovery endpoints:
+13 -2
View File
@@ -51,6 +51,17 @@ 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. You can edit and delete them.
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.
![Apps list](./img/app-list.png)
### 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.
+71
View File
@@ -0,0 +1,71 @@
---
description: Configure and use Homarr Assistant.
tags:
- Assistant
- AI
- Administration
---
# 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 conversation with suggested prompts](/img/assistant/conversation.png)
## Set it up
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.
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.
:::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.
:::
## Use it
- Select **Assistant** in the user menu or press `Shift + A`.
- Ask from `Cmd/Ctrl + K`.
- Add the [Assistant widget](../widgets/assistant) to a board.
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.
Use **Conversations** to search, switch, rename, export, or delete threads.
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
- **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.
See [MCP](./mcp) for external AI clients and [Custom Widget agent authoring](./custom-widgets/agent-authoring) for the
widget authoring workflow.
+7
View File
@@ -22,6 +22,8 @@ Navigate to `Management` → `Tools` → `Backup`. You must have **admin** permi
## Exporting a Backup
![Export database card with the Download Backup action](/img/backup/export-database.png)
Click the **Export** button in the export card. You'll be prompted to type `I understand` to confirm. Once confirmed, a ZIP file downloads containing:
| File | Description |
@@ -33,6 +35,8 @@ The backup contains the complete database. It includes Custom Widgets and all ot
## Restoring a Backup
![Restore database card with the backup ZIP drop zone](/img/backup/restore-database.png)
### 1. Upload
Drag and drop or select a `.zip` backup file. The import card validates that the file contains a valid `db.sqlite` and metadata.
@@ -49,6 +53,9 @@ Before restoring, Homarr uses **sql.js WASM** to analyze the backup directly in
Type `I understand` to acknowledge that restoring will replace your current database. This cannot be undone.
During first-run onboarding, the database is still empty, so the restore preview goes directly to **Restore Database**
without the extra typed confirmation. After Homarr restarts, onboarding opens the restored default home board.
### 4. Restore
The backup ZIP is sent to the server, validated, and applied. After a successful restore:
Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 256 KiB

+200 -23
View File
@@ -11,18 +11,26 @@ tags:
import iconColorImage from "./img/icon-color.png";
import itemRadiusImage from "./img/item-radius.png";
import layoutLargeImage from "./img/layout-large.png";
import layoutMediumImage from "./img/layout-medium.png";
import layoutSmallImage from "./img/layout-small.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";
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/board).
Other boards can be viewed by direct links (e.g. my-homarr.com/board/smart-home) or via the management pages.
A user may also have a "home board" which they will see at the root path (e.g. my-homarr.com or my-homarr.com/boards).
Other boards can be viewed by direct links (e.g. my-homarr.com/boards/smart-home) or via the management pages.
The management page includes a compact structural preview of each board's Base layout so you can recognize it before
opening it. Containers include their visible widgets and app icons instead of appearing empty.
Names of boards must be unique because this will be used in the URL to load the desired board.
Press `Shift+C`, or select **Switch board** from the user menu, to open the board switcher. It shows
structural previews of the other boards you can access. Start typing to filter by name, use the arrow keys to move
through the results, and press `Enter` to open the selected board.
## Create a new board
When creating a board, the availability of the board name will be checked for you.
@@ -36,8 +44,10 @@ Underscores and hyphen-minus are allowed.
### Column count
Homarr has a complex drag-and-drop system that can be used from the browser.
The column count defines how many horizontal columns are available to drag and drop elements to.
The column count defines how many fixed-width cells are available in each row.
One cell contains a `200 × 200` logical-pixel tile.
Homarr scales the whole board uniformly instead of changing widget proportions, and always fits it to the available width without horizontal scrolling.
The main canvas and enabled rails stay at least one viewport tall, and the canvas grows automatically while you drag or resize items near its lower edge.
### Public
@@ -88,32 +98,170 @@ Homarr will fall back to the Homarr logo if nothing is set.
### Layout
In this section you can configure the different responsive layouts. Depending on your screens width the layout will change.
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/layout-settings.png)
<img
src={layoutSettingsImage}
alt="Board settings showing protected Mobile and Base responsive layouts with rail controls"
style={{ maxWidth: "100%" }}
/>
On each element the position and size of items can be changed seperately.
When adding a new layout the position and sizes will automatically be generated depending on the column count.
When adding a new item / dynamic section that will always be 1x1 on each layout and placed on the first available position.
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 `1 × 1` tile has a `200 × 200` logical-pixel content area.
The `24` logical-pixel gap between tiles is separate from that size.
A `2 × 1` tile therefore contains two fixed cell widths plus the gap between them.
Widgets see their logical size, while the board's visual scale makes the complete layout larger or smaller.
When fitting below 100%, Homarr keeps widget typography and control density at their normal readable size.
On larger canvases, the complete board grows together like browser zoom.
The complete canvas always fits the available width and never adds horizontal board scrolling.
- **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.
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.
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.
The canvas is at least one window tall, even when it is empty.
Its left and right edges are hard drag boundaries, while its bottom edge extends automatically as a tile is dragged or resized downward.
Nested Containers keep their own resizable bounds.
New items use their widget's default size and new Containers use a useful `4 × 3` size when space allows.
#### Name
The name of the layout can be choosen freely. It is only used for the management and will not be visible on the board.
The name of the layout can be chosen freely. It is only used for management and is not displayed on the board.
#### Column count
Amount of columns for this layout. Defines how many items can be placed in a row. When changed it will automatically adjust the position and size of the items.
The total number of columns in the layout. Changing it projects the saved positions and sizes into the new grid width without overlap.
#### Fixed rails
Each responsive layout can enable a fixed left rail, a fixed right rail, or both.
Set a rail width to zero to disable it for that layout.
Choose a width from one to three columns with the three-step slider.
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.
Rails fill the available window height and remain fixed while the page scrolls. They do not grow or scroll independently.
Apps, widgets, and Containers inside them still use the same fixed `200 × 200` cells and can be rearranged in edit mode, where each rail shows its name and boundary.
#### Breakpoint
Minimum width of the screen where the layout will be used.
Once the minimum of the next layout is reached the layout will change.
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.
#### 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.
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.
<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 short tip appears after entering edit mode as a reminder of the modifier key. A move is applied only when every
selected item fits in the destination.
The widget editor also lists its selected integrations. Users with full access can choose **Edit connection** to test,
diagnose, and repair an integration without leaving the board. A successful repair refreshes the affected widget; the
saved integration changes apply everywhere that connection is used.
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.
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.
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
<img src={layoutSmallImage} height={256} />
<img src={layoutMediumImage} height={256} />
<img src={layoutLargeImage} height={256} />
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.
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.
### Containers and rails
Containers can hold apps, widgets, and nested Containers.
The same Container model works on the main canvas, inside a fixed rail, or inside another Container.
Move and resize Containers directly in edit mode; nested content stays within the Container's local bounds.
Resize outlines follow the pointer continuously while a dashed preview marks the grid size that will be applied on release.
Each responsive layout stores its own Container position and size.
Each Container can configure:
- Its label and whether that label is visible.
- Whether users can collapse and expand it like an accordion.
- Whether its menu shows **Open all in new tabs** for the app links it contains.
- Its border color and custom CSS classes.
Collapsing a Container reduces its full-width header to a half-row expansion bar and compacts the surrounding layout without losing its expanded size.
Expanding it smoothly restores those bounds and moves following items out of the way without reloading the widgets inside it.
### Background
@@ -159,7 +307,7 @@ Change the color of your bookmark / app icons.
#### Item radius
Change the radius of your items / categories.
Change the radius of your items and Containers.
<img src={itemRadiusImage} height={100} />
@@ -168,6 +316,11 @@ Change the radius of your items / categories.
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.
[See this guide on how to work with custom CSS](/docs/advanced/styling/).
### Behaviour
@@ -176,6 +329,21 @@ The styling will only be applied on the respective board page.
This will disable the status check for apps.
#### Work directly from the board
Widgets expose their routine controls without requiring edit mode:
- Hold `Shift` while hovering a widget that supports advanced view to expand it in place while the other widgets
remain visible but dimmed. Release `Shift` to close it. Hold `Shift` plus `Ctrl` on Windows/Linux or `Cmd` on macOS to
keep the view open until you close it. On touch devices, open it from the widget menu. Other widgets adapt in place
instead.
- 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.
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.
### Access control
With the access control, you can permit a single user or a group to your board.
@@ -233,7 +401,8 @@ 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 and layout as the original board.
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.
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)
@@ -291,5 +460,13 @@ For the global home board you can only select boards that are public.
### Mobile home board
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)
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.
@@ -17,7 +17,9 @@ Install the Agent Skill from the verified Homarr repository:
npx skills add https://github.com/homarr-labs/homarr --skill homarr-custom-widget
```
The portable skill gives concise authoring guidance. The Homarr MCP prompt gives connected agents a retrieval workflow: read the live schema, then fetch only the component documentation needed for the widget.
The portable skill gives concise authoring guidance. For connected agents, Homarr returns the complete `SKILL.md` and
all bundled skill references once in one lazy tool response. The Homarr MCP prompt then gives the agent a
retrieval workflow: read the live schema and fetch only the component documentation needed for the widget.
The workbench's **Copy AI prompt** action is separate. It creates a self-contained prompt below 12,000 characters for an ordinary ChatGPT or Claude conversation. It includes the lean contract, bindings, recommended components, and two polished examples. It never tells an offline model to call MCP tools.
@@ -37,38 +39,61 @@ Repository code cannot give this attribution.
The authoring-prompt tool description contains one sentence. The prompt contains the workflow instructions and resource links.
`GET /api/custom-widgets/skill` and the MCP tool return the concise skill discovery payload:
`GET /api/custom-widgets/skill` and `customWidget_getSkill` return a self-contained structured skill payload:
```ts
{
name: "homarr-custom-widget";
version: string;
skillMd: string;
skillMd: string; // Complete SKILL.md
references: Record<string, string>;
skillsShUrl: string;
sourceUrl: string;
installCommand: string;
}
```
The full installed component catalog stays available through `homarr://custom-widgets/components` and per-component resources. It is deliberately not embedded in the skill or clipboard prompt.
The `homarr://custom-widgets/skill` MCP resource returns the same `SKILL.md` and references once as bundled Markdown.
The component catalog is a compact index of names, categories, safety levels, shared prop names, policies, and example
IDs. Per-component results contain only component-specific props and a reference to shared prop documentation. Use
`customWidget_getSharedProps` once with only the shared prop names needed across the selected components. This avoids
repeating hundreds of global props in every tool result. The catalog is deliberately not embedded in the skill or
clipboard prompt; the skill response still contains the complete authoring, schema, runtime, and security references.
These interfaces do not return an archive URL or archive hash.
## Create a Custom Widget with MCP
1. Get the prompt, live schema, and component catalog.
2. Read the external API documentation.
3. Build one definition.
4. Validate the definition without saving it.
5. Create a preview session.
6. Test the queries and actions.
7. Open the preview URL with browser tools.
8. Inspect the redacted journal and revise the local candidate until it is valid.
9. Create the widget, including any source URLs and credentials the user already supplied.
10. Otherwise, ask the user to configure the saved definition with the ten-minute Homarr source-setup URL.
11. Create a final preview with the saved definition ID so Homarr loads its stored credentials, then test it again.
12. Update the saved definition if the final verification finds a problem.
13. Repeat the procedure for the next widget.
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.
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`.
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.
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.
15. Update the saved definition if the final verification finds a problem. `customWidget_update` also accepts
`templateLines` for multiline JSX without resetting omitted options.
16. Repeat the procedure for the next widget.
Successful validation returns a compact summary instead of echoing the full widget and template. Preview creation
returns its session, links, and query checklist without repeating the submitted definition. Keep the candidate locally
and reuse it for the next lifecycle step.
Each source-configuration URL applies to one definition or preview and one source. It is short-lived, single-use, and
can only be opened and completed by an authenticated Homarr administrator. The administrator confirms the server URL,
@@ -94,11 +119,51 @@ Homarr maintainers can run the same self-contained prompt that the workbench cop
OPENROUTER_API_KEY=... pnpm --filter @homarr/custom-widgets test:ai
```
The suite uses DeepSeek V4 Pro to author Pokédex, Portainer, football, Jellyfin, and Home Assistant widgets. Before DeepSeek V4 Flash judges a result, it must pass Homarr's real clipboard parser, canonical schema, and deterministic scenario checks for the documented API routes, authentication, bindings, actions, and essential runtime components. It saves every response, validated widget manifest, and detailed category scores under `packages/custom-widgets/.ai-evaluations/`.
The manifest suite covers nine complex and public-API prompts. Its deterministic fixture-backed cases include a fake
homelab health API, CoinMarketCap's keyless public API, Bored, Agify, and a multi-request Pokédex. Before the configured
model judges a result, the candidate must pass Homarr's real clipboard parser, canonical schema, and deterministic
checks for documented endpoints, authentication, request triggers, query bindings, response fields, and essential
runtime components.
Each scenario stops as soon as it passes and has a hard limit of ten generation attempts. Override the models or
per-scenario limit with `OPENROUTER_GENERATOR_MODEL`, `OPENROUTER_JUDGE_MODEL`, and
To exercise the real Homarr Assistant lifecycle instead of accepting a returned JSON block, run:
```sh
OPENROUTER_API_KEY=... pnpm --filter @homarr/custom-widgets test:assistant-ai
```
The same suites can target the Homarr provider with a Workshop PocketBase token instead of an OpenRouter key:
```sh
AI_PROVIDER_BASE_URL=https://homarr.dev/api/ai/v1 \
AI_PROVIDER_API_KEY=<workshop-token> \
AI_PROVIDER_MODEL=homarr/model \
pnpm --filter @homarr/custom-widgets test:assistant-ai
```
`AI_PROVIDER_JUDGE_MODEL` can select a separate judge when the configured endpoint offers one. The Homarr provider
offers exactly one model, so generation and review both use `homarr/model`.
The assistant test exposes the same lazy skill, live schema, component catalog, targeted component references,
validation, preview-query, and create-from-preview tools as the product. It refuses to save a definition that was not
validated exactly or whose preview queries were not all tested. Multi-request fixtures return a different payload for
each documented path and require manual-query parameters. Use `--case=pokedex`, `--case=coinmarketcap-keyless`,
`--case=bored-activity`, or `--case=agify-name` for a focused run.
Both suites use the configured provider models for generation and harsh review. They save tool traces, validated
manifests, raw reviews, and detailed category scores under
`packages/custom-widgets/.ai-evaluations/`. These live tests are maintainer-invoked rather than CI-required because
they consume a configured provider account and exercise a nondeterministic model. The repository's ordinary tests
cover the lifecycle enforcement, fixture routing, score calculation, and failure behavior without network access.
Each scenario stops as soon as it passes and has a hard limit of ten generation attempts. Override the endpoint,
models, or per-scenario limit with `AI_PROVIDER_BASE_URL`, `AI_PROVIDER_API_KEY`, `AI_PROVIDER_MODEL`,
`AI_PROVIDER_JUDGE_MODEL`, and
`CUSTOM_WIDGET_AI_MAX_LOOPS`. Generated artifacts are local and ignored by Git; promote a reviewed, validated result
into the bundled-widget source deliberately.
The reference API check uses a real PokeAPI list/detail request and a read-only Portainer container query when `PORTAINER_BASE_URL` and `PORTAINER_API_KEY` are set. Use `pnpm --filter @homarr/custom-widgets test:reference-apis -- --pokeapi-only` for the public PokeAPI check alone. To exercise start, restart, and stop against a disposable container, also set `PORTAINER_TEST_CONTAINER_ID` and the explicit safeguard `PORTAINER_ALLOW_MUTATIONS=1`; the script restores the container's original running state.
Use `pnpm --filter @homarr/custom-widgets test:reference-apis -- --public-ai-apis-only` to verify the live Bored and
CoinMarketCap response shapes used by the assistant fixtures. The same command also verifies Agify when
`AGIFY_API_KEY` is configured; otherwise it reports the authenticated check as skipped without weakening the
deterministic Agify fixture tests.
@@ -1,8 +1,37 @@
# 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 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.
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%" }}
/>
## Manual workflow
@@ -21,7 +50,9 @@ The workbench is visual-first: configure the default API connection, add request
<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` with a query ID and value/label paths.
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.
## ChatGPT or Claude
@@ -31,7 +62,8 @@ 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 prompt deliberately
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
recommends a focused Mantine component set, visual hierarchy, responsive layout, theme compatibility, and loading,
empty, failure, and success states.
@@ -7,7 +7,21 @@ 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
@@ -15,7 +29,7 @@ Use normal Mantine layouts, typography, feedback, dates, and charts. Official co
<Stack>
<TextInput bind="search" defaultValue="" label="Search" />
<SubFetch requestId="search" params={{ query: inputs.search }}>
{(results) => (
{(results, meta) => (
<Stack>
{(results ?? []).map((item) => (
<Text key={item.id}>{item.name}</Text>
@@ -32,14 +46,23 @@ 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. Patterns with lookbehind, backreferences, nested quantifiers, excessive length, or unsupported flags are rejected.
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.
## Runtime components
- `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.
- `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.
- `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.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

@@ -40,6 +40,17 @@ 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.
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.
<CustomWidgetCodeExample
id="complete-widget-manifest"
label="Complete manifest"
@@ -56,9 +67,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. 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.
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.
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.
@@ -93,14 +104,12 @@ 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. 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.
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.
## Import and Workshop
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.
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.
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.
@@ -125,6 +134,7 @@ 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. The Tautulli example is the highest-scoring widget 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. Tautulli Activity is one of the bundled examples preserved from Homarr's AI authoring evaluation:
<BundledCustomWidgetGallery />
@@ -1,6 +1,11 @@
# Configure requests and security
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.
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.
## Named requests
@@ -35,19 +40,28 @@ 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.
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" } }`.
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" } }`.
## 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 for anonymous viewers, while ordinary scheduled query rendering remains
available.
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.
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,7 +41,8 @@ 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 the current schema errors.
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.
## Workshop unavailable
Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

@@ -13,9 +13,27 @@ Check [the available integrations](/docs/category/integrations) for further info
## Create an integration
To create an integration, you can choose the supported application from the dropdown at the top right:
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.
![](./img/create-integration-dropdown.png)
The integrations management page uses a searchable list. Each row shows the integration type and URL, with the actions you have permission to use.
### Accessing the page
The `/manage/integrations` page is reachable when at least one of the following applies:
- you can manage all integrations (global `integration-full-all`),
- you can create integrations (`integration-create`), or
- you have been granted full access to at least one specific integration.
What the page shows depends on the case:
- **Manage all (`integration-full-all`)**: the full list of integrations is shown.
- **Create only (`integration-create`)**: the page shows an empty "create an integration" state instead of the list, with the create action still available.
- **Delegated (full access on one or more)**: only the integrations you have full access to are listed, above a note making clear that the view is scoped to what you can manage.
- **None**: the page is not reachable and returns a not-found page.
Where you have a mix of access, the list combines the integrations you can manage. Integration lists are filtered
server side, so a user never receives data about integrations they lack access to.
Next, you'll need to fill out the required fields to configure the integration:
@@ -59,6 +77,22 @@ 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
@@ -0,0 +1,34 @@
---
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.
+83 -44
View File
@@ -8,15 +8,24 @@ tags:
# Model Context Protocol (MCP)
Homarr exposes a [Model Context Protocol](https://modelcontextprotocol.io) server at `/api/mcp/mcp`, enabling AI assistants like Claude, Cursor, VS Code Copilot, and Claude Code to interact with your Homarr instance using natural language.
Homarr exposes a [Model Context Protocol](https://modelcontextprotocol.io) server at `/api/mcp`, enabling AI assistants like Claude, Cursor, VS Code Copilot, and Claude Code to interact with your Homarr instance using natural language.
The endpoint implements the stateless MCP 2026-07-28 protocol and accepts 2025 Streamable HTTP clients through a
stateless compatibility path. It does not create protocol sessions or require sticky sessions. Homarr creates the
server and extracts its tools only after an authenticated request reaches the route. The previous `/api/mcp/mcp` URL
remains available as a compatibility alias, but new clients should always use `/api/mcp`.
With MCP, you can ask your AI assistant to manage apps, integrations, users, boards, Docker containers, media servers, DNS settings, and more - all through Homarr's existing tRPC API, now exposed as 50+ AI-callable tools.
The optional [Homarr Assistant](./assistant) uses these same tool definitions internally.
Unlike external MCP clients, it uses the current Homarr session directly and asks for in-conversation
approval before running mutating procedures.
## Getting Started
The MCP endpoint lives under the **API** page at `Management` → `Tools` → `API` → **MCP** tab.
The MCP endpoint lives under the **API** page at `Management` → `Tools` → `API` → **AI / MCP** tab.
![MCP Instructions Page](./api/img/mcp-instructions.png)
![AI / MCP tab showing the endpoint, API-key warning, and client configuration](./api/img/mcp-instructions.png)
### 1. Create an API Key
@@ -42,7 +51,7 @@ Add the following to your client's MCP configuration file:
{
"mcpServers": {
"homarr": {
"url": "https://your-homarr-instance.com/api/mcp/mcp",
"url": "https://your-homarr-instance.com/api/mcp",
"headers": {
"ApiKey": "<your-api-key>"
}
@@ -73,7 +82,7 @@ Replace `<your-api-key>` with your actual API key. Never commit API keys to vers
"mcpServers": {
"homarr": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://your-homarr-instance.com/api/mcp/mcp", "--header", "ApiKey:<your-api-key>"]
"args": ["-y", "mcp-remote", "https://your-homarr-instance.com/api/mcp", "--header", "ApiKey:<your-api-key>"]
}
}
}
@@ -96,6 +105,10 @@ Custom Widget and Workshop authoring are different: their tools, prompts, and re
key owner is a Homarr administrator. Non-administrator keys cannot discover or invoke those capabilities. Creating an
API key never elevates the owner.
OAuth authorization and token requests bind issued credentials to the canonical `/api/mcp` resource. Dynamic client
registration remains available for compatibility with current MCP clients while the ecosystem transitions toward
pre-registered clients and Client ID Metadata Documents.
## Testing the Connection
Verify your MCP endpoint is working:
@@ -103,13 +116,17 @@ Verify your MCP endpoint is working:
```bash
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Accept: application/json" \
-H "ApiKey: <your-api-key>" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}' \
https://your-homarr-instance.com/api/mcp/mcp
-H "Mcp-Method: server/discover" \
-H "Mcp-Name: homarr" \
-d '{"jsonrpc":"2.0","method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientInfo":{"name":"test","version":"1.0"},"io.modelcontextprotocol/clientCapabilities":{}}},"id":1}' \
https://your-homarr-instance.com/api/mcp
```
A successful response includes the server capabilities and available tools. Use `tools/list` to see all available tools:
A successful response includes the supported protocol versions and server capabilities. Use `tools/list` to see all
available tools. MCP v2 clients add the per-request metadata and `Mcp-Method` / `Mcp-Name` headers automatically; the
abbreviated request below uses the stateless 2025 compatibility path for readability:
```bash
curl -s -X POST \
@@ -117,33 +134,36 @@ curl -s -X POST \
-H "Accept: application/json, text/event-stream" \
-H "ApiKey: <your-api-key>" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
https://your-homarr-instance.com/api/mcp/mcp
https://your-homarr-instance.com/api/mcp
```
Tool catalogs are returned in deterministic order and may be cached privately by the client for five minutes. The
removed HTTP+SSE `/sse` and `/message` transports return `410 Gone`; use the single Streamable HTTP endpoint instead.
## Available Tools
Homarr exposes 50+ tools organized by namespace. Each tool is either a **query** (GET, read-only) or **mutation** (POST, write action).
| Namespace | Example Tools | Type |
| ------------------ | --------------------------------------------------------- | ---------- |
| `app` | List all apps, get app by ID, create/update/delete app | GET + POST |
| `apiKeys` | List API keys | GET |
| `beszel` | Systems status, alerts, historical system metrics | GET |
| `board` | List boards, get board by ID, create/update/delete board | GET + POST |
| `docker` | List containers, inspect container, start/stop/restart | GET + POST |
| `icon` | Search icons | GET |
| `info` | Server health, version info | GET |
| `integration` | List integrations, test connection, delete integration | GET + POST |
| `invite` | List invitations, delete invite | GET + POST |
| `calendar` | Calendar events from media servers | GET |
| `customWidget` | Validate, preview, create, and iteratively update widgets | GET + POST |
| `dnsHole` | DNS hole summary, toggle blocking | GET + POST |
| `downloads` | Active downloads, pause/resume/delete | GET + POST |
| `healthMonitoring` | Resource usage stats | GET |
| `patchmon` | Host patch and security update statistics | GET |
| `mediaRequests` | Approve/decline media requests | GET + POST |
| `mediaServer` | Media server sessions and stats | GET |
| `smartHome` | Execute automations, device states | GET + POST |
| Namespace | Example Tools | Type |
| ------------------ | ---------------------------------------------------------- | ---------- |
| `app` | List all apps, get app by ID, create/update/delete app | GET + POST |
| `apiKeys` | List API keys | GET |
| `beszel` | Systems status, alerts, historical system metrics | GET |
| `board` | List boards, read settings/CSS, create/update/delete board | GET + POST |
| `docker` | List containers, inspect container, start/stop/restart | GET + POST |
| `icon` | Search icons | GET |
| `info` | Server health, version info | GET |
| `integration` | List integrations, test connection, delete integration | GET + POST |
| `invite` | List invitations, delete invite | GET + POST |
| `calendar` | Calendar events from media servers | GET |
| `customWidget` | Validate, preview, create, and iteratively update widgets | GET + POST |
| `dnsHole` | DNS hole summary, toggle blocking | GET + POST |
| `downloads` | Active downloads, pause/resume/delete | GET + POST |
| `healthMonitoring` | Resource usage stats | GET |
| `patchmon` | Host patch and security update statistics | GET |
| `mediaRequests` | Approve/decline media requests | GET + POST |
| `mediaServer` | Media server sessions and stats | GET |
| `smartHome` | Execute automations, device states | GET + POST |
The tools table on the MCP tab is dynamically generated - it always reflects the latest available procedures from your Homarr instance.
@@ -175,21 +195,36 @@ An agent creates one `homarr-custom-widget-v2` definition at a time. Start with
The API key owner must be a Homarr administrator. The same rule applies to management, import, migration, preview,
secrets, publication, Workshop installation, and the HTTP authoring resources.
Then, read the live schema and component resources. These resources describe the installed Mantine components.
Load the installed skill before authoring. `customWidget_getSkill` returns the complete `SKILL.md` entrypoint and every
bundled reference in one response, so an MCP client does not need filesystem access or a second documentation fetch.
Then read the live schema and only the component resources required by the design. These resources describe the
installed Mantine components.
1. Call `customWidget_getAuthoringPrompt` and `customWidget_schema`.
2. Build one Custom Widget definition.
3. Call `customWidget_validate` without saving the definition.
4. Call `customWidget_previewCreate`.
5. Test named requests with `customWidget_previewQuery` and `customWidget_previewAction`.
6. Open the returned preview URL.
7. Examine `customWidget_previewJournal`.
8. Revise the local candidate and create another preview session until it is ready.
9. Call `customWidget_create`, supplying any credentials and deployment-specific source URLs the user already provided.
10. If the user must enter the source URL or credentials, call `customWidget_configurationRequestUser` with the new definition ID and wait for completion.
11. Create a final preview with that definition ID so Homarr loads its stored credentials, then test it again.
12. Use `customWidget_update` or `customWidget_templatePatch` for any final saved-definition corrections.
13. Repeat this procedure for the next Custom Widget.
1. Call `customWidget_getSkill`, `customWidget_getAuthoringPrompt`, `customWidget_schema`, and
`customWidget_getComponentCatalog`.
2. Fetch only the relevant installed component documentation with `customWidget_getComponent` and, when useful,
one interaction example with `customWidget_getExample`. Component results contain component-specific props; collect
any needed shared prop names from the compact catalog and fetch them together once with `customWidget_getSharedProps`.
3. Build one Custom Widget definition. Use `templateLines` for generated multiline JSX to avoid a fragile JSON-escaped
template string.
4. Call `customWidget_validate` without saving the definition and repair every returned issue path.
5. Call `customWidget_previewCreate`.
6. Call `customWidget_previewQuery` for every entry in the returned `queries` list. Supply the listed invocation
parameters for manual queries and inspect the real HTTP status and response shape before binding template fields.
7. Test relevant actions with `customWidget_previewAction` in simulation mode.
8. Open the returned `previewPath` or `previewUrl`.
9. Examine `customWidget_previewJournal`.
10. Revise the local candidate and create another preview session until validation, query data, actions, and rendering
are ready.
11. Call `customWidget_create`, supplying any credentials and deployment-specific source URLs the user already provided.
12. Open the returned `managementPath` to review the exact saved definition.
13. If the user must enter the source URL or credentials, call `customWidget_configurationRequestUser` with the new definition ID and wait for completion.
14. Create a final preview with that definition ID so Homarr loads its stored credentials, then test it again.
15. Use `customWidget_update` or `customWidget_templatePatch` for any final saved-definition corrections.
16. Repeat this procedure for the next Custom Widget.
Validation and preview creation return compact lifecycle metadata. They do not echo the submitted widget or duplicate
its template as both a string and line array; the agent should retain and reuse its local candidate.
The agent receives the configuration-request status. The agent does not receive plaintext credentials. A preview-scoped setup URL configures only that temporary preview; use a definition-scoped URL when the values must persist.
@@ -225,6 +260,10 @@ The Custom Widget tools are:
- `customWidget_workshopGet`
- `customWidget_workshopInstall`
- `customWidget_getAuthoringPrompt`
- `customWidget_getComponentCatalog`
- `customWidget_getComponent`
- `customWidget_getSharedProps`
- `customWidget_getExample`
- `customWidget_getSkill`
Custom Widget permissions are not assignable group capabilities. Administrator status is required for the complete
@@ -8,8 +8,7 @@ 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`.
![](./img/search-engines.png)
The management page provides a searchable list with the shortcut, URL template, source, and available row actions for each engine.
You can add any search engine that has a search URL that accepts a search term as a parameter.
+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 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.
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.
## Analytics
+72 -13
View File
@@ -163,19 +163,77 @@ 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.
Currently, the following permissions are available:
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.
- 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
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.
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
@@ -220,7 +278,8 @@ Clicking the `Copy & close` button will copy the URL to your clipboard.
### Self-onboarding
Clicking the invitation link will bring up a form with fields for the username and password:
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:
![](./img/invitation-self-onboarding.png)
@@ -1,169 +0,0 @@
---
tags:
- Workshop
- Custom Widgets
- Custom CSS
---
# Community Workshop
:::warning Experimental feature
This feature is experimental and will probably change. If you encounter bugs, need more capabilities, or find something confusing, tell us in the [official discussion thread](https://github.com/homarr-labs/homarr/discussions/categories/custom-widgets). Your feedback directly shapes what comes next. Happy hacking!
:::
The [Community Workshop](pathname:///workshop) is where Homarr users share and discover
**custom CSS themes** and **custom widgets**. You can browse submissions, upvote the ones
you like, report abuse, and install entries directly into your own Homarr instance.
The central `homarr.dev` Workshop is a public community. Browsing it on the website is public. Browsing, installing,
publishing, or importing Workshop content **inside a Homarr instance is restricted to Homarr administrators**.
:::warning
The Workshop contains community-made content. Moderation is not a security boundary, and PocketBase does not interpret
or validate submission content. Always review widget capabilities, network origins, actions, and CSS before installing.
Homarr's installation-time validator is definitive and can reject content that the public service accepted earlier.
Back up your instance before installing untrusted content.
:::
## Browsing and voting
- Filter by **Widgets** or **CSS**, show or hide outdated entries, and sort by popularity, creation date, recent updates, or discussion activity.
- Sign in with **GitHub** to upvote/downvote, submit your own creations, or report a submission.
- Each submission shows its title, description, linked GitHub author, revision, optional screenshots, comments, and full content. Widget pages also summarize API sources, actions, options, network access, and required credentials before installation.
- A new submission receives one real upvote owned by its author. Votes remain unique per user and submission.
- Open report counts are public on submission cards so visitors can make an informed installation decision. The submission author and Workshop moderators can read each report category and explanation; reporter identities remain moderator-only.
- Signed-in moderators see a **Moderation** button in the Workshop header. The moderation queue is sorted by open report count and lets moderators inspect every report, dismiss it, or delete the submission. Reports do not automatically remove a submission.
- The manual **Outdated** badge warns visitors that a submission may need an update.
## Comments
Comments are publicly readable and appear in chronological conversation bubbles with the author's avatar and GitHub profile. Signed-in users can post; authors can edit or delete their own comments, and Workshop moderators can delete any comment. Failed posts keep the text you typed. When SMTP and `WORKSHOP_PUBLIC_ORIGIN` are configured, the submission author receives one email for a comment from another user. Self-comments, edits, and deletions do not send mail.
## Submitting
1. Sign in with GitHub.
2. Click **Share yours** and choose the type:
- **Custom widget** — paste the JSON exported from **Manage → Custom Widgets → Export**.
It must match the `homarr-custom-widget-v2` schema (validated before submission).
- **Custom CSS** — paste your stylesheet (max 16384 characters).
3. Review the widget JSON or CSS in the syntax-aware editor, then add a title, description, optional screenshots, and changelog.
4. Submit the entry.
Custom widget JSON is normalized to readable multi-line formatting when it is published, and the Workshop detail page
also formats older compact submissions before displaying them.
Community members can report submissions as outdated or no longer working, malicious, spam, copyright-infringing,
inappropriate, or another concern. An outdated report does not automatically mark or remove the submission.
Submissions are published immediately. Reported entries are reviewed and may be removed.
Authors can edit a submission later to publish a new revision. The edit form can keep or remove existing screenshots and add new PNG, JPG, or WebP images without changing the Workshop URL.
When SMTP and `WORKSHOP_PUBLIC_ORIGIN` are configured, every current Workshop moderator receives an email when a report is submitted. If a moderator deletes the reported submission, its author receives a separate removal email. Author self-deletion does not send a moderation email.
:::info Separate roles
A Workshop moderator is a user with PocketBase's internal `isAdmin` field. This role moderates the central community
and is unrelated to a Homarr instance administrator.
:::
## Submission guidelines
Submissions must be useful to Homarr users and safe to install. Do not submit content that is
malicious, deceptive, illegal, hateful, sexually explicit, gore, gambling-focused, spam, or content
that primarily promotes unrelated products or services.
## Installing into Homarr
You can install workshop entries without leaving Homarr:
- **Custom widgets (Homarr administrators only)** — go to **Manage → Custom Widgets**, or use **Import from Workshop** in the
board widget picker. Choose **Install widget**, review the complete source and capabilities, then confirm API sources and any local secrets in the second modal step.
- **Custom CSS from Workshop (Homarr administrators only)** — open a board's **Settings → Custom CSS**, click **Import from Workshop**, choose a theme, review its full CSS in the second same-size modal step, and click **Import CSS** once. The board setting is still saved separately.
Alternatively, open a submission in the Workshop and use **Download widget JSON** or **Copy CSS**. The submission page shows the matching manual installation steps before the source code.
Custom Widget imports detect required bearer, basic, header, or query credentials. You can enter them during import or explicitly defer them; secret values are never stored in Workshop content. Missing credentials remain visible on the local Custom Widget afterward.
## Workshop deployment
Workshop is powered by [PocketBase](https://pocketbase.io). The production image compiles the current Docusaurus site and serves it together with the API from one origin. The deployment lives in `apps/workshop/` with a Dockerfile, Compose configuration, version-controlled migrations, hooks, a smoke test, and ACT-local workflow instructions.
Submission links include Open Graph and X/Twitter metadata for the submission title, description, and type. The first uploaded screenshot is used as the social preview image; submissions without screenshots use the Homarr logo.
Run `pnpm docker:workshop` for PocketBase on port 8090, then run
`WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs` for Docusaurus on port 3003. `pnpm docker:docs` builds the
combined production layout. Normal start and stop commands retain data; `down --volumes` is an intentional,
irreversible development reset.
GitHub needs only the central callback `https://<workshop-host>/api/oauth2-redirect`. Keep PocketBase origins unrestricted
unless every self-hosted Homarr caller is explicitly listed. Configure both `GITHUB_CLIENT_ID` and
`GITHUB_CLIENT_SECRET`; supplying only one is a startup error. Set `WORKSHOP_PUBLIC_ORIGIN` in production so generated
links and email use the public host. Workshop stores only the OAuth provider username in `users.name`, and
users cannot rename it. Map **OAuth2 username** to `name`; leave OAuth2 id, full name, and avatar mappings
empty. Profile and avatar URLs are derived from `https://github.com/<username>` and
`https://github.com/<username>.png`, so PocketBase does not store duplicate profile metadata or avatar files. Configure
SMTP for comment, report, and removal notices.
The public URL contract is:
| Variable | Purpose | Default |
| ------------------------ | ------------------------------------------------- | ----------------------------- |
| `HOMARR_WEBSITE_URL` | Documentation/site base used by Homarr links | `https://homarr.dev` |
| `WORKSHOP_API_URL` | PocketBase API used by Homarr and standalone docs | `HOMARR_WEBSITE_URL` |
| `WORKSHOP_WEB_URL` | Public Workshop page linked from Homarr | `HOMARR_WEBSITE_URL/workshop` |
| `WORKSHOP_PUBLIC_ORIGIN` | PocketBase-generated links and email | Required in production |
| `PB_ALLOWED_ORIGINS` | PocketBase browser origins | `*` for the central community |
The deprecated docs-only `WORKSHOP_URL` alias remains for one release; `WORKSHOP_API_URL` takes precedence. The
combined Workshop image writes these public URLs into its browser runtime configuration whenever the container starts;
the Docker build arguments remain fallbacks. Restart the container after changing these values, without rebuilding it.
### Staging and demo deployment
Use `apps/workshop/docker-compose.staging.yml` with the published `ghcr.io/homarr-labs/workshop:v2` image for a v2
preview. Copy `apps/workshop/.env.staging.example` to `.env.staging`; its default public contract is:
```dotenv
HOMARR_WEBSITE_URL=https://v2.preview.homarr.dev
WORKSHOP_API_URL=https://v2.preview.homarr.dev
WORKSHOP_WEB_URL=https://v2.preview.homarr.dev/workshop
WORKSHOP_PUBLIC_ORIGIN=https://v2.preview.homarr.dev
PB_ALLOWED_ORIGINS=*
```
Start or update it from the repository root:
```sh
docker compose --env-file apps/workshop/.env.staging \
-f apps/workshop/docker-compose.staging.yml pull
docker compose --env-file apps/workshop/.env.staging \
-f apps/workshop/docker-compose.staging.yml up --detach
curl --fail http://127.0.0.1:8090/api/health
```
The service binds to `127.0.0.1:8090`. Terminate TLS at a reverse proxy and forward the **entire hostname**, including
`/api`, `/_/`, and `/workshop-runtime-config.js`; forwarding only `/workshop` breaks PocketBase and runtime URL loading.
This combined image serves both the documentation site and PocketBase, so use a dedicated Workshop hostname if the
chosen hostname already serves Homarr itself. In that case, replace the hostname in all four URL values and keep
`/workshop` only on `WORKSHOP_WEB_URL`.
The Compose volume preserves PocketBase data across pulls and container recreation. The mutable `v2` tag is suitable
for previews; use the `sha-<commit>` tag for promotion. For the public central Workshop, `PB_ALLOWED_ORIGINS=*` allows
self-hosted Homarr origins to use the API and OAuth. A private demo may use a comma-separated origin allowlist instead.
GitHub OAuth requires both credentials and the callback
`https://v2.preview.homarr.dev/api/oauth2-redirect`. Restart after changing environment values, then verify the health
endpoint, `/workshop`, a public listing request, GitHub sign-in, and an installation from the Homarr preview.
Back up application and PocketBase data before deployment. Publish the combined image by immutable digest, test the
exact digest in staging, rehearse application rollback and PocketBase restore, then promote that same digest.
## Client architecture
Workshop access is centralized in `@homarr/workshop/backend`. It exports the typed `WorkshopBackend`, its typed PocketBase instance, stable TanStack Query option factories, and the `useWorkshop…` query and mutation hooks used by Homarr. The documentation site uses the same backend rather than maintaining a second API contract.
Listing fields derived by the `workshop_listings` view—such as submission type, score, and counts—are normalized by the shared client. Healthy deployments keep filtering, sorting, and pagination in PocketBase. If a persistent development volume still has an older view that rejects those fields, the client retries without them and applies the same operations locally; it can also fall back to the base submissions collection. Production deployments should nevertheless apply the current migration so the view exposes the complete metadata.
@@ -0,0 +1,47 @@
---
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`.
@@ -0,0 +1,43 @@
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",
},
],
},
};
+7 -1
View File
@@ -4,6 +4,8 @@ description: "Display and edit a selected note from Anchor"
hide_title: true
---
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -15,6 +17,8 @@ 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";
@@ -33,10 +37,12 @@ import anchorNoteWidgetScreenshot from "./img/widget.png";
### Interactions
- Click **Edit** to start editing the selected note.
- Hover the widget, focus within it, or use a touch device to reveal **Edit**, then select it to start editing the note.
- Click **Save** to persist changes to Anchor.
- Click **Cancel** to discard unsaved changes.
<WidgetInteractionGuide />
### Adding the widget
<AddingWidget />
+6
View File
@@ -25,6 +25,12 @@ import piHoleApp from "./img/pi-hole-ping-icon.png";
<img src={piHoleApp} alt="app pi-hole with-status-check status-check-icon" width={128 * 1.5} height={128 * 1.5} />
<img src={jellyseerrApp} alt="app jellyseerr with-description-tooltip" height={128 * 1.5} />
</div>
### 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.
### Adding the widget
<AddingWidget />
@@ -0,0 +1,38 @@
---
title: "ArchiveTeam Warrior"
description: "Monitor ArchiveTeam Warrior status, projects, bandwidth, and active archival jobs."
hide_title: true
---
import { archiveTeamWarriorIntegration } from "@site/docs/integrations/archiveteam-warrior";
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 { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
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.
### Supported integration
<WidgetIntegrations
items={[
{
integration: archiveTeamWarriorIntegration,
note: "Shows status and active archival work from one Warrior instance",
},
]}
/>
<WidgetInteractionGuide />
### Adding the widget
<AddingWidget />
### Configuration
<WidgetConfig configuration={archiveTeamWarriorWidget.configuration} />
@@ -0,0 +1,19 @@
import type { WidgetDefinition } from "@site/src/types";
import { IconArchive } from "@tabler/icons-react";
export const archiveTeamWarriorWidget: WidgetDefinition = {
icon: IconArchive,
name: "ArchiveTeam Warrior",
description: "Monitor ArchiveTeam Warrior status, projects, bandwidth, and active archival jobs.",
path: "../../widgets/archive-team-warrior",
configuration: {
items: [
{
name: "Show broadcast message",
description: "Show the message broadcast by the selected Warrior project when space allows.",
values: { type: "boolean" },
defaultValue: "yes",
},
],
},
};
@@ -0,0 +1,69 @@
---
title: "Assistant"
description: "Have a complete assistant conversation directly on a board."
hide_title: true
---
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetHeader } from "@site/src/components/widgets/header";
import { assistantWidget } from ".";
import assistantWidgetImage from "../../management/img/assistant-widget.png";
<WidgetHeader widget={assistantWidget} categories={["Productivity"]} />
The Assistant widget brings the same conversation available from the user menu and command menu onto a
board. Messages, conversations, tool calls, approvals, files, mentions, model controls, reasoning controls,
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.
### Adding the widget
<AddingWidget />
The default widget size is ten columns by four rows and automatically fits boards with fewer columns. Controls
and labels condense as the widget becomes narrower, while the conversation remains scrollable and usable on
touch devices. When a board layout does not provide enough height for conversation controls, the widget shows
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.
### Conversation features
- Create, switch, rename, and delete conversations.
- Send text and supported file or image attachments by picker, drag and drop, or image paste.
- Mention permitted apps, integrations, boards, widgets, and tools with `@`.
- Use `/` shortcuts for common dashboard tasks.
- Review tool calls, approve mutations, and answer structured assistant questions inline.
- Answer pending structured questions from the dock above the composer without scrolling through the conversation.
- Enable conversation-scoped automatic approvals from the shield control to approve protected mutations, use presented form details, and choose answers categorized as affirmative without individual clicks.
- 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.
- 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.
+25
View File
@@ -0,0 +1,25 @@
import type { WidgetDefinition } from "@site/src/types";
import { IconRobot } from "@tabler/icons-react";
export const assistantWidget: WidgetDefinition = {
icon: IconRobot,
name: "Assistant",
description: "Have a complete assistant conversation directly on a board.",
path: "../../widgets/assistant",
configuration: {
items: [
{
name: "Conversation",
description: "Follow the active conversation or attach the widget to one persisted conversation.",
values: { type: "select", options: ["Current conversation", "Pinned conversation"] },
defaultValue: "Current conversation",
},
{
name: "Pinned conversation",
description: "Search for a conversation. This field is hidden when Current conversation is selected.",
values: { type: "string" },
defaultValue: "",
},
],
},
};
@@ -4,6 +4,8 @@ description: "Displays media and listening statistics from Navidrome or Audioboo
hide_title: true
---
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -36,6 +38,8 @@ Only options relevant to your linked integration are shown in the configuration
]}
/>
<WidgetInteractionGuide />
### Adding the widget
<AddingWidget />
+6
View File
@@ -4,6 +4,8 @@ description: "Displays missing subtitle counts and health indicators from your B
hide_title: true
---
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -34,6 +36,10 @@ 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 />
### Adding the widget
<AddingWidget />
@@ -4,6 +4,8 @@ description: "View Beszel alert configurations and history with triggered/ok sta
hide_title: true
---
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -32,6 +34,8 @@ This widget displays your Beszel alert configurations separated by status (trigg
]}
/>
<WidgetInteractionGuide advancedView={false} />
### Adding the widget
<AddingWidget />
@@ -8,6 +8,7 @@ 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";
@@ -15,7 +16,7 @@ import beszelSystemGrid from "./img/beszel-system-grid.png";
<WidgetHeader widget={beszelSystemGridWidget} categories={["System Monitoring"]} />
This widget displays all your Beszel-monitored systems as a responsive card grid. Each card shows real-time metrics like CPU, memory, disk, network, temperature, and more. 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.
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.
Select a system card to open the complete statistics view, including system, filesystem, Docker, historical, and realtime charts.
@@ -34,6 +35,8 @@ Select a system card to open the complete statistics view, including system, fil
]}
/>
<WidgetInteractionGuide advancedView={false} />
### Adding the widget
<AddingWidget />
@@ -4,6 +4,8 @@ description: "Time-series charts for CPU, memory, disk, network, and Docker cont
hide_title: true
---
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -15,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. You can use the realtime Live view or select a historical period 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. 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.
### Screenshots
@@ -32,6 +34,8 @@ This widget displays time-series charts for a selected Beszel system, including
]}
/>
<WidgetInteractionGuide advancedView={false} />
### Adding the widget
<AddingWidget />
@@ -8,14 +8,15 @@ 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";
<WidgetHeader widget={beszelSystemTableWidget} categories={["System Monitoring"]} />
This widget displays all your Beszel-monitored systems in a sortable data table. Each row shows system metrics like CPU, memory, disk, network, temperature, and more. Columns can be toggled on or off, dragged into a different order, and resized from their headers. Column order and widths are saved automatically for each widget. When Beszel reports extra filesystems, the disk column adds a hover breakdown for each mount alongside the root disk.
This widget displays all your Beszel-monitored systems in a sortable data table. Each row shows system metrics like CPU, memory, disk, network, temperature, and more. System and load-average status dots are displayed inline with their values. Columns can be toggled on or off, dragged into a different order, and resized from their headers. Column order and widths are saved automatically for each widget. When Beszel reports extra filesystems, the disk column adds a hover breakdown for each mount alongside the root disk.
Select a system row to open the complete statistics view, including system, filesystem, Docker, historical, and realtime charts.
Select a system row to open the complete statistics view.
### Interactions
@@ -35,6 +36,8 @@ Select a system row to open the complete statistics view, including system, file
]}
/>
<WidgetInteractionGuide advancedView={false} />
### Adding the widget
<AddingWidget />
+11 -4
View File
@@ -4,6 +4,8 @@ description: "Displays multiple app links"
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";
@@ -11,20 +13,23 @@ import { bookmarksWidget } from ".";
<WidgetHeader widget={bookmarksWidget} categories={["Links", "Apps"]} />
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.
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.
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
import gridIconsOnly from "./img/grid-icons-only.png";
import gridWithTitle from "./img/grid-with-title.png";
import verticalListAll from "./img/vertical-list-all.png";
import vericalListSimple from "./img/vertical-list-simple.png";
import verticalListSimple from "./img/vertical-list-simple.png";
<div className='flex gap-1'>
<div className='flex flex-col gap-1'>
<img src={gridIconsOnly} alt="bookmarks grid icons-only" width={128} height={128} />
<img src={vericalListSimple} alt="bookmarks vertical-list no-domain" width={128} height={128} />
<img src={verticalListSimple} alt="bookmarks vertical-list no-domain" width={128} height={128} />
</div>
<img src={gridWithTitle} alt="bookmarks grid with-title" width={256 + 4} height={256 + 4} />
@@ -32,6 +37,8 @@ import vericalListSimple from "./img/vertical-list-simple.png";
</div>
<WidgetInteractionGuide />
### Adding the widget
<AddingWidget />
+7 -1
View File
@@ -47,9 +47,15 @@ export const bookmarksWidget: WidgetDefinition = {
{
name: "Bookmarks",
description: "List of bookmarks to display in the widget.",
values: "Select of all your apps and order them as you like.",
values: "Select one or more Apps directly, then 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.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+14 -11
View File
@@ -4,6 +4,8 @@ description: "Display events from your integrations in a calendar view within a
hide_title: true
---
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -24,19 +26,17 @@ import { icalIntegration } from "@site/docs/integrations/ical";
The calendar widget shows a month view of events from different integrations.
It allows you to view calendar events or upcoming releases from your media managers.
Calendar requests use normal human month numbers (`1` for January through `12` for December), including when
the same data is requested through Homarr's MCP or Assistant tools.
### Screenshots
import popover from "./img/popover.png";
import withIntegrations from "./img/with-integrations.png";
import withoutIntegrations from "./img/without-integrations.png";
import currentView from "./img/current-view.png";
import advancedView from "./img/advanced-view.png";
<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 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>
### Supported Integrations
@@ -69,9 +69,12 @@ import withoutIntegrations from "./img/without-integrations.png";
### Interactions
- Colored dots directly below a date mark its events. They scale with the widget and are hidden when the calendar is too short.
- Hover over a marked date to preview its events in a popover.
- Click on a marked date to pin the popover open (it will stay visible even after moving the mouse away).
- Click the date again or click outside to close the pinned 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.
- If one selected integration is unavailable, its warning indicator remains visible while events from healthy integrations continue to load.
<WidgetInteractionGuide />
### Adding the widget
+24
View File
@@ -4,6 +4,8 @@ description: "Displays the current date and time."
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";
@@ -13,6 +15,26 @@ 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";
@@ -25,6 +47,8 @@ import tokyoClock from "./img/tokyo-12-hour.png";
<img src={tokyoClock} alt="clock format-12-hours timezone-tokyo" width={256} height={128} />
</div>
<WidgetInteractionGuide />
### Adding the widget
<AddingWidget />
+35 -22
View File
@@ -20,18 +20,6 @@ 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.",
@@ -52,21 +40,46 @@ export const clockWidget: WidgetDefinition = {
},
{
name: "Date format",
description: "Format of the date displayed below the clock.",
values: "A collection of date formats, e.g. 'DD/MM/YYYY', 'MM/DD/YYYY', etc.",
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",
defaultValue: "dddd, MMMM D",
},
{
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: "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 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: "-",
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",
},
],
},
+8
View File
@@ -4,6 +4,8 @@ description: "Overview of your Coolify instance with servers, applications, and
hide_title: true
---
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -25,6 +27,12 @@ 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 />
### Adding the widget
<AddingWidget />
@@ -0,0 +1,44 @@
---
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
@@ -0,0 +1,31 @@
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",
},
],
},
};
+9 -7
View File
@@ -1,24 +1,26 @@
---
title: "Custom API"
description: "The Custom API widget shows a Custom JSX v2 definition with server-side API requests, widget options, and actions."
description: "Displays data from a custom API endpoint using configurable display types and optional JSX templates."
hide_title: true
---
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { customApiWidget } from ".";
<WidgetHeader widget={customApiWidget} categories={["Custom", "API"]} />
The Custom API widget shows a Custom JSX v2 definition. Create the definition in [Management → Custom Widgets](/docs/management/custom-widgets).
A definition contains API sources, named requests, a widget options schema, and one safe JSX template.
Temporary inputs exist only while the tile is mounted. Homarr encrypts source credentials separately.
Homarr does not send these credentials to the browser. Homarr does not include them in an export.
### Add the widget
<WidgetInteractionGuide advancedView={false} />
### Adding the widget
<AddingWidget />
@@ -29,9 +31,9 @@ Select a refresh interval. The board item stores the definition ID and the valid
Credentials remain on the server.
:::note
Enable the definition before you use it. The definition must also be available to the board.
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.
:::
### Permissions and actions
@@ -1,9 +1,11 @@
---
title: "DNS Hole Controls"
description: "Control PiHole or AdGuard from your dashboard."
description: "Control Pi-hole, AdGuard Home, or Technitium DNS from your dashboard."
hide_title: true
---
import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -18,7 +20,9 @@ import { technitiumDnsIntegration } from "@site/docs/integrations/technitium-dns
<WidgetHeader widget={dnsHoleControlsWidget} categories={["DNS Hole"]} />
This widget allows you to enable or disable your 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.
### Screenshots
@@ -46,9 +50,12 @@ import smallSize from "./img/small.png";
### Interactions
- Enable / Disable one dns-hole by clicking on the badge.
- Disable one dns-hole in x seconds by clicking on the clock icon.
- Do all of the above for all dns-holes by using the top button row.
- Enable or disable one DNS service by clicking its status badge.
- Temporarily disable one service with the clock action.
- Apply an action to all available services from the top action row. Homarr keeps successful results visible and reports how many sources failed.
- Interaction controls only appear when the current user has permission for that integration.
<WidgetInteractionGuide advancedView={false} />
### Adding the widget
Loaded 100 of 1735 files, more files were not shown because too many files have changed in this diff. Show more