Add font settings

This commit is contained in:
2025-05-29 15:42:16 +08:00
parent 5f102edcf7
commit 44f7baad10
28 changed files with 710 additions and 13 deletions

View File

@@ -253,6 +253,21 @@ export class EditorConfig {
*/
"fontSize": number;
/**
* 字体族
*/
"fontFamily": string;
/**
* 字体粗细
*/
"fontWeight": string;
/**
* 行高
*/
"lineHeight": number;
/**
* 是否启用Tab缩进
*/
@@ -283,6 +298,15 @@ export class EditorConfig {
if (!("fontSize" in $$source)) {
this["fontSize"] = 0;
}
if (!("fontFamily" in $$source)) {
this["fontFamily"] = "";
}
if (!("fontWeight" in $$source)) {
this["fontWeight"] = "";
}
if (!("lineHeight" in $$source)) {
this["lineHeight"] = 0;
}
if (!("enableTabIndent" in $$source)) {
this["enableTabIndent"] = false;
}

View File

@@ -0,0 +1,146 @@
/* HarmonyOS Sans 字体定义 */
/* HarmonyOS Sans Regular */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Light */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Light.ttf') format('truetype');
font-weight: 300;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Medium */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Medium.ttf') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Semibold */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Semibold.ttf') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Bold */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Black */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Black.ttf') format('truetype');
font-weight: 900;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Thin */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Thin.ttf') format('truetype');
font-weight: 100;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC 简体中文字体 */
/* HarmonyOS Sans SC Regular */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Light */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Light.ttf') format('truetype');
font-weight: 300;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Medium */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Medium.ttf') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Semibold */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Semibold.ttf') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Bold */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Black */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Black.ttf') format('truetype');
font-weight: 900;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Thin */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Thin.ttf') format('truetype');
font-weight: 100;
font-style: normal;
font-display: swap;
}
/* 字体加载优化 */
.font-loading {
font-family: system-ui, -apple-system, sans-serif;
}
.font-loaded {
font-family: 'HarmonyOS Sans SC', 'HarmonyOS Sans', 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
}
/* CodeMirror 专用字体类 */
.cm-harmonyos-font {
font-family: 'HarmonyOS Sans SC', 'HarmonyOS Sans', 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif !important;
font-feature-settings: 'liga' 1, 'calt' 1;
font-variant-ligatures: contextual;
text-rendering: optimizeLegibility;
}

View File

@@ -1,3 +1,4 @@
/* 导入所有CSS文件 */
@import 'normalize.css';
@import 'variables.css';
@import 'variables.css';
@import "fonts.css";

View File

@@ -15,6 +15,8 @@ import {
updateTabConfig,
createAutoSavePlugin,
createSaveShortcutPlugin,
createFontExtensionFromBackend,
updateFontConfig,
} from './extensions';
import { useI18n } from 'vue-i18n';
import { DocumentService } from '@/../bindings/voidraft/internal/services';
@@ -56,6 +58,14 @@ const createEditor = async () => {
configStore.config.tabType
);
// 创建字体扩展
const fontExtension = createFontExtensionFromBackend({
fontFamily: configStore.config.fontFamily,
fontSize: configStore.config.fontSize,
lineHeight: configStore.config.lineHeight,
fontWeight: configStore.config.fontWeight
});
// 创建统计信息更新扩展
const statsExtension = createStatsUpdateExtension(
editorStore.updateDocumentStats
@@ -82,6 +92,7 @@ const createEditor = async () => {
const extensions: Extension[] = [
...basicExtensions,
...tabExtensions,
fontExtension,
statsExtension,
saveShortcutPlugin,
autoSavePlugin
@@ -143,6 +154,17 @@ const reconfigureTabSettings = () => {
);
};
// 重新配置字体设置
const reconfigureFontSettings = () => {
if (!editorStore.editorView) return;
updateFontConfig(editorStore.editorView as EditorView, {
fontFamily: configStore.config.fontFamily,
fontSize: configStore.config.fontSize,
lineHeight: configStore.config.lineHeight,
fontWeight: configStore.config.fontWeight
});
};
// 监听Tab设置变化
watch(() => configStore.config.tabSize, reconfigureTabSettings);
watch(() => configStore.config.enableTabIndent, reconfigureTabSettings);
@@ -150,9 +172,19 @@ watch(() => configStore.config.tabType, reconfigureTabSettings);
// 监听字体大小变化
watch(() => configStore.config.fontSize, () => {
reconfigureFontSettings();
editorStore.applyFontSize();
});
// 监听字体族变化
watch(() => configStore.config.fontFamily, reconfigureFontSettings);
// 监听字体粗细变化
watch(() => configStore.config.fontWeight, reconfigureFontSettings);
// 监听行高变化
watch(() => configStore.config.lineHeight, reconfigureFontSettings);
onMounted(() => {
// 创建编辑器
createEditor();

View File

@@ -0,0 +1,219 @@
import { EditorView } from '@codemirror/view';
import { Extension, Compartment } from '@codemirror/state';
// 字体配置接口
export interface FontConfig {
fontFamily: string;
fontSize?: number;
lineHeight?: number;
fontWeight?: string;
}
// 创建字体配置compartment
export const fontCompartment = new Compartment();
// 默认鸿蒙字体配置
export const HARMONYOS_FONT_CONFIG: FontConfig = {
fontFamily: '"HarmonyOS Sans SC", "HarmonyOS Sans", "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
};
// 从后端配置创建字体配置
export function createFontConfigFromBackend(backendConfig: {
fontFamily?: string;
fontSize?: number;
lineHeight?: number;
fontWeight?: string;
}): FontConfig {
return {
fontFamily: backendConfig.fontFamily || HARMONYOS_FONT_CONFIG.fontFamily,
fontSize: backendConfig.fontSize || HARMONYOS_FONT_CONFIG.fontSize,
lineHeight: backendConfig.lineHeight || HARMONYOS_FONT_CONFIG.lineHeight,
fontWeight: backendConfig.fontWeight || HARMONYOS_FONT_CONFIG.fontWeight,
};
}
// 创建字体样式扩展
export function createFontExtension(config: Partial<FontConfig> = {}): Extension {
const fontConfig = { ...HARMONYOS_FONT_CONFIG, ...config };
const styles: Record<string, any> = {
'&': {
fontFamily: fontConfig.fontFamily,
...(fontConfig.fontSize && { fontSize: `${fontConfig.fontSize}px` }),
...(fontConfig.lineHeight && { lineHeight: fontConfig.lineHeight.toString() }),
...(fontConfig.fontWeight && { fontWeight: fontConfig.fontWeight }),
},
'.cm-content': {
fontFamily: fontConfig.fontFamily,
...(fontConfig.fontSize && { fontSize: `${fontConfig.fontSize}px` }),
...(fontConfig.lineHeight && { lineHeight: fontConfig.lineHeight.toString() }),
...(fontConfig.fontWeight && { fontWeight: fontConfig.fontWeight }),
},
'.cm-editor': {
fontFamily: fontConfig.fontFamily,
},
'.cm-scroller': {
fontFamily: fontConfig.fontFamily,
},
'.cm-gutters': {
fontFamily: fontConfig.fontFamily,
...(fontConfig.fontSize && { fontSize: `${fontConfig.fontSize}px` }),
},
'.cm-lineNumbers': {
fontFamily: fontConfig.fontFamily,
...(fontConfig.fontSize && { fontSize: `${Math.max(10, fontConfig.fontSize - 1)}px` }),
},
'.cm-tooltip': {
fontFamily: fontConfig.fontFamily,
...(fontConfig.fontSize && { fontSize: `${Math.max(12, fontConfig.fontSize - 1)}px` }),
},
'.cm-completionLabel': {
fontFamily: fontConfig.fontFamily,
},
'.cm-completionDetail': {
fontFamily: fontConfig.fontFamily,
}
};
return EditorView.theme(styles);
}
// 创建响应式字体大小扩展
export function createResponsiveFontExtension(baseFontSize: number = 14): Extension {
return fontCompartment.of(createFontExtension({
fontSize: baseFontSize,
lineHeight: 1.5
}));
}
// 从后端配置创建字体扩展
export function createFontExtensionFromBackend(backendConfig: {
fontFamily?: string;
fontSize?: number;
lineHeight?: number;
fontWeight?: string;
}): Extension {
const fontConfig = createFontConfigFromBackend(backendConfig);
return fontCompartment.of(createFontExtension(fontConfig));
}
// 动态更新字体配置
export function updateFontConfig(view: EditorView, config: Partial<FontConfig>): void {
const newFontExtension = createFontExtension(config);
// 使用compartment重新配置字体扩展
view.dispatch({
effects: fontCompartment.reconfigure(newFontExtension)
});
}
// 预设字体配置
export const FONT_PRESETS = {
// 鸿蒙字体系列
harmonyos: {
name: '鸿蒙字体',
fontFamily: '"HarmonyOS Sans SC", "HarmonyOS Sans", "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
},
harmonyosCondensed: {
name: '鸿蒙紧凑字体',
fontFamily: '"HarmonyOS Sans Condensed", "HarmonyOS Sans SC", "HarmonyOS Sans", "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif',
fontSize: 14,
lineHeight: 1.4,
fontWeight: 'normal'
},
// 编程专用字体
jetbrainsMono: {
name: 'JetBrains Mono',
fontFamily: '"JetBrains Mono", "Fira Code", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
},
firaCode: {
name: 'Fira Code',
fontFamily: '"Fira Code", "JetBrains Mono", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
},
sourceCodePro: {
name: 'Source Code Pro',
fontFamily: '"Source Code Pro", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
},
// 系统字体
systemMono: {
name: '系统等宽字体',
fontFamily: '"SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, "Courier New", monospace',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
},
cascadiaCode: {
name: 'Cascadia Code',
fontFamily: '"Cascadia Code", "SF Mono", Monaco, Consolas, "Ubuntu Mono", monospace',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
},
// 中文友好字体
microsoftYaHei: {
name: '微软雅黑',
fontFamily: '"Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
},
pingFang: {
name: '苹方字体',
fontFamily: '"PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
},
// 经典字体
arial: {
name: 'Arial',
fontFamily: 'Arial, "Helvetica Neue", Helvetica, sans-serif',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
},
helvetica: {
name: 'Helvetica',
fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
}
} as const;
// 字体预设类型
export type FontPresetKey = keyof typeof FONT_PRESETS;
// 获取所有字体预设选项
export function getFontPresetOptions() {
return Object.entries(FONT_PRESETS).map(([key, preset]) => ({
value: key as FontPresetKey,
label: preset.name,
fontFamily: preset.fontFamily
}));
}
// 根据预设创建字体扩展
export function createPresetFontExtension(preset: keyof typeof FONT_PRESETS, overrides: Partial<FontConfig> = {}): Extension {
const config = { ...FONT_PRESETS[preset], ...overrides };
return createFontExtension(config);
}

View File

@@ -2,4 +2,5 @@
export * from './tabExtension';
export * from './wheelZoomExtension';
export * from './statsExtension';
export * from './autoSaveExtension';
export * from './autoSaveExtension';
export * from './fontExtension';

View File

@@ -66,6 +66,13 @@ export default {
selectDirectory: 'Select Directory',
fontSize: 'Font Size',
fontSizeDescription: 'Editor font size',
fontSettings: 'Font Settings',
fontPreset: 'Font Preset',
fontPresetDescription: 'Choose a predefined font combination',
fontWeight: 'Font Weight',
fontWeightDescription: 'Set the thickness of the font',
lineHeight: 'Line Height',
lineHeightDescription: 'Set the spacing between text lines',
tabSettings: 'Tab Settings',
tabSize: 'Tab Size',
tabType: 'Tab Type',

View File

@@ -66,6 +66,13 @@ export default {
selectDirectory: '选择目录',
fontSize: '字体大小',
fontSizeDescription: '编辑器字体大小',
fontSettings: '字体设置',
fontPreset: '字体预设',
fontPresetDescription: '选择预设的字体组合',
fontWeight: '字体粗细',
fontWeightDescription: '设置字体的粗细程度',
lineHeight: '行高',
lineHeightDescription: '设置文本行之间的间距',
tabSettings: 'Tab 设置',
tabSize: 'Tab 大小',
tabType: 'Tab 类型',

View File

@@ -1,4 +1,4 @@
import {createRouter, createWebHistory, RouteRecordRaw} from 'vue-router';
import {createRouter, createWebHashHistory, createWebHistory, RouteRecordRaw} from 'vue-router';
import Editor from '@/editor/Editor.vue';
import Settings from '@/settings/Settings.vue';
import GeneralPage from '@/settings/pages/GeneralPage.vue';
@@ -49,7 +49,7 @@ const routes: RouteRecordRaw[] = [
];
const router = createRouter({
history: createWebHistory(),
history: createWebHashHistory(),
routes: routes
});

View File

@@ -6,10 +6,54 @@ import SettingSection from '../components/SettingSection.vue';
import SettingItem from '../components/SettingItem.vue';
import ToggleSwitch from '../components/ToggleSwitch.vue';
import { TabType } from '@/../bindings/voidraft/internal/models/models';
import { getFontPresetOptions, type FontPresetKey } from '@/editor/extensions/fontExtension';
const { t } = useI18n();
const configStore = useConfigStore();
// 字体预设选项
const fontPresetOptions = getFontPresetOptions();
const currentFontPreset = computed(() => configStore.getCurrentFontPreset());
// 字体预设选择
const handleFontPresetChange = (event: Event) => {
const target = event.target as HTMLSelectElement;
const presetKey = target.value;
if (presetKey && presetKey !== 'custom') {
configStore.setFontPreset(presetKey as FontPresetKey);
}
};
// 字体粗细选项
const fontWeightOptions = [
{ value: '100', label: '极细 (100)' },
{ value: '200', label: '超细 (200)' },
{ value: '300', label: '细 (300)' },
{ value: 'normal', label: '正常 (400)' },
{ value: '500', label: '中等 (500)' },
{ value: '600', label: '半粗 (600)' },
{ value: 'bold', label: '粗体 (700)' },
{ value: '800', label: '超粗 (800)' },
{ value: '900', label: '极粗 (900)' }
];
// 字体粗细选择
const handleFontWeightChange = (event: Event) => {
const target = event.target as HTMLSelectElement;
configStore.setFontWeight(target.value);
};
// 行高控制
const increaseLineHeight = () => {
const newLineHeight = Math.min(3.0, configStore.config.lineHeight + 0.1);
configStore.setLineHeight(Math.round(newLineHeight * 10) / 10);
};
const decreaseLineHeight = () => {
const newLineHeight = Math.max(1.0, configStore.config.lineHeight - 0.1);
configStore.setLineHeight(Math.round(newLineHeight * 10) / 10);
};
// 字体大小控制
const increaseFontSize = () => {
configStore.increaseFontSize();
@@ -38,7 +82,27 @@ const decreaseTabSize = () => {
<template>
<div class="settings-page">
<SettingSection :title="t('settings.fontSize')">
<SettingSection :title="t('settings.fontSettings')">
<SettingItem
:title="t('settings.fontPreset')"
:description="t('settings.fontPresetDescription')"
>
<select
class="font-preset-select"
:value="currentFontPreset || 'custom'"
@change="handleFontPresetChange"
>
<option value="custom">自定义</option>
<option
v-for="option in fontPresetOptions"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</option>
</select>
</SettingItem>
<SettingItem
:title="t('settings.fontSize')"
:description="t('settings.fontSizeDescription')"
@@ -49,11 +113,48 @@ const decreaseTabSize = () => {
<button @click="increaseFontSize" class="control-button">+</button>
</div>
</SettingItem>
<div class="font-size-preview" :style="{ fontSize: `${configStore.config.fontSize}px` }">
<div class="preview-label">预览</div>
<SettingItem
:title="t('settings.fontWeight')"
:description="t('settings.fontWeightDescription')"
>
<select
class="font-weight-select"
:value="configStore.config.fontWeight"
@change="handleFontWeightChange"
>
<option
v-for="option in fontWeightOptions"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</option>
</select>
</SettingItem>
<SettingItem
:title="t('settings.lineHeight')"
:description="t('settings.lineHeightDescription')"
>
<div class="number-control">
<button @click="decreaseLineHeight" class="control-button">-</button>
<span>{{ configStore.config.lineHeight.toFixed(1) }}</span>
<button @click="increaseLineHeight" class="control-button">+</button>
</div>
</SettingItem>
<div class="font-preview" :style="{
fontSize: `${configStore.config.fontSize}px`,
fontFamily: configStore.config.fontFamily,
fontWeight: configStore.config.fontWeight,
lineHeight: configStore.config.lineHeight
}">
<div class="preview-label">字体预览</div>
<div class="preview-text">
<span>function example() {</span>
<span class="indent">console.log("Hello, World!");</span>
<span class="indent">console.log("Hello, 世界!");</span>
<span class="indent">const message = "鸿蒙字体测试";</span>
<span>}</span>
</div>
</div>
@@ -195,6 +296,62 @@ const decreaseTabSize = () => {
}
}
.font-preview {
margin: 15px 0 5px 20px;
padding: 15px;
background-color: #252525;
border: 1px solid #444444;
border-radius: 4px;
.preview-label {
font-size: 12px;
color: #888888;
margin-bottom: 8px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
.preview-text {
display: flex;
flex-direction: column;
span {
color: #d0d0d0;
}
.indent {
padding-left: 20px;
color: #4a9eff;
}
}
}
.font-preset-select,
.font-weight-select {
min-width: 180px;
padding: 8px 12px;
border: 1px solid #555555;
border-radius: 4px;
background-color: #3a3a3a;
color: #e0e0e0;
font-size: 13px;
cursor: pointer;
&:focus {
outline: none;
border-color: #4a9eff;
}
&:hover {
border-color: #666666;
}
option {
background-color: #3a3a3a;
color: #e0e0e0;
padding: 4px 8px;
}
}
.tab-type-toggle {
min-width: 100px;
padding: 8px 15px;

View File

@@ -7,10 +7,14 @@ import {EditorConfig, TabType, LanguageType} from '@/../bindings/voidraft/intern
import {useLogStore} from './logStore';
import { useI18n } from 'vue-i18n';
import { ConfigUtils } from '@/utils/configUtils';
import { FONT_PRESETS, getFontPresetOptions, type FontPresetKey } from '@/editor/extensions/fontExtension';
// 配置键映射 - 前端字段到后端配置键的映射
const CONFIG_KEY_MAP = {
fontSize: 'editor.font_size',
fontFamily: 'editor.font_family',
fontWeight: 'editor.font_weight',
lineHeight: 'editor.line_height',
enableTabIndent: 'editor.enable_tab_indent',
tabSize: 'editor.tab_size',
tabType: 'editor.tab_type',
@@ -33,6 +37,9 @@ export const useConfigStore = defineStore('config', () => {
// 配置状态
const config = ref<EditorConfig>(new EditorConfig({
fontSize: CONFIG_LIMITS.fontSize.default,
fontFamily: '"HarmonyOS Sans SC", "HarmonyOS Sans", "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif',
fontWeight: 'normal',
lineHeight: 1.5,
enableTabIndent: true,
tabSize: CONFIG_LIMITS.tabSize.default,
tabType: CONFIG_LIMITS.tabType.default,
@@ -77,7 +84,11 @@ export const useConfigStore = defineStore('config', () => {
if (!configLoaded.value) return;
try {
const backendKey = CONFIG_KEY_MAP[key];
const backendKey = CONFIG_KEY_MAP[key as keyof typeof CONFIG_KEY_MAP];
if (!backendKey) {
throw new Error(`No backend key mapping found for ${String(key)}`);
}
await ConfigService.Set(backendKey, value);
// 更新本地状态
@@ -85,7 +96,7 @@ export const useConfigStore = defineStore('config', () => {
logStore.info(t('config.saveSuccess'));
} catch (error) {
console.error(`Failed to update config ${key}:`, error);
console.error(`Failed to update config ${String(key)}:`, error);
logStore.error(t('config.saveFailed'));
throw error;
}
@@ -156,6 +167,55 @@ export const useConfigStore = defineStore('config', () => {
async function setTabSize(size: number): Promise<void> {
await updateConfig('tabSize', size);
}
// 字体预设相关方法
async function setFontPreset(presetKey: FontPresetKey): Promise<void> {
const preset = FONT_PRESETS[presetKey];
if (!preset) {
throw new Error(`Unknown font preset: ${presetKey}`);
}
try {
// 批量更新字体相关配置
await updateConfig('fontFamily', preset.fontFamily);
await updateConfig('fontWeight', preset.fontWeight);
await updateConfig('lineHeight', preset.lineHeight);
// 可选择是否同时更新字体大小
// await updateConfig('fontSize', preset.fontSize);
logStore.info(`字体预设已切换为: ${preset.name}`);
} catch (error) {
console.error('Failed to set font preset:', error);
logStore.error('字体预设设置失败');
throw error;
}
}
// 获取当前字体预设(如果匹配的话)
function getCurrentFontPreset(): FontPresetKey | null {
const currentFamily = config.value.fontFamily;
for (const [key, preset] of Object.entries(FONT_PRESETS)) {
if (preset.fontFamily === currentFamily) {
return key as FontPresetKey;
}
}
return null;
}
// 设置字体族
async function setFontFamily(fontFamily: string): Promise<void> {
await updateConfig('fontFamily', fontFamily);
}
// 设置字体粗细
async function setFontWeight(fontWeight: string): Promise<void> {
await updateConfig('fontWeight', fontWeight);
}
// 设置行高
async function setLineHeight(lineHeight: number): Promise<void> {
await updateConfig('lineHeight', lineHeight);
}
return {
// 状态
@@ -189,6 +249,13 @@ export const useConfigStore = defineStore('config', () => {
setTabSize,
// 窗口操作
toggleAlwaysOnTop
toggleAlwaysOnTop,
// 字体预设相关方法
setFontPreset,
getCurrentFontPreset,
setFontFamily,
setFontWeight,
setLineHeight
};
});

View File

@@ -29,6 +29,9 @@ type DocumentConfig struct {
// EditorConfig 定义编辑器配置
type EditorConfig struct {
FontSize int `json:"fontSize" yaml:"font_size" mapstructure:"font_size"` // 字体大小
FontFamily string `json:"fontFamily" yaml:"font_family" mapstructure:"font_family"` // 字体族
FontWeight string `json:"fontWeight" yaml:"font_weight" mapstructure:"font_weight"` // 字体粗细
LineHeight float64 `json:"lineHeight" yaml:"line_height" mapstructure:"line_height"` // 行高
EnableTabIndent bool `json:"enableTabIndent" yaml:"enable_tab_indent" mapstructure:"enable_tab_indent"` // 是否启用Tab缩进
TabSize int `json:"tabSize" yaml:"tab_size" mapstructure:"tab_size"` // Tab大小
TabType TabType `json:"tabType" yaml:"tab_type" mapstructure:"tab_type"` // Tab类型空格或Tab
@@ -79,6 +82,9 @@ func NewDefaultAppConfig() *AppConfig {
return &AppConfig{
Editor: EditorConfig{
FontSize: 13,
FontFamily: `"HarmonyOS Sans SC", "HarmonyOS Sans", "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif`,
FontWeight: "normal",
LineHeight: 1.5,
EnableTabIndent: true,
TabSize: 4,
TabType: TabTypeSpaces,

View File

@@ -282,6 +282,9 @@ func setDefaults(v *viper.Viper) {
// 编辑器配置默认值
v.SetDefault("editor.font_size", defaultConfig.Editor.FontSize)
v.SetDefault("editor.font_family", defaultConfig.Editor.FontFamily)
v.SetDefault("editor.font_weight", defaultConfig.Editor.FontWeight)
v.SetDefault("editor.line_height", defaultConfig.Editor.LineHeight)
v.SetDefault("editor.enable_tab_indent", defaultConfig.Editor.EnableTabIndent)
v.SetDefault("editor.tab_size", defaultConfig.Editor.TabSize)
v.SetDefault("editor.tab_type", defaultConfig.Editor.TabType)

31
main.go
View File

@@ -24,6 +24,14 @@ var assets embed.FS
// logs any error that might occur.
func main() {
serviceManager := services.NewServiceManager()
var encryptionKey = [32]byte{
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19,
0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11,
0x0e, 0x0f, 0x0c, 0x0d, 0x0a, 0x0b, 0x08, 0x09,
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01,
}
var window *application.WebviewWindow
// Create a new Wails application by providing the necessary options.
// Variables 'Name' and 'Description' are for application metadata.
// 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files.
@@ -39,6 +47,25 @@ func main() {
Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
SingleInstance: &application.SingleInstanceOptions{
UniqueID: "com.voidraft",
EncryptionKey: encryptionKey,
OnSecondInstanceLaunch: func(data application.SecondInstanceData) {
if window != nil {
window.EmitEvent("secondInstanceLaunched", data)
window.Restore()
window.Focus()
}
log.Printf("Second instance launched with args: %v\n", data.Args)
log.Printf("Working directory: %s\n", data.WorkingDir)
if data.AdditionalData != nil {
log.Printf("Additional data: %v\n", data.AdditionalData)
}
},
AdditionalData: map[string]string{
"launchtime": time.Now().Local().String(),
},
},
})
// Create a new window with the necessary options.
@@ -58,7 +85,7 @@ func main() {
TitleBar: application.MacTitleBarHiddenInset,
},
BackgroundColour: application.NewRGB(27, 38, 54),
URL: "/",
URL: "/#/",
})
mainWindow.Center()
settingsWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
@@ -77,7 +104,7 @@ func main() {
},
Windows: application.WindowsWindow{},
BackgroundColour: application.NewRGB(27, 38, 54),
URL: "/settings",
URL: "/#/settings",
})
settingsWindow.Center()