From 365ef7f02b830499955fa2fff827d2a1a5260afa Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 10 Sep 2026 16:36:54 +0200 Subject: [PATCH] Revert "feat: announce Homarr v2 beta" (#6813) --- .github/ISSUE_TEMPLATE/v2_beta_feedback.yml | 131 ------ apps/docs/docs/getting-started/index.mdx | 6 - .../app/[locale]/boards/_layout-creator.tsx | 41 +- .../backup/_components/backup-export-card.tsx | 69 +++- .../backup/backup-export-button.tsx | 79 ---- apps/nextjs/src/components/backup/index.ts | 1 - apps/nextjs/src/components/layout/header.tsx | 44 +- apps/nextjs/src/components/layout/shell.tsx | 4 +- .../layout/v2-beta-announcement/constants.ts | 7 - .../v2-beta-announcement.module.css | 94 ----- .../v2-beta-announcement.tsx | 377 ------------------ .../v2-beta-dashboard-shell.tsx | 42 -- packages/definitions/src/cookie.ts | 1 - 13 files changed, 97 insertions(+), 799 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/v2_beta_feedback.yml delete mode 100644 apps/nextjs/src/components/backup/backup-export-button.tsx delete mode 100644 apps/nextjs/src/components/layout/v2-beta-announcement/constants.ts delete mode 100644 apps/nextjs/src/components/layout/v2-beta-announcement/v2-beta-announcement.module.css delete mode 100644 apps/nextjs/src/components/layout/v2-beta-announcement/v2-beta-announcement.tsx delete mode 100644 apps/nextjs/src/components/layout/v2-beta-announcement/v2-beta-dashboard-shell.tsx diff --git a/.github/ISSUE_TEMPLATE/v2_beta_feedback.yml b/.github/ISSUE_TEMPLATE/v2_beta_feedback.yml deleted file mode 100644 index 5d0b5b38d..000000000 --- a/.github/ISSUE_TEMPLATE/v2_beta_feedback.yml +++ /dev/null @@ -1,131 +0,0 @@ -name: Homarr v2 beta feedback -description: Report a specific bug, unexpected behavior, or improvement for the Homarr v2 beta -title: "feedback(v2): " -labels: ["needs triage"] -body: - - type: markdown - attributes: - value: | - Thanks for testing Homarr v2. It will become the default when it is ready, and every detailed report helps us get there. - - We cannot guess what you wanted from “this looks wrong.” Tell us exactly which feature is affected, what happened, why it is wrong for your use case, and what you expected instead. Add screenshots or a short recording whenever possible. - - **Not useful:** “The board looks wrong.” - - **Useful:** “The container changes from four columns to three after I move a widget at 1440 × 900 and 100% browser zoom. I expected it to keep four columns.” - - - type: dropdown - id: feedback-type - attributes: - label: Feedback type - options: - - Bug - - Upgrade or migration - - Usability or design - - Performance - - Other - validations: - required: true - - - type: input - id: version - attributes: - label: Homarr v2 version or image tag - description: Copy the version from the About page or provide the exact Docker image tag you tested. - placeholder: ghcr.io/homarr-labs/homarr-test:v2 - validations: - required: true - - - type: input - id: affected-area - attributes: - label: Affected page, feature, or widget - description: Name the exact page, widget, integration, container, sidebar, or action involved. - placeholder: "Board editor: moving a widget into a container" - validations: - required: true - - - type: textarea - id: behavior - attributes: - label: What happened, why is it wrong, and what should happen instead? - description: Describe the actual result, why it does not work for you, and the exact result you expected. - placeholder: | - What happened: - Why this is wrong for my use case: - What I expected instead: - validations: - required: true - - - type: textarea - id: reproduction - attributes: - label: Exact steps to reproduce - description: Start from the page you opened and list every click, keypress, drag, setting, and value needed to reproduce it. - placeholder: | - 1. Open... - 2. Set... - 3. Drag or click... - 4. Observe... - validations: - required: true - - - type: textarea - id: visual-evidence - attributes: - label: Screenshots or screen recordings - description: Drag screenshots or a recording into this field. Include the full page for context and a close-up of the problem. Blur secrets and personal data. If you cannot add visuals, explain why. - placeholder: Drag screenshots or a recording here, or explain why none are available. - validations: - required: true - - - type: textarea - id: environment - attributes: - label: Device and Homarr environment - description: Provide every detail that could affect the result. - placeholder: | - Device type and model: - Operating system and version: - Browser and version: - Installation method (Docker, Unraid, Kubernetes, other): - Database (SQLite, MySQL, PostgreSQL): - Reverse proxy and version, if used: - validations: - required: true - - - type: textarea - id: board-context - attributes: - label: Board and display context - description: Include the layout details even when you are not sure they matter. - placeholder: | - Display resolution: - Browser zoom: - Desktop or mobile layout: - Board column count: - Approximate number of items: - Container or sidebar nesting: - Relevant board or widget settings: - validations: - required: true - - - type: textarea - id: additional-information - attributes: - label: Logs and additional information - description: Add relevant browser console errors, Homarr logs, integration details, or anything else that may help. Remove secrets and personal information first. - - - type: checkboxes - id: checks - attributes: - label: Before submitting - options: - - label: I added screenshots or a recording where possible, or explained why I could not. - required: true - - label: I described both what happened and the exact result I expected. - required: true - - label: I searched the existing issues for the same feedback. - required: true - - label: I removed passwords, API keys, tokens, and other private information. - required: true diff --git a/apps/docs/docs/getting-started/index.mdx b/apps/docs/docs/getting-started/index.mdx index 9efaa54bc..4dc986658 100644 --- a/apps/docs/docs/getting-started/index.mdx +++ b/apps/docs/docs/getting-started/index.mdx @@ -11,12 +11,6 @@ tags: import Link from '@docusaurus/Link'; import { IconArrowRight } from '@tabler/icons-react'; -:::info Homarr v2 beta is available - -We've been working on Homarr's v2 update for a while now. There is no fixed release date yet, it will be ready when it's ready. We **need your help** and feedback to help us become confident enough to release it for everyone. Read [what is new](https://v2.preview.homarr.dev/blog/2026/09/03/homarr-2.0), browse the [v2 documentation](https://v2.preview.homarr.dev/docs/getting-started/) and [Workshop](https://v2.preview.homarr.dev/workshop), or try the [hosted preview](https://app-v2.preview.homarr.dev). If anything breaks or does not feel right, [tell us on Discord](https://discord.com/invite/aCsmEV5RgA) in the dedicated v2 channel for the fastest response, or [open a dedicated GitHub issue](https://github.com/homarr-labs/homarr/issues/new?template=v2_beta_feedback.yml). SQLite administrators can download a full backup directly from the beta announcement before changing images. - -::: - Before you try to install Homarr, please review if your device meets the requirements. Installing on an unsupported device may lead to unstable behaviour and no support will be provided. diff --git a/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx b/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx index bbe100e70..24ebe3d03 100644 --- a/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx +++ b/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx @@ -1,21 +1,17 @@ -import { createHash } from "node:crypto"; - import type { JSX, PropsWithChildren } from "react"; import { notFound, redirect } from "next/navigation"; -import { cookies } from "next/headers"; +import { AppShellMain } from "@mantine/core"; import { TRPCError } from "@trpc/server"; import { getRscUserSettingsAsync } from "@homarr/api/user-server"; import { auth } from "@homarr/auth/next"; import { BoardProvider } from "@homarr/boards/context"; import { EditModeProvider } from "@homarr/boards/edit-mode"; -import { dbEnv } from "@homarr/core/infrastructure/db/env"; import { createLogger } from "@homarr/core/infrastructure/logs"; -import { v2BetaAnnouncementCookieKey } from "@homarr/definitions"; -import { MainHeaderContent } from "~/components/layout/header"; +import { MainHeader } from "~/components/layout/header"; import { BoardLogoWithTitle } from "~/components/layout/logo/board-logo"; -import { V2BetaDashboardShell } from "~/components/layout/v2-beta-announcement/v2-beta-dashboard-shell"; +import { ClientShell } from "~/components/layout/shell"; import { BoardTourGate } from "~/components/onboarding/board-tour-gate"; import { env } from "~/env"; import { getCurrentColorSchemeAsync } from "~/theme/color-scheme"; @@ -46,7 +42,6 @@ export const createBoardLayout = ({ }>) => { const resolvedParams = await params; const sessionPromise = auth(); - const cookiesPromise = cookies(); const initialBoardPromise = getInitialBoard(resolvedParams).then( (board) => ({ status: "fulfilled", board }) as const, (error: unknown) => ({ status: "rejected", error }) as const, @@ -63,12 +58,11 @@ export const createBoardLayout = ({ return false; } }); - const [session, initialBoardResult, colorScheme, shouldRunBoardTour, cookieStore] = await Promise.all([ + const [session, initialBoardResult, colorScheme, shouldRunBoardTour] = await Promise.all([ sessionPromise, initialBoardPromise, colorSchemePromise, shouldRunBoardTourPromise, - cookiesPromise, ]); if (initialBoardResult.status === "rejected") { const { error } = initialBoardResult; @@ -89,11 +83,6 @@ export const createBoardLayout = ({ throw error; } const initialBoard = initialBoardResult.board; - const viewerIdentity = session?.user.id ?? "anonymous"; - const viewerHash = createHash("sha256").update(viewerIdentity).digest("hex").slice(0, 16); - const dismissalCookieName = `${v2BetaAnnouncementCookieKey}.${viewerHash}`; - const isAnnouncementDismissed = cookieStore.get(dismissalCookieName)?.value === "dismissed"; - const canExportBackup = dbEnv.DRIVER === "better-sqlite3" && Boolean(session?.user.permissions.includes("admin")); return ( @@ -102,20 +91,14 @@ export const createBoardLayout = ({ - } - actions={headerActions} - hasNavigation={false} - /> - } - > - {children} - + + } + actions={headerActions} + hasNavigation={false} + /> + {children} + diff --git a/apps/nextjs/src/app/[locale]/manage/tools/backup/_components/backup-export-card.tsx b/apps/nextjs/src/app/[locale]/manage/tools/backup/_components/backup-export-card.tsx index 8f4d7f06e..dc49f71a8 100644 --- a/apps/nextjs/src/app/[locale]/manage/tools/backup/_components/backup-export-card.tsx +++ b/apps/nextjs/src/app/[locale]/manage/tools/backup/_components/backup-export-card.tsx @@ -1,14 +1,49 @@ "use client"; -import { Card, Group, Stack, Text, Title } from "@mantine/core"; -import { IconDatabaseExport } from "@tabler/icons-react"; +import { useState } from "react"; +import { Alert, Button, Card, Group, Stack, Text, Title } from "@mantine/core"; +import { IconAlertTriangle, IconDatabaseExport, IconDownload } from "@tabler/icons-react"; import { useScopedI18n } from "@homarr/translation/client"; -import { BackupExportButton } from "~/components/backup"; +import { DangerousActionConfirmation } from "~/components/backup/dangerous-action-confirmation"; export const BackupExportCard = () => { const t = useScopedI18n("management.page.tool.backup.export"); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [showConfirm, setShowConfirm] = useState(false); + + const handleExport = async () => { + setLoading(true); + setError(null); + try { + const response = await fetch("/api/backup/export"); + if (!response.ok) { + const errorMessage = await response.json().then( + (data) => data.error, + () => `Server returned ${response.status}`, + ); + throw new Error(errorMessage ?? t("error")); + } + + const blob = await response.blob(); + const url = URL.createObjectURL(blob); + const filename = + response.headers.get("Content-Disposition")?.match(/filename="(.+)"/)?.[1] ?? "homarr-backup.zip"; + + const a = document.createElement("a"); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + setShowConfirm(false); + } catch (err) { + setError(err instanceof Error ? err.message : t("error")); + } finally { + setLoading(false); + } + }; return ( @@ -20,7 +55,33 @@ export const BackupExportCard = () => { {t("description")} - + {error && ( + }> + {error} + + )} + + {showConfirm ? ( + } + cancelLabel={t("confirm.cancel")} + onConfirm={handleExport} + onCancel={() => setShowConfirm(false)} + disabled={loading} + color="orange" + /> + ) : ( + + + + )} ); diff --git a/apps/nextjs/src/components/backup/backup-export-button.tsx b/apps/nextjs/src/components/backup/backup-export-button.tsx deleted file mode 100644 index 6116d0a16..000000000 --- a/apps/nextjs/src/components/backup/backup-export-button.tsx +++ /dev/null @@ -1,79 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { Alert, Button, Group, Stack } from "@mantine/core"; -import { IconAlertTriangle, IconDownload } from "@tabler/icons-react"; - -import { useScopedI18n } from "@homarr/translation/client"; - -import { DangerousActionConfirmation } from "./dangerous-action-confirmation"; - -export const BackupExportButton = () => { - const t = useScopedI18n("management.page.tool.backup.export"); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - const [showConfirm, setShowConfirm] = useState(false); - - const handleExport = async () => { - setLoading(true); - setError(null); - try { - const response = await fetch("/api/backup/export"); - if (!response.ok) { - const errorMessage = await response.json().then( - (data) => data.error, - () => `Server returned ${response.status}`, - ); - throw new Error(errorMessage ?? t("error")); - } - - const blob = await response.blob(); - const url = URL.createObjectURL(blob); - const filename = - response.headers.get("Content-Disposition")?.match(/filename="(.+)"/)?.[1] ?? "homarr-backup.zip"; - - const anchor = document.createElement("a"); - anchor.href = url; - anchor.download = filename; - anchor.click(); - URL.revokeObjectURL(url); - setShowConfirm(false); - } catch (cause) { - setError(cause instanceof Error ? cause.message : t("error")); - } finally { - setLoading(false); - } - }; - - return ( - - {error && ( - }> - {error} - - )} - - {showConfirm ? ( - } - cancelLabel={t("confirm.cancel")} - onConfirm={handleExport} - onCancel={() => setShowConfirm(false)} - disabled={loading} - color="orange" - /> - ) : ( - - - - )} - - ); -}; diff --git a/apps/nextjs/src/components/backup/index.ts b/apps/nextjs/src/components/backup/index.ts index 1f6196125..accd1a935 100644 --- a/apps/nextjs/src/components/backup/index.ts +++ b/apps/nextjs/src/components/backup/index.ts @@ -1,4 +1,3 @@ -export { BackupExportButton } from "./backup-export-button"; export { DatabaseRestoreFlow } from "./database-restore-flow"; export { DangerousActionConfirmation } from "./dangerous-action-confirmation"; export type { BackupAnalysis, RestoreStep } from "./types"; diff --git a/apps/nextjs/src/components/layout/header.tsx b/apps/nextjs/src/components/layout/header.tsx index a512229d0..2c46608c0 100644 --- a/apps/nextjs/src/components/layout/header.tsx +++ b/apps/nextjs/src/components/layout/header.tsx @@ -19,31 +19,25 @@ interface Props { export const MainHeader = ({ logo, actions, hasNavigation = true }: Props) => { return ( - + + + {hasNavigation && } + + {logo ?? } + + + + + + + {actions} + + + + + + + ); }; - -export const MainHeaderContent = ({ logo, actions, hasNavigation = true }: Props) => ( - <> - - - {hasNavigation && } - - {logo ?? } - - - - - - - {actions} - - - - - - - - -); diff --git a/apps/nextjs/src/components/layout/shell.tsx b/apps/nextjs/src/components/layout/shell.tsx index 01ef8b7e0..b51cf3220 100644 --- a/apps/nextjs/src/components/layout/shell.tsx +++ b/apps/nextjs/src/components/layout/shell.tsx @@ -10,13 +10,11 @@ import { navigationCollapsedAtom } from "./header/burger"; interface ClientShellProps { hasHeader?: boolean; hasNavigation?: boolean; - headerHeight?: number; } export const ClientShell = ({ hasHeader = true, hasNavigation = true, - headerHeight = 60, children, }: PropsWithChildren) => { const collapsed = useAtomValue(navigationCollapsedAtom); @@ -25,7 +23,7 @@ export const ClientShell = ({ return ( " }, - { kind: "added", value: "+ WORKSHOP_API_URL: https://v2.preview.homarr.dev/ # Only during the beta" }, - { kind: "context", value: "" }, - { kind: "context", value: " volumes:" }, - { kind: "context", value: " homarr-v2:" }, -] as const; - -interface V2BetaAnnouncementProps { - canExportBackup: boolean; - dismissalCookieName: string; - onDismiss: () => void; -} - -export const V2BetaAnnouncement = ({ canExportBackup, dismissalCookieName, onDismiss }: V2BetaAnnouncementProps) => { - const { openModal } = useModalAction(V2BetaAnnouncementModal); - - const dismiss = () => { - setClientCookie(dismissalCookieName, "dismissed", { - maxAge: 60 * 60 * 24 * 7, - path: "/", - sameSite: "lax", - secure: window.location.protocol === "https:", - }); - onDismiss(); - }; - - return ( - - - - - We've been cooking, Homarr v2 beta is here - - - Custom Widgets & CSS workshop, new drag-and-drop, new container system, side-bars are back, new widgets - and so much more, come check it out and help us make v2 ready for everyone - - - - - - - - - - - - ); -}; - -interface V2BetaAnnouncementModalProps { - canExportBackup: boolean; -} - -const V2BetaAnnouncementModal = createModal(({ innerProps }) => { - return ( - - - - Help us get Homarr v2 ready - - We've been working on Homarr's v2 update for a while now. There is no fixed release date yet, it - will be ready when it's ready. We{" "} - - need your help - {" "} - and feedback to help us become confident enough to release it for everyone. - - - - - - } title="Workshop"> - Share and discover Custom Widgets and CSS made by the Homarr community. - - } title="Complete drag-and-drop rewrite"> - Move, resize, multi-select, and arrange your dashboard with a board editor rebuilt from the ground up. - - } title="Custom Widgets v2"> - We think you can make almost any widget with live previews, API requests, options, actions, and validation. - - } title="Assistant"> - Assistant can directly make a Custom Widget for you. Try giving it the API docs for your latest selfhosted - tool and asking it to build a custom widget for it. - - - - - - - - - } - aria-label="Test Homarr v2 on your server" - > - - Test Homarr v2 on your server - - Back up this instance, then update your Docker Compose file. - - - - - - - - - - } title="Your testing is essential"> - - Everyone uses Homarr in a different way. If anything breaks or does not feel right, the easiest way to get it - fixed as soon as possible is to{" "} - - tell us about it on Discord - {" "} - in our dedicated v2 channel. You can also{" "} - - open a dedicated GitHub issue - - {"."} Every report matters. - - - - - - - - - - - - - - - ); -}).withOptions({ - defaultTitle: "Homarr v2 beta", - size: 1000, - yOffset: 128, - zIndex: getDefaultZIndex("modal") + 2, -}); - -interface V2BetaInstallInstructionsProps { - canExportBackup: boolean; -} - -const V2BetaInstallInstructions = ({ canExportBackup }: V2BetaInstallInstructionsProps) => ( - - {canExportBackup ? ( - - - - - Back up this Homarr instance - - - Download a full backup before testing v2. The archive contains sensitive data, so keep it somewhere safe. - - - - - ) : ( - - Direct backup downloads are available here to Homarr administrators using SQLite. If you use MySQL or - PostgreSQL, back up the database with its own tools before testing v2. - - )} - - - Update your Docker Compose file - - Replace the current image and add the beta-only Workshop URL. Use the example below as a starting point. - - - - - - - - docker-compose.yml - - - - {composeDiffLines.map((line, index) => ( - - {line.value || " "} - - ))} - - - - - Replace <SECRET_KEY> with your existing encryption key. The Workshop URL is only required while - the beta uses the preview Workshop. - - -); - -interface FeatureCardProps { - icon: ReactNode; - title: string; - children: ReactNode; -} - -const FeatureCard = ({ icon, title, children }: FeatureCardProps) => ( - - - - {icon} - - - - {title} - - - {children} - - - - -); diff --git a/apps/nextjs/src/components/layout/v2-beta-announcement/v2-beta-dashboard-shell.tsx b/apps/nextjs/src/components/layout/v2-beta-announcement/v2-beta-dashboard-shell.tsx deleted file mode 100644 index 0fa2c0bbe..000000000 --- a/apps/nextjs/src/components/layout/v2-beta-announcement/v2-beta-dashboard-shell.tsx +++ /dev/null @@ -1,42 +0,0 @@ -"use client"; - -import type { PropsWithChildren, ReactNode } from "react"; -import { useState } from "react"; -import { AppShellHeader, AppShellMain } from "@mantine/core"; - -import { ClientShell } from "../shell"; -import { V2BetaAnnouncement } from "./v2-beta-announcement"; - -interface V2BetaDashboardShellProps { - canExportBackup: boolean; - dismissalCookieName: string; - initiallyDismissed: boolean; - header: ReactNode; -} - -export const V2BetaDashboardShell = ({ - canExportBackup, - dismissalCookieName, - initiallyDismissed, - header, - children, -}: PropsWithChildren) => { - const [isAnnouncementVisible, setIsAnnouncementVisible] = useState(!initiallyDismissed); - const headerHeight = isAnnouncementVisible ? 112 : 60; - - return ( - - - {isAnnouncementVisible && ( - setIsAnnouncementVisible(false)} - /> - )} - {header} - - {children} - - ); -}; diff --git a/packages/definitions/src/cookie.ts b/packages/definitions/src/cookie.ts index 67ba5a36d..69cb24930 100644 --- a/packages/definitions/src/cookie.ts +++ b/packages/definitions/src/cookie.ts @@ -1,3 +1,2 @@ export const colorSchemeCookieKey = "homarr.color-scheme"; export const localeCookieKey = "homarr.locale"; -export const v2BetaAnnouncementCookieKey = "homarr.v2-beta-announcement";