恢复最终预览框线开关

在最终预览恢复 Show frame 控件

开关启用后显示节点框线、标签和调整控件

补充最终预览框线回归测试
This commit is contained in:
2026-08-18 21:13:53 +08:00
parent 33258657c3
commit 43e8a6f904
3 changed files with 47 additions and 5 deletions
@@ -45,6 +45,7 @@ export function PreviewWorkspace({
const [spaceHeld, setSpaceHeld] = useState(false);
const [renderMode, setRenderMode] =
useState<UiEditorRenderMode>('editor-overlay');
const [showFrame, setShowFrame] = useState(false);
const tree = controller.treeForActiveImage ?? null;
const logicalSize = useMemo(() => {
if (!activeImage) return null;
@@ -293,6 +294,16 @@ export function PreviewWorkspace({
</button>
</div>
{renderMode === 'final-preview' ? (
<button
type="button"
className={`rounded-md border px-2 py-1 text-[10px] ${showFrame ? 'border-orange-500 bg-orange-50 text-orange-700' : 'border-(--platform-subpanel-border) text-(--platform-text-soft)'}`}
aria-pressed={showFrame}
onClick={() => setShowFrame((current) => !current)}
>
Show frame
</button>
) : null}
<span className="text-[10px] text-(--platform-text-soft)">
{Object.keys(controller.images).length} ·{' '}
{Object.keys(controller.sprites).length}
@@ -352,6 +363,7 @@ export function PreviewWorkspace({
<UiTreeRenderer
tree={tree}
renderMode={renderMode}
showFrame={showFrame}
hiddenNodeIds={controller.hiddenNodeIds}
selectedNodeId={controller.selectedNodeId}
resources={{
@@ -20,6 +20,7 @@ type NodePointerDown = (
type UiTreeRendererProps = {
tree: UITree | null;
renderMode: UiEditorRenderMode;
showFrame: boolean;
hiddenNodeIds: ReadonlySet<NodeId>;
selectedNodeId: NodeId | null;
resources: PreviewComponentResources;
@@ -57,6 +58,7 @@ function RenderNode({
node,
isRoot,
renderMode,
showFrame,
hiddenNodeIds,
selectedNodeId,
resources,
@@ -80,6 +82,7 @@ function RenderNode({
}
const isEditorOverlay = renderMode === 'editor-overlay';
const isFrameVisible = isEditorOverlay || showFrame;
const exclusiveVisibleChildId =
node.children_display_mode === 'Exclusive'
? (node.children.find((child) => !hiddenNodeIds.has(child.id))?.id ??
@@ -91,7 +94,7 @@ function RenderNode({
className={`absolute min-h-0 min-w-0 ${isRoot ? 'cursor-default' : 'cursor-move'}`}
style={{
...geometry,
...(isEditorOverlay
...(isFrameVisible
? {
outline:
selectedNodeId === node.id
@@ -113,9 +116,9 @@ function RenderNode({
onPointerMove={onNodePointerMove}
onPointerUp={onNodePointerUp}
onPointerCancel={onNodePointerUp}
title={isEditorOverlay ? node.metadata.name || undefined : undefined}
title={isFrameVisible ? node.metadata.name || undefined : undefined}
>
{isEditorOverlay && node.metadata.name ? (
{isFrameVisible && node.metadata.name ? (
<span className="pointer-events-none absolute left-0 top-0 max-w-full truncate bg-blue-600/80 px-1 text-[9px] leading-4 text-white">
{node.metadata.name}
</span>
@@ -134,6 +137,7 @@ function RenderNode({
key={child.id}
node={child}
renderMode={renderMode}
showFrame={showFrame}
hiddenNodeIds={hiddenNodeIds}
selectedNodeId={selectedNodeId}
resources={resources}
@@ -148,7 +152,7 @@ function RenderNode({
/>
),
)}
{isEditorOverlay && !isRoot && selectedNodeId === node.id
{isFrameVisible && !isRoot && selectedNodeId === node.id
? RESIZE_HANDLES.map((handle) => (
<div
key={handle.id}
@@ -51,13 +51,15 @@ const resources = {
function renderTree(
renderMode: 'editor-overlay' | 'final-preview',
hiddenNodeIds: ReadonlySet<string>,
options: { showFrame?: boolean; selectedNodeId?: string | null } = {},
) {
return render(
<UiTreeRenderer
tree={tree}
renderMode={renderMode}
showFrame={options.showFrame ?? false}
hiddenNodeIds={hiddenNodeIds}
selectedNodeId={null}
selectedNodeId={options.selectedNodeId ?? null}
resources={resources}
onSelectNode={vi.fn()}
onNodePointerDown={vi.fn()}
@@ -72,6 +74,29 @@ function renderTree(
}
describe('UI tree preview visibility', () => {
it('shows final-preview node frames and resize handles only when requested', () => {
const withoutFrames = renderTree('final-preview', new Set(), {
selectedNodeId: 'child',
});
const plainChild = withoutFrames.container.querySelector(
'[data-node-id="child"]',
) as HTMLDivElement;
expect(plainChild.style.outline).toBe('');
expect(screen.queryByLabelText('nw 调整控制点')).toBeNull();
withoutFrames.unmount();
const withFrames = renderTree('final-preview', new Set(), {
showFrame: true,
selectedNodeId: 'child',
});
const framedChild = withFrames.container.querySelector(
'[data-node-id="child"]',
) as HTMLDivElement;
expect(framedChild.style.outline).toBe('2px solid rgb(249 115 22)');
expect(screen.getByText('child')).toBeTruthy();
expect(screen.getAllByLabelText(/调整控制点$/)).toHaveLength(8);
});
it('hides a node subtree in editor overlay and final preview', () => {
const editorOverlay = renderTree('editor-overlay', new Set(['child']));
expect(
@@ -98,6 +123,7 @@ describe('UI tree preview visibility', () => {
<UiTreeRenderer
tree={exclusiveTree}
renderMode="final-preview"
showFrame={false}
hiddenNodeIds={hiddenNodeIds}
selectedNodeId={null}
resources={resources}