Add floating canvas panels, theme settings, and SVG line-spacing analysis.
Canvas Studio now uses dockable floating panels, app settings/help navigation, and improved SVG export; the backend adds an SVG line-spacing analysis API with SciPy acceleration and new design templates.
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { BackendAsset, CanvasDocument, CanvasTemplate } from '../types';
|
||||
import { apiUrl, ensureOk } from './api';
|
||||
import { cloneDocument, normalizeDocument } from './canvasDocument';
|
||||
|
||||
const API_BASE = '';
|
||||
|
||||
export function templateId(template: CanvasTemplate) {
|
||||
return template.template_id || template.id || '';
|
||||
}
|
||||
@@ -24,8 +23,7 @@ export function templateCoverId(template: CanvasTemplate) {
|
||||
}
|
||||
|
||||
export async function listDesignTemplates(): Promise<CanvasTemplate[]> {
|
||||
const res = await fetch(`${API_BASE}/api/design-templates`);
|
||||
if (!res.ok) throw new Error(`读取模板库失败 (${res.status})`);
|
||||
const res = await ensureOk(await fetch(apiUrl('/api/design-templates')), '读取模板库失败');
|
||||
const items = (await res.json()) as CanvasTemplate[];
|
||||
return items.map(item => ({ ...item, document: normalizeDocument(item.document) }));
|
||||
}
|
||||
@@ -43,8 +41,7 @@ export async function createCanvasTemplate(input: {
|
||||
fd.append('document', JSON.stringify(normalizeDocument(input.document)));
|
||||
fd.append('reference_asset_ids', JSON.stringify(input.referenceAssetIds || []));
|
||||
fd.append('cover_asset_id', input.coverAssetId || input.referenceAssetIds?.[0] || '');
|
||||
const res = await fetch(`${API_BASE}/api/design-templates`, { method: 'POST', body: fd });
|
||||
if (!res.ok) throw new Error(`保存模板失败 (${res.status})`);
|
||||
const res = await ensureOk(await fetch(apiUrl('/api/design-templates'), { method: 'POST', body: fd }), '保存模板失败');
|
||||
const template = (await res.json()) as CanvasTemplate;
|
||||
return { ...template, document: normalizeDocument(template.document) };
|
||||
}
|
||||
@@ -65,21 +62,18 @@ export async function updateCanvasTemplate(
|
||||
if (partial.document) fd.append('document', JSON.stringify(normalizeDocument(partial.document)));
|
||||
if (partial.referenceAssetIds) fd.append('reference_asset_ids', JSON.stringify(partial.referenceAssetIds));
|
||||
if (partial.coverAssetId !== undefined) fd.append('cover_asset_id', partial.coverAssetId);
|
||||
const res = await fetch(`${API_BASE}/api/design-templates/${id}`, { method: 'PATCH', body: fd });
|
||||
if (!res.ok) throw new Error(`更新模板失败 (${res.status})`);
|
||||
const res = await ensureOk(await fetch(apiUrl(`/api/design-templates/${id}`), { method: 'PATCH', body: fd }), '更新模板失败');
|
||||
const template = (await res.json()) as CanvasTemplate;
|
||||
return { ...template, document: normalizeDocument(template.document) };
|
||||
}
|
||||
|
||||
export async function deleteCanvasTemplate(id: string) {
|
||||
const res = await fetch(`${API_BASE}/api/design-templates/${id}`, { method: 'DELETE' });
|
||||
if (!res.ok) throw new Error(`删除模板失败 (${res.status})`);
|
||||
await ensureOk(await fetch(apiUrl(`/api/design-templates/${id}`), { method: 'DELETE' }), '删除模板失败');
|
||||
}
|
||||
|
||||
export async function listAssets(type = ''): Promise<BackendAsset[]> {
|
||||
const query = type ? `?type=${encodeURIComponent(type)}` : '';
|
||||
const res = await fetch(`${API_BASE}/api/assets${query}`);
|
||||
if (!res.ok) throw new Error(`读取素材失败 (${res.status})`);
|
||||
const res = await ensureOk(await fetch(apiUrl(`/api/assets${query}`)), '读取素材失败');
|
||||
return (await res.json()) as BackendAsset[];
|
||||
}
|
||||
|
||||
@@ -88,8 +82,7 @@ export async function uploadAsset(file: File, type = 'reference'): Promise<Backe
|
||||
fd.append('file', file);
|
||||
fd.append('name', file.name.replace(/\.(svg|png|jpe?g)$/i, ''));
|
||||
fd.append('type', type);
|
||||
const res = await fetch(`${API_BASE}/api/assets`, { method: 'POST', body: fd });
|
||||
if (!res.ok) throw new Error(`上传参考图失败 (${res.status})`);
|
||||
const res = await ensureOk(await fetch(apiUrl('/api/assets'), { method: 'POST', body: fd }), '上传参考图失败');
|
||||
return (await res.json()) as BackendAsset;
|
||||
}
|
||||
|
||||
@@ -97,7 +90,7 @@ export function assetUrl(assetOrPath?: BackendAsset | string) {
|
||||
if (!assetOrPath) return '';
|
||||
const raw = typeof assetOrPath === 'string' ? assetOrPath : assetOrPath.file_url;
|
||||
if (!raw) return '';
|
||||
return raw.startsWith('http') ? raw : `${API_BASE}${raw}`;
|
||||
return apiUrl(raw);
|
||||
}
|
||||
|
||||
export function duplicateDocument(document: CanvasDocument): CanvasDocument {
|
||||
|
||||
Reference in New Issue
Block a user