Files
Genarrative/apps/ai-game-creator-shell/src/view/project-development/useWorkbenchToolbarLayout.ts
T
suzmii b8849b8c01 优化 AGC 工作台顶栏布局:一行到底、版本入口钉右分级、播放并入运行页签
- 顶栏去掉两侧 flex-wrap、工具条改 overflow: hidden,空间不足按 data-layout 三档降级而不是换行
- 新增 workbenchToolbarModel(档位顺序与 1px 判据)与 useWorkbenchToolbarLayout(ResizeObserver 管宽度、MutationObserver 管内容,实测内容宽度写档位)
- 版本入口 margin-left: auto 钉在动作区最右,标识改「版本 N(原因 · 时间)」,窄档只留「版本 N」(序号复用资源画布版本卡编号口径)
- 新增 WorkbenchMoreActionsMenu:「打开项目目录 / 资源面板 / 整理画布」在 collapsed-actions 档收进「更多」下拉
- 删除独立「播放」按钮,运行页签前加 ▶ 图标,点页签即切视图并载入游戏资源
- 运行画面右下角新增「刷新运行画面」,LocalGamePreviewFrame 新增 reloadNonce 换 iframe 元素身份重新载入页面
- 用例:新增 workbenchToolbarLayout / runPreviewRefresh;改写 appSurface 顶栏与播放/刷新断言、版本标识断言与顶栏样式断言
2026-10-04 14:04:02 +08:00

88 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { type RefObject, useLayoutEffect } from 'react';
import {
resolveWorkbenchToolbarLayout,
type WorkbenchToolbarLayout,
} from './workbenchToolbarModel';
/**
* 顶栏当前档位挂在 DOM 属性上(`data-layout`),并**只由这个 hook 写**。
*
* 不让 React 渲染这个属性:档位是量出来的,不是 state 推出来的。这条链路上每次量宽都要先把
* 候选档位真的写到 DOM(否则量到的还是当前档位的宽度),React 受控属性会被这类「量一次写一次」
* 的中间值污染,且每次窗口拖动都要触发一轮工作台整树重渲染——顶栏只有这一处样式差异,不值得。
* React 不声明它就不会覆盖它,见 `styles.css` 里的 `[data-layout=...]` 选择器。
*/
export const WORKBENCH_TOOLBAR_LAYOUT_ATTRIBUTE = 'data-layout';
/**
* 把候选档位写到 DOM 上量一次内容宽度。
*
* 顶栏是 `flex-wrap: nowrap` + `overflow: hidden`:放不下时溢出的那部分仍计入 `scrollWidth`,
* 所以「量得的内容宽度 > clientWidth」就是这一档放不下。
*/
function measureToolbarContentWidth(
element: HTMLElement,
candidate: WorkbenchToolbarLayout,
): number {
element.dataset.layout = candidate;
return element.scrollWidth;
}
/** 重新爬一次档位阶梯并落定;返回落定后的档位(测试与排障用)。 */
export function applyWorkbenchToolbarLayout(
element: HTMLElement,
): WorkbenchToolbarLayout {
const layout = resolveWorkbenchToolbarLayout(
element.clientWidth,
(candidate) => measureToolbarContentWidth(element, candidate),
);
element.dataset.layout = layout;
return layout;
}
/**
* 让顶栏按实际可用宽度自己选档位(`workbenchToolbarModel` 给了顺序与判据)。
*
* 两个信号都要:
* - `ResizeObserver`:可用宽度变了(窗口拖动、对话列收放);
* - `MutationObserver`:宽度不变但**内容**变了(切运行页、出现「恢复草稿」、面板开合)。
* 这种变化不改顶栏宽度,只看尺寸会停在旧档位上——动作变少了却还收在「更多」里。
*
* 观测本身不引起递归:写的是被观测节点自己的属性,而属性不在观测范围内(`childList` /
* `characterData` / `subtree` 都不含根节点自身属性),且档位变化不改顶栏尺寸。
* 没有 `ResizeObserver` 的环境(jsdom)只按内容变化重算,量到 0 宽就停在默认的 `full`——
* 与关闭脚本前的默认形态一致。
*/
export function useWorkbenchToolbarLayout(
toolbarRef: RefObject<HTMLElement | null>,
): void {
useLayoutEffect(() => {
const element = toolbarRef.current;
if (!element) {
return undefined;
}
applyWorkbenchToolbarLayout(element);
const resizeObserver =
typeof ResizeObserver === 'function'
? new ResizeObserver(() => applyWorkbenchToolbarLayout(element))
: null;
resizeObserver?.observe(element);
const mutationObserver =
typeof MutationObserver === 'function'
? new MutationObserver(() => applyWorkbenchToolbarLayout(element))
: null;
mutationObserver?.observe(element, {
childList: true,
characterData: true,
subtree: true,
});
return () => {
resizeObserver?.disconnect();
mutationObserver?.disconnect();
};
}, [toolbarRef]);
}