feat(panorama): enhance 3D panorama node with AI generation support and unified interaction toggle

This commit is contained in:
HouYunFei
2026-07-16 13:21:25 +08:00
parent 2d2c7e3d5e
commit 3f179a607a
12 changed files with 547 additions and 120 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "canvas-plugin-html",
"version": "1.1.0",
"version": "1.2.0",
"private": true,
"type": "module",
"description": "Infinite Canvas HTML 节点插件",
+10 -22
View File
@@ -62,7 +62,6 @@ function HtmlEditor({ ctx, value }: { ctx: CanvasNodeContentProps["ctx"]; value:
function HtmlContent({ ctx }: CanvasNodeContentProps) {
const value = ctx.node.metadata?.content || "";
const editing = Boolean(ctx.node.metadata?.editing);
const interactive = Boolean(ctx.node.metadata?.interactive);
const upstreamText = useMemo(
() =>
ctx
@@ -87,15 +86,15 @@ function HtmlContent({ ctx }: CanvasNodeContentProps) {
);
}
// 预览态:iframe 仅在「节点已选中 + 开启交互」时接收鼠标;否则穿透,保证首次点击能选中节点、弹出工具条。
const liveIframe = interactive && ctx.isSelected;
// 预览态:iframe 的鼠标交互由宿主「交互 ⇄ 移动」开关统一控制(见 interactionToggle),
// 这里无需再手动做 pointer-events 穿透。data-canvas-no-zoom 保证交互时滚动作用于页面而非缩放画布。
return (
<div data-canvas-no-zoom={liveIframe || undefined} style={{ position: "relative", height: "100%", width: "100%" }}>
<div data-canvas-no-zoom style={{ position: "relative", height: "100%", width: "100%" }}>
<iframe
title="html-preview"
sandbox="allow-scripts allow-forms"
srcDoc={html}
style={{ height: "100%", width: "100%", border: 0, borderRadius: 16, background: "#fff", display: "block", pointerEvents: liveIframe ? "auto" : "none" }}
style={{ height: "100%", width: "100%", border: 0, borderRadius: 16, background: "#fff", display: "block" }}
/>
</div>
);
@@ -104,7 +103,7 @@ function HtmlContent({ ctx }: CanvasNodeContentProps) {
export default definePlugin({
id: "html",
name: "HTML 节点",
version: "1.1.0",
version: "1.2.0",
description: "沙箱 iframe 渲染 HTML,支持 {{input}} 注入上游文本",
nodes: [
{
@@ -116,13 +115,14 @@ export default definePlugin({
defaultMetadata: { content: "" },
minimapColor: "#ec4899",
hidePanel: true, // 纯展示型节点:点击/新建不弹出下方生图面板
// 宿主统一提供「交互 ⇄ 移动」开关;编辑态强制可交互(编辑器始终可操作)并隐藏该开关
interactionToggle: true,
forceInteractive: (node) => Boolean(node.metadata?.editing),
Content: HtmlContent,
// 所有开关都在节点外的悬浮工具条,不占用节点内部空间
// 仅保留「编辑/预览」开关;交互/移动 由宿主自动注入
toolbar: (ctx) => {
const editing = Boolean(ctx.node.metadata?.editing);
const interactive = Boolean(ctx.node.metadata?.interactive);
const hasContent = Boolean(ctx.node.metadata?.content);
const items = [
return [
{
id: "html-toggle-edit",
title: editing ? "预览渲染结果" : "编辑 HTML 源码",
@@ -132,18 +132,6 @@ export default definePlugin({
onClick: () => ctx.updateMetadata({ editing: !editing }),
},
];
// 预览且有内容时,才提供「交互」开关:开=可点击/滚动页面,关=整块可拖动
if (!editing && hasContent) {
items.push({
id: "html-toggle-interactive",
title: interactive ? "锁定预览(整块可拖动)" : "允许与页面交互(点击/滚动)",
label: interactive ? "锁定" : "交互",
icon: interactive ? "🔒" : "👆",
active: interactive,
onClick: () => ctx.updateMetadata({ interactive: !interactive }),
});
}
return items;
},
},
],