feat: 添加编辑器自定义字体功能
This commit is contained in:
@@ -58,6 +58,9 @@ const mapRowsToAppearanceSettings = (rows: DbAppearanceSettingsRow[]): Appearanc
|
|||||||
settings.terminalBackgroundOverlayOpacity = parseFloat(row.value);
|
settings.terminalBackgroundOverlayOpacity = parseFloat(row.value);
|
||||||
terminalBackgroundOverlayOpacityFound = true;
|
terminalBackgroundOverlayOpacityFound = true;
|
||||||
break;
|
break;
|
||||||
|
case 'editorFontFamily':
|
||||||
|
settings.editorFontFamily = row.value || null; // 如果为空字符串,则视为 null
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +72,7 @@ const mapRowsToAppearanceSettings = (rows: DbAppearanceSettingsRow[]): Appearanc
|
|||||||
terminalFontFamily: settings.terminalFontFamily ?? defaults.terminalFontFamily,
|
terminalFontFamily: settings.terminalFontFamily ?? defaults.terminalFontFamily,
|
||||||
terminalFontSize: settings.terminalFontSize ?? defaults.terminalFontSize,
|
terminalFontSize: settings.terminalFontSize ?? defaults.terminalFontSize,
|
||||||
editorFontSize: settings.editorFontSize ?? defaults.editorFontSize,
|
editorFontSize: settings.editorFontSize ?? defaults.editorFontSize,
|
||||||
|
editorFontFamily: settings.editorFontFamily ?? defaults.editorFontFamily,
|
||||||
terminalBackgroundImage: settings.terminalBackgroundImage ?? defaults.terminalBackgroundImage,
|
terminalBackgroundImage: settings.terminalBackgroundImage ?? defaults.terminalBackgroundImage,
|
||||||
pageBackgroundImage: settings.pageBackgroundImage ?? defaults.pageBackgroundImage,
|
pageBackgroundImage: settings.pageBackgroundImage ?? defaults.pageBackgroundImage,
|
||||||
// 修改:只有当数据库中未找到记录时才使用默认值
|
// 修改:只有当数据库中未找到记录时才使用默认值
|
||||||
@@ -91,6 +95,7 @@ const getDefaultAppearanceSettings = (): Omit<AppearanceSettings, '_id'> => {
|
|||||||
terminalFontFamily: 'Consolas, "Courier New", monospace, "Microsoft YaHei", "微软雅黑"',
|
terminalFontFamily: 'Consolas, "Courier New", monospace, "Microsoft YaHei", "微软雅黑"',
|
||||||
terminalFontSize: 14,
|
terminalFontSize: 14,
|
||||||
editorFontSize: 14,
|
editorFontSize: 14,
|
||||||
|
editorFontFamily: 'Consolas, "Noto Sans SC", "Microsoft YaHei"',
|
||||||
terminalBackgroundImage: undefined,
|
terminalBackgroundImage: undefined,
|
||||||
pageBackgroundImage: undefined,
|
pageBackgroundImage: undefined,
|
||||||
terminalBackgroundEnabled: true, // 默认启用
|
terminalBackgroundEnabled: true, // 默认启用
|
||||||
@@ -117,6 +122,7 @@ export const ensureDefaultSettingsExist = async (db: sqlite3.Database): Promise<
|
|||||||
{ key: 'terminalFontFamily', value: defaults.terminalFontFamily },
|
{ key: 'terminalFontFamily', value: defaults.terminalFontFamily },
|
||||||
{ key: 'terminalFontSize', value: defaults.terminalFontSize },
|
{ key: 'terminalFontSize', value: defaults.terminalFontSize },
|
||||||
{ key: 'editorFontSize', value: defaults.editorFontSize },
|
{ key: 'editorFontSize', value: defaults.editorFontSize },
|
||||||
|
{ key: 'editorFontFamily', value: defaults.editorFontFamily },
|
||||||
{ key: 'terminalBackgroundImage', value: defaults.terminalBackgroundImage ?? '' }, // 数据库中使用空字符串
|
{ key: 'terminalBackgroundImage', value: defaults.terminalBackgroundImage ?? '' }, // 数据库中使用空字符串
|
||||||
{ key: 'pageBackgroundImage', value: defaults.pageBackgroundImage ?? '' }, // 数据库中使用空字符串
|
{ key: 'pageBackgroundImage', value: defaults.pageBackgroundImage ?? '' }, // 数据库中使用空字符串
|
||||||
{ key: 'terminalBackgroundEnabled', value: defaults.terminalBackgroundEnabled },
|
{ key: 'terminalBackgroundEnabled', value: defaults.terminalBackgroundEnabled },
|
||||||
|
|||||||
@@ -69,6 +69,28 @@ export const updateSettings = async (settingsDto: UpdateAppearanceDto): Promise<
|
|||||||
settingsDto.editorFontSize = size;
|
settingsDto.editorFontSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证 editorFontFamily (如果提供了)
|
||||||
|
if (settingsDto.hasOwnProperty('editorFontFamily')) {
|
||||||
|
if (settingsDto.editorFontFamily === null) {
|
||||||
|
// 允许用户将字体设置为空 (null),表示重置或使用默认
|
||||||
|
// 无需额外操作,仓库层会处理 null
|
||||||
|
} else if (typeof settingsDto.editorFontFamily === 'string') {
|
||||||
|
const fontFamily = settingsDto.editorFontFamily;
|
||||||
|
// 校验字体名称格式和长度
|
||||||
|
if (fontFamily.length > 255) {
|
||||||
|
throw new Error('编辑器字体名称过长,最多允许 255 个字符。');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fontFamily.trim() === '' && fontFamily !== '') {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 如果提供了 editorFontFamily 但不是 string 或 null
|
||||||
|
throw new Error('无效的编辑器字体名称类型,应为字符串或 null。');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 验证 terminalBackgroundOverlayOpacity (如果提供了)
|
// 验证 terminalBackgroundOverlayOpacity (如果提供了)
|
||||||
if (settingsDto.terminalBackgroundOverlayOpacity !== undefined && settingsDto.terminalBackgroundOverlayOpacity !== null) {
|
if (settingsDto.terminalBackgroundOverlayOpacity !== undefined && settingsDto.terminalBackgroundOverlayOpacity !== null) {
|
||||||
const opacity = Number(settingsDto.terminalBackgroundOverlayOpacity);
|
const opacity = Number(settingsDto.terminalBackgroundOverlayOpacity);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export interface AppearanceSettings {
|
|||||||
terminalBackgroundImage?: string; // 终端背景图片 URL 或路径
|
terminalBackgroundImage?: string; // 终端背景图片 URL 或路径
|
||||||
pageBackgroundImage?: string; // 页面背景图片 URL 或路径
|
pageBackgroundImage?: string; // 页面背景图片 URL 或路径
|
||||||
editorFontSize?: number; // 编辑器字体大小 (px)
|
editorFontSize?: number; // 编辑器字体大小 (px)
|
||||||
|
editorFontFamily?: string | null; // Monaco Editor 字体偏好
|
||||||
terminalBackgroundEnabled?: boolean; // 终端背景是否启用
|
terminalBackgroundEnabled?: boolean; // 终端背景是否启用
|
||||||
terminalBackgroundOverlayOpacity?: number; // 终端背景蒙版透明度 (0-1)
|
terminalBackgroundOverlayOpacity?: number; // 终端背景蒙版透明度 (0-1)
|
||||||
updatedAt?: number;
|
updatedAt?: number;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nexus-terminal/frontend",
|
"name": "@nexus-terminal/frontend",
|
||||||
"version": "0.7",
|
"version": "0.7.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -5,18 +5,21 @@ import MonacoEditor from './MonacoEditor.vue';
|
|||||||
import FileEditorTabs from './FileEditorTabs.vue';
|
import FileEditorTabs from './FileEditorTabs.vue';
|
||||||
import type { FileTab } from '../stores/fileEditor.store';
|
import type { FileTab } from '../stores/fileEditor.store';
|
||||||
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store';
|
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store';
|
||||||
import { useSessionStore } from '../stores/session.store';
|
import { useSessionStore } from '../stores/session.store';
|
||||||
import { useSettingsStore } from '../stores/settings.store';
|
import { useSettingsStore } from '../stores/settings.store';
|
||||||
import { storeToRefs } from 'pinia';
|
import { useAppearanceStore } from '../stores/appearance.store'; // +++ 导入外观 Store +++
|
||||||
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
|
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
|
||||||
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
||||||
const sessionStore = useSessionStore(); // +++ 实例化会话 Store +++
|
const sessionStore = useSessionStore(); // +++ 实例化会话 Store +++
|
||||||
const settingsStore = useSettingsStore(); // +++ 实例化设置 Store +++
|
const settingsStore = useSettingsStore(); // +++ 实例化设置 Store +++
|
||||||
|
const appearanceStore = useAppearanceStore(); // +++ 实例化外观 Store +++
|
||||||
const { shareFileEditorTabsBoolean } = storeToRefs(settingsStore); // +++ 获取共享设置 +++
|
const { shareFileEditorTabsBoolean } = storeToRefs(settingsStore); // +++ 获取共享设置 +++
|
||||||
|
const { currentEditorFontFamily } = storeToRefs(appearanceStore); // +++ 获取编辑器字体家族设置 +++
|
||||||
|
|
||||||
// --- Props ---
|
// --- Props ---
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tabs: {
|
tabs: {
|
||||||
@@ -344,16 +347,17 @@ const handleKeyDown = (event: KeyboardEvent) => {
|
|||||||
v-else-if="activeTab"
|
v-else-if="activeTab"
|
||||||
ref="monacoEditorRef"
|
ref="monacoEditorRef"
|
||||||
:key="activeTab.id"
|
:key="activeTab.id"
|
||||||
v-model="localEditorContent"
|
v-model="localEditorContent"
|
||||||
:language="currentTabLanguage"
|
:language="currentTabLanguage"
|
||||||
|
:font-family="currentEditorFontFamily"
|
||||||
theme="vs-dark"
|
theme="vs-dark"
|
||||||
class="editor-instance"
|
class="editor-instance"
|
||||||
@request-save="handleSaveRequest"
|
@request-save="handleSaveRequest"
|
||||||
:initialScrollTop="activeTab?.scrollTop ?? 0"
|
:initialScrollTop="activeTab?.scrollTop ?? 0"
|
||||||
:initialScrollLeft="activeTab?.scrollLeft ?? 0"
|
:initialScrollLeft="activeTab?.scrollLeft ?? 0"
|
||||||
@update:scrollPosition="handleEditorScroll"
|
@update:scrollPosition="handleEditorScroll"
|
||||||
/>
|
/>
|
||||||
<div v-else class="editor-placeholder">{{ t('fileManager.selectFileToEdit') }}</div>
|
<div v-else class="editor-placeholder">{{ t('fileManager.selectFileToEdit') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,17 +25,21 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
initialScrollTop: { // 新增 prop
|
fontFamily: {
|
||||||
|
type: String,
|
||||||
|
default: 'Consolas, "Courier New", monospace',
|
||||||
|
},
|
||||||
|
initialScrollTop: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
initialScrollLeft: { // 新增 prop
|
initialScrollLeft: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'request-save', 'update:scrollPosition']); // 新增 emit
|
const emit = defineEmits(['update:modelValue', 'request-save', 'update:scrollPosition']);
|
||||||
|
|
||||||
const editorContainer = ref<HTMLElement | null>(null);
|
const editorContainer = ref<HTMLElement | null>(null);
|
||||||
let editorInstance: monaco.editor.IStandaloneCodeEditor | null = null;
|
let editorInstance: monaco.editor.IStandaloneCodeEditor | null = null;
|
||||||
@@ -48,7 +52,8 @@ onMounted(() => {
|
|||||||
value: props.modelValue,
|
value: props.modelValue,
|
||||||
language: props.language,
|
language: props.language,
|
||||||
theme: props.theme,
|
theme: props.theme,
|
||||||
fontSize: localFontSize.value,
|
fontSize: localFontSize.value,
|
||||||
|
fontFamily: props.fontFamily, // 使用 prop 的字体家族
|
||||||
automaticLayout: true,
|
automaticLayout: true,
|
||||||
readOnly: props.readOnly,
|
readOnly: props.readOnly,
|
||||||
minimap: { enabled: true },
|
minimap: { enabled: true },
|
||||||
@@ -211,6 +216,12 @@ watch(() => props.readOnly, (newReadOnly) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(() => props.fontFamily, (newFontFamily) => {
|
||||||
|
if (editorInstance) {
|
||||||
|
editorInstance.updateOptions({ fontFamily: newFontFamily });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// --- 移除对全局字体大小的监听 ---
|
// --- 移除对全局字体大小的监听 ---
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted, watch, computed } from 'vue';
|
import { ref, reactive, onMounted, watch, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useAppearanceStore } from '../stores/appearance.store';
|
import { useAppearanceStore } from '../stores/appearance.store';
|
||||||
|
import { useUiNotificationsStore } from '../stores/uiNotifications.store';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import type { ITheme } from 'xterm';
|
import type { ITheme } from 'xterm';
|
||||||
import type { TerminalTheme } from '../types/terminal-theme.types';
|
import type { TerminalTheme } from '../types/terminal-theme.types';
|
||||||
@@ -9,6 +10,7 @@ import { defaultXtermTheme } from '../features/appearance/config/default-themes'
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const appearanceStore = useAppearanceStore();
|
const appearanceStore = useAppearanceStore();
|
||||||
|
const notificationsStore = useUiNotificationsStore();
|
||||||
const {
|
const {
|
||||||
appearanceSettings,
|
appearanceSettings,
|
||||||
currentUiTheme,
|
currentUiTheme,
|
||||||
@@ -17,7 +19,8 @@ const {
|
|||||||
allTerminalThemes,
|
allTerminalThemes,
|
||||||
currentTerminalFontFamily,
|
currentTerminalFontFamily,
|
||||||
currentTerminalFontSize,
|
currentTerminalFontSize,
|
||||||
currentEditorFontSize,
|
currentEditorFontSize,
|
||||||
|
currentEditorFontFamily,
|
||||||
pageBackgroundImage,
|
pageBackgroundImage,
|
||||||
|
|
||||||
terminalBackgroundImage,
|
terminalBackgroundImage,
|
||||||
@@ -30,10 +33,11 @@ const {
|
|||||||
const editableUiTheme = ref<Record<string, string>>({});
|
const editableUiTheme = ref<Record<string, string>>({});
|
||||||
const editableTerminalFontFamily = ref('');
|
const editableTerminalFontFamily = ref('');
|
||||||
const editableTerminalFontSize = ref(14);
|
const editableTerminalFontSize = ref(14);
|
||||||
const editableEditorFontSize = ref(14);
|
const editableEditorFontSize = ref(14);
|
||||||
|
const editableEditorFontFamily = ref('');
|
||||||
|
|
||||||
|
|
||||||
const editableUiThemeString = ref('');
|
const editableUiThemeString = ref('');
|
||||||
const themeParseError = ref<string | null>(null);
|
const themeParseError = ref<string | null>(null);
|
||||||
const localTerminalBackgroundEnabled = ref(true);
|
const localTerminalBackgroundEnabled = ref(true);
|
||||||
const editableTerminalBackgroundOverlayOpacity = ref(0.5); // 本地编辑状态,默认 0.5
|
const editableTerminalBackgroundOverlayOpacity = ref(0.5); // 本地编辑状态,默认 0.5
|
||||||
@@ -95,7 +99,8 @@ const initializeEditableState = () => {
|
|||||||
// --- 其他初始化保持不变 ---
|
// --- 其他初始化保持不变 ---
|
||||||
editableTerminalFontFamily.value = currentTerminalFontFamily.value;
|
editableTerminalFontFamily.value = currentTerminalFontFamily.value;
|
||||||
editableTerminalFontSize.value = currentTerminalFontSize.value;
|
editableTerminalFontSize.value = currentTerminalFontSize.value;
|
||||||
editableEditorFontSize.value = currentEditorFontSize.value; // <-- 新增
|
editableEditorFontSize.value = currentEditorFontSize.value;
|
||||||
|
editableEditorFontFamily.value = currentEditorFontFamily.value; // 初始化编辑器字体家族
|
||||||
localTerminalBackgroundEnabled.value = isTerminalBackgroundEnabled.value; // <-- 重新添加:在此处初始化
|
localTerminalBackgroundEnabled.value = isTerminalBackgroundEnabled.value; // <-- 重新添加:在此处初始化
|
||||||
editableTerminalBackgroundOverlayOpacity.value = currentTerminalBackgroundOverlayOpacity.value; // 初始化蒙版透明度
|
editableTerminalBackgroundOverlayOpacity.value = currentTerminalBackgroundOverlayOpacity.value; // 初始化蒙版透明度
|
||||||
console.log(`[StyleCustomizer initializeEditableState] Initializing localTerminalBackgroundEnabled to: ${localTerminalBackgroundEnabled.value} (from store: ${isTerminalBackgroundEnabled.value})`);
|
console.log(`[StyleCustomizer initializeEditableState] Initializing localTerminalBackgroundEnabled to: ${localTerminalBackgroundEnabled.value} (from store: ${isTerminalBackgroundEnabled.value})`);
|
||||||
@@ -154,7 +159,8 @@ watch([
|
|||||||
// 字体等信息需要从 newSettings 中获取
|
// 字体等信息需要从 newSettings 中获取
|
||||||
editableTerminalFontFamily.value = newSettings?.terminalFontFamily || '';
|
editableTerminalFontFamily.value = newSettings?.terminalFontFamily || '';
|
||||||
editableTerminalFontSize.value = newSettings?.terminalFontSize || 14;
|
editableTerminalFontSize.value = newSettings?.terminalFontSize || 14;
|
||||||
editableEditorFontSize.value = newSettings?.editorFontSize || 14; // <-- 新增同步
|
editableEditorFontSize.value = newSettings?.editorFontSize || 14;
|
||||||
|
editableEditorFontFamily.value = newSettings?.editorFontFamily || 'Consolas, "Noto Sans SC", "Microsoft YaHei"'; // 同步编辑器字体家族
|
||||||
// localTerminalBackgroundEnabled.value = newSettings?.terminalBackgroundEnabled ?? true; // <-- 移除此行,避免冲突
|
// localTerminalBackgroundEnabled.value = newSettings?.terminalBackgroundEnabled ?? true; // <-- 移除此行,避免冲突
|
||||||
// editableTerminalBackgroundOverlayOpacity.value = newSettings?.terminalBackgroundOverlayOpacity ?? 0.5; // 在 initializeEditableState 中处理
|
// editableTerminalBackgroundOverlayOpacity.value = newSettings?.terminalBackgroundOverlayOpacity ?? 0.5; // 在 initializeEditableState 中处理
|
||||||
}
|
}
|
||||||
@@ -195,7 +201,7 @@ const closeCustomizer = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 当前活动的标签页
|
// 当前活动的标签页
|
||||||
const currentTab = ref<'ui' | 'terminal' | 'background' | 'other'>('ui'); // <-- 添加 'other'
|
const currentTab = ref<'ui' | 'terminal' | 'background' | 'other'>('ui');
|
||||||
|
|
||||||
// --- 处理函数 ---
|
// --- 处理函数 ---
|
||||||
|
|
||||||
@@ -203,9 +209,10 @@ const currentTab = ref<'ui' | 'terminal' | 'background' | 'other'>('ui'); // <--
|
|||||||
const handleSaveUiTheme = async () => {
|
const handleSaveUiTheme = async () => {
|
||||||
try {
|
try {
|
||||||
await appearanceStore.saveCustomUiTheme(editableUiTheme.value);
|
await appearanceStore.saveCustomUiTheme(editableUiTheme.value);
|
||||||
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.uiThemeSaved') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("保存 UI 主题失败:", error);
|
console.error("保存 UI 主题失败:", error);
|
||||||
alert(t('styleCustomizer.uiThemeSaveFailed', { message: error.message }));
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.uiThemeSaveFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -213,9 +220,10 @@ const handleSaveUiTheme = async () => {
|
|||||||
const handleResetUiTheme = async () => {
|
const handleResetUiTheme = async () => {
|
||||||
try {
|
try {
|
||||||
await appearanceStore.resetCustomUiTheme();
|
await appearanceStore.resetCustomUiTheme();
|
||||||
|
notificationsStore.addNotification({ type: 'info', message: t('styleCustomizer.uiThemeReset') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("重置 UI 主题失败:", error);
|
console.error("重置 UI 主题失败:", error);
|
||||||
alert(t('styleCustomizer.uiThemeResetFailed', { message: error.message }));
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.uiThemeResetFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -256,10 +264,10 @@ const applyDarkMode = async () => {
|
|||||||
// 深拷贝覆盖当前编辑的主题
|
// 深拷贝覆盖当前编辑的主题
|
||||||
editableUiTheme.value = JSON.parse(JSON.stringify(darkModeTheme));
|
editableUiTheme.value = JSON.parse(JSON.stringify(darkModeTheme));
|
||||||
await appearanceStore.saveCustomUiTheme(editableUiTheme.value);
|
await appearanceStore.saveCustomUiTheme(editableUiTheme.value);
|
||||||
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.darkModeApplied') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("应用黑暗模式失败:", error);
|
console.error("应用黑暗模式失败:", error);
|
||||||
// TODO: 添加 i18n 翻译 'styleCustomizer.darkModeApplyFailed'
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.darkModeApplyFailed', { message: error.message || '未知错误' }) });
|
||||||
alert(t('styleCustomizer.darkModeApplyFailed', { message: error.message || '未知错误' }));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -382,7 +390,7 @@ const handleUiThemeStringChange = () => {
|
|||||||
// 尝试提供更具体的错误信息
|
// 尝试提供更具体的错误信息
|
||||||
let errorMessage = error.message || t('styleCustomizer.errorInvalidJsonConfig');
|
let errorMessage = error.message || t('styleCustomizer.errorInvalidJsonConfig');
|
||||||
if (error instanceof SyntaxError) {
|
if (error instanceof SyntaxError) {
|
||||||
errorMessage = `${t('styleCustomizer.errorJsonSyntax')}: ${error.message}`; // 需要添加翻译
|
errorMessage = `${t('styleCustomizer.errorJsonSyntax')}: ${error.message}`;
|
||||||
}
|
}
|
||||||
themeParseError.value = errorMessage;
|
themeParseError.value = errorMessage;
|
||||||
}
|
}
|
||||||
@@ -393,10 +401,10 @@ const handleUiThemeStringChange = () => {
|
|||||||
const handleSaveTerminalFont = async () => {
|
const handleSaveTerminalFont = async () => {
|
||||||
try {
|
try {
|
||||||
await appearanceStore.setTerminalFontFamily(editableTerminalFontFamily.value);
|
await appearanceStore.setTerminalFontFamily(editableTerminalFontFamily.value);
|
||||||
alert(t('styleCustomizer.terminalFontSaved'));
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.terminalFontSaved') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("保存终端字体失败:", error);
|
console.error("保存终端字体失败:", error);
|
||||||
alert(t('styleCustomizer.terminalFontSaveFailed', { message: error.message }));
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.terminalFontSaveFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -405,14 +413,14 @@ const handleSaveTerminalFontSize = async () => {
|
|||||||
try {
|
try {
|
||||||
const size = Number(editableTerminalFontSize.value);
|
const size = Number(editableTerminalFontSize.value);
|
||||||
if (isNaN(size) || size <= 0) {
|
if (isNaN(size) || size <= 0) {
|
||||||
alert(t('styleCustomizer.errorInvalidFontSize')); // 需要添加翻译
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.errorInvalidFontSize') });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await appearanceStore.setTerminalFontSize(size);
|
await appearanceStore.setTerminalFontSize(size);
|
||||||
alert(t('styleCustomizer.terminalFontSizeSaved')); // 需要添加翻译
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.terminalFontSizeSaved') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("保存终端字体大小失败:", error);
|
console.error("保存终端字体大小失败:", error);
|
||||||
alert(t('styleCustomizer.terminalFontSizeSaveFailed', { message: error.message })); // 需要添加翻译
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.terminalFontSizeSaveFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -421,17 +429,28 @@ const handleSaveEditorFontSize = async () => {
|
|||||||
try {
|
try {
|
||||||
const size = Number(editableEditorFontSize.value);
|
const size = Number(editableEditorFontSize.value);
|
||||||
if (isNaN(size) || size <= 0) {
|
if (isNaN(size) || size <= 0) {
|
||||||
alert(t('styleCustomizer.errorInvalidEditorFontSize')); // 需要添加翻译
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.errorInvalidEditorFontSize') });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await appearanceStore.setEditorFontSize(size);
|
await appearanceStore.setEditorFontSize(size);
|
||||||
alert(t('styleCustomizer.editorFontSizeSaved')); // 需要添加翻译
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.editorFontSizeSaved') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("保存编辑器字体大小失败:", error);
|
console.error("保存编辑器字体大小失败:", error);
|
||||||
alert(t('styleCustomizer.editorFontSizeSaveFailed', { message: error.message })); // 需要添加翻译
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.editorFontSizeSaveFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 保存编辑器字体家族
|
||||||
|
const handleSaveEditorFontFamily = async () => {
|
||||||
|
try {
|
||||||
|
await appearanceStore.setEditorFontFamily(editableEditorFontFamily.value);
|
||||||
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.editorFontFamilySaved') });
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("保存编辑器字体失败:", error);
|
||||||
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.editorFontFamilySaveFailed', { message: error.message }) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 应用选定的终端主题
|
// 应用选定的终端主题
|
||||||
const handleApplyTheme = async (theme: TerminalTheme) => {
|
const handleApplyTheme = async (theme: TerminalTheme) => {
|
||||||
// theme._id 是字符串 ID
|
// theme._id 是字符串 ID
|
||||||
@@ -450,10 +469,10 @@ const handleApplyTheme = async (theme: TerminalTheme) => {
|
|||||||
try {
|
try {
|
||||||
// setActiveTerminalTheme action 现在需要字符串 ID
|
// setActiveTerminalTheme action 现在需要字符串 ID
|
||||||
await appearanceStore.setActiveTerminalTheme(theme._id);
|
await appearanceStore.setActiveTerminalTheme(theme._id);
|
||||||
// 成功后 activeTerminalThemeId 会自动更新
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.setActiveThemeSuccess', { themeName: theme.name }) });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("应用终端主题失败:", error);
|
console.error("应用终端主题失败:", error);
|
||||||
alert(t('styleCustomizer.setActiveThemeFailed', { message: error.message }));
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.setActiveThemeFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -494,7 +513,7 @@ const handleEditTheme = async (theme: TerminalTheme) => {
|
|||||||
// 检查 theme._id 是否存在
|
// 检查 theme._id 是否存在
|
||||||
if (!theme._id) {
|
if (!theme._id) {
|
||||||
console.error("尝试编辑没有 ID 的主题:", theme);
|
console.error("尝试编辑没有 ID 的主题:", theme);
|
||||||
alert(t('styleCustomizer.errorEditThemeNoId')); // 需要添加翻译: "无法编辑没有 ID 的主题"
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.errorEditThemeNoId') });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,7 +525,7 @@ const handleEditTheme = async (theme: TerminalTheme) => {
|
|||||||
// 1. 加载主题数据
|
// 1. 加载主题数据
|
||||||
themeDataToEdit = await appearanceStore.loadTerminalThemeData(theme._id);
|
themeDataToEdit = await appearanceStore.loadTerminalThemeData(theme._id);
|
||||||
if (!themeDataToEdit) {
|
if (!themeDataToEdit) {
|
||||||
throw new Error(t('styleCustomizer.errorLoadThemeDataFailed')); // 需要添加翻译: "加载主题数据失败"
|
throw new Error(t('styleCustomizer.errorLoadThemeDataFailed'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 如果是预设主题,准备创建副本
|
// 2. 如果是预设主题,准备创建副本
|
||||||
@@ -544,7 +563,7 @@ const handleEditTheme = async (theme: TerminalTheme) => {
|
|||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("编辑主题失败 (加载数据时):", error);
|
console.error("编辑主题失败 (加载数据时):", error);
|
||||||
saveThemeError.value = error.message || t('styleCustomizer.errorEditThemeFailed'); // 需要添加翻译: "编辑主题失败"
|
saveThemeError.value = error.message || t('styleCustomizer.errorEditThemeFailed');
|
||||||
// 不进入编辑模式
|
// 不进入编辑模式
|
||||||
isEditingTheme.value = false;
|
isEditingTheme.value = false;
|
||||||
editingTheme.value = null;
|
editingTheme.value = null;
|
||||||
@@ -560,7 +579,7 @@ const handleSaveEditingTheme = async () => {
|
|||||||
// 在保存前,确保 themeData 是最新的(以防 textarea 未失去焦点)
|
// 在保存前,确保 themeData 是最新的(以防 textarea 未失去焦点)
|
||||||
handleTerminalThemeStringChange(); // 先尝试解析 textarea
|
handleTerminalThemeStringChange(); // 先尝试解析 textarea
|
||||||
if (terminalThemeParseError.value) {
|
if (terminalThemeParseError.value) {
|
||||||
saveThemeError.value = t('styleCustomizer.errorFixJsonBeforeSave'); // 需要添加翻译: "请先修复 JSON 格式错误再保存"
|
saveThemeError.value = t('styleCustomizer.errorFixJsonBeforeSave');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,18 +597,18 @@ const handleSaveEditingTheme = async () => {
|
|||||||
editingTheme.value._id,
|
editingTheme.value._id,
|
||||||
updateDto.name,
|
updateDto.name,
|
||||||
updateDto.themeData
|
updateDto.themeData
|
||||||
);
|
);
|
||||||
alert(t('styleCustomizer.themeUpdatedSuccess'));
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.themeUpdatedSuccess') });
|
||||||
} else { // 新建
|
} else { // 新建
|
||||||
// 确保传递的是 CreateTerminalThemeDto 兼容的格式
|
// 确保传递的是 CreateTerminalThemeDto 兼容的格式
|
||||||
const createDto = { name: editingTheme.value.name, themeData: currentThemeData }; // 使用解析后的数据
|
const createDto = { name: editingTheme.value.name, themeData: currentThemeData }; // 使用解析后的数据
|
||||||
await appearanceStore.createTerminalTheme(
|
await appearanceStore.createTerminalTheme(
|
||||||
createDto.name,
|
createDto.name,
|
||||||
createDto.themeData
|
createDto.themeData
|
||||||
);
|
);
|
||||||
alert(t('styleCustomizer.themeCreatedSuccess'));
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.themeCreatedSuccess') });
|
||||||
}
|
}
|
||||||
isEditingTheme.value = false; // 关闭编辑
|
isEditingTheme.value = false; // 关闭编辑
|
||||||
editingTheme.value = null;
|
editingTheme.value = null;
|
||||||
editableTerminalThemeString.value = ''; // 清理
|
editableTerminalThemeString.value = ''; // 清理
|
||||||
terminalThemeParseError.value = null; // 清理
|
terminalThemeParseError.value = null; // 清理
|
||||||
@@ -680,10 +699,10 @@ const handleDeleteTheme = async (theme: TerminalTheme) => {
|
|||||||
if (theme.isPreset) return;
|
if (theme.isPreset) return;
|
||||||
try {
|
try {
|
||||||
await appearanceStore.deleteTerminalTheme(theme._id!);
|
await appearanceStore.deleteTerminalTheme(theme._id!);
|
||||||
alert(t('styleCustomizer.themeDeletedSuccess'));
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.themeDeletedSuccess') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("删除终端主题失败:", error);
|
console.error("删除终端主题失败:", error);
|
||||||
alert(t('styleCustomizer.themeDeleteFailed', { message: error.message }));
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.themeDeleteFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -702,12 +721,15 @@ const handleImportThemeFile = async (event: Event) => {
|
|||||||
// 可以选择在前端解析文件名作为默认名称传递给后端
|
// 可以选择在前端解析文件名作为默认名称传递给后端
|
||||||
const defaultName = file.name.endsWith('.json') ? file.name.slice(0, -5) : file.name;
|
const defaultName = file.name.endsWith('.json') ? file.name.slice(0, -5) : file.name;
|
||||||
await appearanceStore.importTerminalTheme(file, defaultName); // 传递文件名作为备选名称
|
await appearanceStore.importTerminalTheme(file, defaultName); // 传递文件名作为备选名称
|
||||||
alert(t('styleCustomizer.importSuccess'));
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.importSuccess') });
|
||||||
input.value = ''; // 清空文件输入,以便再次选择相同文件
|
input.value = ''; // 清空文件输入,以便再次选择相同文件
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("导入主题失败:", error);
|
console.error("导入主题失败:", error);
|
||||||
importError.value = error.message || t('styleCustomizer.importFailed');
|
const determinedErrorMessage = error.message || t('styleCustomizer.importFailed');
|
||||||
input.value = '';
|
importError.value = determinedErrorMessage; // importError.value 的类型是 string | null
|
||||||
|
// 确保传递给 addNotification 的 message 是 string 类型
|
||||||
|
notificationsStore.addNotification({ type: 'error', message: determinedErrorMessage });
|
||||||
|
input.value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -720,13 +742,15 @@ const handleExportActiveTheme = async () => {
|
|||||||
try {
|
try {
|
||||||
// exportTerminalTheme action 需要字符串 ID
|
// exportTerminalTheme action 需要字符串 ID
|
||||||
await appearanceStore.exportTerminalTheme(currentIdNum.toString());
|
await appearanceStore.exportTerminalTheme(currentIdNum.toString());
|
||||||
|
// 导出通常是下载文件,可能不需要显式通知,或者可以有一个信息性通知
|
||||||
|
notificationsStore.addNotification({ type: 'info', message: t('styleCustomizer.exportInitiated') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("导出主题失败:", error);
|
console.error("导出主题失败:", error);
|
||||||
alert(t('styleCustomizer.exportFailed', { message: error.message }));
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.exportFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn("尝试导出主题,但 activeTerminalThemeId 为 null 或 undefined");
|
console.warn("尝试导出主题,但 activeTerminalThemeId 为 null 或 undefined");
|
||||||
alert(t('styleCustomizer.noActiveThemeToExport'));
|
notificationsStore.addNotification({ type: 'warning', message: t('styleCustomizer.noActiveThemeToExport') });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -747,10 +771,12 @@ const handlePageBgUpload = async (event: Event) => {
|
|||||||
const file = input.files[0];
|
const file = input.files[0];
|
||||||
try {
|
try {
|
||||||
await appearanceStore.uploadPageBackground(file);
|
await appearanceStore.uploadPageBackground(file);
|
||||||
alert(t('styleCustomizer.pageBgUploadSuccess'));
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.pageBgUploadSuccess') });
|
||||||
input.value = ''; // 清空以便再次选择
|
input.value = ''; // 清空以便再次选择
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
uploadError.value = error.message || t('styleCustomizer.uploadFailed');
|
const determinedErrorMessage = error.message || t('styleCustomizer.uploadFailed');
|
||||||
|
uploadError.value = determinedErrorMessage;
|
||||||
|
notificationsStore.addNotification({ type: 'error', message: determinedErrorMessage }); // 显示错误通知
|
||||||
input.value = '';
|
input.value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -762,10 +788,12 @@ const handleTerminalBgUpload = async (event: Event) => {
|
|||||||
const file = input.files[0];
|
const file = input.files[0];
|
||||||
try {
|
try {
|
||||||
await appearanceStore.uploadTerminalBackground(file);
|
await appearanceStore.uploadTerminalBackground(file);
|
||||||
alert(t('styleCustomizer.terminalBgUploadSuccess'));
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.terminalBgUploadSuccess') });
|
||||||
input.value = '';
|
input.value = '';
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
uploadError.value = error.message || t('styleCustomizer.uploadFailed');
|
const determinedErrorMessage = error.message || t('styleCustomizer.uploadFailed');
|
||||||
|
uploadError.value = determinedErrorMessage;
|
||||||
|
notificationsStore.addNotification({ type: 'error', message: determinedErrorMessage }); // 显示错误通知
|
||||||
input.value = '';
|
input.value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -774,20 +802,20 @@ const handleTerminalBgUpload = async (event: Event) => {
|
|||||||
const handleRemovePageBg = async () => {
|
const handleRemovePageBg = async () => {
|
||||||
try {
|
try {
|
||||||
await appearanceStore.removePageBackground();
|
await appearanceStore.removePageBackground();
|
||||||
alert(t('styleCustomizer.pageBgRemoved'));
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.pageBgRemoved') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("移除页面背景失败:", error);
|
console.error("移除页面背景失败:", error);
|
||||||
alert(t('styleCustomizer.removeBgFailed', { message: error.message }));
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.removeBgFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveTerminalBg = async () => {
|
const handleRemoveTerminalBg = async () => {
|
||||||
try {
|
try {
|
||||||
await appearanceStore.removeTerminalBackground();
|
await appearanceStore.removeTerminalBackground();
|
||||||
alert(t('styleCustomizer.terminalBgRemoved'));
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.terminalBgRemoved') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("移除终端背景失败:", error);
|
console.error("移除终端背景失败:", error);
|
||||||
alert(t('styleCustomizer.removeBgFailed', { message: error.message }));
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.removeBgFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -802,7 +830,7 @@ const handleToggleTerminalBackground = async () => {
|
|||||||
console.error("更新终端背景启用状态失败:", error);
|
console.error("更新终端背景启用状态失败:", error);
|
||||||
// 失败时回滚本地状态
|
// 失败时回滚本地状态
|
||||||
localTerminalBackgroundEnabled.value = !newValue;
|
localTerminalBackgroundEnabled.value = !newValue;
|
||||||
alert(t('styleCustomizer.errorToggleTerminalBg', { message: error.message })); // 需要添加翻译
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.errorToggleTerminalBg', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -811,14 +839,14 @@ const handleSaveTerminalBackgroundOverlayOpacity = async () => {
|
|||||||
try {
|
try {
|
||||||
const opacity = Number(editableTerminalBackgroundOverlayOpacity.value);
|
const opacity = Number(editableTerminalBackgroundOverlayOpacity.value);
|
||||||
if (isNaN(opacity) || opacity < 0 || opacity > 1) {
|
if (isNaN(opacity) || opacity < 0 || opacity > 1) {
|
||||||
alert(t('styleCustomizer.errorInvalidOpacityValue')); // 需要添加翻译 "无效的透明度值,必须在0到1之间"
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.errorInvalidOpacityValue') });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await appearanceStore.setTerminalBackgroundOverlayOpacity(opacity);
|
await appearanceStore.setTerminalBackgroundOverlayOpacity(opacity);
|
||||||
alert(t('styleCustomizer.terminalBgOverlayOpacitySaved')); // 需要添加翻译 "终端背景蒙版透明度已保存"
|
notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.terminalBgOverlayOpacitySaved') });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("保存终端背景蒙版透明度失败:", error);
|
console.error("保存终端背景蒙版透明度失败:", error);
|
||||||
alert(t('styleCustomizer.terminalBgOverlayOpacitySaveFailed', { message: error.message })); // 需要添加翻译 "终端背景蒙版透明度保存失败"
|
notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.terminalBgOverlayOpacitySaveFailed', { message: error.message }) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1372,8 +1400,16 @@ const handlePreviewButtonMouseDown = async (event: MouseEvent, themeToPreview: T
|
|||||||
<input type="number" id="editorFontSize" v-model.number="editableEditorFontSize" class="border border-border px-[0.7rem] py-2 rounded text-sm bg-background text-foreground max-w-[100px] justify-self-start box-border transition duration-200 ease-in-out focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary" min="1" />
|
<input type="number" id="editorFontSize" v-model.number="editableEditorFontSize" class="border border-border px-[0.7rem] py-2 rounded text-sm bg-background text-foreground max-w-[100px] justify-self-start box-border transition duration-200 ease-in-out focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary" min="1" />
|
||||||
<button @click="handleSaveEditorFontSize" class="px-3 py-1.5 text-sm border border-border rounded bg-header hover:bg-border transition duration-200 ease-in-out whitespace-nowrap justify-self-start mt-1 md:mt-0">{{ t('common.save') }}</button>
|
<button @click="handleSaveEditorFontSize" class="px-3 py-1.5 text-sm border border-border rounded bg-header hover:bg-border transition duration-200 ease-in-out whitespace-nowrap justify-self-start mt-1 md:mt-0">{{ t('common.save') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4 md:my-6">
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-[auto_1fr_auto] items-start md:items-center gap-2 md:gap-3 mb-3">
|
||||||
|
<label for="editorFontFamily" class="text-left text-foreground text-sm font-medium overflow-hidden text-ellipsis block w-full mb-1 md:mb-0">{{ t('styleCustomizer.editorFontFamily') }}:</label>
|
||||||
|
<input type="text" id="editorFontFamily" v-model="editableEditorFontFamily" class="border border-border px-[0.7rem] py-2 rounded text-sm bg-background text-foreground w-full box-border transition duration-200 ease-in-out focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary">
|
||||||
|
<button @click="handleSaveEditorFontFamily" class="px-3 py-1.5 text-sm border border-border rounded bg-header hover:bg-border transition duration-200 ease-in-out whitespace-nowrap justify-self-start mt-1 md:mt-0">{{ t('common.save') }}</button>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="flex justify-end p-3 md:p-4 border-t border-border bg-footer flex-shrink-0 flex-wrap gap-2">
|
<footer class="flex justify-end p-3 md:p-4 border-t border-border bg-footer flex-shrink-0 flex-wrap gap-2">
|
||||||
|
|||||||
@@ -70,6 +70,9 @@
|
|||||||
"editorFontSize": "Editor Font Size",
|
"editorFontSize": "Editor Font Size",
|
||||||
"editorFontSizeSaved": "Editor font size saved.",
|
"editorFontSizeSaved": "Editor font size saved.",
|
||||||
"editorFontSizeSaveFailed": "Failed to save editor font size: {message}",
|
"editorFontSizeSaveFailed": "Failed to save editor font size: {message}",
|
||||||
|
"editorFontFamily": "Editor Font Family",
|
||||||
|
"editorFontFamilySaved": "Editor font family saved.",
|
||||||
|
"editorFontFamilySaveFailed": "Failed to save editor font family: {message}",
|
||||||
"errorInvalidEditorFontSize": "Invalid font size. Please enter a positive number.",
|
"errorInvalidEditorFontSize": "Invalid font size. Please enter a positive number.",
|
||||||
"uiThemeJsonEditorTitle": "UI Theme JSON Editor",
|
"uiThemeJsonEditorTitle": "UI Theme JSON Editor",
|
||||||
"uiThemeJsonEditorDesc": "Directly edit the UI theme configuration using JSON. Changes here will reflect in the color pickers above after blurring the textarea.",
|
"uiThemeJsonEditorDesc": "Directly edit the UI theme configuration using JSON. Changes here will reflect in the color pickers above after blurring the textarea.",
|
||||||
|
|||||||
@@ -1208,6 +1208,9 @@
|
|||||||
"editorFontSize": "エディターフォントサイズ",
|
"editorFontSize": "エディターフォントサイズ",
|
||||||
"editorFontSizeSaveFailed": "エディターフォントサイズの保存に失敗しました: {message}",
|
"editorFontSizeSaveFailed": "エディターフォントサイズの保存に失敗しました: {message}",
|
||||||
"editorFontSizeSaved": "エディターフォントサイズを保存しました。",
|
"editorFontSizeSaved": "エディターフォントサイズを保存しました。",
|
||||||
|
"editorFontFamily": "エディターフォントファミリー",
|
||||||
|
"editorFontFamilySaved": "エディターフォントファミリーを保存しました。",
|
||||||
|
"editorFontFamilySaveFailed": "エディターフォントファミリーの保存に失敗しました: {message}",
|
||||||
"errorFixJsonBeforeSave": "保存する前に JSON フォーマットのエラーを修正してください。",
|
"errorFixJsonBeforeSave": "保存する前に JSON フォーマットのエラーを修正してください。",
|
||||||
"errorInvalidEditorFontSize": "無効なフォントサイズです。正数を入力してください。",
|
"errorInvalidEditorFontSize": "無効なフォントサイズです。正数を入力してください。",
|
||||||
"errorInvalidFontSize": "無効なフォントサイズです。正数を入力してください。",
|
"errorInvalidFontSize": "無効なフォントサイズです。正数を入力してください。",
|
||||||
|
|||||||
@@ -70,6 +70,9 @@
|
|||||||
"editorFontSize": "编辑器字体大小",
|
"editorFontSize": "编辑器字体大小",
|
||||||
"editorFontSizeSaved": "编辑器字体大小已保存。",
|
"editorFontSizeSaved": "编辑器字体大小已保存。",
|
||||||
"editorFontSizeSaveFailed": "保存编辑器字体大小失败: {message}",
|
"editorFontSizeSaveFailed": "保存编辑器字体大小失败: {message}",
|
||||||
|
"editorFontFamily": "编辑器字体",
|
||||||
|
"editorFontFamilySaved": "编辑器字体已保存。",
|
||||||
|
"editorFontFamilySaveFailed": "保存编辑器字体失败: {message}",
|
||||||
"errorInvalidEditorFontSize": "无效的字体大小。请输入一个正数。",
|
"errorInvalidEditorFontSize": "无效的字体大小。请输入一个正数。",
|
||||||
"uiThemeJsonEditorTitle": "界面主题 JSON 编辑器",
|
"uiThemeJsonEditorTitle": "界面主题 JSON 编辑器",
|
||||||
"uiThemeJsonEditorDesc": "直接使用 JSON 编辑界面主题配置。在此处更改并在文本区域失焦后,上面的颜色选择器将同步更新。",
|
"uiThemeJsonEditorDesc": "直接使用 JSON 编辑界面主题配置。在此处更改并在文本区域失焦后,上面的颜色选择器将同步更新。",
|
||||||
|
|||||||
@@ -115,6 +115,11 @@ export const useAppearanceStore = defineStore('appearance', () => {
|
|||||||
const size = appearanceSettings.value.editorFontSize;
|
const size = appearanceSettings.value.editorFontSize;
|
||||||
return typeof size === 'number' && size > 0 ? size : 14;
|
return typeof size === 'number' && size > 0 ? size : 14;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 当前编辑器字体家族
|
||||||
|
const currentEditorFontFamily = computed<string>(() => {
|
||||||
|
return appearanceSettings.value.editorFontFamily || 'Consolas, "Noto Sans SC", "Microsoft YaHei"'; // 提供默认值
|
||||||
|
});
|
||||||
|
|
||||||
// 终端背景是否启用
|
// 终端背景是否启用
|
||||||
const isTerminalBackgroundEnabled = computed<boolean>(() => {
|
const isTerminalBackgroundEnabled = computed<boolean>(() => {
|
||||||
@@ -275,6 +280,14 @@ export const useAppearanceStore = defineStore('appearance', () => {
|
|||||||
async function setEditorFontSize(size: number) {
|
async function setEditorFontSize(size: number) {
|
||||||
await updateAppearanceSettings({ editorFontSize: size });
|
await updateAppearanceSettings({ editorFontSize: size });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置编辑器字体家族
|
||||||
|
* @param fontFamily 字体列表字符串
|
||||||
|
*/
|
||||||
|
async function setEditorFontFamily(fontFamily: string) {
|
||||||
|
await updateAppearanceSettings({ editorFontFamily: fontFamily });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置终端背景是否启用
|
* 设置终端背景是否启用
|
||||||
@@ -639,6 +652,7 @@ export const useAppearanceStore = defineStore('appearance', () => {
|
|||||||
terminalFontSizeDesktop, // + 用于在设置中分别显示/设置桌面端字号
|
terminalFontSizeDesktop, // + 用于在设置中分别显示/设置桌面端字号
|
||||||
terminalFontSizeMobile, // + 用于在设置中分别显示/设置移动端字号
|
terminalFontSizeMobile, // + 用于在设置中分别显示/设置移动端字号
|
||||||
currentEditorFontSize,
|
currentEditorFontSize,
|
||||||
|
currentEditorFontFamily, // 新增
|
||||||
pageBackgroundImage,
|
pageBackgroundImage,
|
||||||
terminalBackgroundImage,
|
terminalBackgroundImage,
|
||||||
currentTerminalBackgroundOverlayOpacity,
|
currentTerminalBackgroundOverlayOpacity,
|
||||||
@@ -651,7 +665,8 @@ export const useAppearanceStore = defineStore('appearance', () => {
|
|||||||
setTerminalFontFamily,
|
setTerminalFontFamily,
|
||||||
setTerminalFontSize, // 设置桌面端字体大小
|
setTerminalFontSize, // 设置桌面端字体大小
|
||||||
setTerminalFontSizeMobile, // + 设置移动端字体大小
|
setTerminalFontSizeMobile, // + 设置移动端字体大小
|
||||||
setEditorFontSize,
|
setEditorFontSize,
|
||||||
|
setEditorFontFamily, // 新增
|
||||||
setTerminalBackgroundEnabled,
|
setTerminalBackgroundEnabled,
|
||||||
createTerminalTheme,
|
createTerminalTheme,
|
||||||
updateTerminalTheme,
|
updateTerminalTheme,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export interface AppearanceSettings {
|
|||||||
terminalBackgroundImage?: string;
|
terminalBackgroundImage?: string;
|
||||||
pageBackgroundImage?: string;
|
pageBackgroundImage?: string;
|
||||||
editorFontSize?: number;
|
editorFontSize?: number;
|
||||||
|
editorFontFamily?: string | null; // Monaco Editor 字体偏好
|
||||||
terminalBackgroundEnabled?: boolean; // 终端背景是否启用
|
terminalBackgroundEnabled?: boolean; // 终端背景是否启用
|
||||||
terminalBackgroundOverlayOpacity?: number; // 终端背景蒙版透明度 (0-1)
|
terminalBackgroundOverlayOpacity?: number; // 终端背景蒙版透明度 (0-1)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user