From 211e0595210f9d7ccfe60d555aeac8c3546d98ea Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Mon, 20 Jul 2026 18:11:10 +0200 Subject: [PATCH] fix(custom-widgets): simplify error sharing --- .agents/skills/homarr-custom-widget/SKILL.md | 2 +- .../management/custom-widgets/authoring.mdx | 4 +- .../custom-widgets/_custom-widget-ai-card.tsx | 6 +- .../custom-widgets/_custom-widget-form.tsx | 73 ++++++++++++------- .../_use-custom-widget-form-actions.ts | 37 +--------- packages/custom-widgets/src/core/ai-prompt.ts | 20 ----- .../src/core/authoring-resources.ts | 2 +- .../custom-widgets/src/test/ai-prompt.spec.ts | 24 +----- .../src/test/authoring-resources.spec.ts | 2 + packages/translation/src/lang/en.json | 10 +-- 10 files changed, 65 insertions(+), 115 deletions(-) diff --git a/.agents/skills/homarr-custom-widget/SKILL.md b/.agents/skills/homarr-custom-widget/SKILL.md index 7643c4524..15e708b85 100644 --- a/.agents/skills/homarr-custom-widget/SKILL.md +++ b/.agents/skills/homarr-custom-widget/SKILL.md @@ -31,4 +31,4 @@ Create one widget at a time. Repeat the workflow when the user asks for several - Include responsive loading, empty, error, and success states. - Keep actions explicit, permission-scoped, confirmed when destructive, and followed by targeted invalidation. -Read [references/schema.md](references/schema.md), [references/runtime.md](references/runtime.md), and [references/security.md](references/security.md) before offline authoring. When connected, prefer live resources because installed Mantine components can differ by Homarr release. +When connected through MCP, use the live schema, component, example, and skill resources. If live resources are unavailable, follow this document and the authoring prompt; do not assume access to repository-relative files. diff --git a/apps/docs/docs/management/custom-widgets/authoring.mdx b/apps/docs/docs/management/custom-widgets/authoring.mdx index c5794b9c0..7ca1b3fe4 100644 --- a/apps/docs/docs/management/custom-widgets/authoring.mdx +++ b/apps/docs/docs/management/custom-widgets/authoring.mdx @@ -4,7 +4,7 @@ import { CanonicalCustomWidgetExample } from "@site/src/components/custom-widget The desktop workbench keeps code and preview side by side. Mobile uses Configure and Preview tabs. Save and preview actions stay available while the form scrolls. -CodeMirror edits requests, options, and JSX. The editor supplies installed Mantine components, request/source IDs, option names, temporary input names, runtime components, and icons. The request toolbar jumps to a declaration and atomically renames its invalidation, dynamic-option, and JSX references. Diagnostics are copyable and secret-free. +CodeMirror edits requests, options, and JSX. The editor supplies installed Mantine components, request/source IDs, option names, temporary input names, runtime components, and icons. The request toolbar jumps to a declaration and atomically renames its invalidation, dynamic-option, and JSX references. Save validation errors appear as selectable plain text with a direct **Copy errors** action. The **Schema** action beside Undo, Redo, and Format expands the generated JSON Schema plus minimal and full examples. These references are generated from the same validators used by imports, MCP, Workshop, and saved definitions. @@ -25,7 +25,7 @@ Enter a short request and optional API documentation URL, then select **Copy AI 1. A fenced `json` block with one complete widget and `template: "__HOMARR_TEMPLATE__"`. 2. A fenced `jsx` block with the readable template. -Use **Paste AI response** to fill the workbench. Enter credentials yourself, test, and preview. If validation or a request fails, select **Copy diagnostics for AI** and paste the correction back into Homarr. +Use **Paste AI response** to fill the workbench. Enter credentials yourself, test, and preview. If saving fails, select or copy the validation errors shown by Homarr and give that plain text to the model. Homarr does not generate a separate fix prompt or assume the model can access local skill files. The prompt is under 8,000 characters. It includes the installed Mantine version and capability rules but not a complete prop list, MCP workflow, server URL, or credentials. diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-ai-card.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-ai-card.tsx index e50476164..c99d1a203 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-ai-card.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-ai-card.tsx @@ -1,7 +1,7 @@ "use client"; import { Button, Card, Group, Stack, Text, TextInput, Textarea } from "@mantine/core"; -import { IconCopy, IconSparkles } from "@tabler/icons-react"; +import { IconSparkles } from "@tabler/icons-react"; import type { HomarrCustomWidgetV2 } from "@homarr/custom-widgets/core"; import { useScopedI18n } from "@homarr/translation/client"; @@ -15,7 +15,6 @@ interface AiCardProps { documentationUrl: string; onDocumentationUrlChange(value: string): void; onPaste(): void; - onCopyDiagnostics(): void; } export function CustomWidgetAiCard(props: AiCardProps) { @@ -48,9 +47,6 @@ export function CustomWidgetAiCard(props: AiCardProps) { - diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form.tsx index 99d4e38ce..aafb33492 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form.tsx @@ -1,12 +1,25 @@ "use client"; import { useEffect, useMemo, useRef, useState } from "react"; -import { Alert, Box, Button, List, Paper, SegmentedControl, Stack, Text, TextInput, Textarea } from "@mantine/core"; +import { + Alert, + Box, + Button, + CopyButton, + Paper, + SegmentedControl, + Stack, + Text, + TextInput, + Textarea, +} from "@mantine/core"; import { IconAlertCircle, IconApi, IconBraces, + IconCheck, IconCode, + IconCopy, IconDatabase, IconEye, IconSettings, @@ -119,21 +132,17 @@ export function CustomWidgetForm({ mode, initialValues, definitionId }: CustomWi [candidate, requestDiagnostics, templateDiagnostics], ); - const { save, runPreview, pasteAiResponse, copyDiagnostics, saveIssues, savePending, previewPending } = - useCustomWidgetFormActions({ - mode, - definitionId, - form, - candidate, - templateDiagnostics, - requestDiagnostics, - preview, - setPreview, - setMobilePane, - setOptionsSnapshot, - request, - documentationUrl, - }); + const { save, runPreview, pasteAiResponse, saveIssues, savePending, previewPending } = useCustomWidgetFormActions({ + mode, + definitionId, + form, + candidate, + preview, + setPreview, + setMobilePane, + setOptionsSnapshot, + }); + const saveIssueText = saveIssues.map((issue) => `${issue.path ? `${issue.path}: ` : ""}${issue.message}`).join("\n"); useEffect(() => { const beforeUnload = (event: BeforeUnloadEvent) => { @@ -169,14 +178,29 @@ export function CustomWidgetForm({ mode, initialValues, definitionId }: CustomWi {w("saveError.description")} - - {saveIssues.map((issue, index) => ( - - {issue.path ? `${issue.path}: ` : ""} - {issue.message} - - ))} - +