chore: reconcile assistant layer after stack rebase

This commit is contained in:
ajnart committed 2026-08-14 11:32:11 +02:00
1 parent 83f80eb9a7
commit 87f264a286
13 files changed
+8185 -62

No files matched your search

@@ -1,55 +0,0 @@
import { describe, expect, test } from "vitest";
import { buildAiPrompt } from "./_copy-ai-prompt-button";
const schema = {
type: "object",
required: ["name", "url", "displayType", "displayConfig"],
};
describe("buildAiPrompt", () => {
test("asks Homarr Assistant to create a definition and includes the user's request", () => {
const prompt = buildAiPrompt(
schema,
'{"status":"online"}',
{
$schema: "homarr-custom-widget-v2",
name: "Status",
url: "https://example.com/status",
},
"Show a compact green status when the service is online.",
);
expect(prompt).toContain("`customWidget_create`");
expect(prompt).toContain("`secrets: []`");
expect(prompt).toContain("do not ask for typed confirmation");
expect(prompt).toContain('```json\n{"status":"online"}\n```');
expect(prompt).toContain('"name": "Status"');
expect(prompt).toContain("Show a compact green status when the service is online.");
});
test("asks Homarr Assistant to update the selected definition without replacing its secrets", () => {
const prompt = buildAiPrompt(
schema,
null,
{
name: "Existing widget",
authType: "bearer",
},
"Use a progress bar.",
"widget-definition-id",
);
expect(prompt).toContain("custom widget definition `widget-definition-id`");
expect(prompt).toContain("`customWidget_update`");
expect(prompt).toContain("Preserve configured secrets by omitting the `secrets` field");
expect(prompt).not.toContain('"secrets"');
});
test("keeps copy-to-clipboard prompts useful before a request is entered", () => {
const prompt = buildAiPrompt(schema);
expect(prompt).toContain("PASTE_YOUR_API_RESPONSE_HERE");
expect(prompt).toContain("Describe what you want the widget to show or change here.");
});
});
@@ -213,6 +213,7 @@ export const AssistantConfigureWidgetTool = ({
openModal(
{
kind: args.kind,
definition: widgetImports[args.kind].definition,
value: configuration.value,
integrationData: configuration.integrationData,
integrationSupport: configuration.integrationSupport,
-2
View File
@@ -16,7 +16,6 @@ import { bazarrRouter } from "./router/widgets/bazarr";
import { serverSettingsRouter } from "./router/serverSettings";
import { beszelRouter } from "./router/widgets/beszel";
import { calendarRouter } from "./router/widgets/calendar";
import { customWidgetRouter } from "./router/custom-widget/custom-widget-router";
import { dnsHoleRouter } from "./router/widgets/dns-hole";
import { downloadsRouter } from "./router/widgets/downloads";
import { healthMonitoringRouter } from "./router/widgets/health-monitoring";
@@ -40,7 +39,6 @@ export const mcpRouter = createTRPCRouter({
serverSettings: serverSettingsRouter,
beszel: beszelRouter,
calendar: calendarRouter,
customWidget: customWidgetRouter,
dnsHole: dnsHoleRouter,
downloads: downloadsRouter,
healthMonitoring: healthMonitoringRouter,
@@ -1 +0,0 @@
ALTER TABLE `assistant_configuration` ADD `web_search_enabled` boolean DEFAULT false NOT NULL;
File diff suppressed because it is too large. Load diff
@@ -1 +0,0 @@
ALTER TABLE "assistant_configuration" ADD COLUMN "web_search_enabled" boolean DEFAULT false NOT NULL;
File diff suppressed because it is too large. Load diff
@@ -1 +0,0 @@
ALTER TABLE `assistant_configuration` ADD `web_search_enabled` integer DEFAULT false NOT NULL;
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -536,7 +536,7 @@ export const assistantMessages = pgTable(
.references(() => assistantThreads.id, { onDelete: "cascade" }),
parentId: varchar({ length: 128 }),
format: varchar({ length: 64 }).notNull().default("ai-sdk/v6"),
content: text().default(emptySuperJSON).notNull(),
content: text().notNull(),
createdAt: timestamp().notNull().defaultNow(),
},
(message) => ({
+1 -1
View File
@@ -527,7 +527,7 @@ export const assistantMessages = sqliteTable(
.references(() => assistantThreads.id, { onDelete: "cascade" }),
parentId: text(),
format: text().notNull().default("ai-sdk/v6"),
content: text().default(emptySuperJSON).notNull(),
content: text().notNull(),
createdAt: int({ mode: "timestamp" })
.notNull()
.default(sql`(unixepoch())`),
+2
View File
@@ -34,6 +34,7 @@ import {
IconPhoto,
IconReportSearch,
IconRocket,
IconRobot,
IconRoute,
IconRss,
IconServer2,
@@ -56,6 +57,7 @@ export const widgetCatalogIcons: Record<WidgetKind, TablerIcon> = {
clock: IconClock,
weather: IconCloud,
app: IconApps,
assistant: IconRobot,
iframe: IconBrowser,
video: IconDeviceCctv,
notebook: IconNotes,
+2
View File
@@ -25,6 +25,7 @@ const moduleLoaders: Record<WidgetKind, () => Promise<WidgetModule>> = {
clock: () => import("./clock") as Promise<WidgetModule>,
weather: () => import("./weather") as Promise<WidgetModule>,
app: () => import("./app") as Promise<WidgetModule>,
assistant: () => import("./assistant") as Promise<WidgetModule>,
archiveTeamWarrior: () => import("./archive-team-warrior") as Promise<WidgetModule>,
anchorNote: () => import("./anchor-note") as Promise<WidgetModule>,
notebook: () => import("./notebook") as Promise<WidgetModule>,
@@ -85,6 +86,7 @@ const componentLoaders: Record<WidgetKind, () => Promise<WidgetComponentModule>>
clock: () => import("./clock/component") as Promise<WidgetComponentModule>,
weather: () => import("./weather/component") as Promise<WidgetComponentModule>,
app: () => import("./app/component") as Promise<WidgetComponentModule>,
assistant: () => import("./assistant/component") as Promise<WidgetComponentModule>,
archiveTeamWarrior: () => import("./archive-team-warrior/component") as Promise<WidgetComponentModule>,
anchorNote: () => import("./anchor-note/component") as Promise<WidgetComponentModule>,
notebook: () => import("./notebook/component") as Promise<WidgetComponentModule>,