feat(clipping): implement hide/recover toggle for all section dialogs

Update all three section dialogs to support hide/show toggle:

SectionAxisDialogManager:
- onHideToggle now calls hideSection()/recoverSection()

SectionBoxDialogManager:
- onHideToggle now calls hideSection()/recoverSection()

SectionPlanePanel:
- Add isHidden state tracking
- Change onHide to onHideToggle(isHidden)
- Add setHiddenState/getHiddenState methods
- Update button to toggle active state

SectionPlaneDialogManager:
- Switch to onHideToggle callback
- Call hideSection()/recoverSection() based on toggle state

Behavior: Click hide button to hide section, click again to recover.
This commit is contained in:
yuding
2026-02-02 16:36:17 +08:00
parent 41abd9ed67
commit 4a09d52283
44 changed files with 17877 additions and 10807 deletions

View File

@@ -0,0 +1,199 @@
/**
* AI 聊天管理器
* 负责管理 AI 聊天抽屉的显示、隐藏和消息交互
*/
import { AiChat } from '../components/ai-chat';
import type { Message, QuestionOption, StepStatus } from '../components/ai-chat/types';
import { ManagerRegistry } from '../core/manager-registry';
/**
* AI 聊天管理器
* 管理 AI 聊天组件的生命周期和消息交互
*/
export class AiChatManager {
/** AI 聊天组件实例 */
private aiChat: AiChat | null = null;
/** Manager 注册表 */
private registry: ManagerRegistry;
/** 是否已初始化 */
private initialized = false;
constructor() {
this.registry = ManagerRegistry.getInstance();
}
/**
* 初始化 AI 聊天组件
* 创建 AiChat 实例并注册事件
*/
public init(): void {
if (this.initialized) return;
const wrapper = this.registry.wrapper;
if (!wrapper) {
console.warn('[AiChatManager] wrapper 不存在,无法初始化');
return;
}
this.aiChat = new AiChat({
container: wrapper,
width: 440,
title: 'aiChat.title',
placeholder: 'aiChat.placeholder',
quickPrompts: [
{ id: 'summarize', label: 'aiChat.quickPrompt.summarize' },
{ id: 'explain', label: 'aiChat.quickPrompt.explain' },
{ id: 'generate', label: 'aiChat.quickPrompt.generate' }
],
onSend: (message) => {
console.log('[AiChatManager] 用户发送消息:', message);
this.aiChat?.addUserMessage(message);
this.registry.emit('aiChat:message-sent', { message });
},
onQuestionSubmit: (questionId, optionId, customAnswer) => {
console.log('[AiChatManager] 用户回答问题:', { questionId, optionId, customAnswer });
this.registry.emit('aiChat:question-answered', { questionId, optionId, customAnswer });
},
onNewChat: () => {
console.log('[AiChatManager] 新建对话');
this.registry.emit('aiChat:new-chat', {});
},
onHistory: () => {
console.log('[AiChatManager] 打开历史');
this.registry.emit('aiChat:history-opened', {});
},
onSettings: () => {
console.log('[AiChatManager] 打开设置');
this.registry.emit('aiChat:settings-opened', {});
},
onClose: () => {
console.log('[AiChatManager] 关闭 AI 聊天');
this.registry.emit('aiChat:closed', {});
this.registry.toolbar?.setBtnActive('aiChat', false);
}
});
this.initialized = true;
}
/**
* 显示 AI 聊天抽屉
*/
public show(): void {
if (!this.aiChat) {
this.init();
}
this.aiChat?.show();
this.registry.emit('aiChat:opened', {});
this.registry.toolbar?.setBtnActive('aiChat', true);
}
/**
* 隐藏 AI 聊天抽屉
*/
public hide(): void {
this.aiChat?.hide();
this.registry.emit('aiChat:closed', {});
this.registry.toolbar?.setBtnActive('aiChat', false);
}
/**
* 切换 AI 聊天抽屉显示状态
*/
public toggle(): void {
if (this.aiChat?.isVisible()) {
this.hide();
} else {
this.show();
}
}
/**
* 检查 AI 聊天抽屉是否可见
*/
public isVisible(): boolean {
return this.aiChat?.isVisible() ?? false;
}
/**
* 添加用户消息
* @param content 消息内容
* @returns 消息 ID
*/
public addUserMessage(content: string): string {
return this.aiChat?.addUserMessage(content) ?? '';
}
/**
* 添加 AI 消息
* @param content 消息内容
* @returns 消息 ID
*/
public addAiMessage(content: string): string {
return this.aiChat?.addAiMessage(content) ?? '';
}
/**
* 添加步骤消息
* @param status 步骤状态
* @param content 步骤描述
* @returns 消息 ID
*/
public addStepMessage(status: StepStatus, content: string): string {
return this.aiChat?.addStepMessage(status, content) ?? '';
}
/**
* 添加思考中消息
* @returns 消息 ID
*/
public addThinkingMessage(): string {
return this.aiChat?.addThinkingMessage() ?? '';
}
/**
* 添加问答卡片消息
* @param title 问题标题
* @param question 问题内容
* @param options 选项列表
* @returns 消息 ID
*/
public addQuestionMessage(title: string, question: string, options: QuestionOption[]): string {
return this.aiChat?.addQuestionMessage(title, question, options) ?? '';
}
/**
* 更新消息
* @param id 消息 ID
* @param updates 更新内容
*/
public updateMessage(id: string, updates: Partial<Message>): void {
this.aiChat?.updateMessage(id, updates);
}
/**
* 删除消息
* @param id 消息 ID
*/
public removeMessage(id: string): void {
this.aiChat?.removeMessage(id);
}
/**
* 清空所有消息
*/
public clearMessages(): void {
this.aiChat?.clearMessages();
}
/**
* 销毁 AI 聊天管理器
*/
public destroy(): void {
if (this.aiChat) {
this.aiChat.destroy();
this.aiChat = null;
}
this.initialized = false;
}
}

View File

@@ -152,7 +152,7 @@ export class ConstructTreeManagerBtn extends BaseManager {
this.dialog = this.registry.dialog!.create({
title: 'constructTree.title',
minWidth: 320,
height: 420,
height: 600,
content: tabMount,
position: { x: 20, y: 20 },
resizable: false,

View File

@@ -57,8 +57,12 @@ export class SectionAxisDialogManager extends BaseDialogManager {
defaultAxis: 'x',
defaultHidden: false,
onHideToggle: (isHidden) => {
// 隐藏功能:第三方引擎无 API仅输出日志
console.log('[SectionAxisDialogManager] 隐藏切换(暂不支持):', isHidden);
console.log('[SectionAxisDialogManager] 隐藏切换:', isHidden);
if (isHidden) {
this.registry.engine3d?.hideSection();
} else {
this.registry.engine3d?.recoverSection();
}
},
onReverse: () => {
// 反向功能:第三方引擎无 API仅输出日志

View File

@@ -57,8 +57,12 @@ export class SectionBoxDialogManager extends BaseDialogManager {
defaultHidden: false,
defaultReversed: false,
onHideToggle: (isHidden) => {
// 底层暂不支持隐藏功能
console.log('[SectionBoxDialogManager] 隐藏切换(底层暂不支持):', isHidden);
console.log('[SectionBoxDialogManager] 隐藏切换:', isHidden);
if (isHidden) {
this.registry.engine3d?.hideSection();
} else {
this.registry.engine3d?.recoverSection();
}
},
onReverseToggle: (isReversed) => {
// 底层暂不支持反向功能

View File

@@ -47,9 +47,14 @@ export class SectionPlaneDialogManager extends BaseDialogManager {
/** 创建对话框内容 */
protected createContent(): HTMLElement {
this.panel = new SectionPlanePanel({
onHide: () => {
console.log('[SectionPlaneDialogManager] 隐藏剖切面');
this.registry.engine3d?.hideSection();
defaultHidden: false,
onHideToggle: (isHidden) => {
console.log('[SectionPlaneDialogManager] 隐藏切换:', isHidden);
if (isHidden) {
this.registry.engine3d?.hideSection();
} else {
this.registry.engine3d?.recoverSection();
}
},
onReverse: () => {
console.log('[SectionPlaneDialogManager] 反向 (not supported in new API)');