diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/packages/_package-assistant-context.ts b/apps/nextjs/src/app/[locale]/manage/custom-widgets/packages/_package-assistant-context.ts index a2603344f..2d01e5973 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/packages/_package-assistant-context.ts +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/packages/_package-assistant-context.ts @@ -14,7 +14,7 @@ export const packageAssistantSdkContext = { services: "useWidgetServices() exposes notify({title,message?,color?}), navigate(href), openAdvanced(), closeAdvanced(), openDetail({title,content:ReactNode}), closeDetail(), confirm({title,message,destructive?}):Promise, updateOptions(record):Promise. Use updateOptions from an authored configuration surface. Managed storage is isolated memory in preview. Query results and local options are not part of Assistant context.", server: - "Optional server.ts imports @homarr/widget-sdk/server and default-exports defineWidgetServer({handler:async(input,context)=>value}). Declare every handler in manifest.handlers with kind:'query'|'action'|'subscription'|'migration', permission:'view'|'modify'|'full', inputSchema? (JSON Schema), guestAccess?; actions default to modify. fetchWidgetConnection(context,{connection:'slot',path,method?,query?,body?,responseType?}) returns {data,ok,status,contentType,durationMs,updatedAt}. getWidgetConnection(context,{connection,serviceType?}) returns {configuration:{kind,serviceType?,settings?:TSettings,...},secrets:Record} only to trusted server code for Node SQL, MQTT, SSH, files or other service clients; never return secrets to browser queries. getWidgetServerContext(context) returns options,installationId,bindingNames,isPreview. callWidgetIntegration(context,{connection,capability,input?}) invokes native adapters; subscribeWidgetIntegration yields native stream events. callWidgetRunner and subscribeWidgetRunner address owner-managed remote runners. Server modules have trusted Node capabilities. Secrets stay in owner-bound connections. Do not invent integration capabilities: use reference packages or declared capability docs.", + "Optional server.ts imports @homarr/widget-sdk/server and default-exports defineWidgetServer({handler:async(input,context)=>value}). Declare every handler in manifest.handlers with kind:'query'|'action'|'subscription'|'migration', permission:'view'|'modify'|'full', inputSchema? (JSON Schema), guestAccess?; actions default to modify. fetchWidgetConnection(context,{connection:'slot',path,method?,query?,body?,responseType?}) returns {data,ok,status,contentType,durationMs,updatedAt}. getWidgetConnection(context,{connection,serviceType?}) returns {configuration:{kind,serviceType?,settings?:TSettings,...},secrets:Record} only to trusted server code for Node SQL, MQTT, SSH, files or other service clients; never return secrets to browser queries. getWidgetServerContext(context) returns options,installationId,bindingNames,bindingLabels,isPreview. Declare connections with multiple:true for a repeatable group, and integrationKind (for example sonarr) to filter compatible native integrations. getWidgetConnectionGroup(context,slot) returns all {connection,label} members for queries and actions; do not hard-code first/second server slots. callWidgetIntegration(context,{connection,capability,input?}) invokes native adapters; subscribeWidgetIntegration yields native stream events. callWidgetRunner and subscribeWidgetRunner address owner-managed remote runners. Server modules have trusted Node capabilities. Secrets stay in owner-bound connections. Do not invent integration capabilities: use reference packages or declared capability docs.", lifecycle: "Proposals only change the visible draft after explicit review; they never save, trust, run or activate code. The owner saves the draft, reviews dependencies and trust, runs exact-candidate Preview, then explicitly activates the saved release for all placements. SDK actions are simulated in default previews, but arbitrary trusted browser/Node code still executes. A preview session proves compilation/session creation only, not successful UI rendering, live action success or all application behavior. Any source/options/binding edit invalidates preview evidence.", }; diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/packages/_package-assistant.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/packages/_package-assistant.tsx index 2f227df14..f2a41baee 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/packages/_package-assistant.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/packages/_package-assistant.tsx @@ -1,9 +1,10 @@ "use client"; -import { Button, Divider, Group, Popover, Stack, Text, Textarea } from "@mantine/core"; +import { Alert, Button, Divider, Group, Popover, Stack, Text, Textarea } from "@mantine/core"; import { IconSparkles } from "@tabler/icons-react"; import { useState } from "react"; import { useI18n } from "@homarr/translation/client"; +import { Link } from "@homarr/ui"; import { useOptionalHomarrAssistant } from "~/components/assistant/assistant-context"; export function PackageAssistant() { @@ -11,8 +12,10 @@ export function PackageAssistant() { const assistant = useOptionalHomarrAssistant(); const [request, setRequest] = useState(""); const [opened, setOpened] = useState(false); - if (!assistant?.enabled) return null; + const [sendFailed, setSendFailed] = useState(false); const send = (intent: string) => { + if (!assistant?.enabled || assistant.isRunning) return; + setSendFailed(false); assistant.open(); if ( assistant.sendPrompt( @@ -21,45 +24,69 @@ export function PackageAssistant() { ) { setOpened(false); setRequest(""); - } + } else setSendFailed(true); }; return ( - - - {t("context")} - -