Compare commits

..
Author SHA1 Message Date
SeDemal 0f9c31a4fb feat: Unify scaling and apply to clock widget 2025-02-15 09:57:57 +01:00
172 changed files with 1716 additions and 6297 deletions

No files matched your search

-1
View File
@@ -31,7 +31,6 @@ body:
label: Version
description: What version of Homarr are you running?
options:
- 1.5.0
- 1.4.0
- 1.3.1
- 1.3.0
+1 -1
View File
@@ -1 +1 @@
22.14.0
22.13.1
-51
View File
@@ -1,54 +1,3 @@
## [1.6.0](https://github.com/homarr-labs/homarr/compare/v1.5.0...v1.6.0) (2025-02-14)
### Features
* **lang:** add catalan language ([#2283](https://github.com/homarr-labs/homarr/issues/2283)) ([749b0c6](https://github.com/homarr-labs/homarr/commit/749b0c6ee9dc7802ae10393b90e19bf3c43bf9e0))
* **settings:** add simple-ping settings ([#2118](https://github.com/homarr-labs/homarr/issues/2118)) ([dff6cb9](https://github.com/homarr-labs/homarr/commit/dff6cb9d31c3884bf811774a7c90dad27ac19691))
### Bug Fixes
* add error handling for available updates ([#2289](https://github.com/homarr-labs/homarr/issues/2289)) ([0502e47](https://github.com/homarr-labs/homarr/commit/0502e477707932db96b25372ddab8263572b389c))
* add mysql pool with keep-alive to not close the connection ([#2282](https://github.com/homarr-labs/homarr/issues/2282)) ([a38afb0](https://github.com/homarr-labs/homarr/commit/a38afb05d35cf2bdba378be3744f3a8b6c9d38f6))
* **calendar:** info popover overlap ([#2268](https://github.com/homarr-labs/homarr/issues/2268)) ([584c660](https://github.com/homarr-labs/homarr/commit/584c660906f5ae7e8ab1c88ce9cfbbbbdcdb5145))
* **deps:** update dependency @ctrl/qbittorrent to ^9.3.0 ([#2310](https://github.com/homarr-labs/homarr/issues/2310)) ([8649fac](https://github.com/homarr-labs/homarr/commit/8649faca7929daff426607f104057fce26edd049))
* **deps:** update dependency @ctrl/qbittorrent to ^9.4.0 ([#2315](https://github.com/homarr-labs/homarr/issues/2315)) ([4d3cc1d](https://github.com/homarr-labs/homarr/commit/4d3cc1d80698e659a669de7d4bd0d185ad68605e))
* **deps:** update dependency @extractus/feed-extractor to v7.1.4 ([#2281](https://github.com/homarr-labs/homarr/issues/2281)) ([7edaa48](https://github.com/homarr-labs/homarr/commit/7edaa48fb9bcf48e8b734beec5a8bad003124233))
* **deps:** update dependency drizzle-orm to ^0.39.3 ([#2295](https://github.com/homarr-labs/homarr/issues/2295)) ([8ec0080](https://github.com/homarr-labs/homarr/commit/8ec0080fca17c530cdd0576ba101299a0f769463))
* **deps:** update dependency jotai to ^2.12.0 ([#2306](https://github.com/homarr-labs/homarr/issues/2306)) ([fc43da5](https://github.com/homarr-labs/homarr/commit/fc43da59f845934da428bcafd4ec46551ace8a81))
* **deps:** update dependency sass to ^1.85.0 ([#2326](https://github.com/homarr-labs/homarr/issues/2326)) ([8ee39eb](https://github.com/homarr-labs/homarr/commit/8ee39eb6db89fdeaec0fae7988ffa97280c2f5ab))
* **deps:** update dependency typescript-eslint to ^8.24.0 ([#2297](https://github.com/homarr-labs/homarr/issues/2297)) ([8371f46](https://github.com/homarr-labs/homarr/commit/8371f46d34ad36fd25f23d91804d3f95439d7012))
* **deps:** update dependency zod to ^3.24.2 ([#2311](https://github.com/homarr-labs/homarr/issues/2311)) ([379675a](https://github.com/homarr-labs/homarr/commit/379675a901637a1b878595a80d0776d05311218d))
* **deps:** update mantine monorepo to ^7.16.3 ([#2275](https://github.com/homarr-labs/homarr/issues/2275)) ([42a6814](https://github.com/homarr-labs/homarr/commit/42a68140b1493cc171977d996eea16d876dce60c))
* **deps:** update nextjs monorepo to ^15.1.7 ([#2302](https://github.com/homarr-labs/homarr/issues/2302)) ([2399967](https://github.com/homarr-labs/homarr/commit/2399967868652436ee6827bce8883c1b4a00af0e))
* **deps:** update turbo monorepo to ^2.4.1 ([#2296](https://github.com/homarr-labs/homarr/issues/2296)) ([2b9f834](https://github.com/homarr-labs/homarr/commit/2b9f834d9645aaf5f5aa1b8a5f1e48300389ac8b))
* **deps:** update turbo monorepo to ^2.4.2 ([#2313](https://github.com/homarr-labs/homarr/issues/2313)) ([1045465](https://github.com/homarr-labs/homarr/commit/10454656a286c320a8d3b5ff12a138025d871b20))
* **users:** edit profile username not lowercase ([#2279](https://github.com/homarr-labs/homarr/issues/2279)) ([b7fd247](https://github.com/homarr-labs/homarr/commit/b7fd2470ee0d85821f79a34e839e62fbab6989c6))
## [1.5.0](https://github.com/homarr-labs/homarr/compare/v1.4.0...v1.5.0) (2025-02-07)
### Features
* add api key delete ([#2210](https://github.com/homarr-labs/homarr/issues/2210)) ([8058b62](https://github.com/homarr-labs/homarr/commit/8058b6207ad4be1d646182b67b810c397097c054))
* custom time/date format in clock widget ([#2180](https://github.com/homarr-labs/homarr/issues/2180)) ([be603ba](https://github.com/homarr-labs/homarr/commit/be603badfed63ad7583a5265bef3b9e85f6b6b61))
* improve consistency and design ([#1867](https://github.com/homarr-labs/homarr/issues/1867)) ([5f36d8b](https://github.com/homarr-labs/homarr/commit/5f36d8b12528fd2c8c3da783b266d614203ec46a))
* open all apps in category ([#2212](https://github.com/homarr-labs/homarr/issues/2212)) ([bb67bf5](https://github.com/homarr-labs/homarr/commit/bb67bf5fa2968d91ad3522ebae960a6880f85987))
### Bug Fixes
* **deps:** update dependency @tabler/icons-react to ^3.30.0 ([#2266](https://github.com/homarr-labs/homarr/issues/2266)) ([c849109](https://github.com/homarr-labs/homarr/commit/c849109df5b622aca8cf7b392b94685581bf6b32))
* **deps:** update dependency drizzle-orm to ^0.39.2 ([#2255](https://github.com/homarr-labs/homarr/issues/2255)) ([3d0b319](https://github.com/homarr-labs/homarr/commit/3d0b319a88bdb8d955d7942553d92988bf5b2c28))
* **deps:** update dependency ioredis to v5.5.0 ([#2264](https://github.com/homarr-labs/homarr/issues/2264)) ([0da5a1b](https://github.com/homarr-labs/homarr/commit/0da5a1b0fb86ff2074f12a5e028fcc09ff94a4a5))
* **deps:** update dependency jotai to ^2.11.3 ([#2215](https://github.com/homarr-labs/homarr/issues/2215)) ([a385337](https://github.com/homarr-labs/homarr/commit/a3853372687aed543fad2dd89bfb13871af0f2c1))
* **deps:** update dependency octokit to ^4.1.1 ([#2262](https://github.com/homarr-labs/homarr/issues/2262)) ([3cbb656](https://github.com/homarr-labs/homarr/commit/3cbb656ee9b540d2dd4c7db37946efacbbfdc522))
* **deps:** update dependency sass to ^1.84.0 ([#2257](https://github.com/homarr-labs/homarr/issues/2257)) ([7bbbaf3](https://github.com/homarr-labs/homarr/commit/7bbbaf343befaaa3d03a91e932f683c5aec4b41e))
* **deps:** update dependency semver-parser to ^4.1.8 ([#2216](https://github.com/homarr-labs/homarr/issues/2216)) ([46cfb23](https://github.com/homarr-labs/homarr/commit/46cfb2383b6628472d3972da2166884330bf6d24))
* **deps:** update dependency trpc-to-openapi to ^2.1.3 ([#2244](https://github.com/homarr-labs/homarr/issues/2244)) ([c623a42](https://github.com/homarr-labs/homarr/commit/c623a420cadce677e39b99fceeb3f3a391f879ea))
* **deps:** update dependency typescript-eslint to ^8.23.0 ([#2232](https://github.com/homarr-labs/homarr/issues/2232)) ([83044d2](https://github.com/homarr-labs/homarr/commit/83044d23dd7accb1b3a21cdde1e034969a494ccb))
* **deps:** update testcontainers-node monorepo to ^10.18.0 ([#2259](https://github.com/homarr-labs/homarr/issues/2259)) ([7a8e671](https://github.com/homarr-labs/homarr/commit/7a8e67120576b333bfcf73171b54ccfeeda0d44a))
* **deps:** update turbo monorepo to ^2.4.0 ([#2207](https://github.com/homarr-labs/homarr/issues/2207)) ([6ce7bc0](https://github.com/homarr-labs/homarr/commit/6ce7bc0ee2dcd5150d72fdbc2070c05ba836ebd5))
* foreign key reference default search engine ([#2211](https://github.com/homarr-labs/homarr/issues/2211)) ([26468a4](https://github.com/homarr-labs/homarr/commit/26468a413795afa7cc3444058bfc73435fb7f2a0))
* make system health temp optional ([#2247](https://github.com/homarr-labs/homarr/issues/2247)) ([da09826](https://github.com/homarr-labs/homarr/commit/da098263930c9c9611a1505d52d3816bd2a49e7e))
## [1.4.0](https://github.com/homarr-labs/homarr/compare/v1.3.1...v1.4.0) (2025-01-31)
### Features
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:22.14.0-alpine AS base
FROM node:22.13.1-alpine AS base
FROM base AS builder
RUN apk add --no-cache libc6-compat
+13 -14
View File
@@ -18,7 +18,6 @@
"@homarr/analytics": "workspace:^0.1.0",
"@homarr/api": "workspace:^0.1.0",
"@homarr/auth": "workspace:^0.1.0",
"@homarr/boards": "workspace:^0.1.0",
"@homarr/certificates": "workspace:^0.1.0",
"@homarr/common": "workspace:^0.1.0",
"@homarr/cron-job-status": "workspace:^0.1.0",
@@ -43,15 +42,15 @@
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/widgets": "workspace:^0.1.0",
"@mantine/colors-generator": "^7.16.3",
"@mantine/core": "^7.16.3",
"@mantine/dropzone": "^7.16.3",
"@mantine/hooks": "^7.16.3",
"@mantine/modals": "^7.16.3",
"@mantine/tiptap": "^7.16.3",
"@mantine/colors-generator": "^7.16.2",
"@mantine/core": "^7.16.2",
"@mantine/dropzone": "^7.16.2",
"@mantine/hooks": "^7.16.2",
"@mantine/modals": "^7.16.2",
"@mantine/tiptap": "^7.16.2",
"@million/lint": "1.0.14",
"@t3-oss/env-nextjs": "^0.12.0",
"@tabler/icons-react": "^3.30.0",
"@tabler/icons-react": "^3.29.0",
"@tanstack/react-query": "^5.66.0",
"@tanstack/react-query-devtools": "^5.66.0",
"@tanstack/react-query-next-experimental": "^5.66.0",
@@ -68,33 +67,33 @@
"dotenv": "^16.4.7",
"flag-icons": "^7.3.2",
"glob": "^11.0.1",
"jotai": "^2.12.0",
"jotai": "^2.11.3",
"mantine-react-table": "2.0.0-beta.8",
"next": "15.1.7",
"next": "15.1.6",
"postcss-preset-mantine": "^1.17.0",
"prismjs": "^1.29.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-error-boundary": "^5.0.0",
"react-simple-code-editor": "^0.14.1",
"sass": "^1.85.0",
"sass": "^1.83.4",
"superjson": "2.2.2",
"swagger-ui-react": "^5.18.3",
"use-deep-compare-effect": "^1.8.1",
"zod": "^3.24.2"
"zod": "^3.24.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/chroma-js": "3.1.1",
"@types/node": "^22.13.4",
"@types/node": "^22.13.1",
"@types/prismjs": "^1.26.5",
"@types/react": "19.0.8",
"@types/react-dom": "19.0.3",
"@types/swagger-ui-react": "^5.18.0",
"concurrently": "^9.1.2",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"node-loader": "^2.1.0",
"prettier": "^3.4.2",
"typescript": "^5.7.3"
@@ -5,13 +5,12 @@ import { Box, LoadingOverlay, Stack } from "@mantine/core";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";
import { BoardCategorySection } from "~/components/board/sections/category-section";
import { BoardEmptySection } from "~/components/board/sections/empty-section";
import { BoardBackgroundVideo } from "~/components/layout/background";
import { fullHeightWithoutHeaderAndFooter } from "~/constants";
import { useIsBoardReady } from "./_ready-context";
import { useIsBoardReady, useRequiredBoard } from "./_context";
let boardName: string | null = null;
@@ -0,0 +1,118 @@
"use client";
import type { Dispatch, PropsWithChildren, SetStateAction } from "react";
import { createContext, useCallback, useContext, useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { updateBoardName } from "./_client";
const BoardContext = createContext<{
board: RouterOutputs["board"]["getHomeBoard"];
isReady: boolean;
markAsReady: (id: string) => void;
isEditMode: boolean;
setEditMode: Dispatch<SetStateAction<boolean>>;
} | null>(null);
export const BoardProvider = ({
children,
initialBoard,
}: PropsWithChildren<{
initialBoard: RouterOutputs["board"]["getBoardByName"];
}>) => {
const pathname = usePathname();
const utils = clientApi.useUtils();
const [readySections, setReadySections] = useState<string[]>([]);
const [isEditMode, setEditMode] = useState(false);
const { data } = clientApi.board.getBoardByName.useQuery(
{ name: initialBoard.name },
{
initialData: initialBoard,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
},
);
// Update the board name so it can be used within updateBoard method
updateBoardName(initialBoard.name);
// Invalidate the board when the pathname changes
// This allows to refetch the board when it might have changed - e.g. if someone else added an item
useEffect(() => {
return () => {
setReadySections([]);
void utils.board.getBoardByName.invalidate({ name: initialBoard.name });
};
}, [pathname, utils, initialBoard.name]);
useEffect(() => {
setReadySections((previous) => previous.filter((id) => data.sections.some((section) => section.id === id)));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data.sections.length, setReadySections]);
const markAsReady = useCallback((id: string) => {
setReadySections((previous) => (previous.includes(id) ? previous : [...previous, id]));
}, []);
return (
<BoardContext.Provider
value={{
board: data,
isReady: data.sections.length === readySections.length,
markAsReady,
isEditMode,
setEditMode,
}}
>
{children}
</BoardContext.Provider>
);
};
export const useMarkSectionAsReady = () => {
const context = useContext(BoardContext);
if (!context) {
throw new Error("Board is required");
}
return context.markAsReady;
};
export const useIsBoardReady = () => {
const context = useContext(BoardContext);
if (!context) {
throw new Error("Board is required");
}
return context.isReady;
};
export const useRequiredBoard = () => {
const optionalBoard = useOptionalBoard();
if (!optionalBoard) {
throw new Error("Board is required");
}
return optionalBoard;
};
export const useOptionalBoard = () => {
const context = useContext(BoardContext);
return context?.board;
};
export const useEditMode = () => {
const context = useContext(BoardContext);
if (!context) {
throw new Error("Board is required");
}
return [context.isEditMode, context.setEditMode] as const;
};
@@ -1,6 +1,6 @@
"use client";
import { useRequiredBoard } from "@homarr/boards/context";
import { useRequiredBoard } from "./_context";
export const CustomCss = () => {
const board = useRequiredBoard();
@@ -20,8 +20,6 @@ import {
} from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";
import { useEditMode } from "@homarr/boards/edit-mode";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useConfirmModal, useModalAction } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
@@ -34,6 +32,7 @@ import { CategoryEditModal } from "~/components/board/sections/category/category
import { useDynamicSectionActions } from "~/components/board/sections/dynamic/dynamic-actions";
import { HeaderButton } from "~/components/layout/header/button";
import { env } from "~/env";
import { useEditMode, useRequiredBoard } from "./_context";
export const BoardContentHeaderActions = () => {
const [isEditMode] = useEditMode();
@@ -120,7 +119,7 @@ const AddMenu = () => {
};
const EditModeMenu = () => {
const [isEditMode, { open, close }] = useEditMode();
const [isEditMode, setEditMode] = useEditMode();
const board = useRequiredBoard();
const utils = clientApi.useUtils();
const t = useScopedI18n("board.action.edit");
@@ -132,7 +131,7 @@ const EditModeMenu = () => {
});
void utils.board.getBoardByName.invalidate({ name: board.name });
void revalidatePathActionAsync(`/boards/${board.name}`);
close();
setEditMode(false);
},
onError() {
showErrorNotification({
@@ -144,8 +143,8 @@ const EditModeMenu = () => {
const toggle = useCallback(() => {
if (isEditMode) return saveBoard(board);
open();
}, [board, isEditMode, saveBoard, open]);
setEditMode(true);
}, [board, isEditMode, saveBoard, setEditMode]);
useHotkeys([["mod+e", toggle]]);
usePreventLeaveWithDirty(isEditMode);
@@ -1,67 +0,0 @@
"use client";
import type { PropsWithChildren } from "react";
import { createContext, useCallback, useContext, useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";
const BoardReadyContext = createContext<{
isReady: boolean;
markAsReady: (id: string) => void;
} | null>(null);
export const BoardReadyProvider = ({ children }: PropsWithChildren) => {
const pathname = usePathname();
const utils = clientApi.useUtils();
const board = useRequiredBoard();
const [readySections, setReadySections] = useState<string[]>([]);
// Reset sections required for ready state
useEffect(() => {
return () => {
setReadySections([]);
};
}, [pathname, utils]);
useEffect(() => {
setReadySections((previous) => previous.filter((id) => board.sections.some((section) => section.id === id)));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [board.sections.length, setReadySections]);
const markAsReady = useCallback((id: string) => {
setReadySections((previous) => (previous.includes(id) ? previous : [...previous, id]));
}, []);
return (
<BoardReadyContext.Provider
value={{
isReady: board.sections.length === readySections.length,
markAsReady,
}}
>
{children}
</BoardReadyContext.Provider>
);
};
export const useMarkSectionAsReady = () => {
const context = useContext(BoardReadyContext);
if (!context) {
throw new Error("BoardReadyProvider is required");
}
return context.markAsReady;
};
export const useIsBoardReady = () => {
const context = useContext(BoardReadyContext);
if (!context) {
throw new Error("BoardReadyProvider is required");
}
return context.isReady;
};
@@ -4,10 +4,10 @@ import type { PropsWithChildren } from "react";
import type { MantineColorsTuple } from "@mantine/core";
import { createTheme, darken, lighten, MantineProvider } from "@mantine/core";
import { useRequiredBoard } from "@homarr/boards/context";
import type { ColorScheme } from "@homarr/definitions";
import { useColorSchemeManager } from "../../_client-providers/mantine";
import { useRequiredBoard } from "./_context";
export const BoardMantineProvider = ({
children,
@@ -1,48 +0,0 @@
"use client";
import { Button, Group, Stack, Switch } from "@mantine/core";
import { useForm } from "@homarr/form";
import { useI18n } from "@homarr/translation/client";
import type { Board } from "../../_types";
import { useSavePartialSettingsMutation } from "./_shared";
interface Props {
board: Board;
}
export const BehaviorSettingsContent = ({ board }: Props) => {
const t = useI18n();
const { mutate: savePartialSettings, isPending } = useSavePartialSettingsMutation(board);
const form = useForm({
initialValues: {
disableStatus: board.disableStatus,
},
});
return (
<form
onSubmit={form.onSubmit((values) => {
savePartialSettings({
id: board.id,
...values,
});
})}
>
<Stack>
<Switch
label={t("board.field.disableStatus.label")}
description={t("board.field.disableStatus.description")}
{...form.getInputProps("disableStatus", { type: "checkbox" })}
/>
<Group justify="end">
<Button type="submit" loading={isPending} color="teal">
{t("common.action.saveChanges")}
</Button>
</Group>
</Stack>
</form>
);
};
@@ -5,11 +5,11 @@ import { useRouter } from "next/navigation";
import { Button, Divider, Group, Stack, Text } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";
import { useConfirmModal, useModalAction } from "@homarr/modals";
import { useScopedI18n } from "@homarr/translation/client";
import { BoardRenameModal } from "~/components/board/modals/board-rename-modal";
import { useRequiredBoard } from "../../(content)/_context";
import classes from "./danger.module.css";
export const DangerZoneSettingsContent = ({ hideVisibility }: { hideVisibility: boolean }) => {
@@ -5,13 +5,13 @@ import { Button, Grid, Group, Loader, Stack, TextInput, Tooltip } from "@mantine
import { useDebouncedValue, useDocumentTitle, useFavicon } from "@mantine/hooks";
import { IconAlertTriangle } from "@tabler/icons-react";
import { useUpdateBoard } from "@homarr/boards/updater";
import { useZodForm } from "@homarr/form";
import { useI18n } from "@homarr/translation/client";
import { validation } from "@homarr/validation";
import { createMetaTitle } from "~/metadata";
import type { Board } from "../../_types";
import { useUpdateBoard } from "../../(content)/_client";
import { useSavePartialSettingsMutation } from "./_shared";
interface Props {
@@ -4,7 +4,6 @@ import { AccordionControl, AccordionItem, AccordionPanel, Container, Stack, Text
import {
IconAlertTriangle,
IconBrush,
IconClick,
IconFileTypeCss,
IconLayout,
IconPhoto,
@@ -24,7 +23,6 @@ import type { TablerIcon } from "@homarr/ui";
import { getBoardPermissionsAsync } from "~/components/board/permissions/server";
import { ActiveTabAccordion } from "../../../../../components/active-tab-accordion";
import { BackgroundSettingsContent } from "./_background";
import { BehaviorSettingsContent } from "./_behavior";
import { BoardAccessSettings } from "./_board-access";
import { ColorSettingsContent } from "./_colors";
import { CustomCssSettingsContent } from "./_customCss";
@@ -97,9 +95,6 @@ export default async function BoardSettingsPage(props: Props) {
<AccordionItemFor value="customCss" icon={IconFileTypeCss}>
<CustomCssSettingsContent board={board} />
</AccordionItemFor>
<AccordionItemFor value="behavior" icon={IconClick}>
<BehaviorSettingsContent board={board} />
</AccordionItemFor>
{hasFullAccess && (
<>
<AccordionItemFor value="access" icon={IconUser}>
@@ -2,9 +2,8 @@
import { IconLayoutBoard } from "@tabler/icons-react";
import { useRequiredBoard } from "@homarr/boards/context";
import { HeaderButton } from "~/components/layout/header/button";
import { useRequiredBoard } from "./(content)/_context";
export const BoardOtherHeaderActions = () => {
const board = useRequiredBoard();
@@ -3,8 +3,6 @@ import { notFound } from "next/navigation";
import { AppShellMain } from "@mantine/core";
import { TRPCError } from "@trpc/server";
import { BoardProvider } from "@homarr/boards/context";
import { EditModeProvider } from "@homarr/boards/edit-mode";
import { logger } from "@homarr/log";
import { MainHeader } from "~/components/layout/header";
@@ -12,9 +10,9 @@ import { BoardLogoWithTitle } from "~/components/layout/logo/board-logo";
import { ClientShell } from "~/components/layout/shell";
import { getCurrentColorSchemeAsync } from "~/theme/color-scheme";
import type { Board } from "./_types";
import { BoardProvider } from "./(content)/_context";
import type { Params } from "./(content)/_creator";
import { CustomCss } from "./(content)/_custom-css";
import { BoardReadyProvider } from "./(content)/_ready-context";
import { BoardMantineProvider } from "./(content)/_theme";
interface CreateBoardLayoutProps<TParams extends Params> {
@@ -44,21 +42,17 @@ export const createBoardLayout = <TParams extends Params>({
return (
<BoardProvider initialBoard={initialBoard}>
<BoardReadyProvider>
<EditModeProvider>
<BoardMantineProvider defaultColorScheme={colorScheme}>
<CustomCss />
<ClientShell hasNavigation={false}>
<MainHeader
logo={<BoardLogoWithTitle size="md" hideTitleOnMobile />}
actions={headerActions}
hasNavigation={false}
/>
<AppShellMain>{children}</AppShellMain>
</ClientShell>
</BoardMantineProvider>
</EditModeProvider>
</BoardReadyProvider>
<BoardMantineProvider defaultColorScheme={colorScheme}>
<CustomCss />
<ClientShell hasNavigation={false}>
<MainHeader
logo={<BoardLogoWithTitle size="md" hideTitleOnMobile />}
actions={headerActions}
hasNavigation={false}
/>
<AppShellMain>{children}</AppShellMain>
</ClientShell>
</BoardMantineProvider>
</BoardProvider>
);
};
-2
View File
@@ -94,8 +94,6 @@ export default async function Layout(props: {
board: {
homeBoardId: serverSettings.board.homeBoardId,
mobileHomeBoardId: serverSettings.board.mobileHomeBoardId,
enableStatusByDefault: serverSettings.board.enableStatusByDefault,
forceDisableStatus: serverSettings.board.forceDisableStatus,
},
search: { defaultSearchEngineId: serverSettings.search.defaultSearchEngineId },
}}
@@ -1,6 +1,6 @@
"use client";
import { Group, Switch, Text } from "@mantine/core";
import { Group, Text } from "@mantine/core";
import { IconLayoutDashboard } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
@@ -56,18 +56,6 @@ export const BoardSettingsForm = ({ defaultValues }: { defaultValues: ServerSett
)}
{...form.getInputProps("mobileHomeBoardId")}
/>
<Text fw={500}>{tBoard("status.title")}</Text>
<Switch
{...form.getInputProps("enableStatusByDefault", { type: "checkbox" })}
label={tBoard("status.enableStatusByDefault.label")}
description={tBoard("status.enableStatusByDefault.description")}
/>
<Switch
{...form.getInputProps("forceDisableStatus", { type: "checkbox" })}
label={tBoard("status.forceDisableStatus.label")}
description={tBoard("status.forceDisableStatus.description")}
/>
</>
)}
</CommonSettingsForm>
@@ -9,7 +9,6 @@ import { ErrorBoundary } from "react-error-boundary";
import type { IntegrationKind, WidgetKind } from "@homarr/definitions";
import { useModalAction } from "@homarr/modals";
import { showSuccessNotification } from "@homarr/notifications";
import { useSettings } from "@homarr/settings";
import { useScopedI18n } from "@homarr/translation/client";
import type { BoardItemAdvancedOptions } from "@homarr/validation";
import { loadWidgetDynamic, reduceWidgetOptionsWithDefaultValues, widgetImports } from "@homarr/widgets";
@@ -30,7 +29,6 @@ interface WidgetPreviewPageContentProps {
}
export const WidgetPreviewPageContent = ({ kind, integrationData }: WidgetPreviewPageContentProps) => {
const settings = useSettings();
const t = useScopedI18n("widgetPreview");
const { openModal: openWidgetEditModal } = useModalAction(WidgetEditModal);
const { openModal: openPreviewDimensionsModal } = useModalAction(PreviewDimensionsModal);
@@ -45,7 +43,7 @@ export const WidgetPreviewPageContent = ({ kind, integrationData }: WidgetPrevie
integrationIds: string[];
advancedOptions: BoardItemAdvancedOptions;
}>({
options: reduceWidgetOptionsWithDefaultValues(kind, settings, {}),
options: reduceWidgetOptionsWithDefaultValues(kind, {}),
integrationIds: [],
advancedOptions: {
customCssClasses: [],
@@ -65,9 +63,8 @@ export const WidgetPreviewPageContent = ({ kind, integrationData }: WidgetPrevie
(currentDefinition.supportedIntegrations as string[]).some((kind) => kind === integration.kind),
),
integrationSupport: "supportedIntegrations" in currentDefinition,
settings,
});
}, [currentDefinition, integrationData, kind, openWidgetEditModal, settings, state]);
}, [currentDefinition, integrationData, kind, openWidgetEditModal, state]);
const Comp = loadWidgetDynamic(kind);
@@ -1,9 +1,9 @@
import { useCallback } from "react";
import { useUpdateBoard } from "@homarr/boards/updater";
import type { BoardItemAdvancedOptions } from "@homarr/validation";
import type { Item } from "~/app/[locale]/boards/_types";
import { useUpdateBoard } from "~/app/[locale]/boards/(content)/_client";
import type { CreateItemInput } from "./actions/create-item";
import { createItemCallback } from "./actions/create-item";
import type { DuplicateItemInput } from "./actions/duplicate-item";
@@ -5,13 +5,11 @@ import combineClasses from "clsx";
import { NoIntegrationSelectedError } from "node_modules/@homarr/widgets/src/errors";
import { ErrorBoundary } from "react-error-boundary";
import { useRequiredBoard } from "@homarr/boards/context";
import { useEditMode } from "@homarr/boards/edit-mode";
import { useSettings } from "@homarr/settings";
import { loadWidgetDynamic, reduceWidgetOptionsWithDefaultValues, widgetImports } from "@homarr/widgets";
import { WidgetError } from "@homarr/widgets/errors";
import type { Item } from "~/app/[locale]/boards/_types";
import { useEditMode, useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
import classes from "../sections/item.module.css";
import { useItemActions } from "./item-actions";
import { BoardItemMenu } from "./item-menu";
@@ -37,8 +35,9 @@ export const BoardItemContent = ({ item }: BoardItemContentProps) => {
styles={{
root: {
"--opacity": board.opacity / 100,
containerType: "size",
overflow: item.kind === "iframe" ? "hidden" : undefined,
"--custom-text-size-scale": item.advancedOptions.customTextSize,
"--custom-spacing-scale": item.advancedOptions.customSpacing,
},
}}
p={0}
@@ -55,12 +54,11 @@ interface InnerContentProps {
}
const InnerContent = ({ item, ...dimensions }: InnerContentProps) => {
const settings = useSettings();
const board = useRequiredBoard();
const [isEditMode] = useEditMode();
const Comp = loadWidgetDynamic(item.kind);
const { definition } = widgetImports[item.kind];
const options = reduceWidgetOptionsWithDefaultValues(item.kind, settings, item.options);
const options = reduceWidgetOptionsWithDefaultValues(item.kind, item.options);
const newItem = { ...item, options };
const { updateItemOptions } = useItemActions();
const updateOptions = ({ newOptions }: { newOptions: Record<string, unknown> }) =>
@@ -3,14 +3,13 @@ import { ActionIcon, Menu } from "@mantine/core";
import { IconCopy, IconDotsVertical, IconLayoutKanban, IconPencil, IconTrash } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { useEditMode } from "@homarr/boards/edit-mode";
import { useConfirmModal, useModalAction } from "@homarr/modals";
import { useSettings } from "@homarr/settings";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { widgetImports } from "@homarr/widgets";
import { WidgetEditModal } from "@homarr/widgets/modals";
import type { Item } from "~/app/[locale]/boards/_types";
import { useEditMode } from "~/app/[locale]/boards/(content)/_context";
import { useSectionContext } from "../sections/section-context";
import { useItemActions } from "./item-actions";
import { ItemMoveModal } from "./item-move-modal";
@@ -36,7 +35,6 @@ export const BoardItemMenu = ({
const { data: integrationData, isPending } = clientApi.integration.all.useQuery();
const currentDefinition = useMemo(() => widgetImports[item.kind].definition, [item.kind]);
const { gridstack } = useSectionContext().refs;
const settings = useSettings();
// Reset error boundary on next render if item has been edited
useEffect(() => {
@@ -77,7 +75,6 @@ export const BoardItemMenu = ({
(currentDefinition.supportedIntegrations as string[]).some((kind) => kind === integration.kind),
),
integrationSupport: "supportedIntegrations" in currentDefinition,
settings,
});
};
@@ -3,9 +3,9 @@ import { useDisclosure } from "@mantine/hooks";
import { IconChevronDown, IconChevronUp } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";
import type { CategorySection } from "~/app/[locale]/boards/_types";
import { useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
import { CategoryMenu } from "./category/category-menu";
import { GridStack } from "./gridstack/gridstack";
import classes from "./item.module.css";
@@ -1,9 +1,9 @@
import { useCallback } from "react";
import { useUpdateBoard } from "@homarr/boards/updater";
import { createId } from "@homarr/db/client";
import type { CategorySection, EmptySection } from "~/app/[locale]/boards/_types";
import { useUpdateBoard } from "~/app/[locale]/boards/(content)/_client";
import type { MoveCategoryInput } from "./actions/move-category";
import { moveCategoryCallback } from "./actions/move-category";
import type { RemoveCategoryInput } from "./actions/remove-category";
@@ -3,7 +3,6 @@ import { useCallback } from "react";
import { fetchApi } from "@homarr/api/client";
import { createId } from "@homarr/db/client";
import { useConfirmModal, useModalAction } from "@homarr/modals";
import { useSettings } from "@homarr/settings";
import { useI18n } from "@homarr/translation/client";
import type { CategorySection } from "~/app/[locale]/boards/_types";
@@ -100,9 +99,8 @@ export const useCategoryMenuActions = (category: CategorySection) => {
);
}, [category, openModal, renameCategory, t]);
const settings = useSettings();
const openAllInNewTabs = useCallback(async () => {
const appIds = filterByItemKind(category.items, settings, "app").map((item) => {
const appIds = filterByItemKind(category.items, "app").map((item) => {
return item.options.appId;
});
@@ -121,7 +119,7 @@ export const useCategoryMenuActions = (category: CategorySection) => {
});
break;
}
}, [category, t, openConfirmModal, settings]);
}, [category, t, openConfirmModal]);
return {
addCategoryAbove,
@@ -13,12 +13,12 @@ import {
IconTrash,
} from "@tabler/icons-react";
import { useEditMode } from "@homarr/boards/edit-mode";
import type { MaybePromise } from "@homarr/common/types";
import { useScopedI18n } from "@homarr/translation/client";
import type { TablerIcon } from "@homarr/ui";
import type { CategorySection } from "~/app/[locale]/boards/_types";
import { useEditMode } from "~/app/[locale]/boards/(content)/_context";
import { useCategoryMenuActions } from "./category-menu-actions";
interface Props {
@@ -1,23 +1,14 @@
import type { WidgetKind } from "@homarr/definitions";
import type { SettingsContextProps } from "@homarr/settings";
import type { WidgetComponentProps } from "@homarr/widgets";
import { reduceWidgetOptionsWithDefaultValues } from "@homarr/widgets";
import type { Item } from "~/app/[locale]/boards/_types";
export const filterByItemKind = <TKind extends WidgetKind>(
items: Item[],
settings: SettingsContextProps,
kind: TKind,
) => {
export const filterByItemKind = <TKind extends WidgetKind>(items: Item[], kind: TKind) => {
return items
.filter((item) => item.kind === kind)
.map((item) => ({
...item,
options: reduceWidgetOptionsWithDefaultValues(
kind,
settings,
item.options,
) as WidgetComponentProps<TKind>["options"],
options: reduceWidgetOptionsWithDefaultValues(kind, item.options) as WidgetComponentProps<TKind>["options"],
}));
};
@@ -1,8 +1,7 @@
import { useMemo } from "react";
import { useRequiredBoard } from "@homarr/boards/context";
import type { DynamicSection, Item, Section } from "~/app/[locale]/boards/_types";
import { useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
import { BoardItemContent } from "../items/item-content";
import { BoardDynamicSection } from "./dynamic-section";
import { GridStackItem } from "./gridstack/gridstack-item";
@@ -1,8 +1,7 @@
import { Box, Card } from "@mantine/core";
import { useRequiredBoard } from "@homarr/boards/context";
import type { DynamicSection } from "~/app/[locale]/boards/_types";
import { useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
import { BoardDynamicSectionMenu } from "./dynamic/dynamic-menu";
import { GridStack } from "./gridstack/gridstack";
import classes from "./item.module.css";
@@ -1,9 +1,9 @@
import { useCallback } from "react";
import { useUpdateBoard } from "@homarr/boards/updater";
import { createId } from "@homarr/db/client";
import type { DynamicSection, EmptySection } from "~/app/[locale]/boards/_types";
import { useUpdateBoard } from "~/app/[locale]/boards/(content)/_client";
interface RemoveDynamicSection {
id: string;
@@ -1,11 +1,11 @@
import { ActionIcon, Menu } from "@mantine/core";
import { IconDotsVertical, IconTrash } from "@tabler/icons-react";
import { useEditMode } from "@homarr/boards/edit-mode";
import { useConfirmModal } from "@homarr/modals";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import type { DynamicSection } from "~/app/[locale]/boards/_types";
import { useEditMode } from "~/app/[locale]/boards/(content)/_context";
import { useDynamicSectionActions } from "./dynamic-actions";
export const BoardDynamicSectionMenu = ({ section }: { section: DynamicSection }) => {
@@ -1,8 +1,7 @@
import combineClasses from "clsx";
import { useEditMode } from "@homarr/boards/edit-mode";
import type { EmptySection } from "~/app/[locale]/boards/_types";
import { useEditMode } from "~/app/[locale]/boards/(content)/_context";
import { GridStack } from "./gridstack/gridstack";
import { useSectionItems } from "./use-section-items";
@@ -2,12 +2,10 @@ import type { RefObject } from "react";
import { createRef, useCallback, useEffect, useRef } from "react";
import { useElementSize } from "@mantine/hooks";
import { useRequiredBoard } from "@homarr/boards/context";
import { useEditMode } from "@homarr/boards/edit-mode";
import type { GridHTMLElement, GridItemHTMLElement, GridStack, GridStackNode } from "@homarr/gridstack";
import type { Section } from "~/app/[locale]/boards/_types";
import { useMarkSectionAsReady } from "~/app/[locale]/boards/(content)/_ready-context";
import { useEditMode, useMarkSectionAsReady, useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
import { useItemActions } from "../../items/item-actions";
import { useSectionActions } from "../section-actions";
import { initializeGridstack } from "./init-gridstack";
@@ -9,4 +9,22 @@
}
background-color: var(--background-color) !important;
border-color: var(--border-color) !important;
container-type: size;
--spacing-base: 0.5cqmin;
--mantine-spacing-xs: calc(1 * var(--spacing-base) * var(--custom-spacing-scale));
--mantine-spacing-sm: calc(2 * var(--spacing-base) * var(--custom-spacing-scale));
--mantine-spacing-md: calc(4 * var(--spacing-base) * var(--custom-spacing-scale));
--mantine-spacing-lg: calc(8 * var(--spacing-base) * var(--custom-spacing-scale));
--mantine-spacing-xl: calc(16 * var(--spacing-base) * var(--custom-spacing-scale));
--text-size-base: 5cqmin;
--mantine-font-size-xs: calc(1 * var(--text-size-base) * var(--custom-text-size-scale));
--mantine-font-size-sm: calc(1.5 * var(--text-size-base) * var(--custom-text-size-scale));
--mantine-font-size-md: calc(2.5 * var(--text-size-base) * var(--custom-text-size-scale));
--mantine-font-size-lg: calc(4 * var(--text-size-base) * var(--custom-text-size-scale));
--mantine-font-size-xl: calc(6 * var(--text-size-base) * var(--custom-text-size-scale));
--text-lh: 1.35;
}
@@ -1,6 +1,6 @@
import { useCallback } from "react";
import { useUpdateBoard } from "@homarr/boards/updater";
import { useUpdateBoard } from "~/app/[locale]/boards/(content)/_client";
interface MoveAndResizeInnerSection {
innerSectionId: string;
@@ -1,6 +1,5 @@
import { useRequiredBoard } from "@homarr/boards/context";
import type { Section } from "~/app/[locale]/boards/_types";
import { useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
export const useSectionItems = (section: Section) => {
const board = useRequiredBoard();
@@ -1,7 +1,7 @@
import { usePathname } from "next/navigation";
import type { AppShellProps } from "@mantine/core";
import { useOptionalBoard } from "@homarr/boards/context";
import { useOptionalBoard } from "~/app/[locale]/boards/(content)/_context";
const supportedVideoFormats = ["mp4", "webm", "ogg"];
const isVideo = (url: string) => supportedVideoFormats.some((format) => url.toLowerCase().endsWith(`.${format}`));
@@ -1,7 +1,6 @@
"use client";
import { useRequiredBoard } from "@homarr/boards/context";
import { useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
import { homarrLogoPath, homarrPageTitle } from "./homarr-logo";
import type { LogoWithTitleProps } from "./logo";
import { Logo, LogoWithTitle } from "./logo";
+2 -2
View File
@@ -44,9 +44,9 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node": "^22.13.4",
"@types/node": "^22.13.1",
"dotenv-cli": "^8.0.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"prettier": "^3.4.2",
"tsx": "4.19.2",
"typescript": "^5.7.3"
+1 -1
View File
@@ -34,7 +34,7 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/ws": "^8.5.14",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"prettier": "^3.4.2",
"typescript": "^5.7.3"
}
+8 -24
View File
@@ -1,6 +1,6 @@
{
"name": "homarr",
"version": "1.6.0",
"version": "1.4.0",
"private": true,
"scripts": {
"build": "cross-env CI=true turbo build",
@@ -38,7 +38,7 @@
"@semantic-release/github": "^11.0.1",
"@semantic-release/npm": "^12.0.1",
"@semantic-release/release-notes-generator": "^14.0.3",
"@turbo/gen": "^2.4.2",
"@turbo/gen": "^2.4.0",
"@vitejs/plugin-react": "^4.3.4",
"@vitest/coverage-v8": "^3.0.5",
"@vitest/ui": "^3.0.5",
@@ -46,40 +46,24 @@
"cross-env": "^7.0.3",
"jsdom": "^26.0.0",
"prettier": "^3.4.2",
"semantic-release": "^24.2.2",
"testcontainers": "^10.18.0",
"turbo": "^2.4.2",
"semantic-release": "^24.2.1",
"testcontainers": "^10.17.2",
"turbo": "^2.4.0",
"typescript": "^5.7.3",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.0.5"
},
"packageManager": "pnpm@10.4.0",
"packageManager": "pnpm@9.15.5",
"engines": {
"node": ">=22.14.0"
"node": ">=22.13.1"
},
"pnpm": {
"onlyBuiltDependencies": [
"@tree-sitter-grammars/tree-sitter-yaml",
"bcrypt",
"better-sqlite3",
"cpu-features",
"esbuild",
"sharp",
"ssh2",
"tree-sitter",
"tree-sitter-json"
],
"allowNonAppliedPatches": true,
"overrides": {
"proxmox-api>undici": "7.3.0"
},
"patchedDependencies": {
"pretty-print-error": "patches/pretty-print-error.patch"
},
"ignoredBuiltDependencies": [
"@scarf/scarf",
"core-js-pure",
"protobufjs"
]
}
}
}
+1 -1
View File
@@ -32,7 +32,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+3 -3
View File
@@ -44,19 +44,19 @@
"@trpc/react-query": "next",
"@trpc/server": "next",
"lodash.clonedeep": "^4.5.0",
"next": "15.1.7",
"next": "15.1.6",
"pretty-print-error": "^1.1.2",
"react": "19.0.0",
"react-dom": "19.0.0",
"superjson": "2.2.2",
"trpc-to-openapi": "^2.1.3",
"zod": "^3.24.2"
"zod": "^3.24.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"prettier": "^3.4.2",
"typescript": "^5.7.3"
}
-3
View File
@@ -484,9 +484,6 @@ export const boardRouter = createTRPCRouter({
// layout settings
columnCount: input.columnCount,
// Behavior settings
disableStatus: input.disableStatus,
})
.where(eq(boards.id, input.id));
}),
@@ -247,7 +247,7 @@ describe("create should create a new integration", () => {
expect(dbSearchEngine!.short).toBe("j");
expect(dbSearchEngine!.name).toBe(input.name);
expect(dbSearchEngine!.iconUrl).toBe(
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellyseerr.svg",
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/jellyseerr.png",
);
});
+2 -2
View File
@@ -237,7 +237,7 @@ describe("editProfile shoud update user", () => {
expect(user).toHaveLength(1);
expect(user[0]).containSubset({
id: defaultOwnerId,
name: "abc",
name: "ABC",
email: "abc@gmail.com",
emailVerified,
});
@@ -272,7 +272,7 @@ describe("editProfile shoud update user", () => {
expect(user).toHaveLength(1);
expect(user[0]).containSubset({
id: defaultOwnerId,
name: "abc",
name: "ABC",
email: "myNewEmail@gmail.com",
emailVerified: null,
});
+3 -11
View File
@@ -1,19 +1,11 @@
import { formatError } from "pretty-print-error";
import { logger } from "@homarr/log";
import { updateCheckerRequestHandler } from "@homarr/request-handler/update-checker";
import { createTRPCRouter, permissionRequiredProcedure } from "../trpc";
export const updateCheckerRouter = createTRPCRouter({
getAvailableUpdates: permissionRequiredProcedure.requiresPermission("admin").query(async () => {
try {
const handler = updateCheckerRequestHandler.handler({});
const data = await handler.getCachedOrUpdatedDataAsync({});
return data.data.availableUpdates;
} catch (error) {
logger.error(`Failed to get available updates\n${formatError(error)}`);
return undefined; // We return undefined to not show the indicator in the UI
}
const handler = updateCheckerRequestHandler.handler({});
const data = await handler.getCachedOrUpdatedDataAsync({});
return data.data.availableUpdates;
}),
});
+4 -2
View File
@@ -523,10 +523,12 @@ const createUserAsync = async (db: Database, input: Omit<z.infer<typeof validati
const salt = await createSaltAsync();
const hashedPassword = await hashPasswordAsync(input.password, salt);
const username = input.username.toLowerCase();
const userId = createId();
await db.insert(users).values({
id: userId,
name: input.username,
name: username,
email: input.email,
password: hashedPassword,
salt,
@@ -541,7 +543,7 @@ const checkUsernameAlreadyTakenAndThrowAsync = async (
ignoreId?: string,
) => {
const user = await db.query.users.findFirst({
where: and(eq(users.name, username), eq(users.provider, provider)),
where: and(eq(users.name, username.toLowerCase()), eq(users.provider, provider)),
});
if (!user) return;
-2
View File
@@ -10,7 +10,6 @@ import { mediaServerRouter } from "./media-server";
import { mediaTranscodingRouter } from "./media-transcoding";
import { minecraftRouter } from "./minecraft";
import { notebookRouter } from "./notebook";
import { optionsRouter } from "./options";
import { rssFeedRouter } from "./rssFeed";
import { smartHomeRouter } from "./smart-home";
import { weatherRouter } from "./weather";
@@ -30,5 +29,4 @@ export const widgetRouter = createTRPCRouter({
healthMonitoring: healthMonitoringRouter,
mediaTranscoding: mediaTranscodingRouter,
minecraft: minecraftRouter,
options: optionsRouter,
});
@@ -1,19 +0,0 @@
import { getServerSettingsAsync } from "@homarr/db/queries";
import type { WidgetOptionsSettings } from "../../../../widgets/src";
import { createTRPCRouter, publicProcedure } from "../../trpc";
export const optionsRouter = createTRPCRouter({
getWidgetOptionSettings: publicProcedure.query(async ({ ctx }): Promise<WidgetOptionsSettings> => {
const serverSettings = await getServerSettingsAsync(ctx.db);
return {
server: {
board: {
enableStatusByDefault: serverSettings.board.enableStatusByDefault,
forceDisableStatus: serverSettings.board.forceDisableStatus,
},
},
};
}),
});
+3 -3
View File
@@ -34,12 +34,12 @@
"bcrypt": "^5.1.1",
"cookies": "^0.9.1",
"ldapts": "7.3.1",
"next": "15.1.7",
"next": "15.1.6",
"next-auth": "5.0.0-beta.25",
"pretty-print-error": "^1.1.2",
"react": "19.0.0",
"react-dom": "19.0.0",
"zod": "^3.24.2"
"zod": "^3.24.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -47,7 +47,7 @@
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/bcrypt": "5.0.2",
"@types/cookies": "0.9.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"prettier": "^3.4.2",
"typescript": "^5.7.3"
}
-9
View File
@@ -1,9 +0,0 @@
import baseConfig from "@homarr/eslint-config/base";
/** @type {import('typescript-eslint').Config} */
export default [
{
ignores: [],
},
...baseConfig,
];
-38
View File
@@ -1,38 +0,0 @@
{
"name": "@homarr/boards",
"version": "0.1.0",
"private": true,
"license": "MIT",
"type": "module",
"exports": {
"./context": "./src/context.tsx",
"./updater": "./src/updater.ts",
"./edit-mode": "./src/edit-mode.tsx"
},
"typesVersions": {
"*": {
"*": [
"src/*"
]
}
},
"scripts": {
"clean": "rm -rf .turbo node_modules",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint",
"typecheck": "tsc --noEmit"
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/api": "workspace:^0.1.0",
"react": "19.0.0",
"react-dom": "19.0.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"typescript": "^5.7.3"
}
}
-70
View File
@@ -1,70 +0,0 @@
"use client";
import type { PropsWithChildren } from "react";
import { createContext, useContext, useEffect } from "react";
import { usePathname } from "next/navigation";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { updateBoardName } from "./updater";
const BoardContext = createContext<{
board: RouterOutputs["board"]["getHomeBoard"];
} | null>(null);
export const BoardProvider = ({
children,
initialBoard,
}: PropsWithChildren<{
initialBoard: RouterOutputs["board"]["getBoardByName"];
}>) => {
const { data } = clientApi.board.getBoardByName.useQuery(
{ name: initialBoard.name },
{
initialData: initialBoard,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
},
);
// Update the board name so it can be used within updateBoard method
updateBoardName(initialBoard.name);
const pathname = usePathname();
const utils = clientApi.useUtils();
// Invalidate the board when the pathname changes
// This allows to refetch the board when it might have changed - e.g. if someone else added an item
useEffect(() => {
return () => {
void utils.board.getBoardByName.invalidate({ name: initialBoard.name });
};
}, [pathname, utils, initialBoard.name]);
return (
<BoardContext.Provider
value={{
board: data,
}}
>
{children}
</BoardContext.Provider>
);
};
export const useRequiredBoard = () => {
const optionalBoard = useOptionalBoard();
if (!optionalBoard) {
throw new Error("Board is required");
}
return optionalBoard;
};
export const useOptionalBoard = () => {
const context = useContext(BoardContext);
return context?.board ?? null;
};
-23
View File
@@ -1,23 +0,0 @@
"use client";
import type { PropsWithChildren } from "react";
import { createContext, useContext } from "react";
import { useDisclosure } from "@mantine/hooks";
const EditModeContext = createContext<ReturnType<typeof useDisclosure> | null>(null);
export const EditModeProvider = ({ children }: PropsWithChildren) => {
const editModeDisclosure = useDisclosure(false);
return <EditModeContext.Provider value={editModeDisclosure}>{children}</EditModeContext.Provider>;
};
export const useEditMode = () => {
const context = useContext(EditModeContext);
if (!context) {
throw new Error("EditMode is required");
}
return context;
};
-34
View File
@@ -1,34 +0,0 @@
"use client";
import { useCallback } from "react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
let boardName: string | null = null;
export const updateBoardName = (name: string | null) => {
boardName = name;
};
type UpdateCallback = (prev: RouterOutputs["board"]["getHomeBoard"]) => RouterOutputs["board"]["getHomeBoard"];
export const useUpdateBoard = () => {
const utils = clientApi.useUtils();
const updateBoard = useCallback(
(updaterWithoutUndefined: UpdateCallback) => {
if (!boardName) {
throw new Error("Board name is not set");
}
utils.board.getBoardByName.setData({ name: boardName }, (previous) =>
previous ? updaterWithoutUndefined(previous) : previous,
);
},
[utils],
);
return {
updateBoard,
};
};
-8
View File
@@ -1,8 +0,0 @@
{
"extends": "@homarr/tsconfig/base.json",
"compilerOptions": {
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
},
"include": ["*.ts", "src"],
"exclude": ["node_modules"]
}
+1 -1
View File
@@ -29,7 +29,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+1 -1
View File
@@ -33,7 +33,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+3 -3
View File
@@ -29,17 +29,17 @@
"dependencies": {
"@homarr/log": "workspace:^0.1.0",
"dayjs": "^1.11.13",
"next": "15.1.7",
"next": "15.1.6",
"react": "19.0.0",
"react-dom": "19.0.0",
"undici": "7.3.0",
"zod": "^3.24.2"
"zod": "^3.24.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+1 -1
View File
@@ -30,7 +30,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+1 -1
View File
@@ -29,7 +29,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+1 -1
View File
@@ -32,7 +32,7 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node-cron": "^3.0.11",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+1 -1
View File
@@ -44,7 +44,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
-9
View File
@@ -1,6 +1,4 @@
import { EVERY_MINUTE } from "@homarr/cron-jobs-core/expressions";
import { db } from "@homarr/db";
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
import { logger } from "@homarr/log";
import { sendPingRequestAsync } from "@homarr/ping";
import { pingChannel, pingUrlChannel } from "@homarr/redis";
@@ -15,13 +13,6 @@ const resetPreviousUrlsAsync = async () => {
export const pingJob = createCronJob("ping", EVERY_MINUTE, {
beforeStart: resetPreviousUrlsAsync,
}).withCallback(async () => {
const boardSettings = await getServerSettingByKeyAsync(db, "board");
if (boardSettings.forceDisableStatus) {
logger.debug("Simple ping is disabled by server settings");
return;
}
const urls = await pingUrlChannel.getAllAsync();
await Promise.allSettled([...new Set(urls)].map(pingAsync));
+3 -8
View File
@@ -1,11 +1,9 @@
import type { Database as BetterSqlite3Connection } from "better-sqlite3";
import Database from "better-sqlite3";
import type { Logger } from "drizzle-orm";
import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
import { drizzle as drizzleSqlite } from "drizzle-orm/better-sqlite3";
import type { MySql2Database } from "drizzle-orm/mysql2";
import { drizzle as drizzleMysql } from "drizzle-orm/mysql2";
import type { Pool as MysqlConnectionPool } from "mysql2";
import mysql from "mysql2";
import { logger } from "@homarr/log";
@@ -31,7 +29,7 @@ const init = () => {
}
};
export let connection: BetterSqlite3Connection | MysqlConnectionPool;
export let connection: Database.Database | mysql.Connection;
export let database: HomarrDatabase;
class WinstonDrizzleLogger implements Logger {
@@ -51,17 +49,14 @@ const initBetterSqlite = () => {
const initMySQL2 = () => {
if (!env.DB_HOST) {
connection = mysql.createPool({ uri: env.DB_URL, maxIdle: 0, idleTimeout: 60000, enableKeepAlive: true });
connection = mysql.createConnection({ uri: env.DB_URL });
} else {
connection = mysql.createPool({
connection = mysql.createConnection({
host: env.DB_HOST,
database: env.DB_NAME,
port: env.DB_PORT,
user: env.DB_USER,
password: env.DB_PASSWORD,
maxIdle: 0,
idleTimeout: 60000,
enableKeepAlive: true,
});
}
@@ -1 +0,0 @@
ALTER TABLE `board` ADD `disable_status` boolean DEFAULT false NOT NULL;
File diff suppressed because it is too large. Load diff
@@ -169,13 +169,6 @@
"when": 1738687012272,
"tag": "0023_fix_on_delete_actions",
"breakpoints": true
},
{
"idx": 24,
"version": "5",
"when": 1738961147412,
"tag": "0024_mean_vin_gonzales",
"breakpoints": true
}
]
}
@@ -1 +0,0 @@
ALTER TABLE `board` ADD `disable_status` integer DEFAULT false NOT NULL;
File diff suppressed because it is too large. Load diff
@@ -169,13 +169,6 @@
"when": 1738686324915,
"tag": "0023_fix_on_delete_actions",
"breakpoints": true
},
{
"idx": 24,
"version": "6",
"when": 1738961178990,
"tag": "0024_bitter_scrambler",
"breakpoints": true
}
]
}
+3 -3
View File
@@ -44,11 +44,11 @@
"@homarr/server-settings": "workspace:^0.1.0",
"@paralleldrive/cuid2": "^2.2.2",
"@t3-oss/env-nextjs": "^0.12.0",
"@testcontainers/mysql": "^10.18.0",
"@testcontainers/mysql": "^10.17.2",
"better-sqlite3": "^11.8.1",
"dotenv": "^16.4.7",
"drizzle-kit": "^0.30.4",
"drizzle-orm": "^0.39.3",
"drizzle-orm": "^0.39.1",
"drizzle-zod": "^0.7.0",
"mysql2": "3.12.0"
},
@@ -58,7 +58,7 @@
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/better-sqlite3": "7.6.12",
"dotenv-cli": "^8.0.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"prettier": "^3.4.2",
"tsx": "4.19.2",
"typescript": "^5.7.3"
-1
View File
@@ -272,7 +272,6 @@ export const boards = mysqlTable("board", {
opacity: int().default(100).notNull(),
customCss: text(),
columnCount: int().default(10).notNull(),
disableStatus: boolean().default(false).notNull(),
});
export const boardUserPermissions = mysqlTable(
-1
View File
@@ -258,7 +258,6 @@ export const boards = sqliteTable("board", {
opacity: int().default(100).notNull(),
customCss: text(),
columnCount: int().default(10).notNull(),
disableStatus: int({ mode: "boolean" }).default(false).notNull(),
});
export const boardUserPermissions = sqliteTable(
+1 -1
View File
@@ -29,7 +29,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+19 -19
View File
@@ -22,109 +22,109 @@ export const integrationDefs = {
sabNzbd: {
name: "SABnzbd",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/sabnzbd.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/sabnzbd.png",
category: ["downloadClient", "usenet"],
},
nzbGet: {
name: "NZBGet",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nzbget.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/nzbget.png",
category: ["downloadClient", "usenet"],
},
deluge: {
name: "Deluge",
secretKinds: [["password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/deluge.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/deluge.png",
category: ["downloadClient", "torrent"],
},
transmission: {
name: "Transmission",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/transmission.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/transmission.png",
category: ["downloadClient", "torrent"],
},
qBittorrent: {
name: "qBittorrent",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/qbittorrent.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/qbittorrent.png",
category: ["downloadClient", "torrent"],
},
sonarr: {
name: "Sonarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/sonarr.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/sonarr.png",
category: ["calendar"],
},
radarr: {
name: "Radarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/radarr.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/radarr.png",
category: ["calendar"],
},
lidarr: {
name: "Lidarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/lidarr.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/lidarr.png",
category: ["calendar"],
},
readarr: {
name: "Readarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/readarr.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/readarr.png",
category: ["calendar"],
},
prowlarr: {
name: "Prowlarr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/prowlarr.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/prowlarr.png",
category: ["indexerManager"],
},
jellyfin: {
name: "Jellyfin",
secretKinds: [["username", "password"], ["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellyfin.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/jellyfin.png",
category: ["mediaService"],
},
plex: {
name: "Plex",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/plex.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/plex.png",
category: ["mediaService"],
},
jellyseerr: {
name: "Jellyseerr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellyseerr.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/jellyseerr.png",
category: ["mediaSearch", "mediaRequest", "search"],
},
overseerr: {
name: "Overseerr",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/overseerr.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/overseerr.png",
category: ["mediaSearch", "mediaRequest", "search"],
},
piHole: {
name: "Pi-hole",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/pi-hole.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/pi-hole.png",
category: ["dnsHole"],
},
adGuardHome: {
name: "AdGuard Home",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/adguard-home.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/adguard-home.png",
category: ["dnsHole"],
},
homeAssistant: {
name: "Home Assistant",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/home-assistant.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/home-assistant.png",
category: ["smartHomeServer"],
},
openmediavault: {
name: "OpenMediaVault",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/openmediavault.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/openmediavault.png",
category: ["healthMonitoring"],
},
dashDot: {
@@ -143,7 +143,7 @@ export const integrationDefs = {
name: "Proxmox",
secretKinds: [["username", "tokenId", "apiKey", "realm"]],
category: ["healthMonitoring"],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/proxmox.svg",
iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/proxmox.png",
},
} as const satisfies Record<string, integrationDefinition>;
+1 -1
View File
@@ -32,7 +32,7 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/dockerode": "^3.3.34",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+3 -3
View File
@@ -26,14 +26,14 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/form": "^7.16.3",
"zod": "^3.24.2"
"@mantine/form": "^7.16.2",
"zod": "^3.24.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+1 -1
View File
@@ -31,7 +31,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+3 -3
View File
@@ -25,7 +25,7 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@ctrl/deluge": "^7.1.0",
"@ctrl/qbittorrent": "^9.4.0",
"@ctrl/qbittorrent": "^9.2.0",
"@ctrl/transmission": "^7.2.0",
"@homarr/certificates": "workspace:^0.1.0",
"@homarr/common": "workspace:^0.1.0",
@@ -39,14 +39,14 @@
"proxmox-api": "1.1.1",
"undici": "7.3.0",
"xml2js": "^0.6.2",
"zod": "^3.24.2"
"zod": "^3.24.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/xml2js": "^0.4.14",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+2 -2
View File
@@ -26,7 +26,7 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"ioredis": "5.5.0",
"ioredis": "5.4.2",
"superjson": "2.2.2",
"winston": "3.17.0"
},
@@ -34,7 +34,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+5 -5
View File
@@ -32,19 +32,19 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^7.16.3",
"@tabler/icons-react": "^3.30.0",
"@mantine/core": "^7.16.2",
"@tabler/icons-react": "^3.29.0",
"dayjs": "^1.11.13",
"next": "15.1.7",
"next": "15.1.6",
"react": "19.0.0",
"react-dom": "19.0.0",
"zod": "^3.24.2"
"zod": "^3.24.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+3 -3
View File
@@ -24,15 +24,15 @@
"dependencies": {
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@mantine/core": "^7.16.3",
"@mantine/hooks": "^7.16.3",
"@mantine/core": "^7.16.2",
"@mantine/hooks": "^7.16.2",
"react": "19.0.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+3 -3
View File
@@ -24,14 +24,14 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/ui": "workspace:^0.1.0",
"@mantine/notifications": "^7.16.3",
"@tabler/icons-react": "^3.30.0"
"@mantine/notifications": "^7.16.2",
"@tabler/icons-react": "^3.29.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+5 -5
View File
@@ -37,14 +37,14 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^7.16.3",
"@mantine/hooks": "^7.16.3",
"@mantine/core": "^7.16.2",
"@mantine/hooks": "^7.16.2",
"adm-zip": "0.5.16",
"next": "15.1.7",
"next": "15.1.6",
"react": "19.0.0",
"react-dom": "19.0.0",
"superjson": "2.2.2",
"zod": "^3.24.2",
"zod": "^3.24.1",
"zod-form-data": "^2.0.5"
},
"devDependencies": {
@@ -52,7 +52,7 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/adm-zip": "0.5.7",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+2 -2
View File
@@ -23,13 +23,13 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"zod": "^3.24.2"
"zod": "^3.24.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+1 -1
View File
@@ -31,7 +31,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+2 -2
View File
@@ -26,14 +26,14 @@
"@homarr/db": "workspace:^",
"@homarr/definitions": "workspace:^",
"@homarr/log": "workspace:^",
"ioredis": "5.5.0",
"ioredis": "5.4.2",
"superjson": "2.2.2"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+3 -3
View File
@@ -22,7 +22,7 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@extractus/feed-extractor": "7.1.4",
"@extractus/feed-extractor": "7.1.3",
"@homarr/common": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
@@ -30,7 +30,7 @@
"@homarr/log": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0",
"dayjs": "^1.11.13",
"octokit": "^4.1.1",
"octokit": "^4.1.0",
"pretty-print-error": "^1.1.2",
"superjson": "2.2.2"
},
@@ -38,7 +38,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
@@ -5,7 +5,7 @@ import { hashObjectBase64, Stopwatch } from "@homarr/common";
import { decryptSecret } from "@homarr/common/server";
import type { MaybeArray } from "@homarr/common/types";
import { db } from "@homarr/db";
import { getItemsWithIntegrationsAsync, getServerSettingsAsync } from "@homarr/db/queries";
import { getItemsWithIntegrationsAsync } from "@homarr/db/queries";
import type { WidgetKind } from "@homarr/definitions";
import { logger } from "@homarr/log";
@@ -33,7 +33,6 @@ export const createRequestIntegrationJobHandler = <
},
) => {
return async () => {
const serverSettings = await getServerSettingsAsync(db);
const itemsForIntegration = await getItemsWithIntegrationsAsync(db, {
kinds: widgetKinds,
});
@@ -53,17 +52,7 @@ export const createRequestIntegrationJobHandler = <
const oneOrMultipleInputs = getInput[itemForIntegration.kind](
reduceWidgetOptionsWithDefaultValues(
itemForIntegration.kind,
{
defaultSearchEngineId: serverSettings.search.defaultSearchEngineId,
openSearchInNewTab: true,
firstDayOfWeek: 1,
homeBoardId: serverSettings.board.homeBoardId,
mobileHomeBoardId: serverSettings.board.mobileHomeBoardId,
pingIconsEnabled: true,
enableStatusByDefault: serverSettings.board.enableStatusByDefault,
forceDisableStatus: serverSettings.board.forceDisableStatus,
},
SuperJSON.parse<Record<string, unknown>>(itemForIntegration.options),
SuperJSON.parse(itemForIntegration.options),
) as never,
);
for (const { integration } of itemForIntegration.integrations) {
+1 -1
View File
@@ -29,7 +29,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
-2
View File
@@ -28,8 +28,6 @@ export const defaultServerSettings = {
board: {
homeBoardId: null as string | null,
mobileHomeBoardId: null as string | null,
enableStatusByDefault: true,
forceDisableStatus: false,
},
appearance: {
defaultColorScheme: "light" as ColorScheme,
+3 -3
View File
@@ -25,8 +25,8 @@
"@homarr/api": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/dates": "^7.16.3",
"next": "15.1.7",
"@mantine/dates": "^7.16.2",
"next": "15.1.6",
"react": "19.0.0",
"react-dom": "19.0.0"
},
@@ -34,7 +34,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+3 -9
View File
@@ -8,7 +8,7 @@ import type { RouterOutputs } from "@homarr/api";
import type { User } from "@homarr/db/schema";
import type { ServerSettings } from "@homarr/server-settings";
export type SettingsContextProps = Pick<
type SettingsContextProps = Pick<
User,
| "firstDayOfWeek"
| "defaultSearchEngineId"
@@ -16,15 +16,11 @@ export type SettingsContextProps = Pick<
| "mobileHomeBoardId"
| "openSearchInNewTab"
| "pingIconsEnabled"
> &
Pick<ServerSettings["board"], "enableStatusByDefault" | "forceDisableStatus">;
>;
interface PublicServerSettings {
search: Pick<ServerSettings["search"], "defaultSearchEngineId">;
board: Pick<
ServerSettings["board"],
"homeBoardId" | "mobileHomeBoardId" | "enableStatusByDefault" | "forceDisableStatus"
>;
board: Pick<ServerSettings["board"], "homeBoardId" | "mobileHomeBoardId">;
}
const SettingsContext = createContext<SettingsContextProps | null>(null);
@@ -43,8 +39,6 @@ export const SettingsProvider = ({
homeBoardId: user?.homeBoardId ?? serverSettings.board.homeBoardId,
mobileHomeBoardId: user?.mobileHomeBoardId ?? serverSettings.board.mobileHomeBoardId,
pingIconsEnabled: user?.pingIconsEnabled ?? false,
enableStatusByDefault: serverSettings.board.enableStatusByDefault,
forceDisableStatus: serverSettings.board.forceDisableStatus,
}}
>
{children}
+7 -7
View File
@@ -33,12 +33,12 @@
"@homarr/settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@mantine/core": "^7.16.3",
"@mantine/hooks": "^7.16.3",
"@mantine/spotlight": "^7.16.3",
"@tabler/icons-react": "^3.30.0",
"jotai": "^2.12.0",
"next": "15.1.7",
"@mantine/core": "^7.16.2",
"@mantine/hooks": "^7.16.2",
"@mantine/spotlight": "^7.16.2",
"@tabler/icons-react": "^3.29.0",
"jotai": "^2.11.3",
"next": "15.1.6",
"react": "19.0.0",
"react-dom": "19.0.0",
"use-deep-compare-effect": "^1.8.1"
@@ -47,7 +47,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
+2 -2
View File
@@ -32,7 +32,7 @@
"dayjs": "^1.11.13",
"deepmerge": "4.3.1",
"mantine-react-table": "2.0.0-beta.8",
"next": "15.1.7",
"next": "15.1.6",
"next-intl": "3.26.3",
"react": "19.0.0",
"react-dom": "19.0.0"
@@ -41,7 +41,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.20.1",
"eslint": "^9.19.0",
"typescript": "^5.7.3"
}
}
-11
View File
@@ -3,17 +3,6 @@ import type { MRT_Localization } from "mantine-react-table";
import { objectKeys } from "@homarr/common";
export const localeConfigurations = {
ca: {
name: "Català",
translatedName: "Catalan",
flagIcon: "es-ct",
importMrtLocalization() {
return import("./mantine-react-table/ca.json");
},
importDayJsLocale() {
return import("dayjs/locale/ca").then((module) => module.default);
},
},
cn: {
name: "中文",
translatedName: "Chinese (Simplified)",
+49 -67
View File
@@ -113,126 +113,126 @@
"subtitle": ""
},
"invite": {
"title": "Uneix-te a Homarr",
"title": "",
"subtitle": "",
"description": ""
},
"init": {
"title": "Nova instal·lació de Homarr",
"subtitle": "Creeu l'usuari administrador inicial"
"title": "",
"subtitle": ""
}
},
"field": {
"email": {
"label": "Adreça electrònica",
"verified": "Verificat"
"label": "",
"verified": ""
},
"username": {
"label": "Nom d’usuari"
"label": ""
},
"password": {
"label": "Contrasenya",
"label": "",
"requirement": {
"length": "Inclou com a mínim 8 caràcters",
"lowercase": "Inclou minúscules",
"uppercase": "Inclou majúscules",
"number": "Inclou nombres",
"special": "Inclou símbols especials"
"length": "",
"lowercase": "",
"uppercase": "",
"number": "",
"special": ""
}
},
"passwordConfirm": {
"label": "Confiremeu la contrasenya"
"label": ""
},
"previousPassword": {
"label": "Contrasenya anterior"
"label": ""
},
"homeBoard": {
"label": "Tauler principal"
"label": ""
},
"pingIconsEnabled": {
"label": "Utilitza icones pels pings"
"label": ""
},
"defaultSearchEngine": {
"label": "Motor de cerca principal"
"label": ""
},
"openSearchInNewTab": {
"label": "Obre els resultats de la cerca en una pestanya nova"
"label": ""
}
},
"error": {
"usernameTaken": "El nom d’usuari ja està en ús"
"usernameTaken": ""
},
"action": {
"login": {
"label": "Inicia la sessió",
"labelWith": "Inicia la sessió amb {provider}",
"label": "",
"labelWith": "",
"notification": {
"success": {
"title": "Inici de sessió correcte",
"message": "Heu iniciat sessió"
"title": "",
"message": ""
},
"error": {
"title": "No s'ha pogut iniciar sessió",
"message": "No s'ha pogut iniciar la sessió"
"title": "",
"message": ""
}
},
"forgotPassword": {
"label": "Heu oblidat la contrasenya?",
"label": "",
"description": ""
}
},
"register": {
"label": "Crear un compte",
"label": "",
"notification": {
"success": {
"title": "S'ha creat el compte",
"message": "Inicieu la sessió per continuar"
"title": "",
"message": ""
},
"error": {
"title": "La creació del compte ha fallat",
"message": "No s'ha pogut crear el compte"
"title": "",
"message": ""
}
}
},
"create": "Crea un usuari",
"create": "",
"changePassword": {
"label": "Canvia la contrasenya",
"label": "",
"notification": {
"success": {
"message": "La contrasenya s'ha canviat correctament"
"message": ""
},
"error": {
"message": "No s'ha pogut canviar la contrasenya"
"message": ""
}
}
},
"changeHomeBoard": {
"notification": {
"success": {
"message": "El tauler d'inici s'ha canviat correctament"
"message": ""
},
"error": {
"message": "No 'ha pogut canviar el tauler d'inici"
"message": ""
}
}
},
"changeSearchPreferences": {
"notification": {
"success": {
"message": "Els paràmetres de cerca s'han canviat correctament"
"message": ""
},
"error": {
"message": "No s'han pogut canviar els paràmetres de cerca"
"message": ""
}
}
},
"changeFirstDayOfWeek": {
"notification": {
"success": {
"message": "Primer dia de la setmana establert correctament"
"message": ""
},
"error": {
"message": "No s'ha pogut canviar el primer dia de la setmana"
"message": ""
}
}
},
@@ -248,7 +248,7 @@
},
"manageAvatar": {
"changeImage": {
"label": "Canvia la imatge",
"label": "",
"notification": {
"success": {
"message": "La imatge s'ha canviat correctament"
@@ -401,21 +401,21 @@
"title": "",
"item": {
"view-logs": {
"label": "Veure registres",
"description": "Permet que els membres visualitzin els registres"
"label": "",
"description": ""
}
}
},
"search-engine": {
"title": "Motors de cerca",
"title": "",
"item": {
"create": {
"label": "Crea motors de cerca",
"description": "Permet que els membres crein motors de cerca"
"label": "",
"description": ""
},
"modify-all": {
"label": "Modifica tots els motors de cerca",
"description": "Permet que els membres modifiquin tots els motors de cerca"
"label": "",
"description": ""
},
"full-all": {
"label": "",
@@ -2052,10 +2052,6 @@
"description": ""
}
},
"disableStatus": {
"label": "",
"description": ""
},
"columnCount": {
"label": ""
},
@@ -2089,9 +2085,6 @@
"customCss": {
"title": ""
},
"behavior": {
"title": ""
},
"access": {
"title": "",
"permission": {
@@ -2483,17 +2476,6 @@
"label": "",
"mobileLabel": "",
"description": ""
},
"status": {
"title": "",
"enableStatusByDefault": {
"label": "",
"description": ""
},
"forceDisableStatus": {
"label": "",
"description": ""
}
}
},
"search": {
Loaded 100 of 172 files, more files were not shown because too many files have changed in this diff. Show more