Compare commits

..
Author SHA1 Message Date
Thomas dc45d1e0de Merge branch 'dev' into fix/credentials-login-session 2026-06-03 10:32:44 +02:00
Thomas 5c0fd755cc Merge branch 'dev' into fix/credentials-login-session 2026-05-29 21:28:41 +02:00
Thomas 9bf6d9851c Merge branch 'dev' into fix/credentials-login-session 2026-05-29 11:17:53 +02:00
Thomas Camlong f1ed293b79 fix: address PR review feedback on password suggestions
Remove redundant tests, rename requirement translations to suggestion for clearer i18n, simplify password input state and requirement display.
2026-05-29 11:16:07 +02:00
Thomas Camlong 0df64f05e5 fix: remove auth session and env schema changes, keep only password requirement relaxation 2026-05-25 11:30:32 +02:00
Thomas Camlong d2ce64252c fix: revert unrelated changes (Dockerfile, Redis null-safety) 2026-05-25 10:09:55 +02:00
Thomas Camlong 63d50ed5fb fix(ci): resolve format, lint and typecheck failures 2026-05-21 16:50:28 +02:00
Thomas Camlong 27325326da fix(auth): resolve credentials login SQLITE_CONSTRAINT_NOTNULL and relax password rules
Session creation failed with NOT NULL constraint on expires column due to
invalid date propagation from duration parsing. Override adapter.createSession
with validated insert, harden signIn callback, fix createDurationSchema for
numeric-only values, and guard events.ts group insert.

Also relaxes password complexity to suggestions-only (min 8 chars enforced),
extracts shared UserCreatePasswordFields component, and fixes login form
passing extra fields to signIn.
2026-05-21 16:30:07 +02:00
26 changed files with 409 additions and 1348 deletions

No files matched your search

@@ -1,11 +1,9 @@
import { cache } from "react";
import { api } from "@homarr/api/server";
import { createBoardContentPage } from "../_creator";
const getHomeBoardAsync = cache(() => api.board.getHomeBoard());
export default createBoardContentPage<{ locale: string }>({
getInitialBoardAsync: getHomeBoardAsync,
async getInitialBoardAsync() {
return await api.board.getHomeBoard();
},
});
@@ -1,11 +1,9 @@
import { cache } from "react";
import { api } from "@homarr/api/server";
import { createBoardContentPage } from "../../_creator";
const getBoardByNameAsync = cache((name: string) => api.board.getBoardByName({ name }));
export default createBoardContentPage<{ locale: string; name: string }>({
getInitialBoardAsync: async ({ name }) => getBoardByNameAsync(name),
async getInitialBoardAsync({ name }) {
return await api.board.getBoardByName({ name });
},
});
@@ -1,24 +1,33 @@
"use client";
import dynamic from "next/dynamic";
import { Box } from "@mantine/core";
import { Box, Stack } from "@mantine/core";
import { useRequiredBoard } from "@homarr/boards/context";
import { useEditMode } from "@homarr/boards/edit-mode";
import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context";
import { StaticBoardGrid } from "~/components/board/sections/static-grid";
import { BoardCategorySection } from "~/components/board/sections/category-section";
import { BoardEmptySection } from "~/components/board/sections/empty-section";
import { BoardBackgroundVideo } from "~/components/layout/background";
const EditModeBoard = dynamic(() => import("./_edit-board"), { ssr: false });
export const ClientBoard = () => {
const board = useRequiredBoard();
const [isEditMode] = useEditMode();
const currentLayoutId = useCurrentLayout();
const fullWidthSortedSections = board.sections
.filter((section) => section.kind === "empty" || section.kind === "category")
.sort((sectionA, sectionB) => sectionA.yOffset - sectionB.yOffset);
return (
<Box h="100%" pos="relative">
<BoardBackgroundVideo />
{isEditMode ? <EditModeBoard /> : <StaticBoardGrid board={board} />}
<Stack h="100%">
{fullWidthSortedSections.map((section) =>
section.kind === "empty" ? (
<BoardEmptySection key={`${currentLayoutId}-${section.id}`} section={section} />
) : (
<BoardCategorySection key={`${currentLayoutId}-${section.id}`} section={section} />
),
)}
</Stack>
</Box>
);
};
@@ -1,3 +1,7 @@
"use client";
export { ClientBoard as DynamicClientBoard } from "./_client";
import dynamic from "next/dynamic";
export const DynamicClientBoard = dynamic(() => import("./_client").then((mod) => mod.ClientBoard), {
ssr: false,
});
@@ -1,31 +0,0 @@
"use client";
import { Stack } from "@mantine/core";
import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context";
import { BoardCategorySection } from "~/components/board/sections/category-section";
import { BoardEmptySection } from "~/components/board/sections/empty-section";
const EditModeBoard = () => {
const board = useRequiredBoard();
const currentLayoutId = useCurrentLayout();
const fullWidthSortedSections = board.sections
.filter((section) => section.kind === "empty" || section.kind === "category")
.toSorted((sectionA, sectionB) => sectionA.yOffset - sectionB.yOffset);
return (
<Stack h="100%">
{fullWidthSortedSections.map((section) =>
section.kind === "empty" ? (
<BoardEmptySection key={`${currentLayoutId}-${section.id}`} section={section} />
) : (
<BoardCategorySection key={`${currentLayoutId}-${section.id}`} section={section} />
),
)}
</Stack>
);
};
export default EditModeBoard;
@@ -19,10 +19,6 @@ import { BoardItemMenu } from "./item-menu";
interface BoardItemContentProps {
item: SectionItem;
fallbackDimensions?: {
width: number;
height: number;
};
}
const getOverflowFromKind = (kind: SectionItem["kind"]) => {
@@ -31,11 +27,9 @@ const getOverflowFromKind = (kind: SectionItem["kind"]) => {
return undefined;
};
export const BoardItemContent = ({ item, fallbackDimensions }: BoardItemContentProps) => {
export const BoardItemContent = ({ item }: BoardItemContentProps) => {
const { ref, width, height } = useElementSize<HTMLDivElement>();
const board = useRequiredBoard();
const fallbackWidth = fallbackDimensions?.width ?? 0;
const fallbackHeight = fallbackDimensions?.height ?? 0;
return (
<>
@@ -58,7 +52,7 @@ export const BoardItemContent = ({ item, fallbackDimensions }: BoardItemContentP
}}
p={0}
>
<InnerContent item={item} width={width || fallbackWidth} height={height || fallbackHeight} />
<InnerContent item={item} width={width} height={height} />
</Card>
{item.advancedOptions.title?.trim() && (
<Badge
@@ -12,7 +12,7 @@ import { widgetImports } from "@homarr/widgets";
import { WidgetEditModal } from "@homarr/widgets/modals";
import type { SectionItem } from "~/app/[locale]/boards/_types";
import { useOptionalSectionContext } from "../sections/section-context";
import { useSectionContext } from "../sections/section-context";
import { useItemActions } from "./item-actions";
import { ItemMoveModal } from "./item-move-modal";
@@ -41,8 +41,7 @@ const BoardItemMenuInner = ({ offset, item, resetErrorBoundary }: BoardItemMenuP
useItemActions();
const { data: integrationData, isPending } = clientApi.integration.all.useQuery();
const currentDefinition = useMemo(() => widgetImports[item.kind].definition, [item.kind]);
const sectionContext = useOptionalSectionContext();
const gridstack = sectionContext?.refs.gridstack;
const { gridstack } = useSectionContext().refs;
const settings = useSettings();
// Reset error boundary on next render if item has been edited
@@ -128,7 +127,7 @@ const BoardItemMenuInner = ({ offset, item, resetErrorBoundary }: BoardItemMenuP
<Menu.Item
leftSection={<IconLayoutKanban size={16} />}
onClick={() => {
if (!gridstack?.current) return;
if (!gridstack.current) return;
openMoveModal({ item, columnCount: gridstack.current.getColumn(), gridStack: gridstack.current });
}}
>
@@ -1,21 +1,48 @@
import { Card, Collapse, Group, Stack, Title, UnstyledButton } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { IconChevronDown, IconChevronUp } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { useSession } from "@homarr/auth/client";
import { useRequiredBoard } from "@homarr/boards/context";
import type { CategorySection } from "~/app/[locale]/boards/_types";
import { CategoryMenu } from "./category/category-menu";
import { GridStack } from "./gridstack/gridstack";
import classes from "./item.module.css";
import { useCategoryCollapse } from "./use-category-collapse";
const localStorageKey = (sectionId: string) => `homarr-section-collapsed-${sectionId}`;
interface Props {
section: CategorySection;
}
export const BoardCategorySection = ({ section }: Props) => {
const { status } = useSession();
const { mutate } = clientApi.section.changeCollapsed.useMutation();
const board = useRequiredBoard();
const [opened, { toggle }] = useCategoryCollapse(section);
const initialCollapsed =
typeof window !== "undefined"
? (localStorage.getItem(localStorageKey(section.id)) ?? String(section.collapsed)) === "true"
: section.collapsed;
const [opened, { toggle }] = useDisclosure(initialCollapsed, {
onOpen() {
if (status === "authenticated") {
mutate({ sectionId: section.id, collapsed: true });
} else if (status === "unauthenticated") {
localStorage.setItem(localStorageKey(section.id), "true");
}
},
onClose() {
if (status === "authenticated") {
mutate({ sectionId: section.id, collapsed: false });
} else if (status === "unauthenticated") {
localStorage.setItem(localStorageKey(section.id), "false");
}
},
});
return (
<Card style={{ "--opacity": board.opacity / 100 }} radius={board.itemRadius} p={0} className={classes.itemCard}>
@@ -20,8 +20,4 @@ export const useSectionContext = () => {
return context;
};
export const useOptionalSectionContext = () => {
return useContext(SectionContext);
};
export const SectionProvider = SectionContext.Provider;
@@ -1,93 +0,0 @@
import { describe, expect, test } from "vitest";
import { BoardMockBuilder } from "../items/actions/test/mocks/board-mock";
import { DynamicSectionMockBuilder } from "../items/actions/test/mocks/dynamic-section-mock";
import { ItemMockBuilder } from "../items/actions/test/mocks/item-mock";
import { LayoutMockBuilder } from "../items/actions/test/mocks/layout-mock";
import {
getEstimatedGridWidth,
getFallbackDimensions,
getFullWidthSortedSections,
getStaticGridElements,
getStaticGridLayouts,
getStaticGridRowCount,
getStaticLayoutSelectionCss,
} from "./static-grid-layout";
describe("static grid layout", () => {
test("sorts layouts and creates breakpoint CSS in mobile-first order", () => {
const mobile = new LayoutMockBuilder({ id: "mobile", breakpoint: 0, columnCount: 4 }).build();
const tablet = new LayoutMockBuilder({ id: "tablet", breakpoint: 768, columnCount: 8 }).build();
const desktop = new LayoutMockBuilder({ id: "desktop", breakpoint: 1024, columnCount: 12 }).build();
const sortedLayouts = getStaticGridLayouts([desktop, mobile, tablet]);
const css = getStaticLayoutSelectionCss("board-1", sortedLayouts);
expect(sortedLayouts.map((layout) => layout.id)).toEqual(["mobile", "tablet", "desktop"]);
expect(css).toContain('[data-static-layout="mobile"] { display: block; }');
expect(css).toContain("@media (min-width: 768px)");
expect(css).toContain('[data-static-layout="tablet"] { display: block; }');
expect(css).toContain("@media (min-width: 1024px)");
expect(css).toContain('[data-static-layout="desktop"] { display: block; }');
});
test("selects and sorts layout-specific section elements", () => {
const rootSectionId = "root";
const mobileLayoutId = "mobile";
const desktopLayoutId = "desktop";
const mobile = new LayoutMockBuilder({ id: mobileLayoutId, breakpoint: 0, columnCount: 4 }).build();
const desktop = new LayoutMockBuilder({ id: desktopLayoutId, breakpoint: 1024, columnCount: 12 }).build();
const item = new ItemMockBuilder({ id: "item" })
.addLayout({ layoutId: mobileLayoutId, sectionId: rootSectionId, xOffset: 2, yOffset: 1, width: 1, height: 1 })
.addLayout({ layoutId: desktopLayoutId, sectionId: rootSectionId, xOffset: 0, yOffset: 1, width: 4, height: 2 })
.build();
const dynamicSection = new DynamicSectionMockBuilder({ id: "dynamic" })
.addLayout({
layoutId: mobileLayoutId,
parentSectionId: rootSectionId,
xOffset: 0,
yOffset: 0,
width: 2,
height: 2,
})
.addLayout({
layoutId: desktopLayoutId,
parentSectionId: rootSectionId,
xOffset: 4,
yOffset: 1,
width: 4,
height: 2,
})
.build();
const board = new BoardMockBuilder()
.addEmptySection({ id: rootSectionId, yOffset: 2 })
.addEmptySection({ id: "first", yOffset: 0 })
.addSection(dynamicSection)
.addItem(item)
.build();
board.layouts = [desktop, mobile];
const mobileElements = getStaticGridElements(board, rootSectionId, mobileLayoutId);
const desktopElements = getStaticGridElements(board, rootSectionId, desktopLayoutId);
const fullWidthSections = getFullWidthSortedSections(board);
expect(mobileElements.map((element) => element.id)).toEqual(["dynamic", "item"]);
expect(desktopElements.map((element) => ({ id: element.id, width: element.width }))).toEqual([
{ id: "item", width: 4 },
{ id: "dynamic", width: 4 },
]);
expect(getStaticGridRowCount(mobileElements)).toBe(2);
expect(fullWidthSections.map((section) => section.id)).toEqual(["first", rootSectionId]);
});
test("estimates grid and item dimensions per layout range", () => {
const mobile = new LayoutMockBuilder({ id: "mobile", breakpoint: 0, columnCount: 4 }).build();
const tablet = new LayoutMockBuilder({ id: "tablet", breakpoint: 768, columnCount: 8 }).build();
const desktop = new LayoutMockBuilder({ id: "desktop", breakpoint: 1024, columnCount: 12 }).build();
expect(getEstimatedGridWidth(mobile, tablet)).toBe(384);
expect(getEstimatedGridWidth(tablet, desktop)).toBe(896);
expect(getEstimatedGridWidth(desktop)).toBe(1280);
expect(getFallbackDimensions({ width: 2, height: 3 }, 400, 4)).toEqual({ width: 180, height: 280 });
});
});
@@ -1,103 +0,0 @@
import type { Board, DynamicSectionItem, EmptySection, SectionItem } from "~/app/[locale]/boards/_types";
type BoardLayout = Board["layouts"][number];
type StaticGridElement = SectionItem | DynamicSectionItem;
const STATIC_GRID_ITEM_INSET = 20;
const DEFAULT_SMALL_VIEWPORT_WIDTH = 360;
const DEFAULT_LARGE_VIEWPORT_WIDTH = 1280;
export const getStaticGridLayouts = (layouts: Board["layouts"]) =>
[...layouts].toSorted((layoutA, layoutB) => layoutA.breakpoint - layoutB.breakpoint);
const escapeCssString = (value: string) => value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
export const getStaticLayoutSelectionCss = (boardId: string, layouts: Board["layouts"]) => {
const sortedLayouts = getStaticGridLayouts(layouts);
const initialLayout = sortedLayouts.at(0);
if (!initialLayout) return "";
const scope = `[data-static-board="${escapeCssString(boardId)}"]`;
const css = [
`${scope} .static-board-layout { display: none; }`,
`${scope} .static-board-layout[data-static-layout="${escapeCssString(initialLayout.id)}"] { display: block; }`,
];
for (const layout of sortedLayouts.slice(1)) {
css.push(
`@media (min-width: ${layout.breakpoint}px) { ${scope} .static-board-layout { display: none; } ${scope} .static-board-layout[data-static-layout="${escapeCssString(layout.id)}"] { display: block; } }`,
);
}
return css.join("\n");
};
export const getFullWidthSortedSections = (board: Board) =>
board.sections
.filter(
(section): section is EmptySection | Extract<Board["sections"][number], { kind: "category" }> =>
section.kind === "empty" || section.kind === "category",
)
.sort((sectionA, sectionB) => sectionA.yOffset - sectionB.yOffset);
export const getSectionItemsForLayout = (board: Board, sectionId: string, layoutId: string): SectionItem[] => {
return board.items
.map(({ layouts, ...item }) => {
const layout = layouts.find((candidate) => candidate.layoutId === layoutId);
if (!layout) return null;
return { ...layout, ...item, type: "item" as const };
})
.filter((item): item is SectionItem => item !== null && item.sectionId === sectionId);
};
export const getDynamicSectionsForLayout = (
board: Board,
sectionId: string,
layoutId: string,
): DynamicSectionItem[] => {
return board.sections
.filter((section) => section.kind === "dynamic")
.map(({ layouts, ...section }) => {
const layout = layouts.find((candidate) => candidate.layoutId === layoutId);
if (!layout) return null;
return { ...layout, ...section, type: "section" as const };
})
.filter((entry): entry is DynamicSectionItem => entry !== null && entry.parentSectionId === sectionId);
};
export const getStaticGridElements = (board: Board, sectionId: string, layoutId: string): StaticGridElement[] => {
return [
...getSectionItemsForLayout(board, sectionId, layoutId),
...getDynamicSectionsForLayout(board, sectionId, layoutId),
].toSorted((itemA, itemB) => {
if (itemA.yOffset === itemB.yOffset) return itemA.xOffset - itemB.xOffset;
return itemA.yOffset - itemB.yOffset;
});
};
export const getStaticGridRowCount = (items: StaticGridElement[], fixedRowCount?: number) => {
return fixedRowCount ?? Math.max(...items.map((item) => item.yOffset + item.height));
};
export const getEstimatedGridWidth = (layout: BoardLayout, nextLayout?: BoardLayout) => {
if (nextLayout) {
const upperBound = Math.max(nextLayout.breakpoint - 1, DEFAULT_SMALL_VIEWPORT_WIDTH);
return Math.max(DEFAULT_SMALL_VIEWPORT_WIDTH, Math.round((layout.breakpoint + upperBound) / 2));
}
return Math.max(layout.breakpoint, DEFAULT_LARGE_VIEWPORT_WIDTH);
};
export const getFallbackDimensions = (
item: Pick<StaticGridElement, "width" | "height">,
estimatedGridWidth: number,
columnCount: number,
) => {
const cellSize = estimatedGridWidth / columnCount;
return {
width: Math.max(0, item.width * cellSize - STATIC_GRID_ITEM_INSET),
height: Math.max(0, item.height * cellSize - STATIC_GRID_ITEM_INSET),
};
};
@@ -1,289 +0,0 @@
"use client";
import { Badge, Card, Collapse, Group, Stack, Title, UnstyledButton } from "@mantine/core";
import { IconChevronDown, IconChevronUp } from "@tabler/icons-react";
import { useRequiredBoard } from "@homarr/boards/context";
import type {
Board,
CategorySection,
DynamicSectionItem,
EmptySection,
SectionItem,
} from "~/app/[locale]/boards/_types";
import { BoardItemContent } from "../items/item-content";
import { CategoryMenu } from "./category/category-menu";
import classes from "./item.module.css";
import {
getEstimatedGridWidth,
getFallbackDimensions,
getFullWidthSortedSections,
getStaticGridElements,
getStaticGridLayouts,
getStaticGridRowCount,
getStaticLayoutSelectionCss,
} from "./static-grid-layout";
import { useCategoryCollapse } from "./use-category-collapse";
interface StaticBoardGridProps {
board: Board;
}
export const StaticBoardGrid = ({ board }: StaticBoardGridProps) => {
const boardId = `board-${board.id}`;
const layouts = getStaticGridLayouts(board.layouts);
const layoutSelectionCss = getStaticLayoutSelectionCss(boardId, layouts);
const fullWidthSortedSections = getFullWidthSortedSections(board);
if (layouts.length === 0) return null;
return (
<>
<style>{layoutSelectionCss}</style>
<div data-static-board={boardId}>
{layouts.map((layout, index) => (
<div key={layout.id} className="static-board-layout" data-static-layout={layout.id}>
<Stack h="100%">
{fullWidthSortedSections.map((section) =>
section.kind === "empty" ? (
<StaticEmptySection
key={section.id}
section={section}
board={board}
layoutId={layout.id}
columnCount={layout.columnCount}
estimatedGridWidth={getEstimatedGridWidth(layout, layouts.at(index + 1))}
/>
) : (
<StaticCategorySection
key={section.id}
section={section}
board={board}
layoutId={layout.id}
columnCount={layout.columnCount}
estimatedGridWidth={getEstimatedGridWidth(layout, layouts.at(index + 1))}
/>
),
)}
</Stack>
</div>
))}
</div>
</>
);
};
interface StaticSectionGridProps {
sectionId: string;
board: Board;
layoutId: string;
columnCount: number;
estimatedGridWidth: number;
isDynamic?: boolean;
fixedRowCount?: number;
}
const StaticSectionGrid = ({
sectionId,
board,
layoutId,
columnCount,
estimatedGridWidth,
isDynamic,
fixedRowCount,
}: StaticSectionGridProps) => {
const allItems = getStaticGridElements(board, sectionId, layoutId);
if (allItems.length === 0) return null;
const rowCount = getStaticGridRowCount(allItems, fixedRowCount);
return (
<div className="static-grid-container" style={{ height: isDynamic ? "100%" : undefined }}>
<div
className="static-grid"
style={{
display: "grid",
gridTemplateColumns: `repeat(${columnCount}, 1fr)`,
gridTemplateRows: isDynamic
? `repeat(${rowCount}, 1fr)`
: `repeat(${rowCount}, calc(100cqi / ${columnCount}))`,
height: isDynamic ? "100%" : undefined,
}}
>
{allItems.map((item) => (
<StaticGridItem
key={item.id}
item={item}
board={board}
layoutId={layoutId}
estimatedGridWidth={estimatedGridWidth}
columnCount={columnCount}
/>
))}
</div>
</div>
);
};
interface StaticGridItemProps {
item: SectionItem | DynamicSectionItem;
board: Board;
layoutId: string;
estimatedGridWidth: number;
columnCount: number;
}
const StaticGridItem = ({ item, board, layoutId, estimatedGridWidth, columnCount }: StaticGridItemProps) => {
const fallbackDimensions = getFallbackDimensions(item, estimatedGridWidth, columnCount);
return (
<div
style={{
gridColumn: `${item.xOffset + 1} / span ${item.width}`,
gridRow: `${item.yOffset + 1} / span ${item.height}`,
position: "relative",
}}
>
<div className="static-grid-item-content">
{item.type === "item" ? (
<BoardItemContent item={item} fallbackDimensions={fallbackDimensions} />
) : (
<StaticDynamicSection
section={item}
board={board}
layoutId={layoutId}
estimatedGridWidth={fallbackDimensions.width}
/>
)}
</div>
</div>
);
};
interface StaticEmptySectionProps {
section: EmptySection;
board: Board;
layoutId: string;
columnCount: number;
estimatedGridWidth: number;
}
const StaticEmptySection = ({ section, board, layoutId, columnCount, estimatedGridWidth }: StaticEmptySectionProps) => {
const items = getStaticGridElements(board, section.id, layoutId);
if (items.length === 0) return null;
return (
<StaticSectionGrid
sectionId={section.id}
board={board}
layoutId={layoutId}
columnCount={columnCount}
estimatedGridWidth={estimatedGridWidth}
/>
);
};
interface StaticCategorySectionProps {
section: CategorySection;
board: Board;
layoutId: string;
columnCount: number;
estimatedGridWidth: number;
}
const StaticCategorySection = ({
section,
board,
layoutId,
columnCount,
estimatedGridWidth,
}: StaticCategorySectionProps) => {
const boardData = useRequiredBoard();
const [opened, { toggle }] = useCategoryCollapse(section);
return (
<Card
style={{ "--opacity": boardData.opacity / 100 }}
radius={boardData.itemRadius}
p={0}
className={classes.itemCard}
>
<Stack>
<Group wrap="nowrap" gap="sm">
<UnstyledButton w="100%" p="sm" onClick={toggle}>
<Group wrap="nowrap">
{opened ? <IconChevronUp size={20} /> : <IconChevronDown size={20} />}
<Title order={3}>{section.name}</Title>
</Group>
</UnstyledButton>
<CategoryMenu category={section} />
</Group>
<Collapse expanded={opened} p="sm" pt={0}>
<StaticSectionGrid
sectionId={section.id}
board={board}
layoutId={layoutId}
columnCount={columnCount}
estimatedGridWidth={estimatedGridWidth}
/>
</Collapse>
</Stack>
</Card>
);
};
interface StaticDynamicSectionProps {
section: DynamicSectionItem;
board: Board;
layoutId: string;
estimatedGridWidth: number;
}
const StaticDynamicSection = ({ section, board, layoutId, estimatedGridWidth }: StaticDynamicSectionProps) => {
const boardData = useRequiredBoard();
const options = section.options;
return (
<Card
className={classes.itemCard}
w="100%"
h="100%"
styles={{
root: {
overflow: "visible",
"--opacity": boardData.opacity / 100,
"--border-color": options.borderColor || undefined,
},
}}
radius={boardData.itemRadius}
p={0}
>
{options.title && (
<Badge
pos="absolute"
top={-15}
left={10}
size="md"
radius={boardData.itemRadius}
color="var(--background-color)"
c="var(--mantine-color-text)"
bd="1px solid var(--border-color)"
>
{options.title}
</Badge>
)}
<StaticSectionGrid
sectionId={section.id}
board={board}
layoutId={layoutId}
columnCount={section.width}
estimatedGridWidth={estimatedGridWidth}
isDynamic
fixedRowCount={section.height}
/>
</Card>
);
};
@@ -1,77 +0,0 @@
"use client";
import { useCallback, useEffect, useState } from "react";
import { clientApi } from "@homarr/api/client";
import { useSession } from "@homarr/auth/client";
import type { CategorySection } from "~/app/[locale]/boards/_types";
export const getCategoryCollapseStorageKey = (sectionId: string) => `homarr-section-collapsed-${sectionId}`;
const categoryCollapseChangeEventName = "homarr-section-collapse-change";
interface CategoryExpandedChangeEventDetail {
sectionId: string;
expanded: boolean;
}
const getInitialExpanded = (section: CategorySection) => {
if (typeof window === "undefined") return section.collapsed;
return (localStorage.getItem(getCategoryCollapseStorageKey(section.id)) ?? String(section.collapsed)) === "true";
};
export const useCategoryCollapse = (section: CategorySection) => {
const { status } = useSession();
const { mutate } = clientApi.section.changeCollapsed.useMutation();
const [opened, setOpened] = useState(() => getInitialExpanded(section));
const persistExpanded = useCallback(
(expanded: boolean) => {
if (status === "authenticated") {
mutate({ sectionId: section.id, collapsed: expanded });
} else if (status === "unauthenticated") {
localStorage.setItem(getCategoryCollapseStorageKey(section.id), String(expanded));
}
},
[mutate, section.id, status],
);
useEffect(() => {
const handleCollapseChange = (event: Event) => {
const { sectionId, expanded } = (event as CustomEvent<CategoryExpandedChangeEventDetail>).detail;
if (sectionId === section.id) {
setOpened(expanded);
}
};
window.addEventListener(categoryCollapseChangeEventName, handleCollapseChange);
return () => {
window.removeEventListener(categoryCollapseChangeEventName, handleCollapseChange);
};
}, [section.id]);
const toggle = useCallback(() => {
setOpened((current) => {
const expanded = !current;
persistExpanded(expanded);
if (typeof window !== "undefined") {
window.dispatchEvent(
new CustomEvent<CategoryExpandedChangeEventDetail>(categoryCollapseChangeEventName, {
detail: {
sectionId: section.id,
expanded,
},
}),
);
}
return expanded;
});
}, [persistExpanded, section.id]);
return [opened, { toggle }] as const;
};
-14
View File
@@ -112,17 +112,3 @@
.grid-stack-empty-wrapper {
display: none !important;
}
.static-grid-container {
container-type: inline-size;
}
.static-grid-item-content {
position: absolute;
inset: 10px;
> .grid-stack-item-content {
width: 100%;
height: 100%;
}
}
-304
View File
@@ -1,304 +0,0 @@
import { chromium } from "@playwright/test";
import { createId } from "@paralleldrive/cuid2";
import SuperJSON from "superjson";
import { describe, expect, test } from "vitest";
import * as sqliteSchema from "../packages/db/schema/sqlite";
import { hashPasswordAsync } from "../packages/auth/security";
import { createHomarrContainer } from "./shared/create-homarr-container";
import { createSqliteDbFileAsync } from "./shared/e2e-db";
import type { SqliteDatabase } from "./shared/e2e-db";
const boardName = "responsive-board";
const credentials = {
username: "responsive-admin",
password: "Comp(exP4sswOrd",
};
const viewports = [
{ width: 320, height: 568 },
{ width: 375, height: 667 },
{ width: 768, height: 1024 },
{ width: 1024, height: 768 },
{ width: 1366, height: 768 },
{ width: 1920, height: 1080 },
];
describe("Board responsive layout", () => {
test("renders static board layouts across common viewports and loads edit mode", async () => {
const { db, localMountPath } = await createSqliteDbFileAsync();
const { categorySectionId } = await seedResponsiveBoardAsync(db);
const homarrContainer = await createHomarrContainer({
mounts: {
"/appdata": localMountPath,
},
}).start();
const browser = await chromium.launch();
const baseUrl = `http://${homarrContainer.getHost()}:${homarrContainer.getMappedPort(7575)}`;
try {
for (const viewport of viewports) {
const context = await browser.newContext({ viewport });
await context.addInitScript(() => {
window.__homarrLayoutShiftScore = 0;
new PerformanceObserver((entries) => {
for (const entry of entries.getEntries()) {
const layoutShift = entry as PerformanceEntry & { value: number; hadRecentInput: boolean };
if (!layoutShift.hadRecentInput) {
window.__homarrLayoutShiftScore += layoutShift.value;
}
}
}).observe({ type: "layout-shift", buffered: true });
});
await context.addInitScript(
({ sectionId }) => {
localStorage.setItem(`homarr-section-collapsed-${sectionId}`, "true");
},
{ sectionId: categorySectionId },
);
const page = await context.newPage();
await page.goto(`${baseUrl}/boards/${boardName}`);
await page.waitForSelector(".static-grid-item-content", { state: "visible" });
await page.waitForTimeout(500);
const result = await page.evaluate(() => {
const visibleLayout = Array.from(document.querySelectorAll<HTMLElement>(".static-board-layout")).find(
(element) => getComputedStyle(element).display !== "none",
);
const grids = Array.from(visibleLayout?.querySelectorAll<HTMLElement>(".static-grid") ?? []);
const hasOverlappingItems = grids.some((grid) => {
const rects = Array.from(grid.children)
.map((element) => element.getBoundingClientRect())
.filter((rect) => rect.width > 0 && rect.height > 0);
return rects.some((rect, index) =>
rects.slice(index + 1).some((otherRect) => {
const horizontalOverlap = Math.min(rect.right, otherRect.right) - Math.max(rect.left, otherRect.left);
const verticalOverlap = Math.min(rect.bottom, otherRect.bottom) - Math.max(rect.top, otherRect.top);
return horizontalOverlap > 1 && verticalOverlap > 1;
}),
);
});
const visibleItems = Array.from(
visibleLayout?.querySelectorAll<HTMLElement>(".static-grid-item-content") ?? [],
).filter((element) => {
const rect = element.getBoundingClientRect();
return rect.width > 0 && rect.height > 0;
});
return {
hasVisibleLayout: Boolean(visibleLayout),
visibleItemCount: visibleItems.length,
hasHorizontalOverflow: document.documentElement.scrollWidth > window.innerWidth + 1,
hasOverlappingItems,
layoutShiftScore: window.__homarrLayoutShiftScore,
};
});
expect(result.hasVisibleLayout).toBe(true);
expect(result.visibleItemCount).toBe(4);
expect(result.hasHorizontalOverflow).toBe(false);
expect(result.hasOverlappingItems).toBe(false);
expect(result.layoutShiftScore).toBeLessThanOrEqual(0.01);
await context.close();
}
const context = await browser.newContext({ viewport: { width: 1366, height: 768 } });
const page = await context.newPage();
await page.goto(`${baseUrl}/auth/login`);
await page.getByLabel("Username").fill(credentials.username);
await page.getByLabel("Password").fill(credentials.password);
await page.locator("css=button[type='submit']").click();
await page.waitForURL(baseUrl);
await page.goto(`${baseUrl}/boards/${boardName}`);
await page.waitForSelector(".static-grid-item-content", { state: "visible" });
await page.keyboard.press("ControlOrMeta+E");
await page.waitForSelector(".grid-stack", { state: "visible" });
await expect.poll(() => page.locator(".grid-stack-item").count()).toBeGreaterThan(0);
await context.close();
} finally {
await browser.close();
await homarrContainer.stop();
}
}, 120_000);
});
const seedResponsiveBoardAsync = async (db: SqliteDatabase) => {
const userId = createId();
const boardId = createId();
const mobileLayoutId = createId();
const tabletLayoutId = createId();
const desktopLayoutId = createId();
const rootSectionId = createId();
const categorySectionId = createId();
const dynamicSectionId = createId();
await db.update(sqliteSchema.onboarding).set({ step: "finish" });
await db.insert(sqliteSchema.users).values({
id: userId,
name: credentials.username,
password: await hashPasswordAsync(credentials.password),
});
await db.insert(sqliteSchema.boards).values({
id: boardId,
name: boardName,
isPublic: true,
creatorId: userId,
});
await db.insert(sqliteSchema.layouts).values([
{ id: mobileLayoutId, name: "Mobile", boardId, columnCount: 4, breakpoint: 0 },
{ id: tabletLayoutId, name: "Tablet", boardId, columnCount: 8, breakpoint: 768 },
{ id: desktopLayoutId, name: "Desktop", boardId, columnCount: 12, breakpoint: 1024 },
]);
await db.insert(sqliteSchema.sections).values([
{ id: rootSectionId, boardId, kind: "empty", xOffset: 0, yOffset: 0 },
{ id: categorySectionId, boardId, kind: "category", xOffset: 0, yOffset: 1, name: "Systems" },
{
id: dynamicSectionId,
boardId,
kind: "dynamic",
options: SuperJSON.stringify({ title: "Nested", borderColor: "", customCssClasses: [] }),
},
]);
await db.insert(sqliteSchema.sectionLayouts).values([
{
sectionId: dynamicSectionId,
layoutId: mobileLayoutId,
parentSectionId: categorySectionId,
xOffset: 0,
yOffset: 0,
width: 4,
height: 2,
},
{
sectionId: dynamicSectionId,
layoutId: tabletLayoutId,
parentSectionId: categorySectionId,
xOffset: 0,
yOffset: 0,
width: 4,
height: 2,
},
{
sectionId: dynamicSectionId,
layoutId: desktopLayoutId,
parentSectionId: categorySectionId,
xOffset: 0,
yOffset: 0,
width: 6,
height: 2,
},
]);
const rootItemId = await insertClockItemAsync(db, boardId);
const categoryItemId = await insertClockItemAsync(db, boardId);
const dynamicItemId = await insertClockItemAsync(db, boardId);
await db.insert(sqliteSchema.itemLayouts).values([
{
itemId: rootItemId,
sectionId: rootSectionId,
layoutId: mobileLayoutId,
xOffset: 0,
yOffset: 0,
width: 2,
height: 1,
},
{
itemId: rootItemId,
sectionId: rootSectionId,
layoutId: tabletLayoutId,
xOffset: 0,
yOffset: 0,
width: 3,
height: 1,
},
{
itemId: rootItemId,
sectionId: rootSectionId,
layoutId: desktopLayoutId,
xOffset: 0,
yOffset: 0,
width: 4,
height: 1,
},
{
itemId: categoryItemId,
sectionId: categorySectionId,
layoutId: mobileLayoutId,
xOffset: 0,
yOffset: 2,
width: 4,
height: 1,
},
{
itemId: categoryItemId,
sectionId: categorySectionId,
layoutId: tabletLayoutId,
xOffset: 4,
yOffset: 0,
width: 4,
height: 2,
},
{
itemId: categoryItemId,
sectionId: categorySectionId,
layoutId: desktopLayoutId,
xOffset: 6,
yOffset: 0,
width: 6,
height: 2,
},
{
itemId: dynamicItemId,
sectionId: dynamicSectionId,
layoutId: mobileLayoutId,
xOffset: 0,
yOffset: 0,
width: 2,
height: 1,
},
{
itemId: dynamicItemId,
sectionId: dynamicSectionId,
layoutId: tabletLayoutId,
xOffset: 0,
yOffset: 0,
width: 2,
height: 1,
},
{
itemId: dynamicItemId,
sectionId: dynamicSectionId,
layoutId: desktopLayoutId,
xOffset: 0,
yOffset: 0,
width: 3,
height: 1,
},
]);
return {
categorySectionId,
};
};
const insertClockItemAsync = async (db: SqliteDatabase, boardId: string) => {
const itemId = createId();
await db.insert(sqliteSchema.items).values({
id: itemId,
boardId,
kind: "clock",
options: SuperJSON.stringify({ is24HourFormat: true }),
advancedOptions: SuperJSON.stringify({ title: null, customCssClasses: [], borderColor: "" }),
});
return itemId;
};
declare global {
interface Window {
__homarrLayoutShiftScore: number;
}
}
@@ -44,7 +44,6 @@ const optionMapping: OptionMapping = {
hideIcon: (oldOptions) => oldOptions.items.some((item) => item.hideIcon),
hideHostname: (oldOptions) => oldOptions.items.some((item) => item.hideHostname),
openNewTab: (oldOptions) => oldOptions.items.some((item) => item.openNewTab),
withBorder: () => undefined,
},
calendar: {
releaseType: (oldOptions) => [oldOptions.radarrReleaseType],
-3
View File
@@ -1406,9 +1406,6 @@
"openNewTab": {
"label": "Open in new tab"
},
"withBorder": {
"label": "Show border"
},
"items": {
"label": "Bookmarks",
"add": "Add bookmark"
+2 -17
View File
@@ -16,7 +16,7 @@ export default function BookmarksWidget({ options, itemId }: WidgetComponentProp
const board = useRequiredBoard();
const [data] = clientApi.app.byIds.useSuspenseQuery(options.items, {
select(data) {
return data.toSorted((appA, appB) => options.items.indexOf(appA.id) - options.items.indexOf(appB.id));
return data.sort((appA, appB) => options.items.indexOf(appA.id) - options.items.indexOf(appB.id));
},
});
@@ -56,7 +56,6 @@ export default function BookmarksWidget({ options, itemId }: WidgetComponentProp
hideIcon={options.hideIcon}
hideHostname={options.hideHostname}
openNewTab={options.openNewTab}
withBorder={options.withBorder}
hasIconColor={board.iconColor !== null}
/>
)}
@@ -68,7 +67,6 @@ export default function BookmarksWidget({ options, itemId }: WidgetComponentProp
hideIcon={options.hideIcon}
hideHostname={options.hideHostname}
openNewTab={options.openNewTab}
withBorder={options.withBorder}
hasIconColor={board.iconColor !== null}
/>
)}
@@ -83,7 +81,6 @@ interface FlexLayoutProps {
hideIcon: boolean;
hideHostname: boolean;
openNewTab: boolean;
withBorder: boolean;
hasIconColor: boolean;
}
@@ -94,7 +91,6 @@ const FlexLayout = ({
hideIcon,
hideHostname,
openNewTab,
withBorder,
hasIconColor,
}: FlexLayoutProps) => {
const board = useRequiredBoard();
@@ -110,15 +106,7 @@ const FlexLayout = ({
key={app.id}
w="100%"
>
<Card
radius={board.itemRadius}
className={classes.card}
w="100%"
display="flex"
p={4}
h="100%"
withBorder={withBorder}
>
<Card radius={board.itemRadius} className={classes.card} w="100%" display="flex" p={4} h="100%">
{direction === "row" ? (
<VerticalItem
app={app}
@@ -150,7 +138,6 @@ interface GridLayoutProps {
hideIcon: boolean;
hideHostname: boolean;
openNewTab: boolean;
withBorder: boolean;
itemDirection: "horizontal" | "vertical";
hasIconColor: boolean;
}
@@ -161,7 +148,6 @@ const GridLayout = ({
hideIcon,
hideHostname,
openNewTab,
withBorder,
itemDirection,
hasIconColor,
}: GridLayoutProps) => {
@@ -182,7 +168,6 @@ const GridLayout = ({
h="100%"
className={combineClasses(classes.card, classes["card-grid"])}
radius={board.itemRadius}
withBorder={withBorder}
p="xs"
>
{itemDirection === "horizontal" ? (
-1
View File
@@ -24,7 +24,6 @@ export const { definition, componentLoader } = createWidgetDefinition("bookmarks
hideIcon: factory.switch({ defaultValue: false }),
hideHostname: factory.switch({ defaultValue: false }),
openNewTab: factory.switch({ defaultValue: true }),
withBorder: factory.switch({ defaultValue: false }),
items: factory.sortableItemList<RouterOutputs["app"]["all"][number], string>({
ItemComponent: ({ item, handle: Handle, removeItem, rootAttributes }) => {
return (
@@ -1,10 +1,20 @@
.statItem {
padding: 12px;
border-radius: var(--mantine-radius-md);
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
background-color: var(--mantine-color-gray-0);
transition: all 0.2s ease;
&:hover {
background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
background-color: var(--mantine-color-gray-1);
}
}
@media (prefers-color-scheme: dark) {
.statItem {
background-color: var(--mantine-color-dark-6);
&:hover {
background-color: var(--mantine-color-dark-5);
}
}
}
+6 -2
View File
@@ -1,7 +1,7 @@
import type { ComponentType } from "react";
import type { Loader } from "next/dynamic";
import dynamic from "next/dynamic";
import { Skeleton } from "@mantine/core";
import { Center, Loader as UiLoader } from "@mantine/core";
import { objectEntries } from "@homarr/common";
import type { IntegrationKind, WidgetKind } from "@homarr/definitions";
@@ -107,7 +107,11 @@ export const loadWidgetDynamic = <TKind extends WidgetKind>(kind: TKind) => {
const newlyLoadedComponent = dynamic<WidgetComponentProps<TKind>>(
widgetImports[kind].componentLoader as Loader<WidgetComponentProps<TKind>>,
{
loading: () => <Skeleton w="100%" h="100%" radius="md" animate />,
loading: () => (
<Center w="100%" h="100%">
<UiLoader />
</Center>
),
},
);
-4
View File
@@ -11,8 +11,6 @@ import prefetchForApps from "./app/prefetch";
import prefetchForBookmarks from "./bookmarks/prefetch";
import type { Prefetch, WidgetOptionsRecordOf } from "./definition";
import type { inferOptionsFromCreator } from "./options";
import prefetchRssFeedsAsync from "./rssFeed/prefetch";
import prefetchWeatherAsync from "./weather/prefetch";
const cachedGetServerSettingsAsync = cache(getServerSettingsAsync);
@@ -21,8 +19,6 @@ const prefetchCallbacks: Partial<{
}> = {
bookmarks: prefetchForBookmarks,
app: prefetchForApps,
weather: prefetchWeatherAsync,
rssFeed: prefetchRssFeedsAsync,
};
export const prefetchForKindAsync = async <TKind extends WidgetKind>(
-22
View File
@@ -1,22 +0,0 @@
import { api, trpc } from "@homarr/api/server";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { Prefetch } from "../definition";
const logger = createLogger({ module: "rssFeedWidgetPrefetch" });
const prefetchRssFeedsAsync: Prefetch<"rssFeed"> = async (queryClient, items) => {
for (const item of items) {
const input = {
urls: item.options.feedUrls,
maximumAmountPosts: item.options.maximumAmountPosts,
};
const data = await api.widget.rssFeed.getFeeds(input);
queryClient.setQueryData(trpc.widget.rssFeed.getFeeds.queryKey(input), data);
}
logger.info("Successfully prefetched RSS feeds", { count: items.length });
};
export default prefetchRssFeedsAsync;
-29
View File
@@ -1,29 +0,0 @@
import { api, trpc } from "@homarr/api/server";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { Prefetch } from "../definition";
const logger = createLogger({ module: "weatherWidgetPrefetch" });
const prefetchWeatherAsync: Prefetch<"weather"> = async (queryClient, items) => {
const locations = items.map((item) => ({
latitude: item.options.location.latitude,
longitude: item.options.location.longitude,
}));
const uniqueLocations = locations.filter(
(loc, idx, arr) =>
arr.findIndex((other) => other.latitude === loc.latitude && other.longitude === loc.longitude) === idx,
);
await Promise.all(
uniqueLocations.map(async (input) => {
const data = await api.widget.weather.atLocation(input);
queryClient.setQueryData(trpc.widget.weather.atLocation.queryKey(input), data);
}),
);
logger.info("Successfully prefetched weather data", { count: uniqueLocations.length });
};
export default prefetchWeatherAsync;
+310 -302
View File
@@ -142,53 +142,53 @@ catalogs:
specifier: ^12.0.1
version: 12.0.1
'@tiptap/extension-color':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-highlight':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-image':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-link':
specifier: ^3.24.0
version: 3.24.0
specifier: ^3.23.6
version: 3.23.6
'@tiptap/extension-placeholder':
specifier: ^3.24.0
version: 3.24.0
specifier: ^3.23.6
version: 3.23.6
'@tiptap/extension-table':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-table-cell':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-table-header':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-table-row':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-task-item':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-task-list':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-text-align':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-text-style':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/extension-underline':
specifier: 3.24.0
version: 3.24.0
specifier: 3.23.6
version: 3.23.6
'@tiptap/react':
specifier: ^3.24.0
version: 3.24.0
specifier: ^3.23.6
version: 3.23.6
'@tiptap/starter-kit':
specifier: ^3.24.0
version: 3.24.0
specifier: ^3.23.6
version: 3.23.6
'@trpc/client':
specifier: ^11.17.0
version: 11.17.0
@@ -756,7 +756,7 @@ importers:
version: 9.2.2(@mantine/core@9.2.2(@mantine/hooks@9.2.2(react@19.2.6))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/hooks@9.2.2(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@mantine/tiptap':
specifier: 'catalog:'
version: 9.2.2(@mantine/core@9.2.2(@mantine/hooks@9.2.2(react@19.2.6))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/hooks@9.2.2(react@19.2.6))(@tiptap/extension-link@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))(@tiptap/react@3.24.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
version: 9.2.2(@mantine/core@9.2.2(@mantine/hooks@9.2.2(react@19.2.6))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/hooks@9.2.2(react@19.2.6))(@tiptap/extension-link@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))(@tiptap/react@3.23.6(@floating-ui/dom@1.7.6)(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tabler/icons-react':
specifier: 'catalog:'
version: 3.44.0(react@19.2.6)
@@ -2396,52 +2396,52 @@ importers:
version: 3.44.0(react@19.2.6)
'@tiptap/extension-color':
specifier: 'catalog:'
version: 3.24.0(@tiptap/extension-text-style@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0)))
version: 3.23.6(@tiptap/extension-text-style@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6)))
'@tiptap/extension-highlight':
specifier: 'catalog:'
version: 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-image':
specifier: 'catalog:'
version: 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-link':
specifier: 'catalog:'
version: 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
version: 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-placeholder':
specifier: 'catalog:'
version: 3.24.0(@tiptap/extensions@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/extensions@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-table':
specifier: 'catalog:'
version: 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
version: 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-table-cell':
specifier: 'catalog:'
version: 3.24.0(@tiptap/extension-table@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/extension-table@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-table-header':
specifier: 'catalog:'
version: 3.24.0(@tiptap/extension-table@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/extension-table@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-table-row':
specifier: 'catalog:'
version: 3.24.0(@tiptap/extension-table@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/extension-table@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-task-item':
specifier: 'catalog:'
version: 3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-task-list':
specifier: 'catalog:'
version: 3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-text-align':
specifier: 'catalog:'
version: 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-text-style':
specifier: 'catalog:'
version: 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-underline':
specifier: 'catalog:'
version: 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
version: 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/react':
specifier: 'catalog:'
version: 3.24.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
version: 3.23.6(@floating-ui/dom@1.7.6)(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tiptap/starter-kit':
specifier: 'catalog:'
version: 3.24.0
version: 3.23.6
clsx:
specifier: 'catalog:'
version: 2.1.1
@@ -3268,9 +3268,15 @@ packages:
'@fastify/proxy-addr@5.1.0':
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
'@floating-ui/core@1.7.3':
resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==}
'@floating-ui/core@1.7.5':
resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
'@floating-ui/dom@1.7.4':
resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==}
'@floating-ui/dom@1.7.6':
resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
@@ -4926,215 +4932,215 @@ packages:
'@testcontainers/redis@12.0.1':
resolution: {integrity: sha512-x5iDR6Y2Mc+2rTJYSQlRTtBMCPt77pKxDogpqRwXgJVmWoDrWFpiFcA/dw6IEoNuyxYUg03tbS8/VzCJI5JX6A==}
'@tiptap/core@3.24.0':
resolution: {integrity: sha512-GTAsXAI32p4hEZgPzvUv2RPrObxamy9AFhmhG10fXSvN/cDUs8naEYVIqDV3Sh99jMwQEbTFKW1E1mcspsY6ow==}
'@tiptap/core@3.23.6':
resolution: {integrity: sha512-MRB3pHz4Oxqmcawh0cQ5iOGdY5xtNYp/1CoK7hdTLzw5K0C6/gTC2VvanB1R4INaB6EpBkxG/GiWkVirDRnuXw==}
peerDependencies:
'@tiptap/pm': 3.24.0
'@tiptap/pm': 3.23.6
'@tiptap/extension-blockquote@3.24.0':
resolution: {integrity: sha512-DgwEEJ1GbDQcT054ynxoaZGmB9apGeUklPrinq9o6xdLHpdg+bO9HCQzggdB8n21VLLglb8jfAEWsVNwh3eASQ==}
'@tiptap/extension-blockquote@3.23.6':
resolution: {integrity: sha512-2RmnqNqTltZ2k1F7IfjoDNs935Uq4rRDR7d98mqkg3OlDktcQIyBpv0t9dTay6H5bkQeZUuS8ogK2S1E8Edjug==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-bold@3.24.0':
resolution: {integrity: sha512-CujogYaynasklFKHADUseuvj8X2FnWktTCCo3Hl+nlyRvBTmm5TK2aqiamg3v2P4dBh3O6a70mo8BfRJPuiR1g==}
'@tiptap/extension-bold@3.23.6':
resolution: {integrity: sha512-1LMhjnytdbbhWHSoOwnLxZAOQZWPkKyXVCNmaIk0Mhi4tLPUXptG4qKS5sVYTCveE5H6IBPFrbgBFi5dMI6krA==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-bubble-menu@3.24.0':
resolution: {integrity: sha512-jRXD+JPu9ayvq78g8hsCxx4q/qUFtrdfIYirRSf5YUseuuUbtfrq83AsGabcygpUTefjJkMQoXNITkh6294Ggw==}
'@tiptap/extension-bubble-menu@3.23.6':
resolution: {integrity: sha512-Mwkyp9LkDHFbqmWRIkp63FinRxFu3ajC4qSb9t4mnHsb4kAdbNLLsGtbFg+le0SWk4CxGwAOwM7SzeJ+6UGqCA==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/pm': 3.23.6
'@tiptap/extension-bullet-list@3.24.0':
resolution: {integrity: sha512-IOpAm5c4XVVVvkOef+V9XYMVpea+3MgBpCQgn83UQRlwO9eIMwmcyxOznu7gQPQVShTEpkt4T6uK+ZN9o8meIA==}
'@tiptap/extension-bullet-list@3.23.6':
resolution: {integrity: sha512-RMRgfXZykr/13X8UBOwvpgysVOo9KchwqMoEbvqQSj4YFfU56iIn59C8sbxiQ1sKfeltUf0wH4fPc0I4iwKqAA==}
peerDependencies:
'@tiptap/extension-list': 3.24.0
'@tiptap/extension-list': 3.23.6
'@tiptap/extension-code-block@3.24.0':
resolution: {integrity: sha512-NZglw4oHoH6oJ5+HvxxQCYk+wODJmsxzUpRQdsOmje08sekQH+Zt9i4UKimBhg4urpd5r+dKXTslab9a5eQ86w==}
'@tiptap/extension-code-block@3.23.6':
resolution: {integrity: sha512-4kccgcn5yHThxrzsIhJny3EwfEZYIk+BjUCL4uIuzOyWvExtGhZ6JMHVCZeMhI8D1/bX1LNkkAKN5DXPzH4lXQ==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/pm': 3.23.6
'@tiptap/extension-code@3.24.0':
resolution: {integrity: sha512-MAQtrPRQ+HRmcGotWbksdIGeH1gqayFAdvi4lNGeFT7taHXP1o1XD7CQp7iYIKmg8IU4/MQ+RdetSfuC1A9edQ==}
'@tiptap/extension-code@3.23.6':
resolution: {integrity: sha512-KG8KXFYyLrtYvT7AZ1WGV61ofx8pDe5g9pH658MERxqQGii+Pyfc6xkz04l7XeBts/7+571UQp/0O7i/z560TA==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-color@3.24.0':
resolution: {integrity: sha512-gVv9zfQ9diq/sk+SVJzqadShsOtDK1XPojBZR1zXg1QxedzdwLKuRDxvnlfIc3kFIyaDP4mNrek+anednTbWBA==}
'@tiptap/extension-color@3.23.6':
resolution: {integrity: sha512-2WxYc+X+OGFXbq/Ak0VXODEzfK1l5wz33oTWSuhdSw44HAQM7+SSyiDnMCXDQdqi063ANqMmQmlw5WngjQ6wCA==}
peerDependencies:
'@tiptap/extension-text-style': 3.24.0
'@tiptap/extension-text-style': 3.23.6
'@tiptap/extension-document@3.24.0':
resolution: {integrity: sha512-yxgM3+yXy2XZzEwH43y2Kp8D1BkblxEWLXqo0YCoAKtxyKCcEaT8kdlf70kS7D0+VSzYU4D0iN7VdQIYHcL2mA==}
'@tiptap/extension-document@3.23.6':
resolution: {integrity: sha512-XDAIgG9KcKumFM9KJWUEUhXPbFIhhl47bfy5GknareWTRKke85rcoj/oxKKO9ihLZr8JfpbXjqnS4SCm5yhYPw==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-dropcursor@3.24.0':
resolution: {integrity: sha512-Dbv1c5LnvG3PT+yEbCNroyOeeUkHq9wcir2pbC7wri7g7d2sCi0+HvKH0MAxLwY3j5NJJSiSyG2ypMaXOAs4sg==}
'@tiptap/extension-dropcursor@3.23.6':
resolution: {integrity: sha512-+XWEoRKf3lXxi7Le1aOM2xU1XHwxICGpXjT3m4QaYqUgIpsq8gQEuso6kVg8DnTD7biKQs6+oIQ0o2b/gTW9WA==}
peerDependencies:
'@tiptap/extensions': 3.24.0
'@tiptap/extensions': 3.23.6
'@tiptap/extension-floating-menu@3.24.0':
resolution: {integrity: sha512-7QEbf3mUzFAkejjQGX9f0L507oMtnOBRwHt2skUTR+9yXgudsN8zaDBSSRHLeMWGk9b7L293ZMA6zCRrZaHrfA==}
'@tiptap/extension-floating-menu@3.23.6':
resolution: {integrity: sha512-2kjuDcEq69lEcECl75xqY5MyzUSh2zcC5aLrpwP1WwhJz5bxsIFHiaps5AP6h9R4A+ZBj5b2haay2Y1wDUU3VA==}
peerDependencies:
'@floating-ui/dom': ^1.0.0
'@tiptap/core': 3.24.0
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/pm': 3.23.6
'@tiptap/extension-gapcursor@3.24.0':
resolution: {integrity: sha512-CzCP5/jni5RFwW9jCfBO6auh83GbaioMTpSk6tyR3sd+CbwlBcUdsJFGJkbaRdiSS9dgIyi+6hRbhjpYdHcp+w==}
'@tiptap/extension-gapcursor@3.23.6':
resolution: {integrity: sha512-wbKmxXsszxWacEkrHucRpSQbiKjz4fmOebD6OVyL9AcrmlbxNk8vcM3iyh/8cVeRy09XY+morM165t/u7/z4IQ==}
peerDependencies:
'@tiptap/extensions': 3.24.0
'@tiptap/extensions': 3.23.6
'@tiptap/extension-hard-break@3.24.0':
resolution: {integrity: sha512-T/ZEBiHQPMyTqDvXG0tiqBToNeuSemIPmNtdoGSgBN/degVl7VJZqQIrLIvOUHfjf3QkRs7TE/mcqTJsIboO/g==}
'@tiptap/extension-hard-break@3.23.6':
resolution: {integrity: sha512-KeUm+tkUfIVSX9QM9XOIhaay0Fn36sLKUo5NVYjN3uJaxFvaZXZmTlxdO85OTdgF2P5sqh9LomrIgliaFRGk4w==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-heading@3.24.0':
resolution: {integrity: sha512-GCSgapIzQPqEGNcVGE0/Pcjg5wITMLYJlrS3GGVw7BPmECJwgexcoOsEwkxtzJnXT/HpFXbvOFW43sM0KeHSjg==}
'@tiptap/extension-heading@3.23.6':
resolution: {integrity: sha512-A/0jPhxnUh9THSZymlu0OGPZe1wdFdwHAXnRCmqvYUCwJjrG7LCC/ahzmcj1tcNzI9hgHyuYPSfev8RXYrNu/w==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-highlight@3.24.0':
resolution: {integrity: sha512-+Vx6YKSP+lzCBiTbgQOpR2rIOzL8Ns6UnOBtW4O6tYc7TMTexycaG66RWxXMpAD6QkamwLwX3H5TTYviWtU9RQ==}
'@tiptap/extension-highlight@3.23.6':
resolution: {integrity: sha512-GWv6R4iZzVrNeB33qtJDBt/BfVjuc5S/sUF/VDFAtK8h86bg4cEm1tGLEsui4T3+vij/t+P+eoGhkzF6NGogdg==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-horizontal-rule@3.24.0':
resolution: {integrity: sha512-DFzWJTrb23x+qssLLs85vEyho8ItUGp3RY9XUsVTIAGZn5IsoUw8wMsvIBlH1ux4Ch7gLchtcD6kpTdMdrL9kw==}
'@tiptap/extension-horizontal-rule@3.23.6':
resolution: {integrity: sha512-hEUlz4H+I64r+TH6LCuNCRgO7JTHncXGmx9+WbU69EOfY8O0ZurcgeJc8HeiAKL+r9YuC1e5YHfFxgCaaC0jlg==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/pm': 3.23.6
'@tiptap/extension-image@3.24.0':
resolution: {integrity: sha512-mH+bvsX2cPKuZzV7YMQi4FV2YbDP+Kmq36bY+Bwi/x4mYUc8u0cjQxcu8RzLO7GtsgUJPxGMwfkQxmDqXFLZvw==}
'@tiptap/extension-image@3.23.6':
resolution: {integrity: sha512-vvNGxArvD2dW+XvV0KdYovRVUzCy8QVNulc2r5pV7umnG1E6cCmMkiHiif8J2ePJu2KtysAvJQe0iF+UqueGMw==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-italic@3.24.0':
resolution: {integrity: sha512-mf3cbNlbMPUNj3IyUkIke+o3ZpOUrtVeY5Yqs5IM/VhkUUh/PdIzqw74VuqEAJ0Z4oZ6nNDHeYLrl3Be1j99lQ==}
'@tiptap/extension-italic@3.23.6':
resolution: {integrity: sha512-wol5KdwCPAvpiYhH9PLlvO8ZnJHwZtIboVevrfOGgBcKlXRA3dedR4OAMXHnUtkkzu9KtliLg1+TYzEx4JZG9Q==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-link@3.24.0':
resolution: {integrity: sha512-MwMoNGG2mL5XGFV1tEGunBRglwsIbW+ZOB2QnKiv+Mcbi2JCWMrorndJZBqpVPR5nM+Bef2KnpchEJmYlQLvKQ==}
'@tiptap/extension-link@3.23.6':
resolution: {integrity: sha512-KNZz7z7P2/qbQsx5bPAbSPjrKDg1VHsedGlLHJCr8U2VRD5VgmDLkMpkouP1CsDg15qgyUKv/nDib5KgPpLNWA==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/pm': 3.23.6
'@tiptap/extension-list-item@3.24.0':
resolution: {integrity: sha512-zl/U3viJiV9OzkKM37AHIUN1af1TSLrcbHUUoNLkfJ33Nq+NlpaXpCVK0rKRqiLFJf7zk/a5KWG5CrOy9TxjKA==}
'@tiptap/extension-list-item@3.23.6':
resolution: {integrity: sha512-3zzyhdkUWcHVpXuvy6KiIwjh29rbH6gEDEqPQqHLrl1XGnO9pnShC7pSHctlCDjmcx3O4n9cd4QMtVBlUerbiA==}
peerDependencies:
'@tiptap/extension-list': 3.24.0
'@tiptap/extension-list': 3.23.6
'@tiptap/extension-list-keymap@3.24.0':
resolution: {integrity: sha512-69fKcrngYGEKWNn4R5oLwl0YuV3FY4kufEValVcjnihUmqJTE1vx+fwctYoTsOGnIuNGpUIQ7f9YDD/0w34qBw==}
'@tiptap/extension-list-keymap@3.23.6':
resolution: {integrity: sha512-x8bPcLViGzg/RAmQM/XtmfqIwQ/Pv9Q8mkd+OgfUiTqjeJqKwVQmiqbLFNa7zw81+H61M+HDU+qGAaQ3vRIMjw==}
peerDependencies:
'@tiptap/extension-list': 3.24.0
'@tiptap/extension-list': 3.23.6
'@tiptap/extension-list@3.24.0':
resolution: {integrity: sha512-GcxDVMMmDGj7OFTBrV7JpVgr5wxlr2vmjwH7U8QxZX7OJI5vrsMYl/U6KRTvUpG8wP+Zmo5jRlLM+BbL+a/W3g==}
'@tiptap/extension-list@3.23.6':
resolution: {integrity: sha512-z6vj9+Qht2sjdQkyyHcUpsC/yCIZqTrQiyHDhs/HGKrfvoANyAZGpqdNeKf1wSyjIso+27tQuIH5NDfk8ygyNw==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/pm': 3.23.6
'@tiptap/extension-ordered-list@3.24.0':
resolution: {integrity: sha512-buRa6bmBDw0TztH+rAcusIye14DiLDS+yGheo6GiNCTD7kKJnksXagBdxvip3jhW5sx7gyAKvoBmvGSg1BbsGA==}
'@tiptap/extension-ordered-list@3.23.6':
resolution: {integrity: sha512-1m/wWB/ZtXcmG2vNdiUkCqsOgqv5vBjCv/mVaHhF9OvV+zQS8YDjoWE7zEuT/GgELdT77Xq8lHrn4nCDudB3/A==}
peerDependencies:
'@tiptap/extension-list': 3.24.0
'@tiptap/extension-list': 3.23.6
'@tiptap/extension-paragraph@3.24.0':
resolution: {integrity: sha512-wD06aB6hO7LgcrlhGiw7I64k2tus9kNoICX5R+UecBSB1DVJdzKvXoXL2kPNv4DqYvljHdkIeK/OpuOTQd6MJA==}
'@tiptap/extension-paragraph@3.23.6':
resolution: {integrity: sha512-+7m58LUSncodjrIyXks4RZ3tLNYrvgT77wRR4l3HnM5OABY3GDsDTqi7c1t1yI29NVOSk/DUacqy6UwYAj1DGg==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-placeholder@3.24.0':
resolution: {integrity: sha512-3jfYYCIuwMADhvZ92vB6c80YiTmgTSFR23JqyLps8qkQtV59Va5CBYpwJtSs1+VrbCVnNxhZBHhVXusZO3uRkA==}
'@tiptap/extension-placeholder@3.23.6':
resolution: {integrity: sha512-8I6b2aevF74aLgymKMxbDxSLxWA2y+2dh0zZDeI8sRZ2m6WHHes+Kyuuwkq1HIPcR+ZLpbec74cmf6lcL/yvqQ==}
peerDependencies:
'@tiptap/extensions': 3.24.0
'@tiptap/extensions': 3.23.6
'@tiptap/extension-strike@3.24.0':
resolution: {integrity: sha512-sfN1iQs6Fdlorrfe8wipDkTPwu/Egx3s2fkY7TAWusTGFHwlovuRUGFKqCL9dI4N3u6uqUMpEuWmQNgv+aQGjQ==}
'@tiptap/extension-strike@3.23.6':
resolution: {integrity: sha512-oF7FEZ37f15aCe5kPgzGDYf/m+hr7VdQ/Ko/Hds/UM9pX7AG1fdtmRrl6wqkRqDM/incZaC/AQR2/Dpo2VCNGQ==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-table-cell@3.24.0':
resolution: {integrity: sha512-jg62iVc1L8tOycSuwZ5i82EGKGuvelCIkpFX7wvHMl9EiPX9fv70enmb4ua9DyLgRtYg/MNrGOdUATRpxWsFRA==}
'@tiptap/extension-table-cell@3.23.6':
resolution: {integrity: sha512-hS9TmmvRlT9/ikT+0ukACS+hmJuii4zQaH47cg3oJkz/Fv7O7tL7GZniKtK6l2OUZGPhY+4SV2RkDB6bD7DXfw==}
peerDependencies:
'@tiptap/extension-table': 3.24.0
'@tiptap/extension-table': 3.23.6
'@tiptap/extension-table-header@3.24.0':
resolution: {integrity: sha512-z1TGVoBD5lVX48hpKcrQY5vETB8aCR61Enk5wGl45zNO/sMEMNxYfDKTkMydyOFUWfGqvOPnjDHJqJuT36/19w==}
'@tiptap/extension-table-header@3.23.6':
resolution: {integrity: sha512-D6o0a1cJXUU0xWakainBFGPnGHinQkPcdu1YqGd/PoFANY38lnuZt/NW2O/OLfLXu5LXDRfpqF1+dsKww27dUA==}
peerDependencies:
'@tiptap/extension-table': 3.24.0
'@tiptap/extension-table': 3.23.6
'@tiptap/extension-table-row@3.24.0':
resolution: {integrity: sha512-kASEYMWZF9ZqO6DRGz0LiD11c3LPfzOlFiJQVDrOyLP7xkJpHzIAKH0K+8u4aohOziDLn8nz+8I+3xjRS46oGg==}
'@tiptap/extension-table-row@3.23.6':
resolution: {integrity: sha512-OauWVzkyRQg0rKOqM/a3PuKPc1S7YXMb1LRN7Nh8Ytvglvd7GFRTbl1lVqdZRaz4Jzopag4PQnriIZfMPUpxWw==}
peerDependencies:
'@tiptap/extension-table': 3.24.0
'@tiptap/extension-table': 3.23.6
'@tiptap/extension-table@3.24.0':
resolution: {integrity: sha512-lr5elob3uJnB+ltgqPDEeVQmIPRx6JoS0I6z93tOgKsI2mIsaw5ErghteeiCTpExdyax7aWR0fn5pZzLVDQL8A==}
'@tiptap/extension-table@3.23.6':
resolution: {integrity: sha512-XbhZXjhsS6AP7ThoZxjAnNs+NiR81YRori25l6E+ORqB7quiPkIXOAi5h4AIpkn/CYIqze6ere11lWsYpDjtaQ==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/pm': 3.23.6
'@tiptap/extension-task-item@3.24.0':
resolution: {integrity: sha512-2VtX6VOp4SJk5E/VHeSC2Acfbge6h69TUBhF7hFebbiCDyB73xCaPKcuClIyzOlWJgpWz2ZPXwVB+Y3meXw+yw==}
'@tiptap/extension-task-item@3.23.6':
resolution: {integrity: sha512-PIHW+3KZCtEmtC7z34rfkusbC42HYr7uw1h5xNvrazQwJGWhKo1UmI0Yea5vyJNKPrTLPCMEm/mgMrj8nNAZfA==}
peerDependencies:
'@tiptap/extension-list': 3.24.0
'@tiptap/extension-list': 3.23.6
'@tiptap/extension-task-list@3.24.0':
resolution: {integrity: sha512-EIbB/WQ7L92WfaY5AF9upH93nIgBHjGt5zwPYoX+ZTpBncebr1AEDF7rc+LHNuSa1g1ToOKbFB88ep02IIXOjA==}
'@tiptap/extension-task-list@3.23.6':
resolution: {integrity: sha512-XNuYbVV7F0TBpxHqqEFvrxwGbmTq01qJrZA6qwbQopOsw5wOsCy4XQsCkeZ8BGkakORCEWGSMjPyVOtIKq2jGA==}
peerDependencies:
'@tiptap/extension-list': 3.24.0
'@tiptap/extension-list': 3.23.6
'@tiptap/extension-text-align@3.24.0':
resolution: {integrity: sha512-WKFtYXGthtkUc+Cwy2fItSr+9FKwLZjkJVAY1GhkRdcq35qTuVhkb4Q4wR2Rhkb6QRqtlxF1NDuTf2vxiQmfBQ==}
'@tiptap/extension-text-align@3.23.6':
resolution: {integrity: sha512-pE+gDmvnrSMWHADDnDSzaO7YUi9kOywQkyrqZ9bEPLddZred7ICoJ4NtD2DqLjDSur+HijaJuByResWb78c6FA==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-text-style@3.24.0':
resolution: {integrity: sha512-1Hy+5tFEAsnoLhZ/eqmza4USvFHwMA8haeAdCGlwTeshBrt+nUKTrEsRHidF60cGsRwlTcuqxSkjT94dULgp5Q==}
'@tiptap/extension-text-style@3.23.6':
resolution: {integrity: sha512-Auasgj469wkQ5ip+Zi2gaRzvqxx9qKG58+1mkT8yPE3QAGnOIg/AaKyQ7pTV77UyL3FHvLnU+KsWCad+qcobww==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-text@3.24.0':
resolution: {integrity: sha512-Im7keLPEihxm3+LyF+drYCoaOY5hlq35lvHAp/el6M8pJ/scts88HrYpdR1Yc4BtpZBIhfHSyWgPaupI4qwdeg==}
'@tiptap/extension-text@3.23.6':
resolution: {integrity: sha512-ipoC2TkIAIOTiF5ByiGgvQB1DqDyfP90wrUB3mohBcgvp7lQnwHszCDGv8dNnmcUek8uXV/uoLu2VXeVQlxjPA==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extension-underline@3.24.0':
resolution: {integrity: sha512-D4W4X3UMq9dLVIOfPB9+UodQ4eAJ8yDcm8qFWAwq0a15YWH6bnwulCuIdV+U5dEG+yaRxN8haB9GrrID9jmrSA==}
'@tiptap/extension-underline@3.23.6':
resolution: {integrity: sha512-P55wGIZGYTVH92Fq0cgI4/O9AhLCaJC3hhxg15RSERP5/YegM9eJHDK/GQ1EE/DvYA+xpYGOV6agKwAUqfA/Iw==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/extensions@3.24.0':
resolution: {integrity: sha512-z6gRYzy2ucJp07OQ0F2W07NxyhMTxPYH1ia2eGiQkWax1i56oExpjMsDHP8THWlg8Tb7NnbfKpkfh881EsmofA==}
'@tiptap/extensions@3.23.6':
resolution: {integrity: sha512-X09/Db1teB+ifXzDGVVFmOeQRx7wTAayE9/280spxpsHkHZvJ5bHRvWIzUzviMIjbBz+NPDIKYPK7gMfh9iaig==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/pm': 3.23.6
'@tiptap/pm@3.24.0':
resolution: {integrity: sha512-QQP/78ryOZDN99gNBV7dgh69/8AYaOYQYFklq/iR+ZRFaaL3+qqHFvPVJapGkzPdymBgNJ34xjFM8n5pJ4QmMg==}
'@tiptap/pm@3.23.6':
resolution: {integrity: sha512-in5CaMaWlJcH2A1q6GJKFtrodE8WLS3M9tIi/f89jPmIVHJShpodC0KZDNyJkrVBQomYk0DEh86Utm6ASXzQww==}
'@tiptap/react@3.24.0':
resolution: {integrity: sha512-KxnrlQbzOgA02EMsfuGGHtNhfkJQGqVlQttmQctI9DOl/F3gcaRqg+wNTBY1Fof8yDaZ8Z1LL1F0C05W0o3vUw==}
'@tiptap/react@3.23.6':
resolution: {integrity: sha512-Tw9KZkYqFMk3vaJAEQKqEYIO/iq3cSJe7OUEGBul4k4GaMQeLItLf5EYhUd0GIPXci1WVVPNntKJsHfX25M37w==}
peerDependencies:
'@tiptap/core': 3.24.0
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6
'@tiptap/pm': 3.23.6
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
'@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0
react: ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
'@tiptap/starter-kit@3.24.0':
resolution: {integrity: sha512-Ef4PCP96vcY2GonXN9J0M8iC6zvxPTmQlL/QZiCwuYqqnH/hNpYIjNSQdTndiDpxRKofa32Sr2HWktgEnL32Bg==}
'@tiptap/starter-kit@3.23.6':
resolution: {integrity: sha512-gykwtGWrnWCmtql1hid3opac/KV8zQvOAnu3bTqIqcHrn1FusbUwKmNzavSbfGvcktHM3hFjb35W48JyVLyu/A==}
'@tree-sitter-grammars/tree-sitter-yaml@0.7.1':
resolution: {integrity: sha512-AynBwkIoQCTgjDR33bDUp9Mqq+YTco0is3n5hRApMqG9of/6A4eQsfC1/uSEeHSUyMQSYawcAWamsexnVpIP4Q==}
@@ -8307,9 +8313,6 @@ packages:
prosemirror-history@1.4.1:
resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==}
prosemirror-inputrules@1.5.1:
resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==}
prosemirror-keymap@1.2.2:
resolution: {integrity: sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==}
@@ -10460,10 +10463,21 @@ snapshots:
'@fastify/forwarded': 3.0.1
ipaddr.js: 2.3.0
'@floating-ui/core@1.7.3':
dependencies:
'@floating-ui/utils': 0.2.11
optional: true
'@floating-ui/core@1.7.5':
dependencies:
'@floating-ui/utils': 0.2.11
'@floating-ui/dom@1.7.4':
dependencies:
'@floating-ui/core': 1.7.3
'@floating-ui/utils': 0.2.11
optional: true
'@floating-ui/dom@1.7.6':
dependencies:
'@floating-ui/core': 1.7.5
@@ -10996,12 +11010,12 @@ snapshots:
dependencies:
react: 19.2.6
'@mantine/tiptap@9.2.2(@mantine/core@9.2.2(@mantine/hooks@9.2.2(react@19.2.6))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/hooks@9.2.2(react@19.2.6))(@tiptap/extension-link@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))(@tiptap/react@3.24.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
'@mantine/tiptap@9.2.2(@mantine/core@9.2.2(@mantine/hooks@9.2.2(react@19.2.6))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/hooks@9.2.2(react@19.2.6))(@tiptap/extension-link@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))(@tiptap/react@3.23.6(@floating-ui/dom@1.7.6)(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
'@mantine/core': 9.2.2(@mantine/hooks@9.2.2(react@19.2.6))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@mantine/hooks': 9.2.2(react@19.2.6)
'@tiptap/extension-link': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/react': 3.24.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tiptap/extension-link': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/react': 3.23.6(@floating-ui/dom@1.7.6)(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
@@ -12218,175 +12232,174 @@ snapshots:
- bare-buffer
- supports-color
'@tiptap/core@3.24.0(@tiptap/pm@3.24.0)':
'@tiptap/core@3.23.6(@tiptap/pm@3.23.6)':
dependencies:
'@tiptap/pm': 3.24.0
'@tiptap/pm': 3.23.6
'@tiptap/extension-blockquote@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-blockquote@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-bold@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-bold@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-bubble-menu@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)':
'@tiptap/extension-bubble-menu@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)':
dependencies:
'@floating-ui/dom': 1.7.6
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/pm': 3.24.0
'@floating-ui/dom': 1.7.4
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/pm': 3.23.6
optional: true
'@tiptap/extension-bullet-list@3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-bullet-list@3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extension-list': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-list': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-code-block@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)':
'@tiptap/extension-code-block@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/pm': 3.23.6
'@tiptap/extension-code@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-code@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-color@3.24.0(@tiptap/extension-text-style@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0)))':
'@tiptap/extension-color@3.23.6(@tiptap/extension-text-style@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6)))':
dependencies:
'@tiptap/extension-text-style': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-text-style': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-document@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-document@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-dropcursor@3.24.0(@tiptap/extensions@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-dropcursor@3.23.6(@tiptap/extensions@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extensions': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extensions': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-floating-menu@3.24.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)':
'@tiptap/extension-floating-menu@3.23.6(@floating-ui/dom@1.7.6)(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)':
dependencies:
'@floating-ui/dom': 1.7.6
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/pm': 3.23.6
optional: true
'@tiptap/extension-gapcursor@3.24.0(@tiptap/extensions@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-gapcursor@3.23.6(@tiptap/extensions@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extensions': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extensions': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-hard-break@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-hard-break@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-heading@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-heading@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-highlight@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-highlight@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-horizontal-rule@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)':
'@tiptap/extension-horizontal-rule@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/pm': 3.23.6
'@tiptap/extension-image@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-image@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-italic@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-italic@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-link@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)':
'@tiptap/extension-link@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/pm': 3.23.6
linkifyjs: 4.3.3
'@tiptap/extension-list-item@3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-list-item@3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extension-list': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-list': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-list-keymap@3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-list-keymap@3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extension-list': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-list': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)':
'@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/pm': 3.23.6
'@tiptap/extension-ordered-list@3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-ordered-list@3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extension-list': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-list': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-paragraph@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-paragraph@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-placeholder@3.24.0(@tiptap/extensions@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-placeholder@3.23.6(@tiptap/extensions@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extensions': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extensions': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-strike@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-strike@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-table-cell@3.24.0(@tiptap/extension-table@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-table-cell@3.23.6(@tiptap/extension-table@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extension-table': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-table': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-table-header@3.24.0(@tiptap/extension-table@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-table-header@3.23.6(@tiptap/extension-table@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extension-table': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-table': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-table-row@3.24.0(@tiptap/extension-table@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-table-row@3.23.6(@tiptap/extension-table@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extension-table': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-table': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-table@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)':
'@tiptap/extension-table@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/pm': 3.23.6
'@tiptap/extension-task-item@3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-task-item@3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extension-list': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-list': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-task-list@3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))':
'@tiptap/extension-task-list@3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/extension-list': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-list': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-text-align@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-text-align@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-text-style@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-text-style@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-text@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-text@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-underline@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))':
'@tiptap/extension-underline@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extensions@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)':
'@tiptap/extensions@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/pm': 3.23.6
'@tiptap/pm@3.24.0':
'@tiptap/pm@3.23.6':
dependencies:
prosemirror-changeset: 2.3.0
prosemirror-commands: 1.6.2
prosemirror-dropcursor: 1.8.1
prosemirror-gapcursor: 1.3.2
prosemirror-history: 1.4.1
prosemirror-inputrules: 1.5.1
prosemirror-keymap: 1.2.2
prosemirror-model: 1.24.1
prosemirror-schema-list: 1.5.1
@@ -12395,10 +12408,10 @@ snapshots:
prosemirror-transform: 1.10.2
prosemirror-view: 1.41.4
'@tiptap/react@3.24.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
'@tiptap/react@3.23.6(@floating-ui/dom@1.7.6)(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/pm': 3.23.6
'@types/react': 19.2.15
'@types/react-dom': 19.2.3(@types/react@19.2.15)
'@types/use-sync-external-store': 0.0.6
@@ -12407,37 +12420,37 @@ snapshots:
react-dom: 19.2.6(react@19.2.6)
use-sync-external-store: 1.6.0(react@19.2.6)
optionalDependencies:
'@tiptap/extension-bubble-menu': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-floating-menu': 3.24.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-bubble-menu': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-floating-menu': 3.23.6(@floating-ui/dom@1.7.6)(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
transitivePeerDependencies:
- '@floating-ui/dom'
'@tiptap/starter-kit@3.24.0':
'@tiptap/starter-kit@3.23.6':
dependencies:
'@tiptap/core': 3.24.0(@tiptap/pm@3.24.0)
'@tiptap/extension-blockquote': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-bold': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-bullet-list': 3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
'@tiptap/extension-code': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-code-block': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-document': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-dropcursor': 3.24.0(@tiptap/extensions@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
'@tiptap/extension-gapcursor': 3.24.0(@tiptap/extensions@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
'@tiptap/extension-hard-break': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-heading': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-horizontal-rule': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-italic': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-link': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-list': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/extension-list-item': 3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
'@tiptap/extension-list-keymap': 3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
'@tiptap/extension-ordered-list': 3.24.0(@tiptap/extension-list@3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0))
'@tiptap/extension-paragraph': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-strike': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-text': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extension-underline': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))
'@tiptap/extensions': 3.24.0(@tiptap/core@3.24.0(@tiptap/pm@3.24.0))(@tiptap/pm@3.24.0)
'@tiptap/pm': 3.24.0
'@tiptap/core': 3.23.6(@tiptap/pm@3.23.6)
'@tiptap/extension-blockquote': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-bold': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-bullet-list': 3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-code': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-code-block': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-document': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-dropcursor': 3.23.6(@tiptap/extensions@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-gapcursor': 3.23.6(@tiptap/extensions@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-hard-break': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-heading': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-horizontal-rule': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-italic': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-link': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-list': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/extension-list-item': 3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-list-keymap': 3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-ordered-list': 3.23.6(@tiptap/extension-list@3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6))
'@tiptap/extension-paragraph': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-strike': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-text': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extension-underline': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))
'@tiptap/extensions': 3.23.6(@tiptap/core@3.23.6(@tiptap/pm@3.23.6))(@tiptap/pm@3.23.6)
'@tiptap/pm': 3.23.6
'@tree-sitter-grammars/tree-sitter-yaml@0.7.1(tree-sitter@0.22.4)':
dependencies:
@@ -15735,11 +15748,6 @@ snapshots:
prosemirror-view: 1.41.4
rope-sequence: 1.3.4
prosemirror-inputrules@1.5.1:
dependencies:
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.2
prosemirror-keymap@1.2.2:
dependencies:
prosemirror-state: 1.4.3
+16 -16
View File
@@ -49,22 +49,22 @@ catalog:
"@testcontainers/mysql": ^12.0.1
"@testcontainers/postgresql": ^12.0.1
"@testcontainers/redis": ^12.0.1
"@tiptap/extension-color": 3.24.0
"@tiptap/extension-highlight": 3.24.0
"@tiptap/extension-image": 3.24.0
"@tiptap/extension-link": ^3.24.0
"@tiptap/extension-placeholder": ^3.24.0
"@tiptap/extension-table": 3.24.0
"@tiptap/extension-table-cell": 3.24.0
"@tiptap/extension-table-header": 3.24.0
"@tiptap/extension-table-row": 3.24.0
"@tiptap/extension-task-item": 3.24.0
"@tiptap/extension-task-list": 3.24.0
"@tiptap/extension-text-align": 3.24.0
"@tiptap/extension-text-style": 3.24.0
"@tiptap/extension-underline": 3.24.0
"@tiptap/react": ^3.24.0
"@tiptap/starter-kit": ^3.24.0
"@tiptap/extension-color": 3.23.6
"@tiptap/extension-highlight": 3.23.6
"@tiptap/extension-image": 3.23.6
"@tiptap/extension-link": ^3.23.6
"@tiptap/extension-placeholder": ^3.23.6
"@tiptap/extension-table": 3.23.6
"@tiptap/extension-table-cell": 3.23.6
"@tiptap/extension-table-header": 3.23.6
"@tiptap/extension-table-row": 3.23.6
"@tiptap/extension-task-item": 3.23.6
"@tiptap/extension-task-list": 3.23.6
"@tiptap/extension-text-align": 3.23.6
"@tiptap/extension-text-style": 3.23.6
"@tiptap/extension-underline": 3.23.6
"@tiptap/react": ^3.23.6
"@tiptap/starter-kit": ^3.23.6
"@trpc/client": ^11.17.0
"@trpc/next": ^11.17.0
"@trpc/react-query": ^11.17.0