mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-29 19:01:44 +00:00
355 lines
11 KiB
TypeScript
355 lines
11 KiB
TypeScript
import { buildPluginConfigSchema } from "openclaw/plugin-sdk/core";
|
|
import { z } from "openclaw/plugin-sdk/zod";
|
|
import type { OpenClawPluginConfigSchema } from "../api.js";
|
|
import {
|
|
DIFF_IMAGE_QUALITY_PRESETS,
|
|
DIFF_INDICATORS,
|
|
DIFF_LAYOUTS,
|
|
DIFF_MODES,
|
|
DIFF_OUTPUT_FORMATS,
|
|
DIFF_THEMES,
|
|
type DiffFileDefaults,
|
|
type DiffImageQualityPreset,
|
|
type DiffIndicators,
|
|
type DiffLayout,
|
|
type DiffMode,
|
|
type DiffOutputFormat,
|
|
type DiffPresentationDefaults,
|
|
type DiffTheme,
|
|
type DiffToolDefaults,
|
|
} from "./types.js";
|
|
|
|
type DiffsPluginConfig = {
|
|
defaults?: {
|
|
fontFamily?: string;
|
|
fontSize?: number;
|
|
lineSpacing?: number;
|
|
layout?: DiffLayout;
|
|
showLineNumbers?: boolean;
|
|
diffIndicators?: DiffIndicators;
|
|
wordWrap?: boolean;
|
|
background?: boolean;
|
|
theme?: DiffTheme;
|
|
fileFormat?: DiffOutputFormat;
|
|
fileQuality?: DiffImageQualityPreset;
|
|
fileScale?: number;
|
|
fileMaxWidth?: number;
|
|
format?: DiffOutputFormat;
|
|
// Backward-compatible aliases retained for existing configs.
|
|
imageFormat?: DiffOutputFormat;
|
|
imageQuality?: DiffImageQualityPreset;
|
|
imageScale?: number;
|
|
imageMaxWidth?: number;
|
|
mode?: DiffMode;
|
|
};
|
|
security?: {
|
|
allowRemoteViewer?: boolean;
|
|
};
|
|
};
|
|
|
|
const DEFAULT_IMAGE_QUALITY_PROFILES = {
|
|
standard: {
|
|
scale: 2,
|
|
maxWidth: 960,
|
|
maxPixels: 8_000_000,
|
|
},
|
|
hq: {
|
|
scale: 2.5,
|
|
maxWidth: 1200,
|
|
maxPixels: 14_000_000,
|
|
},
|
|
print: {
|
|
scale: 3,
|
|
maxWidth: 1400,
|
|
maxPixels: 24_000_000,
|
|
},
|
|
} as const satisfies Record<
|
|
DiffImageQualityPreset,
|
|
{ scale: number; maxWidth: number; maxPixels: number }
|
|
>;
|
|
|
|
export const DEFAULT_DIFFS_TOOL_DEFAULTS: DiffToolDefaults = {
|
|
fontFamily: "Fira Code",
|
|
fontSize: 15,
|
|
lineSpacing: 1.6,
|
|
layout: "unified",
|
|
showLineNumbers: true,
|
|
diffIndicators: "bars",
|
|
wordWrap: true,
|
|
background: true,
|
|
theme: "dark",
|
|
fileFormat: "png",
|
|
fileQuality: "standard",
|
|
fileScale: DEFAULT_IMAGE_QUALITY_PROFILES.standard.scale,
|
|
fileMaxWidth: DEFAULT_IMAGE_QUALITY_PROFILES.standard.maxWidth,
|
|
mode: "both",
|
|
};
|
|
|
|
export type DiffsPluginSecurityConfig = {
|
|
allowRemoteViewer: boolean;
|
|
};
|
|
|
|
export const DEFAULT_DIFFS_PLUGIN_SECURITY: DiffsPluginSecurityConfig = {
|
|
allowRemoteViewer: false,
|
|
};
|
|
|
|
const DiffsPluginJsonSchemaSource = z.strictObject({
|
|
defaults: z
|
|
.strictObject({
|
|
fontFamily: z.string().default(DEFAULT_DIFFS_TOOL_DEFAULTS.fontFamily).optional(),
|
|
fontSize: z.number().min(10).max(24).default(DEFAULT_DIFFS_TOOL_DEFAULTS.fontSize).optional(),
|
|
lineSpacing: z
|
|
.number()
|
|
.min(1)
|
|
.max(3)
|
|
.default(DEFAULT_DIFFS_TOOL_DEFAULTS.lineSpacing)
|
|
.optional(),
|
|
layout: z.enum(DIFF_LAYOUTS).default(DEFAULT_DIFFS_TOOL_DEFAULTS.layout).optional(),
|
|
showLineNumbers: z.boolean().default(DEFAULT_DIFFS_TOOL_DEFAULTS.showLineNumbers).optional(),
|
|
diffIndicators: z
|
|
.enum(DIFF_INDICATORS)
|
|
.default(DEFAULT_DIFFS_TOOL_DEFAULTS.diffIndicators)
|
|
.optional(),
|
|
wordWrap: z.boolean().default(DEFAULT_DIFFS_TOOL_DEFAULTS.wordWrap).optional(),
|
|
background: z.boolean().default(DEFAULT_DIFFS_TOOL_DEFAULTS.background).optional(),
|
|
theme: z.enum(DIFF_THEMES).default(DEFAULT_DIFFS_TOOL_DEFAULTS.theme).optional(),
|
|
fileFormat: z
|
|
.enum(DIFF_OUTPUT_FORMATS)
|
|
.default(DEFAULT_DIFFS_TOOL_DEFAULTS.fileFormat)
|
|
.optional(),
|
|
format: z.enum(DIFF_OUTPUT_FORMATS).optional(),
|
|
fileQuality: z
|
|
.enum(DIFF_IMAGE_QUALITY_PRESETS)
|
|
.default(DEFAULT_DIFFS_TOOL_DEFAULTS.fileQuality)
|
|
.optional(),
|
|
fileScale: z.number().min(1).max(4).default(DEFAULT_DIFFS_TOOL_DEFAULTS.fileScale).optional(),
|
|
fileMaxWidth: z
|
|
.number()
|
|
.min(640)
|
|
.max(2400)
|
|
.default(DEFAULT_DIFFS_TOOL_DEFAULTS.fileMaxWidth)
|
|
.optional(),
|
|
imageFormat: z.enum(DIFF_OUTPUT_FORMATS).optional(),
|
|
imageQuality: z.enum(DIFF_IMAGE_QUALITY_PRESETS).optional(),
|
|
imageScale: z.number().min(1).max(4).optional(),
|
|
imageMaxWidth: z.number().min(640).max(2400).optional(),
|
|
mode: z.enum(DIFF_MODES).default(DEFAULT_DIFFS_TOOL_DEFAULTS.mode).optional(),
|
|
})
|
|
.optional(),
|
|
security: z
|
|
.strictObject({
|
|
allowRemoteViewer: z
|
|
.boolean()
|
|
.default(DEFAULT_DIFFS_PLUGIN_SECURITY.allowRemoteViewer)
|
|
.optional(),
|
|
})
|
|
.optional(),
|
|
});
|
|
|
|
export const diffsPluginConfigSchema: OpenClawPluginConfigSchema = buildPluginConfigSchema(
|
|
DiffsPluginJsonSchemaSource,
|
|
{
|
|
safeParse(value: unknown) {
|
|
if (value === undefined) {
|
|
return { success: true, data: undefined };
|
|
}
|
|
try {
|
|
return { success: true, data: resolveDiffsPluginDefaults(value) };
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: {
|
|
issues: [{ path: [], message: error instanceof Error ? error.message : String(error) }],
|
|
},
|
|
};
|
|
}
|
|
},
|
|
},
|
|
);
|
|
|
|
export function resolveDiffsPluginDefaults(config: unknown): DiffToolDefaults {
|
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
return { ...DEFAULT_DIFFS_TOOL_DEFAULTS };
|
|
}
|
|
|
|
const defaults = (config as DiffsPluginConfig).defaults;
|
|
if (!defaults || typeof defaults !== "object" || Array.isArray(defaults)) {
|
|
return { ...DEFAULT_DIFFS_TOOL_DEFAULTS };
|
|
}
|
|
|
|
const fileQuality = normalizeFileQuality(defaults.fileQuality ?? defaults.imageQuality);
|
|
const profile = DEFAULT_IMAGE_QUALITY_PROFILES[fileQuality];
|
|
|
|
return {
|
|
fontFamily: normalizeFontFamily(defaults.fontFamily),
|
|
fontSize: normalizeFontSize(defaults.fontSize),
|
|
lineSpacing: normalizeLineSpacing(defaults.lineSpacing),
|
|
layout: normalizeLayout(defaults.layout),
|
|
showLineNumbers: defaults.showLineNumbers !== false,
|
|
diffIndicators: normalizeDiffIndicators(defaults.diffIndicators),
|
|
wordWrap: defaults.wordWrap !== false,
|
|
background: defaults.background !== false,
|
|
theme: normalizeTheme(defaults.theme),
|
|
fileFormat: normalizeFileFormat(defaults.fileFormat ?? defaults.imageFormat ?? defaults.format),
|
|
fileQuality,
|
|
fileScale: normalizeFileScale(defaults.fileScale ?? defaults.imageScale, profile.scale),
|
|
fileMaxWidth: normalizeFileMaxWidth(
|
|
defaults.fileMaxWidth ?? defaults.imageMaxWidth,
|
|
profile.maxWidth,
|
|
),
|
|
mode: normalizeMode(defaults.mode),
|
|
};
|
|
}
|
|
|
|
export function resolveDiffsPluginSecurity(config: unknown): DiffsPluginSecurityConfig {
|
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
return { ...DEFAULT_DIFFS_PLUGIN_SECURITY };
|
|
}
|
|
|
|
const security = (config as DiffsPluginConfig).security;
|
|
if (!security || typeof security !== "object" || Array.isArray(security)) {
|
|
return { ...DEFAULT_DIFFS_PLUGIN_SECURITY };
|
|
}
|
|
|
|
return {
|
|
allowRemoteViewer: security.allowRemoteViewer === true,
|
|
};
|
|
}
|
|
|
|
export function toPresentationDefaults(defaults: DiffToolDefaults): DiffPresentationDefaults {
|
|
const {
|
|
fontFamily,
|
|
fontSize,
|
|
lineSpacing,
|
|
layout,
|
|
showLineNumbers,
|
|
diffIndicators,
|
|
wordWrap,
|
|
background,
|
|
theme,
|
|
} = defaults;
|
|
return {
|
|
fontFamily,
|
|
fontSize,
|
|
lineSpacing,
|
|
layout,
|
|
showLineNumbers,
|
|
diffIndicators,
|
|
wordWrap,
|
|
background,
|
|
theme,
|
|
};
|
|
}
|
|
|
|
function normalizeFontFamily(fontFamily?: string): string {
|
|
const normalized = fontFamily?.trim();
|
|
return normalized || DEFAULT_DIFFS_TOOL_DEFAULTS.fontFamily;
|
|
}
|
|
|
|
function normalizeFontSize(fontSize?: number): number {
|
|
if (fontSize === undefined || !Number.isFinite(fontSize)) {
|
|
return DEFAULT_DIFFS_TOOL_DEFAULTS.fontSize;
|
|
}
|
|
const rounded = Math.floor(fontSize);
|
|
return Math.min(Math.max(rounded, 10), 24);
|
|
}
|
|
|
|
function normalizeLineSpacing(lineSpacing?: number): number {
|
|
if (lineSpacing === undefined || !Number.isFinite(lineSpacing)) {
|
|
return DEFAULT_DIFFS_TOOL_DEFAULTS.lineSpacing;
|
|
}
|
|
return Math.min(Math.max(lineSpacing, 1), 3);
|
|
}
|
|
|
|
function normalizeLayout(layout?: DiffLayout): DiffLayout {
|
|
return layout && DIFF_LAYOUTS.includes(layout) ? layout : DEFAULT_DIFFS_TOOL_DEFAULTS.layout;
|
|
}
|
|
|
|
function normalizeDiffIndicators(diffIndicators?: DiffIndicators): DiffIndicators {
|
|
return diffIndicators && DIFF_INDICATORS.includes(diffIndicators)
|
|
? diffIndicators
|
|
: DEFAULT_DIFFS_TOOL_DEFAULTS.diffIndicators;
|
|
}
|
|
|
|
function normalizeTheme(theme?: DiffTheme): DiffTheme {
|
|
return theme && DIFF_THEMES.includes(theme) ? theme : DEFAULT_DIFFS_TOOL_DEFAULTS.theme;
|
|
}
|
|
|
|
function normalizeFileFormat(fileFormat?: DiffOutputFormat): DiffOutputFormat {
|
|
return fileFormat && DIFF_OUTPUT_FORMATS.includes(fileFormat)
|
|
? fileFormat
|
|
: DEFAULT_DIFFS_TOOL_DEFAULTS.fileFormat;
|
|
}
|
|
|
|
function normalizeFileQuality(fileQuality?: DiffImageQualityPreset): DiffImageQualityPreset {
|
|
return fileQuality && DIFF_IMAGE_QUALITY_PRESETS.includes(fileQuality)
|
|
? fileQuality
|
|
: DEFAULT_DIFFS_TOOL_DEFAULTS.fileQuality;
|
|
}
|
|
|
|
function normalizeFileScale(fileScale: number | undefined, fallback: number): number {
|
|
if (fileScale === undefined || !Number.isFinite(fileScale)) {
|
|
return fallback;
|
|
}
|
|
const rounded = Math.round(fileScale * 100) / 100;
|
|
return Math.min(Math.max(rounded, 1), 4);
|
|
}
|
|
|
|
function normalizeFileMaxWidth(fileMaxWidth: number | undefined, fallback: number): number {
|
|
if (fileMaxWidth === undefined || !Number.isFinite(fileMaxWidth)) {
|
|
return fallback;
|
|
}
|
|
const rounded = Math.round(fileMaxWidth);
|
|
return Math.min(Math.max(rounded, 640), 2400);
|
|
}
|
|
|
|
function normalizeMode(mode?: DiffMode): DiffMode {
|
|
return mode && DIFF_MODES.includes(mode) ? mode : DEFAULT_DIFFS_TOOL_DEFAULTS.mode;
|
|
}
|
|
|
|
export function resolveDiffImageRenderOptions(params: {
|
|
defaults: DiffFileDefaults;
|
|
fileFormat?: DiffOutputFormat;
|
|
format?: DiffOutputFormat;
|
|
fileQuality?: DiffImageQualityPreset;
|
|
fileScale?: number;
|
|
fileMaxWidth?: number;
|
|
imageFormat?: DiffOutputFormat;
|
|
imageQuality?: DiffImageQualityPreset;
|
|
imageScale?: number;
|
|
imageMaxWidth?: number;
|
|
}): {
|
|
format: DiffOutputFormat;
|
|
qualityPreset: DiffImageQualityPreset;
|
|
scale: number;
|
|
maxWidth: number;
|
|
maxPixels: number;
|
|
} {
|
|
const format = normalizeFileFormat(
|
|
params.fileFormat ?? params.imageFormat ?? params.format ?? params.defaults.fileFormat,
|
|
);
|
|
const qualityOverrideProvided =
|
|
params.fileQuality !== undefined || params.imageQuality !== undefined;
|
|
const qualityPreset = normalizeFileQuality(
|
|
params.fileQuality ?? params.imageQuality ?? params.defaults.fileQuality,
|
|
);
|
|
const profile = DEFAULT_IMAGE_QUALITY_PROFILES[qualityPreset];
|
|
|
|
const scale = normalizeFileScale(
|
|
params.fileScale ?? params.imageScale,
|
|
qualityOverrideProvided ? profile.scale : params.defaults.fileScale,
|
|
);
|
|
const maxWidth = normalizeFileMaxWidth(
|
|
params.fileMaxWidth ?? params.imageMaxWidth,
|
|
qualityOverrideProvided ? profile.maxWidth : params.defaults.fileMaxWidth,
|
|
);
|
|
|
|
return {
|
|
format,
|
|
qualityPreset,
|
|
scale,
|
|
maxWidth,
|
|
maxPixels: profile.maxPixels,
|
|
};
|
|
}
|