From 2613a556b1b383735e7c167482a249dffe3f94ae Mon Sep 17 00:00:00 2001 From: ajnart Date: Mon, 24 Aug 2026 07:58:48 +0200 Subject: [PATCH] fix: address branding review feedback --- .../[locale]/boards/(content)/_creator.tsx | 5 +---- .../boards/[name]/settings/_general.tsx | 3 +-- apps/nextjs/src/app/[locale]/layout.tsx | 19 ++++++++++--------- .../src/app/[locale]/manage/about/page.tsx | 3 +-- .../app/[locale]/manage/assistant/page.tsx | 3 +-- apps/nextjs/src/app/[locale]/manage/page.tsx | 3 +-- .../_components/branding-settings-form.tsx | 7 ++----- .../src/app/[locale]/manage/settings/page.tsx | 3 +-- .../app/[locale]/manage/tools/api/page.tsx | 3 +-- .../app/[locale]/manage/tools/backup/page.tsx | 3 +-- .../app/[locale]/manage/tools/logs/page.tsx | 3 +-- .../app/[locale]/manage/tools/tasks/page.tsx | 3 +-- .../manage/users/[userId]/general/page.tsx | 3 +-- .../app/[locale]/manage/users/create/page.tsx | 3 +-- .../src/app/[locale]/manage/users/page.tsx | 3 +-- .../src/app/api/assistant/chat/route.ts | 11 ++++++++++- .../header/configurable-header.module.css | 11 ++++++----- apps/nextjs/src/metadata.ts | 4 ---- packages/api/src/router/serverSettings.ts | 12 ++++++++---- packages/api/src/router/user.ts | 9 +++++++-- packages/onboarding/src/setup-studio.tsx | 2 +- packages/translation/src/lang/en.json | 4 ++++ 22 files changed, 61 insertions(+), 59 deletions(-) delete mode 100644 apps/nextjs/src/metadata.ts diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx index 4a66618db..ae4f66802 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx @@ -16,7 +16,6 @@ import type { WidgetKind } from "@homarr/definitions"; import { getI18n } from "@homarr/translation/server"; import { prefetchForKindAsync } from "@homarr/widgets/prefetch"; -import { createMetaTitle } from "~/metadata"; import { env } from "~/env"; import { createBoardLayout } from "../_layout-creator"; import type { Board, Item } from "../_types"; @@ -115,9 +114,7 @@ export const createBoardContentPage = >( const favicon = !isNullOrWhitespace(board.faviconImageUrl) ? board.faviconImageUrl : brandFavicon; return { - title: board.metaTitle - ? { absolute: board.metaTitle } - : createMetaTitle(t("content.metaTitle", { boardName: board.name })), + title: board.metaTitle ? { absolute: board.metaTitle } : t("content.metaTitle", { boardName: board.name }), icons: { icon: favicon, apple: favicon, diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_general.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_general.tsx index 2b30cf25f..5dd02c674 100644 --- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_general.tsx +++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_general.tsx @@ -11,7 +11,6 @@ import { useI18n } from "@homarr/translation/client"; import { useSettings } from "@homarr/settings"; import { SectionCard } from "~/components/manage/section-card"; -import { createMetaTitle } from "~/metadata"; import type { Board } from "../../_types"; import type { FormValues } from "./_settings-form"; @@ -23,7 +22,7 @@ interface Props { export const GeneralSettingsContent = ({ board, form }: Props) => { const t = useI18n("board"); const { branding } = useSettings(); - const defaultMetaTitle = createMetaTitle(t("content.metaTitle", { boardName: board.name }), branding.appName); + const defaultMetaTitle = t("content.metaTitle", { boardName: board.name }); useLogoPreview(form.values.logoImageUrl); const metaTitleStatus = useMetaTitlePreview(form.values.metaTitle, defaultMetaTitle); diff --git a/apps/nextjs/src/app/[locale]/layout.tsx b/apps/nextjs/src/app/[locale]/layout.tsx index b1fd81106..bbce84cce 100644 --- a/apps/nextjs/src/app/[locale]/layout.tsx +++ b/apps/nextjs/src/app/[locale]/layout.tsx @@ -27,6 +27,7 @@ import { SettingsProvider } from "@homarr/settings"; import { SpotlightProvider } from "@homarr/spotlight"; import type { SupportedLanguage } from "@homarr/translation"; import { isLocaleRTL, isLocaleSupported } from "@homarr/translation"; +import { getI18n } from "@homarr/translation/server"; import { resolveHomarrUrlConfig } from "@homarr/workshop/schema"; import { Analytics } from "~/components/layout/analytics"; @@ -53,25 +54,26 @@ const fontSans = Inter({ const logger = createLogger({ module: "rootLayout" }); -const defaultDescription = - "A self-hosted dashboard for the *arr stack and your entire homelab. Integrates with 50+ services, real-time widgets, no config files."; - -// eslint-disable-next-line no-restricted-syntax export const generateMetadata = async (): Promise => { - const [serverSettings, colorScheme] = await Promise.all([getRscServerSettingsAsync(), getCurrentColorSchemeAsync()]); + const [serverSettings, colorScheme, t] = await Promise.all([ + getRscServerSettingsAsync(), + getCurrentColorSchemeAsync(), + getI18n("metadata"), + ]); const { appName, faviconImageUrl, logoImageUrl } = serverSettings.branding; const logo = logoImageUrl ?? "/logo/logo.png"; const favicon = faviconImageUrl ?? logo; + const description = t("description"); return { title: { default: appName, template: `%s • ${appName}`, }, - description: defaultDescription, + description, openGraph: { - title: `${appName} Dashboard`, - description: defaultDescription, + title: t("dashboardTitle", { appName }), + description, url: env.HOMARR_WEBSITE_URL, siteName: appName, }, @@ -88,7 +90,6 @@ export const generateMetadata = async (): Promise => { }; }; -// eslint-disable-next-line no-restricted-syntax export const generateViewport = async (): Promise => { const serverSettings = await getRscServerSettingsAsync(); return { themeColor: serverSettings.branding.primaryColor }; diff --git a/apps/nextjs/src/app/[locale]/manage/about/page.tsx b/apps/nextjs/src/app/[locale]/manage/about/page.tsx index 3ccb77c1a..f105302d2 100644 --- a/apps/nextjs/src/app/[locale]/manage/about/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/about/page.tsx @@ -39,7 +39,6 @@ import { getI18n } from "@homarr/translation/server"; import { homarrLogoPath } from "~/components/layout/logo/constants"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; -import { createMetaTitle } from "~/metadata"; import { getDependenciesAsync, getPackageVersion } from "~/versions/package-reader"; import githubContributors from "@static-data/contributors.json"; import openCollectiveContributors from "@static-data/opencollective-contributors.json"; @@ -50,7 +49,7 @@ export async function generateMetadata() { const t = await getI18n("management"); return { - title: createMetaTitle(t("metaTitle")), + title: t("metaTitle"), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/assistant/page.tsx b/apps/nextjs/src/app/[locale]/manage/assistant/page.tsx index 7ff16144c..a3ee8961e 100644 --- a/apps/nextjs/src/app/[locale]/manage/assistant/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/assistant/page.tsx @@ -5,13 +5,12 @@ import { auth } from "@homarr/auth/next"; import { getI18n } from "@homarr/translation/server"; import { ManagePageLayout } from "~/components/manage/manage-page-layout"; -import { createMetaTitle } from "~/metadata"; import { AssistantConfiguration } from "./_components/assistant-configuration"; export async function generateMetadata() { const t = await getI18n("management.page.settings.section.assistant"); return { - title: createMetaTitle(t("title")), + title: t("title"), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/page.tsx b/apps/nextjs/src/app/[locale]/manage/page.tsx index d635b9db3..b7925c235 100644 --- a/apps/nextjs/src/app/[locale]/manage/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/page.tsx @@ -7,14 +7,13 @@ import { getI18n } from "@homarr/translation/server"; import { Link } from "@homarr/ui"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; -import { createMetaTitle } from "~/metadata"; import { HeroBanner } from "./_components/hero-banner"; export async function generateMetadata(): Promise { const t = await getI18n("management"); return { - title: createMetaTitle(t("metaTitle")), + title: t("metaTitle"), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/settings/_components/branding-settings-form.tsx b/apps/nextjs/src/app/[locale]/manage/settings/_components/branding-settings-form.tsx index 694f7fafe..ae580a324 100644 --- a/apps/nextjs/src/app/[locale]/manage/settings/_components/branding-settings-form.tsx +++ b/apps/nextjs/src/app/[locale]/manage/settings/_components/branding-settings-form.tsx @@ -93,10 +93,7 @@ export const BrandingSettingsForm = ({ form }: BrandingSettingsFormProps) => { value={branding.defaultRadius} labels={ Object.fromEntries( - cornerStyleValues.map((cornerStyle) => [ - cornerStyle, - t(`defaultRadius.options.${cornerStyle}` as never), - ]), + cornerStyleValues.map((cornerStyle) => [cornerStyle, t(`defaultRadius.options.${cornerStyle}`)]), ) as Record<(typeof cornerStyleValues)[number], string> } onChange={(cornerStyle) => form.setFieldValue("branding.defaultRadius", cornerStyle)} @@ -190,7 +187,7 @@ const BrandingSummary = ({ form }: BrandingSettingsFormProps) => { {t("summary.title")} - {t("summary.cornerStyle")}: {t(`defaultRadius.options.${branding.defaultRadius}` as never)} + {t("summary.cornerStyle")}: {t(`defaultRadius.options.${branding.defaultRadius}`)} diff --git a/apps/nextjs/src/app/[locale]/manage/settings/page.tsx b/apps/nextjs/src/app/[locale]/manage/settings/page.tsx index a16a3380a..18052e01d 100644 --- a/apps/nextjs/src/app/[locale]/manage/settings/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/settings/page.tsx @@ -5,14 +5,13 @@ import { auth } from "@homarr/auth/next"; import { getI18n } from "@homarr/translation/server"; import { ManagePageLayout } from "~/components/manage/manage-page-layout"; -import { createMetaTitle } from "~/metadata"; import { SettingsForm } from "./_components/settings-form"; export async function generateMetadata() { const t = await getI18n("management"); return { - title: createMetaTitle(t("metaTitle")), + title: t("metaTitle"), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/tools/api/page.tsx b/apps/nextjs/src/app/[locale]/manage/tools/api/page.tsx index 73370b5c8..eb2cd98b7 100644 --- a/apps/nextjs/src/app/[locale]/manage/tools/api/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/tools/api/page.tsx @@ -9,7 +9,6 @@ import { auth } from "@homarr/auth/next"; import { extractBaseUrlFromHeaders } from "@homarr/common"; import { getI18n } from "@homarr/translation/server"; -import { createMetaTitle } from "~/metadata"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; import { extractMcpTools } from "~/app/api/mcp/_extract-tools"; import { ApiKeysManagement } from "./components/api-keys"; @@ -63,7 +62,7 @@ export async function generateMetadata() { const t = await getI18n("management"); return { - title: createMetaTitle(t("metaTitle")), + title: t("metaTitle"), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/tools/backup/page.tsx b/apps/nextjs/src/app/[locale]/manage/tools/backup/page.tsx index 673a6ab7d..731c5873c 100644 --- a/apps/nextjs/src/app/[locale]/manage/tools/backup/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/tools/backup/page.tsx @@ -5,7 +5,6 @@ import { auth } from "@homarr/auth/next"; import { dbEnv } from "@homarr/core/infrastructure/db/env"; import { getI18n } from "@homarr/translation/server"; -import { createMetaTitle } from "~/metadata"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; import { BackupExportCard } from "./_components/backup-export-card"; import { BackupImportCard } from "./_components/backup-import-card"; @@ -19,7 +18,7 @@ export async function generateMetadata() { const t = await getI18n("management.page.tool.backup"); return { - title: createMetaTitle(t("metaTitle")), + title: t("metaTitle"), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/tools/logs/page.tsx b/apps/nextjs/src/app/[locale]/manage/tools/logs/page.tsx index 0eab621fb..bb18da0a8 100644 --- a/apps/nextjs/src/app/[locale]/manage/tools/logs/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/tools/logs/page.tsx @@ -11,7 +11,6 @@ import { logsEnv } from "@homarr/core/infrastructure/logs/env"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; import { fullHeightWithoutHeaderAndFooter } from "~/constants"; -import { createMetaTitle } from "~/metadata"; import { ClientSideTerminalComponent } from "./client"; import { LogFontSizeSlider } from "./font-size-slider"; import { LogLevelSelection } from "./level-selection"; @@ -25,7 +24,7 @@ export async function generateMetadata() { const t = await getI18n("management"); return { - title: createMetaTitle(t("metaTitle")), + title: t("metaTitle"), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/tools/tasks/page.tsx b/apps/nextjs/src/app/[locale]/manage/tools/tasks/page.tsx index 674227277..14c6af817 100644 --- a/apps/nextjs/src/app/[locale]/manage/tools/tasks/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/tools/tasks/page.tsx @@ -6,7 +6,6 @@ import { auth } from "@homarr/auth/next"; import { getI18n } from "@homarr/translation/server"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; -import { createMetaTitle } from "~/metadata"; import { TasksTable } from "./_components/tasks-table"; export async function generateMetadata() { @@ -17,7 +16,7 @@ export async function generateMetadata() { const t = await getI18n("management"); return { - title: createMetaTitle(t("metaTitle")), + title: t("metaTitle"), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx index 358533b49..4ba467d78 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx @@ -8,7 +8,6 @@ import { getI18n } from "@homarr/translation/server"; import { DangerZoneItem, DangerZoneRoot } from "~/components/manage/danger-zone"; import { catchTrpcNotFound } from "~/errors/trpc-catch-error"; -import { createMetaTitle } from "~/metadata"; import { canAccessUserEditPage } from "../access"; import { DeleteUserButton } from "./_components/_delete-user-button"; import { UserGeneralSettingsForm } from "./_components/_general-settings-form"; @@ -38,7 +37,7 @@ export async function generateMetadata(props: Props) { return { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - title: createMetaTitle(t("metaTitle", { username: user.name! })), + title: t("metaTitle", { username: user.name! }), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/users/create/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/create/page.tsx index 41abd5393..5a2a5e96c 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/create/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/create/page.tsx @@ -8,7 +8,6 @@ import { everyoneGroup } from "@homarr/definitions"; import { getI18n } from "@homarr/translation/server"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; -import { createMetaTitle } from "~/metadata"; import { UserCreateStepperComponent } from "./_components/create-user-stepper"; export async function generateMetadata() { @@ -22,7 +21,7 @@ export async function generateMetadata() { const t = await getI18n("management.page.user.create"); return { - title: createMetaTitle(t("metaTitle")), + title: t("metaTitle"), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/users/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/page.tsx index 57271286d..5ac353e20 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/page.tsx @@ -9,7 +9,6 @@ import { Link } from "@homarr/ui"; import { TourTarget } from "~/components/layout/header/tour-target"; import { ManagePageLayout } from "~/components/manage/manage-page-layout"; import { MobileAffixButton } from "~/components/manage/mobile-affix-button"; -import { createMetaTitle } from "~/metadata"; import { UserListComponent } from "./_components/user-list"; export async function generateMetadata() { @@ -20,7 +19,7 @@ export async function generateMetadata() { const t = await getI18n("management.page.user.list"); return { - title: createMetaTitle(t("metaTitle")), + title: t("metaTitle"), }; } diff --git a/apps/nextjs/src/app/api/assistant/chat/route.ts b/apps/nextjs/src/app/api/assistant/chat/route.ts index a57d41618..ab222be86 100644 --- a/apps/nextjs/src/app/api/assistant/chat/route.ts +++ b/apps/nextjs/src/app/api/assistant/chat/route.ts @@ -4,6 +4,7 @@ import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; import type { MetadataExtractor } from "@ai-sdk/openai-compatible"; import type { ToolSet, UIMessage } from "ai"; import { createUIMessageStream, createUIMessageStreamResponse, jsonSchema, stepCountIs, streamText, tool } from "ai"; +import { cookies } from "next/headers"; import { z } from "zod/v4"; import { createTRPCContext, mcpRouter } from "@homarr/api/mcp"; @@ -24,6 +25,10 @@ import { assistantProviderRequiresApiKey, assistantReasoningModes, } from "@homarr/definitions"; +import { localeCookieKey } from "@homarr/definitions/cookie"; +import type { SupportedLanguage } from "@homarr/translation"; +import { fallbackLocale, isLocaleSupported } from "@homarr/translation"; +import { getI18n } from "@homarr/translation/server"; import { resolveHomarrUrlConfig } from "@homarr/workshop/schema"; import { browserToolContracts } from "~/components/assistant/assistant-tool-contracts"; @@ -414,7 +419,11 @@ export async function POST(request: Request) { const requiresApiKey = configuration ? assistantProviderRequiresApiKey(configuration.provider) : false; if (!configuration?.enabled || !configuration.modelId || (requiresApiKey && !configuration.encryptedApiKey)) { - return Response.json({ error: "Assistant is not configured." }, { status: 503 }); + const requestedLocale = (await cookies()).get(localeCookieKey)?.value; + let locale: SupportedLanguage = fallbackLocale; + if (requestedLocale && isLocaleSupported(requestedLocale)) locale = requestedLocale; + const t = await getI18n({ locale, namespace: "assistant.unavailable" }); + return Response.json({ error: t("notConfigured") }, { status: 503 }); } let homarrProviderToken: string | null | undefined; diff --git a/apps/nextjs/src/components/layout/header/configurable-header.module.css b/apps/nextjs/src/components/layout/header/configurable-header.module.css index 90d7ff292..7dd45702e 100644 --- a/apps/nextjs/src/components/layout/header/configurable-header.module.css +++ b/apps/nextjs/src/components/layout/header/configurable-header.module.css @@ -10,7 +10,6 @@ width: 100%; height: 100%; padding-inline: var(--mantine-spacing-md); - direction: ltr; } .zone { @@ -52,7 +51,7 @@ border-radius: var(--mantine-radius-md); } -.logo :global(img.logo) { +.logo img { display: block; width: auto; height: auto; @@ -65,7 +64,9 @@ flex-wrap: nowrap; } -.logo :global(.mantine-Title-root) { +.logo h1, +.logo h2, +.logo h3 { display: none; } @@ -84,7 +85,7 @@ max-width: 100%; } -.zone :global([data-tour-target="board-search"]) { +.zone [data-tour-target="board-search"] { min-width: 0; max-width: 100%; } @@ -119,7 +120,7 @@ transform: translateY(0) scale(1); } -.floatingControls :global(.mantine-UnstyledButton-root) { +.floatingControls button { display: flex; align-items: center; justify-content: center; diff --git a/apps/nextjs/src/metadata.ts b/apps/nextjs/src/metadata.ts deleted file mode 100644 index 0673d5deb..000000000 --- a/apps/nextjs/src/metadata.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const createMetaTitle = (name: string, appName?: string) => { - if (appName) return `${name} • ${appName}`; - return name; -}; diff --git a/packages/api/src/router/serverSettings.ts b/packages/api/src/router/serverSettings.ts index 7e89bca61..38bfa712d 100644 --- a/packages/api/src/router/serverSettings.ts +++ b/packages/api/src/router/serverSettings.ts @@ -42,10 +42,14 @@ export const serverSettingsRouter = createTRPCRouter({ getAll: permissionRequiredProcedure.requiresPermission("admin").query(async ({ ctx }) => { return await getServerSettingsAsync(ctx.db); }), - getBranding: publicProcedure.query(async ({ ctx }) => { - const branding = await getServerSettingByKeyAsync(ctx.db, "branding"); - return parseBrandingSettings(branding); - }), + getBranding: publicProcedure + .meta({ + mcp: { enabled: true, description: "Returns the public instance branding configuration." }, + }) + .query(async ({ ctx }) => { + const branding = await getServerSettingByKeyAsync(ctx.db, "branding"); + return parseBrandingSettings(branding); + }), getBoardSettings: permissionRequiredProcedure .requiresPermission("admin") .meta({ diff --git a/packages/api/src/router/user.ts b/packages/api/src/router/user.ts index 8c9d192da..734121e64 100644 --- a/packages/api/src/router/user.ts +++ b/packages/api/src/router/user.ts @@ -727,8 +727,6 @@ export const userRouter = createTRPCRouter({ .where(eq(users.id, input.id)); }), changeHeaderPreferences: protectedProcedure - .input(z.object({ id: z.string(), headerPreferences: headerPreferencesMutationSchema })) - .output(z.void()) .meta({ openapi: { method: "PATCH", @@ -736,7 +734,14 @@ export const userRouter = createTRPCRouter({ tags: ["users"], protect: true, }, + mcp: { + enabled: true, + description: + "Update the header layout preferences of a user. REQUIRED: id (user ID), headerPreferences (zones for left, center and right, visible flag and searchDisplay). Admins can change any user; other users can only change their own. Board shortcut items must reference boards the target user can view", + }, }) + .input(z.object({ id: z.string(), headerPreferences: headerPreferencesMutationSchema })) + .output(z.void()) .mutation(async ({ input, ctx }) => { if (!ctx.session.user.permissions.includes("admin") && ctx.session.user.id !== input.id) { throw new TRPCError({ diff --git a/packages/onboarding/src/setup-studio.tsx b/packages/onboarding/src/setup-studio.tsx index 7c724a58b..3efc44c21 100644 --- a/packages/onboarding/src/setup-studio.tsx +++ b/packages/onboarding/src/setup-studio.tsx @@ -1464,7 +1464,7 @@ const BoardBuilder = (props: StudioSectionProps) => { value={props.itemRadius} labels={ Object.fromEntries( - cornerStyleValues.map((cornerStyle) => [cornerStyle, t(`radiusOptions.${cornerStyle}` as never)]), + cornerStyleValues.map((cornerStyle) => [cornerStyle, t(`radiusOptions.${cornerStyle}`)]), ) as Record } onChange={props.setItemRadius} diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index dcc0e4dad..3b4580a11 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -7597,6 +7597,10 @@ } } }, + "metadata": { + "description": "A self-hosted dashboard for the *arr stack and your entire homelab. Integrates with 50+ services, real-time widgets, no config files.", + "dashboardTitle": "{appName} Dashboard" + }, "assistant": { "title": "Assistant", "open": "Open Assistant",