🚧 Refactor basic services

This commit is contained in:
2025-12-14 02:19:50 +08:00
parent d16905c0a3
commit cc4c2189dc
126 changed files with 18164 additions and 4247 deletions

View File

@@ -161,7 +161,7 @@ export const useConfigStore = defineStore('config', () => {
// 初始化语言设置
const initializeLanguage = async (): Promise<void> => {
const initLanguage = async (): Promise<void> => {
try {
// 如果配置未加载,先加载配置
if (!state.configLoaded) {
@@ -210,7 +210,7 @@ export const useConfigStore = defineStore('config', () => {
// 语言相关方法
setLanguage,
initializeLanguage,
initLanguage,
// 主题相关方法
setSystemTheme,

View File

@@ -2,12 +2,11 @@ import {defineStore} from 'pinia';
import {computed, ref} from 'vue';
import {DocumentService} from '@/../bindings/voidraft/internal/services';
import {OpenDocumentWindow} from '@/../bindings/voidraft/internal/services/windowservice';
import {Document} from '@/../bindings/voidraft/internal/models/models';
import {Document} from '@/../bindings/voidraft/internal/models/ent/models';
import {useTabStore} from "@/stores/tabStore";
import type {EditorViewState} from '@/stores/editorStore';
export const useDocumentStore = defineStore('document', () => {
const DEFAULT_DOCUMENT_ID = ref<number>(1); // 默认草稿文档ID
// === 核心状态 ===
const documents = ref<Record<number, Document>>({});
@@ -15,7 +14,6 @@ export const useDocumentStore = defineStore('document', () => {
const currentDocument = ref<Document | null>(null);
// === 编辑器状态持久化 ===
// 修复:使用统一的 EditorViewState 类型定义
const documentStates = ref<Record<number, EditorViewState>>({});
// === UI状态 ===
@@ -26,15 +24,18 @@ export const useDocumentStore = defineStore('document', () => {
// === 计算属性 ===
const documentList = computed(() =>
Object.values(documents.value).sort((a, b) => {
return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
const timeA = a.updated_at ? new Date(a.updated_at).getTime() : 0;
const timeB = b.updated_at ? new Date(b.updated_at).getTime() : 0;
return timeB - timeA;
})
);
// === 私有方法 ===
const setDocuments = (docs: Document[]) => {
documents.value = {};
docs.forEach(doc => {
documents.value[doc.id] = doc;
if (doc.id !== undefined) {
documents.value[doc.id] = doc;
}
});
};
@@ -64,15 +65,7 @@ export const useDocumentStore = defineStore('document', () => {
clearError();
};
const toggleDocumentSelector = () => {
if (showDocumentSelector.value) {
closeDocumentSelector();
} else {
openDocumentSelector();
}
};
// === 文档操作方法 ===
// 在新窗口中打开文档
const openDocumentInNewWindow = async (docId: number): Promise<boolean> => {
@@ -94,7 +87,7 @@ export const useDocumentStore = defineStore('document', () => {
const createNewDocument = async (title: string): Promise<Document | null> => {
try {
const doc = await DocumentService.CreateDocument(title);
if (doc) {
if (doc && doc.id !== undefined) {
documents.value[doc.id] = doc;
return doc;
}
@@ -123,8 +116,6 @@ export const useDocumentStore = defineStore('document', () => {
// 打开文档
const openDocument = async (docId: number): Promise<boolean> => {
try {
closeDocumentSelector();
// 获取完整文档数据
const doc = await DocumentService.GetDocumentByID(docId);
if (!doc) {
@@ -150,12 +141,12 @@ export const useDocumentStore = defineStore('document', () => {
const doc = documents.value[docId];
if (doc) {
doc.title = title;
doc.updatedAt = new Date().toISOString();
doc.updated_at = new Date().toISOString();
}
if (currentDocument.value?.id === docId) {
currentDocument.value.title = title;
currentDocument.value.updatedAt = new Date().toISOString();
currentDocument.value.updated_at = new Date().toISOString();
}
// 同步更新标签页标题
@@ -172,11 +163,6 @@ export const useDocumentStore = defineStore('document', () => {
// 删除文档
const deleteDocument = async (docId: number): Promise<boolean> => {
try {
// 检查是否是默认文档使用ID判断
if (docId === DEFAULT_DOCUMENT_ID.value) {
return false;
}
await DocumentService.DeleteDocument(docId);
// 更新本地状态
@@ -191,7 +177,7 @@ export const useDocumentStore = defineStore('document', () => {
// 如果删除的是当前文档,切换到第一个可用文档
if (currentDocumentId.value === docId) {
const availableDocs = Object.values(documents.value);
if (availableDocs.length > 0) {
if (availableDocs.length > 0 && availableDocs[0].id !== undefined) {
await openDocument(availableDocs[0].id);
} else {
currentDocumentId.value = null;
@@ -218,8 +204,10 @@ export const useDocumentStore = defineStore('document', () => {
// 如果URL中没有指定文档ID则使用持久化的文档ID
await openDocument(currentDocumentId.value);
} else {
// 否则打开默认文档
await openDocument(DEFAULT_DOCUMENT_ID.value);
// 否则打开第一个文档
if (documents.value[0].id) {
await openDocument(documents.value[0].id);
}
}
} catch (error) {
console.error('Failed to initialize document store:', error);
@@ -227,7 +215,6 @@ export const useDocumentStore = defineStore('document', () => {
};
return {
DEFAULT_DOCUMENT_ID,
// 状态
documents,
documentList,
@@ -247,7 +234,6 @@ export const useDocumentStore = defineStore('document', () => {
deleteDocument,
openDocumentSelector,
closeDocumentSelector,
toggleDocumentSelector,
setError,
clearError,
initialize,

View File

@@ -4,7 +4,6 @@ import {EditorView} from '@codemirror/view';
import {EditorState, Extension} from '@codemirror/state';
import {useConfigStore} from './configStore';
import {useDocumentStore} from './documentStore';
import {ExtensionID} from '@/../bindings/voidraft/internal/models/models';
import {DocumentService, ExtensionService} from '@/../bindings/voidraft/internal/services';
import {ensureSyntaxTree} from "@codemirror/language";
import {createBasicSetup} from '@/views/editor/basic/basicSetup';
@@ -28,7 +27,6 @@ import {AsyncManager} from '@/common/utils/asyncManager';
import {generateContentHash} from "@/common/utils/hashUtils";
import {createTimerManager, type TimerManager} from '@/common/utils/timerUtils';
import {EDITOR_CONFIG} from '@/common/constant/editor';
import {createHttpClientExtension} from "@/views/editor/extensions/httpclient";
import {createDebounce} from '@/common/utils/debounce';
export interface DocumentStats {
@@ -93,84 +91,6 @@ export const useEditorStore = defineStore('editor', () => {
}
}, { delay: 500 }); // 500ms 内的多次输入只清理一次
// === 私有方法 ===
/**
* 检查位置是否在代码块分隔符区域内
*/
const isPositionInDelimiter = (view: EditorView, pos: number): boolean => {
try {
const blocks = view.state.field(blockState, false);
if (!blocks) return false;
for (const block of blocks) {
if (pos >= block.delimiter.from && pos < block.delimiter.to) {
return true;
}
}
return false;
} catch {
return false;
}
};
/**
* 调整光标位置到有效的内容区域
* 如果位置在分隔符内,移动到该块的内容开始位置
*/
const adjustCursorPosition = (view: EditorView, pos: number): number => {
try {
const blocks = view.state.field(blockState, false);
if (!blocks || blocks.length === 0) return pos;
// 如果位置在分隔符内,移动到该块的内容开始位置
for (const block of blocks) {
if (pos >= block.delimiter.from && pos < block.delimiter.to) {
return block.content.from;
}
}
return pos;
} catch {
return pos;
}
};
/**
* 恢复编辑器的光标位置(自动滚动到光标处)
*/
const restoreEditorState = (instance: EditorInstance, documentId: number): void => {
const savedState = instance.editorState;
if (savedState) {
// 有保存的状态,恢复光标位置
let pos = Math.min(savedState.cursorPos, instance.view.state.doc.length);
// 确保位置不在分隔符上
if (isPositionInDelimiter(instance.view, pos)) {
pos = adjustCursorPosition(instance.view, pos);
}
// 修复:设置光标位置并居中滚动(更好的用户体验)
instance.view.dispatch({
selection: {anchor: pos, head: pos},
effects: EditorView.scrollIntoView(pos, {
y: "center", // 垂直居中显示
yMargin: 100 // 上下留一些边距
})
});
} else {
// 首次打开或没有记录,光标在文档末尾
const docLength = instance.view.state.doc.length;
instance.view.dispatch({
selection: {anchor: docLength, head: docLength},
effects: EditorView.scrollIntoView(docLength, {
y: "center",
yMargin: 100
})
});
}
};
// 缓存化的语法树确保方法
const ensureSyntaxTreeCached = (view: EditorView, documentId: number): void => {
@@ -245,7 +165,7 @@ export const useEditorStore = defineStore('editor', () => {
increaseFontSize: () => configStore.increaseFontSizeLocal(),
decreaseFontSize: () => configStore.decreaseFontSizeLocal(),
onSave: () => configStore.saveFontSize(),
saveDelay: 500
saveDelay: 1000
});
// 统计扩展
@@ -260,9 +180,6 @@ export const useEditorStore = defineStore('editor', () => {
enableAutoDetection: true
});
const httpExtension = createHttpClientExtension();
// 再次检查操作有效性
if (!operationManager.isOperationValid(operationId, documentId)) {
throw new Error('Operation cancelled');
@@ -277,7 +194,7 @@ export const useEditorStore = defineStore('editor', () => {
}
// 动态扩展传递文档ID以便扩展管理器可以预初始化
const dynamicExtensions = await createDynamicExtensions(documentId);
const dynamicExtensions = await createDynamicExtensions();
// 最终检查操作有效性
if (!operationManager.isOperationValid(operationId, documentId)) {
@@ -296,7 +213,6 @@ export const useEditorStore = defineStore('editor', () => {
contentChangeExtension,
codeBlockExtension,
...dynamicExtensions,
...httpExtension,
];
// 创建编辑器状态
@@ -320,7 +236,6 @@ export const useEditorStore = defineStore('editor', () => {
lastModified: new Date(),
autoSaveTimer: createTimerManager(),
syntaxTreeCache: null,
// 修复:创建实例时从 documentStore 读取持久化的编辑器状态
editorState: documentStore.documentStates[documentId]
};
@@ -401,9 +316,6 @@ export const useEditorStore = defineStore('editor', () => {
//使用 nextTick + requestAnimationFrame 确保 DOM 完全渲染
nextTick(() => {
requestAnimationFrame(() => {
// 恢复编辑器状态(光标位置和滚动位置)
restoreEditorState(instance, documentId);
// 聚焦编辑器
instance.view.focus();
@@ -433,7 +345,6 @@ export const useEditorStore = defineStore('editor', () => {
instance.isDirty = false;
instance.lastModified = new Date();
}
// 如果内容在保存期间被修改了,保持 isDirty 状态
return true;
} catch (error) {
@@ -462,15 +373,14 @@ export const useEditorStore = defineStore('editor', () => {
}, getAutoSaveDelay());
};
// === 公共API ===
// 设置编辑器容器
const setEditorContainer = (container: HTMLElement | null) => {
containerElement.value = container;
// 如果设置容器时已有当前文档,立即加载编辑器
if (container && documentStore.currentDocument) {
loadEditor(documentStore.currentDocument.id, documentStore.currentDocument.content);
if (container && documentStore.currentDocument && documentStore.currentDocument.id !== undefined) {
loadEditor(documentStore.currentDocument.id, documentStore.currentDocument.content || '');
}
};
@@ -696,20 +606,20 @@ export const useEditorStore = defineStore('editor', () => {
};
// 更新扩展
const updateExtension = async (id: ExtensionID, enabled: boolean, config?: any) => {
// 如果只是更新启用状态
if (config === undefined) {
await ExtensionService.UpdateExtensionEnabled(id, enabled);
} else {
// 如果需要更新配置
await ExtensionService.UpdateExtensionState(id, enabled, config);
const updateExtension = async (key: string, enabled: boolean, config?: any) => {
// 更新启用状态
await ExtensionService.UpdateExtensionEnabled(key, enabled);
// 如果需要更新配置
if (config !== undefined) {
await ExtensionService.UpdateExtensionConfig(key, config);
}
// 更新前端编辑器扩展 - 应用于所有实例
const manager = getExtensionManager();
if (manager) {
// 直接更新前端扩展至所有视图
manager.updateExtension(id, enabled, config);
manager.updateExtension(key, enabled, config);
}
// 重新加载扩展配置
@@ -723,23 +633,10 @@ export const useEditorStore = defineStore('editor', () => {
// 监听文档切换
watch(() => documentStore.currentDocument, async (newDoc, oldDoc) => {
if (newDoc && containerElement.value) {
// 修复:在切换到新文档前,只保存旧文档的光标位置
if (oldDoc && oldDoc.id !== newDoc.id && currentEditor.value) {
const oldInstance = editorCache.get(oldDoc.id);
if (oldInstance) {
const currentState: EditorViewState = {
cursorPos: currentEditor.value.state.selection.main.head
};
// 同时保存到实例和 documentStore
oldInstance.editorState = currentState;
documentStore.documentStates[oldDoc.id] = currentState;
}
}
if (newDoc && newDoc.id !== undefined && containerElement.value) {
// 等待 DOM 更新完成,再加载新文档的编辑器
await nextTick();
loadEditor(newDoc.id, newDoc.content);
loadEditor(newDoc.id, newDoc.content || '');
}
});

View File

@@ -1,6 +1,6 @@
import { defineStore } from 'pinia';
import { computed, ref } from 'vue';
import { Extension, ExtensionID } from '@/../bindings/voidraft/internal/models/models';
import { Extension } from '@/../bindings/voidraft/internal/models/ent/models';
import { ExtensionService } from '@/../bindings/voidraft/internal/services';
export const useExtensionStore = defineStore('extension', () => {
@@ -12,9 +12,9 @@ export const useExtensionStore = defineStore('extension', () => {
extensions.value.filter(ext => ext.enabled)
);
// 获取启用的扩展ID列表
// 获取启用的扩展ID列表 (key)
const enabledExtensionIds = computed(() =>
enabledExtensions.value.map(ext => ext.id)
enabledExtensions.value.map(ext => ext.key).filter((k): k is string => k !== undefined)
);
/**
@@ -22,7 +22,8 @@ export const useExtensionStore = defineStore('extension', () => {
*/
const loadExtensions = async (): Promise<void> => {
try {
extensions.value = await ExtensionService.GetAllExtensions();
const result = await ExtensionService.GetAllExtensions();
extensions.value = result.filter((ext): ext is Extension => ext !== null);
} catch (err) {
console.error('[ExtensionStore] Failed to load extensions:', err);
}
@@ -31,8 +32,8 @@ export const useExtensionStore = defineStore('extension', () => {
/**
* 获取扩展配置
*/
const getExtensionConfig = (id: ExtensionID): any => {
const extension = extensions.value.find(ext => ext.id === id);
const getExtensionConfig = (key: string): any => {
const extension = extensions.value.find(ext => ext.key === key);
return extension?.config ?? {};
};

View File

@@ -1,6 +1,6 @@
import {defineStore} from 'pinia';
import {computed, ref} from 'vue';
import {ExtensionID, KeyBinding, KeyBindingCommand} from '@/../bindings/voidraft/internal/models/models';
import {KeyBinding} from '@/../bindings/voidraft/internal/models/ent/models';
import {GetAllKeyBindings} from '@/../bindings/voidraft/internal/services/keybindingservice';
export const useKeybindingStore = defineStore('keybinding', () => {
@@ -14,13 +14,14 @@ export const useKeybindingStore = defineStore('keybinding', () => {
// 按扩展分组的快捷键
const keyBindingsByExtension = computed(() => {
const groups = new Map<ExtensionID, KeyBinding[]>();
const groups = new Map<string, KeyBinding[]>();
for (const binding of keyBindings.value) {
if (!groups.has(binding.extension)) {
groups.set(binding.extension, []);
const ext = binding.extension || '';
if (!groups.has(ext)) {
groups.set(ext, []);
}
groups.get(binding.extension)!.push(binding);
groups.get(ext)!.push(binding);
}
return groups;
@@ -28,13 +29,13 @@ export const useKeybindingStore = defineStore('keybinding', () => {
// 获取指定扩展的快捷键
const getKeyBindingsByExtension = computed(() =>
(extension: ExtensionID) =>
(extension: string) =>
keyBindings.value.filter(kb => kb.extension === extension)
);
// 按命令获取快捷键
const getKeyBindingByCommand = computed(() =>
(command: KeyBindingCommand) =>
(command: string) =>
keyBindings.value.find(kb => kb.command === command)
);
@@ -42,13 +43,14 @@ export const useKeybindingStore = defineStore('keybinding', () => {
* 从后端加载快捷键配置
*/
const loadKeyBindings = async (): Promise<void> => {
keyBindings.value = await GetAllKeyBindings();
const result = await GetAllKeyBindings();
keyBindings.value = result.filter((kb): kb is KeyBinding => kb !== null);
};
/**
* 检查是否存在指定命令的快捷键
*/
const hasCommand = (command: KeyBindingCommand): boolean => {
const hasCommand = (command: string): boolean => {
return keyBindings.value.some(kb => kb.command === command && kb.enabled);
};
@@ -57,9 +59,11 @@ export const useKeybindingStore = defineStore('keybinding', () => {
* 获取扩展相关的所有扩展ID
*/
const getAllExtensionIds = computed(() => {
const extensionIds = new Set<ExtensionID>();
const extensionIds = new Set<string>();
for (const binding of keyBindings.value) {
extensionIds.add(binding.extension);
if (binding.extension) {
extensionIds.add(binding.extension);
}
}
return Array.from(extensionIds);
});

View File

@@ -38,7 +38,7 @@ export const useSystemStore = defineStore('system', () => {
});
// 初始化系统信息
const initializeSystemInfo = async (): Promise<void> => {
const initSystemInfo = async (): Promise<void> => {
if (isLoading.value) return;
isLoading.value = true;
@@ -102,7 +102,7 @@ export const useSystemStore = defineStore('system', () => {
titleBarHeight,
// 方法
initializeSystemInfo,
initSystemInfo,
setWindowOnTop,
toggleWindowOnTop,
resetWindowOnTop,

View File

@@ -2,7 +2,7 @@ import {defineStore} from 'pinia';
import {computed, readonly, ref} from 'vue';
import {useConfigStore} from './configStore';
import {useDocumentStore} from './documentStore';
import type {Document} from '@/../bindings/voidraft/internal/models/models';
import type {Document} from '@/../bindings/voidraft/internal/models/ent/models';
export interface Tab {
documentId: number; // 直接使用文档ID作为唯一标识
@@ -55,6 +55,7 @@ export const useTabStore = defineStore('tab', () => {
*/
const addOrActivateTab = (document: Document) => {
const documentId = document.id;
if (documentId === undefined) return;
if (hasTab(documentId)) {
// 标签页已存在,无需重复添加
@@ -64,7 +65,7 @@ export const useTabStore = defineStore('tab', () => {
// 创建新标签页
const newTab: Tab = {
documentId,
title: document.title
title: document.title || ''
};
tabsMap.value[documentId] = newTab;

View File

@@ -1,159 +1,161 @@
import { defineStore } from 'pinia';
import { computed, ref } from 'vue';
import { SystemThemeType, ThemeType, ThemeColorConfig } from '@/../bindings/voidraft/internal/models/models';
import { ThemeService } from '@/../bindings/voidraft/internal/services';
import { useConfigStore } from './configStore';
import { useEditorStore } from './editorStore';
import type { ThemeColors } from '@/views/editor/theme/types';
import { cloneThemeColors, FALLBACK_THEME_NAME, themePresetList, themePresetMap } from '@/views/editor/theme/presets';
import {defineStore} from 'pinia';
import {computed, ref} from 'vue';
import {SystemThemeType} from '@/../bindings/voidraft/internal/models/models';
import {Type as ThemeType} from '@/../bindings/voidraft/internal/models/ent/theme/models';
import {ThemeService} from '@/../bindings/voidraft/internal/services';
import {useConfigStore} from './configStore';
import {useEditorStore} from './editorStore';
import type {ThemeColors} from '@/views/editor/theme/types';
import {cloneThemeColors, FALLBACK_THEME_NAME, themePresetList, themePresetMap} from '@/views/editor/theme/presets';
type ThemeColorConfig = { [_: string]: any };
type ThemeOption = { name: string; type: ThemeType };
const resolveThemeName = (name?: string) =>
name && themePresetMap[name] ? name : FALLBACK_THEME_NAME;
name && themePresetMap[name] ? name : FALLBACK_THEME_NAME;
const createThemeOptions = (type: ThemeType): ThemeOption[] =>
themePresetList
.filter(preset => preset.type === type)
.map(preset => ({ name: preset.name, type: preset.type }));
themePresetList
.filter(preset => preset.type === type)
.map(preset => ({name: preset.name, type: preset.type}));
const darkThemeOptions = createThemeOptions(ThemeType.ThemeTypeDark);
const lightThemeOptions = createThemeOptions(ThemeType.ThemeTypeLight);
const darkThemeOptions = createThemeOptions(ThemeType.TypeDark);
const lightThemeOptions = createThemeOptions(ThemeType.TypeLight);
const cloneColors = (colors: ThemeColorConfig): ThemeColors =>
JSON.parse(JSON.stringify(colors)) as ThemeColors;
JSON.parse(JSON.stringify(colors)) as ThemeColors;
const getPresetColors = (name: string): ThemeColors => {
const preset = themePresetMap[name] ?? themePresetMap[FALLBACK_THEME_NAME];
const colors = cloneThemeColors(preset.colors);
colors.themeName = name;
return colors;
const preset = themePresetMap[name] ?? themePresetMap[FALLBACK_THEME_NAME];
const colors = cloneThemeColors(preset.colors);
colors.themeName = name;
return colors;
};
const fetchThemeColors = async (themeName: string): Promise<ThemeColors> => {
const safeName = resolveThemeName(themeName);
try {
const theme = await ThemeService.GetThemeByName(safeName);
if (theme?.colors) {
const colors = cloneColors(theme.colors);
colors.themeName = safeName;
return colors;
const safeName = resolveThemeName(themeName);
try {
const theme = await ThemeService.GetThemeByKey(safeName);
if (theme?.colors) {
const colors = cloneColors(theme.colors);
colors.themeName = safeName;
return colors;
}
} catch (error) {
console.error('Failed to load theme override:', error);
}
} catch (error) {
console.error('Failed to load theme override:', error);
}
return getPresetColors(safeName);
return getPresetColors(safeName);
};
export const useThemeStore = defineStore('theme', () => {
const configStore = useConfigStore();
const currentColors = ref<ThemeColors | null>(null);
const configStore = useConfigStore();
const currentColors = ref<ThemeColors | null>(null);
const currentTheme = computed(
() => configStore.config?.appearance?.systemTheme || SystemThemeType.SystemThemeAuto
);
const isDarkMode = computed(
() =>
currentTheme.value === SystemThemeType.SystemThemeDark ||
(currentTheme.value === SystemThemeType.SystemThemeAuto &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
);
const availableThemes = computed<ThemeOption[]>(() =>
isDarkMode.value ? darkThemeOptions : lightThemeOptions
);
const applyThemeToDOM = (theme: SystemThemeType) => {
const themeMap = {
[SystemThemeType.SystemThemeAuto]: 'auto',
[SystemThemeType.SystemThemeDark]: 'dark',
[SystemThemeType.SystemThemeLight]: 'light',
};
document.documentElement.setAttribute('data-theme', themeMap[theme]);
};
const loadThemeColors = async (themeName?: string) => {
const targetName = resolveThemeName(
themeName || configStore.config?.appearance?.currentTheme
const currentTheme = computed(
() => configStore.config?.appearance?.systemTheme || SystemThemeType.SystemThemeAuto
);
currentColors.value = await fetchThemeColors(targetName);
};
const initializeTheme = async () => {
applyThemeToDOM(currentTheme.value);
await loadThemeColors();
};
const isDarkMode = computed(
() =>
currentTheme.value === SystemThemeType.SystemThemeDark ||
(currentTheme.value === SystemThemeType.SystemThemeAuto &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
);
const setTheme = async (theme: SystemThemeType) => {
await configStore.setSystemTheme(theme);
applyThemeToDOM(theme);
refreshEditorTheme();
};
const availableThemes = computed<ThemeOption[]>(() =>
isDarkMode.value ? darkThemeOptions : lightThemeOptions
);
const switchToTheme = async (themeName: string) => {
if (!themePresetMap[themeName]) {
console.error('Theme not found:', themeName);
return false;
}
const applyThemeToDOM = (theme: SystemThemeType) => {
const themeMap = {
[SystemThemeType.SystemThemeAuto]: 'auto',
[SystemThemeType.SystemThemeDark]: 'dark',
[SystemThemeType.SystemThemeLight]: 'light',
};
document.documentElement.setAttribute('data-theme', themeMap[theme]);
};
await loadThemeColors(themeName);
await configStore.setCurrentTheme(themeName);
refreshEditorTheme();
return true;
};
const loadThemeColors = async (themeName?: string) => {
const targetName = resolveThemeName(
themeName || configStore.config?.appearance?.currentTheme
);
currentColors.value = await fetchThemeColors(targetName);
};
const updateCurrentColors = (colors: Partial<ThemeColors>) => {
if (!currentColors.value) return;
Object.assign(currentColors.value, colors);
};
const initTheme = async () => {
applyThemeToDOM(currentTheme.value);
await loadThemeColors();
};
const saveCurrentTheme = async () => {
if (!currentColors.value) {
throw new Error('No theme selected');
}
const setTheme = async (theme: SystemThemeType) => {
await configStore.setSystemTheme(theme);
applyThemeToDOM(theme);
refreshEditorTheme();
};
const themeName = resolveThemeName(currentColors.value.themeName);
currentColors.value.themeName = themeName;
const switchToTheme = async (themeName: string) => {
if (!themePresetMap[themeName]) {
console.error('Theme not found:', themeName);
return false;
}
await ThemeService.UpdateTheme(themeName, currentColors.value as unknown as ThemeColorConfig);
await loadThemeColors(themeName);
await configStore.setCurrentTheme(themeName);
refreshEditorTheme();
return true;
};
await loadThemeColors(themeName);
refreshEditorTheme();
return true;
};
const updateCurrentColors = (colors: Partial<ThemeColors>) => {
if (!currentColors.value) return;
Object.assign(currentColors.value, colors);
};
const resetCurrentTheme = async () => {
if (!currentColors.value) {
throw new Error('No theme selected');
}
const saveCurrentTheme = async () => {
if (!currentColors.value) {
throw new Error('No theme selected');
}
const themeName = resolveThemeName(currentColors.value.themeName);
await ThemeService.ResetTheme(themeName);
const themeName = resolveThemeName(currentColors.value.themeName);
currentColors.value.themeName = themeName;
await loadThemeColors(themeName);
refreshEditorTheme();
return true;
};
await ThemeService.UpdateTheme(themeName, currentColors.value as ThemeColorConfig);
const refreshEditorTheme = () => {
applyThemeToDOM(currentTheme.value);
const editorStore = useEditorStore();
editorStore?.applyThemeSettings();
};
await loadThemeColors(themeName);
refreshEditorTheme();
return true;
};
return {
availableThemes,
currentTheme,
currentColors,
isDarkMode,
setTheme,
switchToTheme,
initializeTheme,
updateCurrentColors,
saveCurrentTheme,
resetCurrentTheme,
refreshEditorTheme,
applyThemeToDOM,
};
const resetCurrentTheme = async () => {
if (!currentColors.value) {
throw new Error('No theme selected');
}
const themeName = resolveThemeName(currentColors.value.themeName);
await ThemeService.ResetTheme(themeName);
await loadThemeColors(themeName);
refreshEditorTheme();
return true;
};
const refreshEditorTheme = () => {
applyThemeToDOM(currentTheme.value);
const editorStore = useEditorStore();
editorStore?.applyThemeSettings();
};
return {
availableThemes,
currentTheme,
currentColors,
isDarkMode,
setTheme,
switchToTheme,
initTheme,
updateCurrentColors,
saveCurrentTheme,
resetCurrentTheme,
refreshEditorTheme,
applyThemeToDOM,
};
});