docs/archives/126-submode-persistence/design.md
重大更新说明:
- ✅ Phase 1-3 全部完成:三种功能模式的子模式独立持久化
- ✅ 架构升级:基础/上下文/图像三种模式的子模式完全独立存储
- ✅ 导航栏统一:所有子模式选择器移至导航栏
- ✅ 测试验证:所有核心功能已通过实际测试
- 📅 完成日期:2025-10-22
| 阶段 | 功能模式 | 状态 | 完成日期 | 验证情况 |
|---|---|---|---|---|
| Phase 1 | 上下文模式 | ✅ 已完成 | 2025-10-22 | ✅ 全部通过 |
| Phase 2 | 基础模式 | ✅ 已完成 | 2025-10-22 | ✅ 全部通过 |
| Phase 3 | 图像模式 | ✅ 已完成 | 2025-10-22 | ✅ 全部通过 |
三种独立存储键 - 完全隔离的状态管理
BASIC_SUB_MODE: 基础模式子模式存储PRO_SUB_MODE: 上下文模式子模式存储IMAGE_SUB_MODE: 图像模式子模式存储三个独立Composables - 单例模式的状态管理器
useBasicSubMode: 管理基础模式的 system/user 选择useProSubMode: 管理上下文模式的 system/user 选择useImageSubMode: 管理图像模式的 text2image/image2image 选择统一的导航栏UI - 一致的用户体验
完整的持久化生命周期
定义: 应用的顶层模式选择,决定使用哪个工作区组件。
类型: 'basic' | 'pro' | 'image'
对应界面:
basic - 基础模式:简单的优化 → 测试流程pro - 上下文模式(高级模式):支持多轮对话、变量、工具image - 图像模式:图像提示词优化UI 表现: 导航栏左侧的功能模式选择器 [基础 | 上下文 | 图像]
持久化: ✅ 已实现(useFunctionMode.ts)
定义: 在特定功能模式下的二级模式选择,进一步细分工作区行为。
类型: 'system' | 'user'
TypeScript定义位置: packages/core/src/services/prompt/types.ts
/**
* 基础模式的子模式类型
* 用于持久化基础模式下的子模式选择
*/
export type BasicSubMode = "system" | "user"
对应界面: 基础模式使用同一个组件,但通过 optimization-mode prop 控制行为差异
UI 表现: 导航栏中的子模式选择器 [系统提示词优化 | 用户提示词优化](仅在基础模式显示)
存储键: UI_SETTINGS_KEYS.BASIC_SUB_MODE = 'app:settings:ui:basic-sub-mode'
Composable: useBasicSubMode.ts (单例模式,全局状态管理)
持久化: ✅ 已实现(2025-10-22)
默认值: 'system'
类型: 'system' | 'user'
TypeScript定义位置: packages/core/src/services/prompt/types.ts
/**
* 上下文模式的子模式类型
* 用于持久化上下文模式下的子模式选择
*/
export type ProSubMode = "system" | "user"
对应界面:
system - 系统提示词优化:ContextSystemWorkspace.vue
user - 用户提示词优化:ContextUserWorkspace.vue
UI 表现: 导航栏中的子模式选择器 [系统提示词优化 | 用户提示词优化](仅在上下文模式显示)
存储键: UI_SETTINGS_KEYS.PRO_SUB_MODE = 'app:settings:ui:pro-sub-mode'
Composable: useProSubMode.ts (单例模式,全局状态管理)
持久化: ✅ 已实现(2025-10-22)
默认值: 'system'
类型: 'text2image' | 'image2image'
TypeScript定义位置: packages/core/src/services/prompt/types.ts
/**
* 图像模式的子模式类型
* 用于持久化图像模式下的子模式选择
*/
export type ImageSubMode = "text2image" | "image2image"
对应界面:
text2image - 文生图:文本描述 → 图像提示词image2image - 图生图:图像 + 文本描述 → 图像提示词UI 表现: 导航栏中的子模式选择器 [文生图 | 图生图](仅在图像模式显示)
存储键: UI_SETTINGS_KEYS.IMAGE_SUB_MODE = 'app:settings:ui:image-sub-mode'
Composable: useImageSubMode.ts (单例模式,全局状态管理)
持久化: ✅ 已实现(2025-10-22)
默认值: 'text2image'
特殊说明:
ImageWorkspace.vue 内部移至导航栏ImageWorkspace.vue 通过监听 image-submode-changed 自定义事件接收导航栏的切换通知重要洞察(用户提出):
"基础模式也应该有自己的存储,这个也应该分开...因为这两个功能模式本质上控制的是不同的,只是当前他们的子模式碰巧都叫 系统/用户提示词优化而已。"
实现方式:
优势:
实现方式:
// 每个 composable 内部维护单例状态
let singleton: {
mode: Ref<SubModeType>
initialized: boolean
initializing: Promise<void> | null
} | null = null
export function useSubMode(services: Ref<AppServices | null>) {
if (!singleton) {
singleton = {
mode: ref<SubModeType>('default'),
initialized: false,
initializing: null
}
}
// ... 返回只读的 mode 和操作方法
}
优势:
实现方式:
const ensureInitialized = async () => {
if (singleton!.initialized) return
if (singleton!.initializing) {
await singleton!.initializing
return
}
singleton!.initializing = (async () => {
try {
const saved = await getPreference<SubModeType>(STORAGE_KEY, DEFAULT_VALUE)
singleton!.mode.value = validate(saved) ? saved : DEFAULT_VALUE
// 持久化默认值(如果未设置过)
if (!validate(saved)) {
await setPreference(STORAGE_KEY, DEFAULT_VALUE)
}
} catch (e) {
console.warn('[useSubMode] 初始化失败,使用默认值', e)
singleton!.mode.value = DEFAULT_VALUE
} finally {
singleton!.initialized = true
singleton!.initializing = null
}
})()
await singleton!.initializing
}
优势:
实现方式:
const setSubMode = async (mode: SubModeType) => {
await ensureInitialized()
singleton!.mode.value = mode
await setPreference(STORAGE_KEY, mode)
console.log(`[useSubMode] 子模式已切换并持久化: ${mode}`)
}
优势:
packages/
├── core/
│ └── src/
│ ├── constants/
│ │ └── storage-keys.ts # ✅ 新增三个存储键
│ └── services/
│ └── prompt/
│ └── types.ts # ✅ 新增三个子模式类型
│
├── ui/
│ └── src/
│ ├── composables/
│ │ ├── useBasicSubMode.ts # ✅ 新增:基础模式子模式管理
│ │ ├── useProSubMode.ts # ✅ 新增:上下文模式子模式管理
│ │ ├── useImageSubMode.ts # ✅ 新增:图像模式子模式管理
│ │ └── index.ts # ✅ 导出新composables
│ ├── components/
│ │ └── image-mode/
│ │ ├── ImageWorkspace.vue # ✅ 修改:移除内部选择器,监听事件
│ │ └── ImageModeSelector.vue # ✅ 保留:移至导航栏使用
│ └── index.ts # ✅ 导出 ImageModeSelector
│
└── web/
└── src/
└── App.vue # ✅ 重大修改:集成三个composables
sequenceDiagram
participant App as App.vue
participant FM as useFunctionMode
participant BSM as useBasicSubMode
participant PSM as useProSubMode
participant ISM as useImageSubMode
participant Storage as LocalStorage
App->>FM: 初始化功能模式
FM->>Storage: 读取 FUNCTION_MODE
Storage-->>FM: 返回 'basic' | 'pro' | 'image'
alt functionMode === 'basic'
App->>BSM: ensureInitialized()
BSM->>Storage: 读取 BASIC_SUB_MODE
Storage-->>BSM: 返回 'system' | 'user'
BSM-->>App: basicSubMode 已恢复
else functionMode === 'pro'
App->>PSM: ensureInitialized()
PSM->>Storage: 读取 PRO_SUB_MODE
Storage-->>PSM: 返回 'system' | 'user'
PSM-->>App: proSubMode 已恢复
else functionMode === 'image'
App->>ISM: ensureInitialized()
ISM->>Storage: 读取 IMAGE_SUB_MODE
Storage-->>ISM: 返回 'text2image' | 'image2image'
ISM-->>App: imageSubMode 已恢复
end
sequenceDiagram
participant User as 用户
participant Nav as 导航栏选择器
participant App as App.vue
participant SM as useSubMode
participant Storage as LocalStorage
participant WS as Workspace组件
User->>Nav: 点击切换子模式
Nav->>App: @change(newMode)
App->>SM: setSubMode(newMode)
SM->>Storage: setPreference(KEY, newMode)
Storage-->>SM: 保存成功
SM->>SM: mode.value = newMode
SM-->>App: 返回
App->>WS: 切换组件 (v-if)
WS-->>User: 显示新界面
文件: packages/core/src/constants/storage-keys.ts
export const UI_SETTINGS_KEYS = {
THEME_ID: 'app:settings:ui:theme-id',
PREFERRED_LANGUAGE: 'app:settings:ui:preferred-language',
BUILTIN_TEMPLATE_LANGUAGE: 'app:settings:ui:builtin-template-language',
FUNCTION_MODE: 'app:settings:ui:function-mode',
// ✅ 子模式持久化(三种功能模式独立存储)
BASIC_SUB_MODE: 'app:settings:ui:basic-sub-mode', // 基础模式的子模式(system/user)
PRO_SUB_MODE: 'app:settings:ui:pro-sub-mode', // 上下文模式的子模式(system/user)
IMAGE_SUB_MODE: 'app:settings:ui:image-sub-mode', // 图像模式的子模式(text2image/image2image)
} as const
文件: packages/core/src/services/prompt/types.ts
/**
* 子模式类型定义(三种功能模式独立)
* 用于持久化各功能模式下的子模式选择
*/
// 基础模式的子模式
export type BasicSubMode = "system" | "user"
// 上下文模式的子模式
export type ProSubMode = "system" | "user"
// 图像模式的子模式
export type ImageSubMode = "text2image" | "image2image"
文件: packages/ui/src/composables/useBasicSubMode.ts
核心代码: (约93行)
import { ref, readonly, type Ref } from 'vue'
import type { AppServices } from '../types/services'
import { usePreferences } from './usePreferenceManager'
import { UI_SETTINGS_KEYS, type BasicSubMode } from '@prompt-optimizer/core'
interface UseBasicSubModeApi {
basicSubMode: Ref<BasicSubMode>
setBasicSubMode: (mode: BasicSubMode) => Promise<void>
switchToSystem: () => Promise<void>
switchToUser: () => Promise<void>
ensureInitialized: () => Promise<void>
}
let singleton: {
mode: Ref<BasicSubMode>
initialized: boolean
initializing: Promise<void> | null
} | null = null
export function useBasicSubMode(services: Ref<AppServices | null>): UseBasicSubModeApi {
if (!singleton) {
singleton = {
mode: ref<BasicSubMode>('system'),
initialized: false,
initializing: null
}
}
const { getPreference, setPreference } = usePreferences(services)
const ensureInitialized = async () => {
if (singleton!.initialized) return
if (singleton!.initializing) {
await singleton!.initializing
return
}
singleton!.initializing = (async () => {
try {
const saved = await getPreference<BasicSubMode>(
UI_SETTINGS_KEYS.BASIC_SUB_MODE,
'system'
)
singleton!.mode.value = (saved === 'system' || saved === 'user')
? saved
: 'system'
console.log(`[useBasicSubMode] 初始化完成,当前值: ${singleton!.mode.value}`)
if (saved !== 'system' && saved !== 'user') {
await setPreference(UI_SETTINGS_KEYS.BASIC_SUB_MODE, 'system')
console.log('[useBasicSubMode] 首次初始化,已持久化默认值: system')
}
} catch (e) {
console.error('[useBasicSubMode] 初始化失败,使用默认值 system:', e)
try {
await setPreference(UI_SETTINGS_KEYS.BASIC_SUB_MODE, 'system')
} catch {
// 忽略设置失败错误
}
} finally {
singleton!.initialized = true
singleton!.initializing = null
}
})()
await singleton!.initializing
}
const setBasicSubMode = async (mode: BasicSubMode) => {
await ensureInitialized()
singleton!.mode.value = mode
await setPreference(UI_SETTINGS_KEYS.BASIC_SUB_MODE, mode)
console.log(`[useBasicSubMode] 子模式已切换并持久化: ${mode}`)
}
const switchToSystem = () => setBasicSubMode('system')
const switchToUser = () => setBasicSubMode('user')
return {
basicSubMode: readonly(singleton.mode) as Ref<BasicSubMode>,
setBasicSubMode,
switchToSystem,
switchToUser,
ensureInitialized
}
}
设计特点:
文件: packages/ui/src/composables/useProSubMode.ts
实现: 与 useBasicSubMode.ts 结构完全相同,只是:
ProSubMode 类型UI_SETTINGS_KEYS.PRO_SUB_MODE 存储键[useProSubMode]文件: packages/ui/src/composables/useImageSubMode.ts
实现: 与 useBasicSubMode.ts 结构相同,但:
ImageSubMode 类型('text2image' | 'image2image')UI_SETTINGS_KEYS.IMAGE_SUB_MODE 存储键'text2image'[useImageSubMode]文件: packages/web/src/App.vue
import {
useBasicSubMode,
useProSubMode,
useImageSubMode,
// ... 其他导入
} from '@prompt-optimizer/ui'
// 功能模式
const { functionMode, setFunctionMode } = useFunctionMode(services as any)
// 三种功能模式的子模式持久化(独立存储)
const { basicSubMode, setBasicSubMode } = useBasicSubMode(services as any)
const { proSubMode, setProSubMode } = useProSubMode(services as any)
const { imageSubMode, setImageSubMode } = useImageSubMode(services as any)
<template #core-nav>
<NSpace :size="12" align="center">
<!-- 功能模式选择器 -->
<FunctionModeSelector
:modelValue="functionMode"
@update:modelValue="handleModeSelect"
/>
<!-- 子模式选择器 - 基础模式 -->
<OptimizationModeSelectorUI
v-if="functionMode === 'basic'"
:modelValue="basicSubMode"
@change="handleBasicSubModeChange"
/>
<!-- 子模式选择器 - 上下文模式 -->
<OptimizationModeSelectorUI
v-if="functionMode === 'pro'"
:modelValue="proSubMode"
@change="handleProSubModeChange"
/>
<!-- 子模式选择器 - 图像模式 -->
<ImageModeSelector
v-if="functionMode === 'image'"
:modelValue="imageSubMode"
@change="handleImageSubModeChange"
/>
</NSpace>
</template>
关键特点:
functionMode 动态显示对应的子模式选择器onMounted(async () => {
// ... 其他初始化代码 ...
// Phase 1: 初始化各功能模式的子模式持久化
// 根据当前功能模式,从存储恢复对应的子模式选择
if (functionMode.value === "basic") {
const { ensureInitialized } = useBasicSubMode(services as any);
await ensureInitialized();
// 同步到 selectedOptimizationMode 以保持兼容性
selectedOptimizationMode.value = basicSubMode.value as OptimizationMode;
console.log(`[App] 基础模式子模式已恢复: ${basicSubMode.value}`);
} else if (functionMode.value === "pro") {
const { ensureInitialized } = useProSubMode(services as any);
await ensureInitialized();
// 同步到 selectedOptimizationMode 以保持兼容性
selectedOptimizationMode.value = proSubMode.value as OptimizationMode;
// 同步到 contextMode(关键!否则界面不会切换)
await handleContextModeChange(
proSubMode.value as import("@prompt-optimizer/core").ContextMode,
);
console.log(`[App] 上下文模式子模式已恢复: ${proSubMode.value}`);
} else if (functionMode.value === "image") {
const { ensureInitialized } = useImageSubMode(services as any);
await ensureInitialized();
console.log(`[App] 图像模式子模式已恢复: ${imageSubMode.value}`);
}
console.log("All services and composables initialized.");
})
const handleModeSelect = async (mode: "basic" | "pro" | "image") => {
await setFunctionMode(mode);
// 恢复各功能模式独立的子模式状态
if (mode === "basic") {
const { ensureInitialized } = useBasicSubMode(services as any);
await ensureInitialized();
selectedOptimizationMode.value = basicSubMode.value as OptimizationMode;
console.log(`[App] 切换到基础模式,已恢复子模式: ${basicSubMode.value}`);
} else if (mode === "pro") {
const { ensureInitialized } = useProSubMode(services as any);
await ensureInitialized();
selectedOptimizationMode.value = proSubMode.value as OptimizationMode;
await handleContextModeChange(
proSubMode.value as import("@prompt-optimizer/core").ContextMode,
);
console.log(`[App] 切换到上下文模式,已恢复子模式: ${proSubMode.value}`);
} else if (mode === "image") {
const { ensureInitialized } = useImageSubMode(services as any);
await ensureInitialized();
console.log(`[App] 切换到图像模式,已恢复子模式: ${imageSubMode.value}`);
}
};
关键逻辑:
selectedOptimizationMode, contextMode)// 基础模式子模式变更处理器
const handleBasicSubModeChange = async (mode: OptimizationMode) => {
await setBasicSubMode(mode as import("@prompt-optimizer/core").BasicSubMode);
selectedOptimizationMode.value = mode;
console.log(`[App] 基础模式子模式已切换并持久化: ${mode}`);
};
// 上下文模式子模式变更处理器
const handleProSubModeChange = async (mode: OptimizationMode) => {
await setProSubMode(mode as import("@prompt-optimizer/core").ProSubMode);
selectedOptimizationMode.value = mode;
if (services.value?.contextMode.value !== mode) {
await handleContextModeChange(
mode as import("@prompt-optimizer/core").ContextMode,
);
}
console.log(`[App] 上下文模式子模式已切换并持久化: ${mode}`);
};
// 图像模式子模式变更处理器
const handleImageSubModeChange = async (mode: import("@prompt-optimizer/core").ImageSubMode) => {
await setImageSubMode(mode);
console.log(`[App] 图像模式子模式已切换并持久化: ${mode}`);
// 通知 ImageWorkspace 更新
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent("image-submode-changed", {
detail: { mode }
}));
}
};
关键特点:
setSubMode 方法(自动持久化)ImageWorkspaceconst handleHistoryReuse = async (context: { record: any; chainId: string; rootPrompt: string; chain: any }) => {
const { record, chain } = context;
const rt = chain.rootRecord.type;
// ... 图像模式逻辑 ...
// 确定目标子模式
let targetMode: OptimizationMode;
if (rt === "optimize" || rt === "contextSystemOptimize") {
targetMode = "system";
} else if (rt === "userOptimize" || rt === "contextUserOptimize") {
targetMode = "user";
} else {
targetMode = chain.rootRecord.metadata?.optimizationMode || "system";
}
// 如果目标模式与当前模式不同,自动切换
if (targetMode !== selectedOptimizationMode.value) {
selectedOptimizationMode.value = targetMode;
// 根据功能模式分别处理子模式的持久化
if (functionMode.value === "basic") {
// 基础模式:持久化子模式选择
await setBasicSubMode(
targetMode as import("@prompt-optimizer/core").BasicSubMode,
);
} else if (functionMode.value === "pro") {
// 上下文模式:持久化子模式并同步 contextMode
await setProSubMode(
targetMode as import("@prompt-optimizer/core").ProSubMode,
);
await handleContextModeChange(
targetMode as import("@prompt-optimizer/core").ContextMode,
);
}
useToast().info(
t("toast.info.optimizationModeAutoSwitched", {
mode: targetMode === "system" ? t("common.system") : t("common.user"),
}),
);
}
// ... 功能模式切换和数据恢复 ...
};
关键改进:
const handleUseFavorite = async (favorite: any) => {
const {
functionMode: favFunctionMode,
optimizationMode: favOptimizationMode,
imageSubMode: favImageSubMode,
} = favorite;
// ... 图像模式逻辑 ...
// 2. 切换优化模式
if (favOptimizationMode && favOptimizationMode !== selectedOptimizationMode.value) {
selectedOptimizationMode.value = favOptimizationMode;
// 根据功能模式分别处理子模式的持久化
if (functionMode.value === "basic") {
// 基础模式:持久化子模式选择
await setBasicSubMode(
favOptimizationMode as import("@prompt-optimizer/core").BasicSubMode,
);
} else if (functionMode.value === "pro") {
// 上下文模式:持久化子模式并同步 contextMode
await setProSubMode(
favOptimizationMode as import("@prompt-optimizer/core").ProSubMode,
);
await handleContextModeChange(
favOptimizationMode as import("@prompt-optimizer/core").ContextMode,
);
}
useToast().info(
t("toast.info.optimizationModeAutoSwitched", {
mode: favOptimizationMode === "system" ? t("common.system") : t("common.user"),
}),
);
}
// 3. 切换功能模式(basic vs context)
const targetFunctionMode = favFunctionMode === "context" ? "pro" : "basic";
if (targetFunctionMode !== functionMode.value) {
await setFunctionMode(targetFunctionMode);
useToast().info(
`已自动切换到${targetFunctionMode === "pro" ? "上下文" : "基础"}模式`,
);
// 功能模式切换后,如果有优化模式信息,确保同步各自的子模式持久化
if (favOptimizationMode) {
if (targetFunctionMode === "basic") {
// 基础模式:持久化子模式选择
await setBasicSubMode(
favOptimizationMode as import("@prompt-optimizer/core").BasicSubMode,
);
} else if (targetFunctionMode === "pro") {
// 上下文模式:持久化子模式并同步 contextMode
await setProSubMode(
favOptimizationMode as import("@prompt-optimizer/core").ProSubMode,
);
await handleContextModeChange(
favOptimizationMode as import("@prompt-optimizer/core").ContextMode,
);
}
}
}
// ... 数据回填 ...
};
关键改进:
文件: packages/ui/src/components/image-mode/ImageWorkspace.vue
<!-- ❌ 移除前 -->
<template>
<NFlex align="center" :size="12">
<ImageModeSelector v-model="imageMode" @change="handleImageModeChange" />
<!-- ... 其他按钮 -->
</NFlex>
</template>
<!-- ✅ 移除后 -->
<template>
<NFlex align="center" :size="12">
<!-- 图像模式选择器已移到导航栏 -->
<NButton ... />
<!-- ... 其他按钮 -->
</NFlex>
</template>
// 🆕 图像子模式变更事件处理器(导航栏切换时同步)
const handleImageSubModeChanged = (e: CustomEvent) => {
const { mode } = e.detail
if (mode && mode !== imageMode.value) {
console.log(`[ImageWorkspace] 接收到导航栏子模式切换事件: ${mode}`)
handleImageModeChange(mode)
}
}
onMounted(() => {
// 🆕 监听导航栏的图像子模式切换事件
window.addEventListener(
"image-submode-changed",
handleImageSubModeChanged as EventListener,
);
})
onBeforeUnmount(() => {
window.removeEventListener(
"image-submode-changed",
handleImageSubModeChanged as EventListener,
);
})
关键改进:
[useBasicSubMode] 初始化完成,当前值: user[useProSubMode] 初始化完成,当前值: system[useImageSubMode] 初始化完成,当前值: text2image测试场景:
预期结果: 基础模式应保持"用户提示词优化"(证明两者独立)
实际结果: ✅ 通过
[App] 切换到基础模式,已恢复子模式: user✅ 状态记忆
✅ 一致性
✅ 独立性
✅ 职责清晰
✅ 类型安全
✅ 可维护性
✅ 可扩展性
✅ 解耦合
✅ 向后兼容
selectedOptimizationMode 变量contextMode 服务保持同步┌─────────────────────────────────────────────────────────────┐
│ App.vue │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ useBasicSub │ │ useProSubMode│ │ useImageSub │ │
│ │ Mode │ │ │ │ Mode │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ LocalStorage (持久化) │ │
│ │ • BASIC_SUB_MODE: 'system' | 'user' │ │
│ │ • PRO_SUB_MODE: 'system' | 'user' │ │
│ │ • IMAGE_SUB_MODE: 'text2image' | 'image2image' │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 导航栏 (Navigation) │
│ ┌──────────────┐ ┌────────────────────────────────┐ │
│ │ FunctionMode │ │ SubMode Selector (动态) │ │
│ │ Selector │ │ • 基础: [系统 | 用户] │ │
│ │ [基础|上下 │ │ • 上下文: [系统 | 用户] │ │
│ │ 文|图像] │ │ • 图像: [文生图 | 图生图] │ │
│ └──────────────┘ └────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Workspace (工作区) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ BasicWork │ │ ContextWork │ │ ImageWork │ │
│ │ space │ │ space │ │ space │ │
│ │ │ │ • System │ │ │ │
│ │ │ │ • User │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
页面加载
↓
读取 FUNCTION_MODE → 确定当前功能模式
↓
根据功能模式读取对应的子模式存储键
↓
┌──────────┬──────────┬──────────┐
│ basic │ pro │ image │
│ ↓ │ ↓ │ ↓ │
│ BASIC_ │ PRO_ │ IMAGE_ │
│ SUB_MODE │ SUB_MODE │ SUB_MODE │
└──────────┴──────────┴──────────┘
↓
恢复子模式状态 → 显示对应的 Workspace
↓
用户切换子模式 → 自动持久化
↓
用户切换功能模式 → 恢复新模式的子模式状态
| 日期 | 里程碑 | 耗时 |
|---|---|---|
| 2025-10-22 | ✅ Phase 1 完成(上下文模式) | 约 2 小时 |
| 2025-10-22 | ✅ Phase 2 完成(基础模式) | 约 1.5 小时 |
| 2025-10-22 | ✅ Phase 3 完成(图像模式) | 约 2 小时 |
| 2025-10-22 | ✅ 完整测试验证 | 约 1.5 小时 |
| 总计 | 全部完成 | 约 7 小时 |
背景: 基础模式和上下文模式的子模式名称相同(都是 system/user),最初考虑共享存储。
用户反馈(关键洞察):
"基础模式也应该有自己的存储,这个也应该分开...因为这两个功能模式本质上控制的是不同的,只是当前他们的子模式碰巧都叫 系统/用户提示词优化而已。"
决策: 采用三个完全独立的存储键
理由:
影响:
背景: 原先上下文模式的子模式选择器在左侧面板上方,图像模式的在工作区内部。
决策: 统一移至导航栏
理由:
影响:
背景: 需要全局唯一的子模式状态
决策: 每个 Composable 内部维护单例状态
理由:
影响:
背景: 现有代码大量使用 selectedOptimizationMode 和 contextMode
决策: 保留旧变量,与新 Composable 同步
理由:
影响:
目前无已知问题。所有核心功能已通过测试。
目标: 逐步移除 selectedOptimizationMode 和 contextMode
时间: 待定(需要大规模重构)
影响: 代码更简洁,但需要修改大量组件
目标: 在整个代码库中统一使用 SubMode 相关术语
时间: 待定
影响: 代码更一致,但需要修改文档和注释
✅ 完成三个阶段的完整实施
✅ 实现完全独立的状态管理
✅ 统一的导航栏UI
✅ 完善的持久化生命周期
✅ 全面的测试验证
用户的核心洞察:
"基础模式也应该有自己的存储,这个也应该分开...因为这两个功能模式本质上控制的是不同的,只是当前他们的子模式碰巧都叫 系统/用户提示词优化而已。"
这一洞察是整个重构的核心指导原则,确保了:
文档版本: v4.0
更新日期: 2025-10-22
状态: ✅ 全部完成并验证通过
开发服务器: http://localhost:18182/