Files
Genarrative/src/components/image-editor/ImageCanvasEditorShellView.tsx
T
k88936 61fe4c48af
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 16s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 16s
Project CI / Backend tests (pull_request) Failing after 16s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 16s
Project CI / Native shell tests (pull_request) Failing after 16s
Project CI / Frontend tests (pull_request) Failing after 7s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
图片画布接入 3D 资源:新增 model3d 类别、3D 预览与 3D 生成入口
- 后端资源投影按 asset_kind 分叉,新增 project_editor_client_media_src 与 EDITOR_MODEL3D_ASSET_KIND:model3d 只投影预览图,模型本体只留 objectKey,预览缺失才回落历史口径
- tripo3d 存储新增按字节魔数识别模型 content_type 与扩展名映射,不采信 provider 声明与下载响应头
- editor_project_storage 把 model3d 加入 EDITOR_CANVAS_ASSET_KINDS 白名单
- 前端 CanvasAssetKind 与 image-canvas-core 类型新增 model3d:画布卡片渲染预览图或 3D 占位,类型角标新增「3D模型」,覆盖资格只在它与空类别之间成立
- 新增 model3d-preview 子目录(Model3dViewerModal / Model3dViewerModel),Tailwind 内联、不新增 CSS,失败原因分超限 / 格式不可识别 / 无 WebGL2 三类展示
- 选中工具条新增只读 preview-model3d 能力位,仅 objectKey 为空时置灰;打开预览同时选中图层并暂停画布快捷键与舞台交互
- 查看器包源对象 format 改为可选,新增「声明 Content-Type → 字节魔数 → 地址扩展名」三级格式判定并导出支持格式清单
- 导出工作流新增 3d_models/ 目录,模型下载按真实响应头 MIME 定扩展名、对象键扩展名兜底
- 新增 3D 生成入口:底部工具栏浮动子选项与文生 / 图生 3D 面板,落 projectResource + canvasCompletion,复用占位框与任务侧栏
- 3D 定价只读实时定价查询,段缺失即入口不可提交;幂等键由前端铸造,用户重试必须换键
- 新增 ADR 0003 / 0004 与实施计划、里程碑文档;更新 Tripo 技术方案、后端架构、CONTEXT 术语、decision-log 与 pitfalls
- .gitignore 忽略仓库根目录手工下载的 3D 样例模型
2026-09-22 10:10:00 +08:00

93 lines
2.8 KiB
TypeScript

import type { ChangeEventHandler, ComponentProps, RefObject } from 'react';
import { ImageCanvasMetadataModalView } from './ImageCanvasMetadataModalView';
import { ImageCanvasSidebarView } from './ImageCanvasSidebarView';
import { ImageCanvasStageView } from './ImageCanvasStageView';
import { ImageCanvasTopbarView } from './ImageCanvasTopbarView';
import { Model3dViewerModal } from './model3d-preview/Model3dViewerModal';
type AssetDragPreview = {
x: number;
y: number;
label: string;
};
type ImageCanvasEditorShellViewProps = {
editorRootRef: RefObject<HTMLElement | null>;
uploadInputRef: RefObject<HTMLInputElement | null>;
uploadAccept?: string;
onUploadInputChange: ChangeEventHandler<HTMLInputElement>;
assetDragPreview: AssetDragPreview | null;
sidebarProps: ComponentProps<typeof ImageCanvasSidebarView>;
topbarProps: ComponentProps<typeof ImageCanvasTopbarView>;
stageProps: ComponentProps<typeof ImageCanvasStageView>;
metadataProps: ComponentProps<typeof ImageCanvasMetadataModalView>;
model3dViewerProps: ComponentProps<typeof Model3dViewerModal>;
};
export function ImageCanvasEditorShellView({
editorRootRef,
uploadInputRef,
uploadAccept = 'image/*',
onUploadInputChange,
assetDragPreview,
sidebarProps,
topbarProps,
stageProps,
metadataProps,
model3dViewerProps,
}: ImageCanvasEditorShellViewProps) {
const uploadInputLabel =
uploadAccept.includes('video') || uploadAccept.includes('audio')
? '上传媒体文件'
: '上传图片文件';
return (
<section
ref={editorRootRef}
className="image-canvas-editor"
aria-label="图片画布编辑器"
onContextMenu={(event) => {
if (
event.target instanceof Element &&
event.target.closest('.image-canvas-editor__metadata-dialog')
) {
return;
}
event.preventDefault();
}}
>
<input
ref={uploadInputRef}
type="file"
accept={uploadAccept}
multiple
aria-label={uploadInputLabel}
hidden
onChange={onUploadInputChange}
/>
{assetDragPreview ? (
<div
className="image-canvas-editor__asset-drag-preview"
style={{
left: Number.isFinite(assetDragPreview.x) ? assetDragPreview.x : 0,
top: Number.isFinite(assetDragPreview.y) ? assetDragPreview.y : 0,
}}
aria-hidden="true"
>
{assetDragPreview.label}
</div>
) : null}
<ImageCanvasSidebarView {...sidebarProps} />
<div className="image-canvas-editor__main">
<ImageCanvasTopbarView {...topbarProps} />
<ImageCanvasStageView {...stageProps} />
</div>
<ImageCanvasMetadataModalView {...metadataProps} />
<Model3dViewerModal {...model3dViewerProps} />
</section>
);
}