03a4410272
主要工作是共享同一套“画布内核与通用 UI 源码”,网站和 Tauri 只分别实现宿主适配层。 --------- Co-authored-by: 段舒康 <kdletters@qq.com> Co-authored-by: kdletters <kdletters@qq.com> Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/136 Co-authored-by: menghao <mh18530625731@163.com> Co-committed-by: menghao <mh18530625731@163.com>
217 lines
5.4 KiB
TypeScript
217 lines
5.4 KiB
TypeScript
import type { ProjectResource } from './resourceProjectionModel';
|
|
|
|
export type LocalProjectResourceEditKind =
|
|
| 'image-reference'
|
|
| 'svg'
|
|
| 'video'
|
|
| 'sound-effect'
|
|
| 'background-music'
|
|
| 'text'
|
|
| 'agent-result'
|
|
| 'version';
|
|
|
|
export type ProjectResourceEditCapability =
|
|
| {
|
|
route: 'image-canvas';
|
|
sourceAssetId: string;
|
|
normalizeSource: false;
|
|
mediaType: 'image/png' | 'image/jpeg' | 'image/webp';
|
|
}
|
|
| {
|
|
route: 'image-canvas';
|
|
sourceAssetId: null;
|
|
normalizeSource: true;
|
|
mediaType: 'image/png' | 'image/jpeg' | 'image/webp';
|
|
}
|
|
| {
|
|
route: 'derive';
|
|
editKind: LocalProjectResourceEditKind;
|
|
sourceMediaType: string;
|
|
semanticNotice: string | null;
|
|
};
|
|
|
|
const extensionMediaTypes: Record<string, string> = {
|
|
png: 'image/png',
|
|
jpg: 'image/jpeg',
|
|
jpeg: 'image/jpeg',
|
|
webp: 'image/webp',
|
|
gif: 'image/gif',
|
|
svg: 'image/svg+xml',
|
|
avif: 'image/avif',
|
|
bmp: 'image/bmp',
|
|
mp4: 'video/mp4',
|
|
webm: 'video/webm',
|
|
mov: 'video/quicktime',
|
|
mp3: 'audio/mpeg',
|
|
wav: 'audio/wav',
|
|
ogg: 'audio/ogg',
|
|
m4a: 'audio/mp4',
|
|
aac: 'audio/aac',
|
|
flac: 'audio/flac',
|
|
opus: 'audio/opus',
|
|
md: 'text/markdown',
|
|
markdown: 'text/markdown',
|
|
mdx: 'application/mdx',
|
|
txt: 'text/plain',
|
|
json: 'application/json',
|
|
yaml: 'application/yaml',
|
|
yml: 'application/yaml',
|
|
toml: 'application/toml',
|
|
html: 'text/html',
|
|
htm: 'text/html',
|
|
css: 'text/css',
|
|
scss: 'text/css',
|
|
less: 'text/css',
|
|
js: 'text/javascript',
|
|
jsx: 'text/javascript',
|
|
mjs: 'text/javascript',
|
|
cjs: 'text/javascript',
|
|
ts: 'text/typescript',
|
|
tsx: 'text/typescript',
|
|
rs: 'text/x-rust',
|
|
py: 'text/x-python',
|
|
go: 'text/x-go',
|
|
java: 'text/plain',
|
|
kt: 'text/plain',
|
|
kts: 'text/plain',
|
|
c: 'text/plain',
|
|
cc: 'text/plain',
|
|
cpp: 'text/plain',
|
|
h: 'text/plain',
|
|
hpp: 'text/plain',
|
|
cs: 'text/plain',
|
|
swift: 'text/plain',
|
|
php: 'text/plain',
|
|
rb: 'text/plain',
|
|
lua: 'text/plain',
|
|
sh: 'text/plain',
|
|
bash: 'text/plain',
|
|
zsh: 'text/plain',
|
|
sql: 'text/plain',
|
|
graphql: 'text/plain',
|
|
gql: 'text/plain',
|
|
xml: 'text/plain',
|
|
csv: 'text/csv',
|
|
ini: 'text/plain',
|
|
conf: 'text/plain',
|
|
vue: 'text/plain',
|
|
svelte: 'text/plain',
|
|
};
|
|
|
|
function pathExtension(path: string) {
|
|
return path.split(/[?#]/u, 1)[0]?.split('.').pop()?.toLowerCase() ?? '';
|
|
}
|
|
|
|
export function canonicalProjectedResourceMediaType(resource: ProjectResource) {
|
|
const declared = resource.mediaType.trim().toLowerCase();
|
|
if (/^[a-z0-9.+-]+\/[a-z0-9.+-]+$/u.test(declared)) {
|
|
return declared;
|
|
}
|
|
return extensionMediaTypes[pathExtension(resource.path)] ?? declared;
|
|
}
|
|
|
|
function isBackgroundMusic(resource: ProjectResource) {
|
|
const semanticSource = `${resource.subtype} ${resource.path}`.toLowerCase();
|
|
return ['background', 'bgm', 'music', 'theme'].some((marker) =>
|
|
semanticSource.includes(marker),
|
|
);
|
|
}
|
|
|
|
export function resolveProjectResourceEditCapability(
|
|
resource: ProjectResource,
|
|
): ProjectResourceEditCapability {
|
|
if (resource.version) {
|
|
return {
|
|
route: 'derive',
|
|
editKind: 'version',
|
|
sourceMediaType: 'application/vnd.genarrative.project-version+json',
|
|
semanticNotice: null,
|
|
};
|
|
}
|
|
if (resource.subtype === 'agent-result') {
|
|
return {
|
|
route: 'derive',
|
|
editKind: 'agent-result',
|
|
sourceMediaType: 'text/markdown',
|
|
semanticNotice: null,
|
|
};
|
|
}
|
|
|
|
const mediaType = canonicalProjectedResourceMediaType(resource);
|
|
if (['image/png', 'image/jpeg', 'image/webp'].includes(mediaType)) {
|
|
return resource.manifestAssetId
|
|
? {
|
|
route: 'image-canvas',
|
|
sourceAssetId: resource.manifestAssetId,
|
|
normalizeSource: false,
|
|
mediaType: mediaType as 'image/png' | 'image/jpeg' | 'image/webp',
|
|
}
|
|
: {
|
|
route: 'image-canvas',
|
|
sourceAssetId: null,
|
|
normalizeSource: true,
|
|
mediaType: mediaType as 'image/png' | 'image/jpeg' | 'image/webp',
|
|
};
|
|
}
|
|
if (mediaType === 'image/svg+xml') {
|
|
return {
|
|
route: 'derive',
|
|
editKind: 'svg',
|
|
sourceMediaType: mediaType,
|
|
semanticNotice: null,
|
|
};
|
|
}
|
|
if (mediaType.startsWith('image/')) {
|
|
return {
|
|
route: 'derive',
|
|
editKind: 'image-reference',
|
|
sourceMediaType: mediaType,
|
|
semanticNotice: null,
|
|
};
|
|
}
|
|
if (mediaType.startsWith('video/')) {
|
|
return {
|
|
route: 'derive',
|
|
editKind: 'video',
|
|
sourceMediaType: mediaType,
|
|
semanticNotice: null,
|
|
};
|
|
}
|
|
if (resource.category === 'audio' || mediaType.startsWith('audio/')) {
|
|
return {
|
|
route: 'derive',
|
|
editKind: isBackgroundMusic(resource)
|
|
? 'background-music'
|
|
: 'sound-effect',
|
|
sourceMediaType: mediaType,
|
|
semanticNotice: '音频将基于原资源语义派生重制,不会修改原音频波形。',
|
|
};
|
|
}
|
|
return {
|
|
route: 'derive',
|
|
editKind: 'text',
|
|
sourceMediaType: mediaType,
|
|
semanticNotice: null,
|
|
};
|
|
}
|
|
|
|
export function defaultDerivedResourceName(resource: ProjectResource) {
|
|
const baseName = resource.label.replace(/\.[^.]+$/u, '').trim();
|
|
return `${baseName || '资源'}-编辑版`;
|
|
}
|
|
|
|
export function resourceEditPromptMaxLength(
|
|
editKind: LocalProjectResourceEditKind,
|
|
) {
|
|
switch (editKind) {
|
|
case 'background-music':
|
|
return 140;
|
|
case 'sound-effect':
|
|
return 1_900;
|
|
case 'video':
|
|
return 4_000;
|
|
default:
|
|
return 32_000;
|
|
}
|
|
}
|