diff --git a/apps/nextjs/src/app/[locale]/widgets/[kind]/page.tsx b/apps/nextjs/src/app/[locale]/widgets/[kind]/page.tsx index 454e7a805..620a7792e 100644 --- a/apps/nextjs/src/app/[locale]/widgets/[kind]/page.tsx +++ b/apps/nextjs/src/app/[locale]/widgets/[kind]/page.tsx @@ -2,6 +2,9 @@ import { Suspense } from "react"; import { notFound } from "next/navigation"; import { Center, Loader } from "@mantine/core"; +import { IntegrationProvider } from "@homarr/auth/client"; +import { auth } from "@homarr/auth/next"; +import { getIntegrationsWithPermissionsAsync } from "@homarr/auth/server"; import type { WidgetKind } from "@homarr/definitions"; import { widgetKinds } from "@homarr/widgets/manifest"; @@ -16,12 +19,15 @@ export default async function WidgetPreview(props: Props) { if (!widgetKinds.includes(kind as WidgetKind)) { notFound(); } + const integrations = await getIntegrationsWithPermissionsAsync(await auth()); return ( -
- }> - - -
+ +
+ }> + + +
+
); } diff --git a/apps/nextjs/src/components/board/items/item-content.tsx b/apps/nextjs/src/components/board/items/item-content.tsx index 49ed7ffab..4a771e769 100644 --- a/apps/nextjs/src/components/board/items/item-content.tsx +++ b/apps/nextjs/src/components/board/items/item-content.tsx @@ -27,6 +27,7 @@ import { loadWidgetResources, reduceWidgetOptionsWithDefinition } from "@homarr/ import type { SectionItem } from "~/app/[locale]/boards/_types"; import { getLogicalTrackSize } from "~/components/board/layout"; +import { useBoardCanvasScale } from "~/components/board/layout/scaled-board-canvas"; import advancedFocusClasses from "../advanced-focus/advanced-focus.module.css"; import { useAdvancedFocus } from "../advanced-focus/context"; import { startAdvancedFocusEntrance } from "../advanced-focus/entrance"; @@ -167,6 +168,7 @@ const LoadedBoardItemContent = ({ const cardRef = useRef(null); const previewEntranceFrameRef = useRef(null); const board = useRequiredBoard(); + const boardCanvasScale = useBoardCanvasScale(); const t = useI18n(); const [isEditMode] = useEditMode(); const settings = useSettings(); @@ -180,7 +182,10 @@ const LoadedBoardItemContent = ({ const { width: viewportWidth, height: viewportHeight } = viewportSize; const supportsAdvancedFocus = definitionSupportsAdvancedFocus(definition); const widgetName = getWidgetName(item.kind, t); - const previewDimensions = useMemo(() => ({ width, height }), [height, width]); + const previewDimensions = useMemo( + () => ({ width, height, scale: boardCanvasScale }), + [boardCanvasScale, height, width], + ); const advancedViewLabel = t("item.advancedFocus.label", { widget: widgetName }); const advancedViewId = `advanced-focus-${item.id}`; const activeFocus = supportsAdvancedFocus && active?.itemId === item.id ? active : null; diff --git a/apps/nextjs/src/components/board/items/item-menu.tsx b/apps/nextjs/src/components/board/items/item-menu.tsx index b14ea80d2..8cb01d47b 100644 --- a/apps/nextjs/src/components/board/items/item-menu.tsx +++ b/apps/nextjs/src/components/board/items/item-menu.tsx @@ -21,7 +21,7 @@ import { LazyWidgetEditModal, preloadWidgetEditModal } from "./lazy-widget-edit- interface BoardItemMenuProps { item: SectionItem; definition: WidgetDefinition; - previewDimensions: { width: number; height: number }; + previewDimensions: { width: number; height: number; scale?: number }; resetErrorBoundary?: () => void; } diff --git a/apps/nextjs/src/components/board/items/widget-context-menu.tsx b/apps/nextjs/src/components/board/items/widget-context-menu.tsx index bf9688b8e..e0d104358 100644 --- a/apps/nextjs/src/components/board/items/widget-context-menu.tsx +++ b/apps/nextjs/src/components/board/items/widget-context-menu.tsx @@ -35,7 +35,7 @@ import { matchesWidgetItemQuery } from "./widget-query-scope"; interface WidgetContextMenuProps { item: SectionItem; definition: WidgetDefinition; - previewDimensions: { width: number; height: number }; + previewDimensions: { width: number; height: number; scale?: number }; widgetStateRef: MutableRefObject | null>; widgetRuntimeRef: WidgetRuntimeRef; sourceRef: RefObject; diff --git a/packages/widgets/src/modals/widget-edit-modal.module.css b/packages/widgets/src/modals/widget-edit-modal.module.css index 222cece6c..0ca0d61ea 100644 --- a/packages/widgets/src/modals/widget-edit-modal.module.css +++ b/packages/widgets/src/modals/widget-edit-modal.module.css @@ -12,7 +12,10 @@ .previewPanel { position: sticky; top: 0; + width: 100%; + max-width: 52rem; min-width: 0; + justify-self: center; padding: var(--mantine-spacing-md); border: 1px solid var(--mantine-color-default-border); border-radius: var(--mantine-radius-md); @@ -141,13 +144,14 @@ } } -@media (max-width: 75em) { +@media (max-width: 68em) { .workspace[data-with-preview] { grid-template-columns: 1fr; } .previewPanel { position: static; + max-width: 44rem; } .previewCanvas { diff --git a/packages/widgets/src/modals/widget-edit-modal.tsx b/packages/widgets/src/modals/widget-edit-modal.tsx index 5fe3690ae..7de2666aa 100644 --- a/packages/widgets/src/modals/widget-edit-modal.tsx +++ b/packages/widgets/src/modals/widget-edit-modal.tsx @@ -14,7 +14,7 @@ import { ErrorBoundary } from "react-error-boundary"; import { z } from "zod/v4"; import { objectEntries } from "@homarr/common"; -import { useSession } from "@homarr/auth/client"; +import { IntegrationProvider, useSession } from "@homarr/auth/client"; import { useOptionalBoard } from "@homarr/boards/context"; import type { WidgetKind } from "@homarr/definitions"; import { createModal, useModalAction } from "@homarr/modals"; @@ -68,7 +68,7 @@ export interface WidgetEditModalProps { onIntegrationSaved?: () => void; onOpenNewIntegration?: (onCreated?: (id: string) => void) => void; previewComponent?: ComponentType>; - previewDimensions?: { width: number; height: number }; + previewDimensions?: { width: number; height: number; scale?: number }; } interface WidgetEditPreviewProps { @@ -78,7 +78,8 @@ interface WidgetEditPreviewProps { state: WidgetEditModalState; itemId?: string; boardId?: string; - dimensions?: { width: number; height: number }; + dimensions?: { width: number; height: number; scale?: number }; + integrationData: IntegrationSelectOption[]; onChangeOptions: (newOptions: Record) => void; } @@ -90,15 +91,19 @@ const WidgetEditPreview = ({ itemId, boardId, dimensions = { width: 400, height: 240 }, + integrationData, onChangeOptions, }: WidgetEditPreviewProps) => { const tItem = useI18n("item.edit"); const { ref, width: availableWidth, height: availableHeight } = useElementSize(); const sourceWidth = Math.max(dimensions.width, 1); const sourceHeight = Math.max(dimensions.height, 1); - let previewScale = 1; + let maximumScale = dimensions.scale ?? 0.9; + if (!Number.isFinite(maximumScale) || maximumScale <= 0) maximumScale = 0.9; + maximumScale = Math.min(maximumScale, 0.9); + let previewScale = maximumScale; if (availableWidth > 0 && availableHeight > 0) { - previewScale = Math.min(availableWidth / sourceWidth, availableHeight / sourceHeight, 1); + previewScale = Math.min(availableWidth / sourceWidth, availableHeight / sourceHeight, maximumScale); } const previewWidth = sourceWidth * previewScale; const previewHeight = sourceHeight * previewScale; @@ -106,6 +111,14 @@ const WidgetEditPreview = ({ const hasIntegrationSupport = "supportedIntegrations" in definition; const integrationRequired = hasIntegrationSupport && definition.integrationsRequired !== false; const isMissingIntegration = integrationRequired && state.integrationIds.length === 0; + const previewIntegrations = integrationData.map((integration) => ({ + id: integration.id, + permissions: integration.permissions ?? { + hasFullAccess: false, + hasInteractAccess: false, + hasUseAccess: false, + }, + })); return ( @@ -149,19 +162,21 @@ const WidgetEditPreview = ({ )} > -
- onChangeOptions(newOptions as Record)} - /> -
+ +
+ onChangeOptions(newOptions as Record)} + /> +
+
)} @@ -375,6 +390,7 @@ export const WidgetEditModal = createModal>(({ itemId={innerProps.itemId} boardId={innerProps.boardId ?? board?.id} dimensions={innerProps.previewDimensions} + integrationData={innerProps.integrationData} onChangeOptions={handlePreviewOptionsChange} /> )}