feat: add custom widget flow workbench
This commit is contained in:
1 parent
c32a5317ca
commit
a6bdbbb4ea
259 files changed
+23875
-1293
No files matched your search
@@ -27,7 +27,7 @@ import {
|
||||
WORKSHOP_API_URL,
|
||||
MAX_WORKSHOP_SCREENSHOTS,
|
||||
WORKSHOP_REQUEST_TIMEOUT_MS,
|
||||
WORKSHOP_SCHEMA_BY_TYPE,
|
||||
getWorkshopContentSchema,
|
||||
workshopCommentSchema,
|
||||
workshopAssistantUsageSchema,
|
||||
workshopReportSchema,
|
||||
@@ -432,7 +432,7 @@ export class WorkshopBackend {
|
||||
const data = new FormData();
|
||||
Object.entries(parsed).forEach(([key, value]) => data.set(key, typeof value === "string" ? value : String(value)));
|
||||
data.set("author", this.currentUser?.id ?? "");
|
||||
data.set("widgetSchema", WORKSHOP_SCHEMA_BY_TYPE[parsed.type]);
|
||||
data.set("widgetSchema", getWorkshopContentSchema(parsed.type, parsed.content));
|
||||
screenshots.forEach((file) => data.append("screenshots", file));
|
||||
try {
|
||||
const result = await this.pocketBase.collection("submissions").create(data);
|
||||
@@ -442,12 +442,20 @@ export class WorkshopBackend {
|
||||
}
|
||||
}
|
||||
|
||||
public async update(id: string, input: WorkshopSubmissionInput, screenshotChanges: WorkshopScreenshotChanges = {}) {
|
||||
public async update(
|
||||
id: string,
|
||||
input: WorkshopSubmissionInput,
|
||||
screenshotChanges: WorkshopScreenshotChanges = {},
|
||||
expectedRevision?: number,
|
||||
) {
|
||||
const parsed = workshopSubmissionInputSchema.parse(input);
|
||||
const additions = screenshotChanges.additions ?? [];
|
||||
const removals = screenshotChanges.removals ?? [];
|
||||
workshopScreenshotsSchema.parse(additions);
|
||||
const current = await this.get(id);
|
||||
if (expectedRevision !== undefined && expectedRevision !== current.revision) {
|
||||
throw new Error("Submission changed since it was reviewed. Reload it before publishing an update.");
|
||||
}
|
||||
const removalSet = new Set(removals);
|
||||
const retainedCount = current.screenshots.filter((filename) => !removalSet.has(filename)).length;
|
||||
if (retainedCount + additions.length > MAX_WORKSHOP_SCREENSHOTS)
|
||||
@@ -455,8 +463,10 @@ export class WorkshopBackend {
|
||||
|
||||
const data: Record<string, unknown> = {
|
||||
...parsed,
|
||||
expectedRevision: current.revision,
|
||||
expectedRevision: expectedRevision ?? current.revision,
|
||||
};
|
||||
const contentSchema = getWorkshopContentSchema(parsed.type, parsed.content);
|
||||
if (parsed.type === "customWidget" && contentSchema !== current.widgetSchema) data.widgetSchema = contentSchema;
|
||||
if (additions.length > 0) data["screenshots+"] = additions;
|
||||
if (removals.length > 0) data["screenshots-"] = removals;
|
||||
try {
|
||||
@@ -712,11 +722,13 @@ export const workshopUpdateMutationOptions = (backend: WorkshopBackend) =>
|
||||
id,
|
||||
input,
|
||||
screenshotChanges,
|
||||
expectedRevision,
|
||||
}: {
|
||||
id: string;
|
||||
input: WorkshopSubmissionInput;
|
||||
screenshotChanges?: WorkshopScreenshotChanges;
|
||||
}) => backend.update(id, input, screenshotChanges),
|
||||
expectedRevision?: number;
|
||||
}) => backend.update(id, input, screenshotChanges, expectedRevision),
|
||||
});
|
||||
|
||||
export const workshopDeleteMutationOptions = (backend: WorkshopBackend) =>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { CUSTOM_WIDGET_SCHEMA, customWidgetImportSchema } from "@homarr/custom-widgets/core";
|
||||
import {
|
||||
CUSTOM_WIDGET_SCHEMA,
|
||||
CUSTOM_WIDGET_SUPPORTED_SCHEMAS,
|
||||
customWidgetImportSchema,
|
||||
} from "@homarr/custom-widgets/core";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const HOMARR_WEBSITE_URL = "https://homarr.dev";
|
||||
@@ -263,6 +267,18 @@ export function validateWorkshopWidget(content: string): WorkshopWidgetValidatio
|
||||
}
|
||||
}
|
||||
|
||||
export function isSupportedWorkshopWidgetSchema(value: string) {
|
||||
return CUSTOM_WIDGET_SUPPORTED_SCHEMAS.some((schema) => schema === value);
|
||||
}
|
||||
|
||||
/** Schema metadata follows the validated package, including during v2 to v3 updates. */
|
||||
export function getWorkshopContentSchema(type: WorkshopSubmissionType, content: string) {
|
||||
if (type === "customCss") return WORKSHOP_CSS_SCHEMA;
|
||||
const result = validateWorkshopWidget(content);
|
||||
if (!result.success) throw new Error(result.error);
|
||||
return result.data.$schema;
|
||||
}
|
||||
|
||||
export function workshopExportFilename(title: string, type: WorkshopSubmissionType = "customWidget") {
|
||||
const safe =
|
||||
title
|
||||
|
||||
Reference in new issue
Block a user