🎨 Updated

This commit is contained in:
2025-06-05 02:11:36 +08:00
parent d9745ac7d1
commit 3e20f47b8e
17 changed files with 354 additions and 1053 deletions

View File

@@ -299,16 +299,6 @@ export class EditingConfig {
*/
"autoSaveDelay": number;
/**
* 变更字符阈值
*/
"changeThreshold": number;
/**
* 最小保存间隔(毫秒)
*/
"minSaveInterval": number;
/** Creates a new EditingConfig instance. */
constructor($$source: Partial<EditingConfig> = {}) {
if (!("fontSize" in $$source)) {
@@ -335,12 +325,6 @@ export class EditingConfig {
if (!("autoSaveDelay" in $$source)) {
this["autoSaveDelay"] = 0;
}
if (!("changeThreshold" in $$source)) {
this["changeThreshold"] = 0;
}
if (!("minSaveInterval" in $$source)) {
this["minSaveInterval"] = 0;
}
Object.assign(this, $$source);
}

View File

@@ -0,0 +1,19 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
/**
* DialogService 对话框服务,处理文件选择等对话框操作
* @module
*/
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
/**
* SelectDirectory 打开目录选择对话框
*/
export function SelectDirectory(): Promise<string> & { cancel(): void } {
let $resultPromise = $Call.ByID(2249533621) as any;
return $resultPromise;
}

View File

@@ -14,10 +14,6 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// @ts-ignore: Unused imports
import * as models$0 from "../models/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
/**
* ForceSave 强制保存当前文档
*/
@@ -46,30 +42,6 @@ export function GetActiveDocumentContent(): Promise<string> & { cancel(): void }
return $resultPromise;
}
/**
* GetDiffInfo 获取两个文本之间的详细差异信息
*/
export function GetDiffInfo(oldText: string, newText: string): Promise<$models.DiffResult> & { cancel(): void } {
let $resultPromise = $Call.ByID(2490726526, oldText, newText) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/**
* GetSaveSettings 获取文档保存设置
*/
export function GetSaveSettings(): Promise<models$0.EditingConfig | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(4257471801) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType4($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/**
* Initialize 初始化文档服务
*/
@@ -79,15 +51,7 @@ export function Initialize(): Promise<void> & { cancel(): void } {
}
/**
* LoadDefaultDocument 加载默认文档
*/
export function LoadDefaultDocument(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2343023569) as any;
return $resultPromise;
}
/**
* SaveDocumentSync 同步保存文档内容 (用于页面关闭前同步保存)
* SaveDocumentSync 同步保存文档内容
*/
export function SaveDocumentSync(content: string): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3770207288, content) as any;
@@ -95,29 +59,13 @@ export function SaveDocumentSync(content: string): Promise<void> & { cancel(): v
}
/**
* ServiceShutdown 实现应用程序关闭时的服务关闭逻辑
* ServiceShutdown 服务关闭
*/
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(638578044) as any;
return $resultPromise;
}
/**
* SetSaveCallback 设置保存回调函数
*/
export function SetSaveCallback(callback: any): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(675315211, callback) as any;
return $resultPromise;
}
/**
* Shutdown 关闭文档服务,确保所有数据保存
*/
export function Shutdown(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3444504909) as any;
return $resultPromise;
}
/**
* UpdateActiveDocumentContent 更新当前活动文档内容
*/
@@ -126,17 +74,6 @@ export function UpdateActiveDocumentContent(content: string): Promise<void> & {
return $resultPromise;
}
/**
* UpdateSaveSettings 更新文档保存设置
*/
export function UpdateSaveSettings(docConfig: models$0.EditingConfig): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1245479534, docConfig) as any;
return $resultPromise;
}
// Private type creation functions
const $$createType0 = models$0.Document.createFrom;
const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = $models.DiffResult.createFrom;
const $$createType3 = models$0.EditingConfig.createFrom;
const $$createType4 = $Create.Nullable($$createType3);

View File

@@ -2,11 +2,13 @@
// This file is automatically generated. DO NOT EDIT
import * as ConfigService from "./configservice.js";
import * as DialogService from "./dialogservice.js";
import * as DocumentService from "./documentservice.js";
import * as HotkeyService from "./hotkeyservice.js";
import * as SystemService from "./systemservice.js";
export {
ConfigService,
DialogService,
DocumentService,
HotkeyService,
SystemService

View File

@@ -5,137 +5,6 @@
// @ts-ignore: Unused imports
import {Create as $Create} from "@wailsio/runtime";
/**
* DiffResult 包含差异比较的结果信息
*/
export class DiffResult {
/**
* 编辑操作列表
*/
"Edits": Edit[];
/**
* 插入的字符数
*/
"InsertCount": number;
/**
* 删除的字符数
*/
"DeleteCount": number;
/**
* 变更的行数
*/
"ChangedLines": number;
/**
* 总变更字符数(插入+删除)
*/
"TotalChanges": number;
/**
* 变更的token数如单词、标识符等
*/
"ChangedTokens": number;
/** Creates a new DiffResult instance. */
constructor($$source: Partial<DiffResult> = {}) {
if (!("Edits" in $$source)) {
this["Edits"] = [];
}
if (!("InsertCount" in $$source)) {
this["InsertCount"] = 0;
}
if (!("DeleteCount" in $$source)) {
this["DeleteCount"] = 0;
}
if (!("ChangedLines" in $$source)) {
this["ChangedLines"] = 0;
}
if (!("TotalChanges" in $$source)) {
this["TotalChanges"] = 0;
}
if (!("ChangedTokens" in $$source)) {
this["ChangedTokens"] = 0;
}
Object.assign(this, $$source);
}
/**
* Creates a new DiffResult instance from a string or object.
*/
static createFrom($$source: any = {}): DiffResult {
const $$createField0_0 = $$createType1;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("Edits" in $$parsedSource) {
$$parsedSource["Edits"] = $$createField0_0($$parsedSource["Edits"]);
}
return new DiffResult($$parsedSource as Partial<DiffResult>);
}
}
/**
* Edit 表示单个编辑操作
*/
export class Edit {
/**
* 操作类型
*/
"Type": EditType;
/**
* 操作内容
*/
"Content": string;
/** Creates a new Edit instance. */
constructor($$source: Partial<Edit> = {}) {
if (!("Type" in $$source)) {
this["Type"] = (0 as EditType);
}
if (!("Content" in $$source)) {
this["Content"] = "";
}
Object.assign(this, $$source);
}
/**
* Creates a new Edit instance from a string or object.
*/
static createFrom($$source: any = {}): Edit {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new Edit($$parsedSource as Partial<Edit>);
}
}
/**
* Edit 表示编辑操作类型
*/
export enum EditType {
/**
* The Go zero value for the underlying type of the enum.
*/
$zero = 0,
/**
* EditInsert 插入操作
*/
EditInsert = 0,
/**
* EditDelete 删除操作
*/
EditDelete = 1,
/**
* EditEqual 相等部分
*/
EditEqual = 2,
};
/**
* MemoryStats 内存统计信息
*/
@@ -202,7 +71,3 @@ export class MemoryStats {
return new MemoryStats($$parsedSource as Partial<MemoryStats>);
}
}
// Private type creation functions
const $$createType0 = Edit.createFrom;
const $$createType1 = $Create.Array($$createType0);

View File

@@ -73,10 +73,9 @@ export default {
selectDirectory: 'Select Directory',
selectDataDirectory: 'Select Data Storage Directory',
defaultDataPath: 'Default data storage path',
enterCustomPath: 'Enter custom data storage path',
pathHint: 'Example: C:\\MyData or /home/user/data or relative path ./data',
bufferFiles: 'Buffer Files Path',
useCustomLocation: 'Use custom location for buffer files',
customDataPath: 'Custom Data Storage Path',
fontSize: 'Font Size',
fontSizeDescription: 'Editor font size',
fontSettings: 'Font Settings',
@@ -96,7 +95,6 @@ export default {
restartRequired: '(Restart required)',
saveOptions: 'Save Options',
autoSaveDelay: 'Auto Save Delay (ms)',
changeThreshold: 'Change Threshold',
minSaveInterval: 'Min Save Interval (ms)'
selectDirectoryFailed: 'Failed to select directory'
}
};

View File

@@ -73,10 +73,9 @@ export default {
selectDirectory: '选择目录',
selectDataDirectory: '选择数据存储目录',
defaultDataPath: '默认数据存储路径',
enterCustomPath: '请输入自定义数据存储路径',
pathHint: '例如: C:\\MyData 或 /home/user/data 或相对路径 ./data',
bufferFiles: '缓冲文件路径',
useCustomLocation: '使用自定义位置存储缓冲文件',
customDataPath: '自定义数据存储路径',
fontSize: '字体大小',
fontSizeDescription: '编辑器字体大小',
fontSettings: '字体设置',
@@ -96,7 +95,6 @@ export default {
restartRequired: '(需要重启)',
saveOptions: '保存选项',
autoSaveDelay: '自动保存延迟(毫秒)',
changeThreshold: '变更字符阈值',
minSaveInterval: '最小保存间隔(毫秒)'
selectDirectoryFailed: '选择目录失败'
}
};

View File

@@ -62,9 +62,7 @@ const EDITING_CONFIG_KEY_MAP: EditingConfigKeyMap = {
enableTabIndent: 'editing.enable_tab_indent',
tabSize: 'editing.tab_size',
tabType: 'editing.tab_type',
autoSaveDelay: 'editing.auto_save_delay',
changeThreshold: 'editing.change_threshold',
minSaveInterval: 'editing.min_save_interval'
autoSaveDelay: 'editing.auto_save_delay'
} as const;
const APPEARANCE_CONFIG_KEY_MAP: AppearanceConfigKeyMap = {
@@ -137,9 +135,7 @@ const DEFAULT_CONFIG: AppConfig = {
enableTabIndent: true,
tabSize: CONFIG_LIMITS.tabSize.default,
tabType: CONFIG_LIMITS.tabType.default,
autoSaveDelay: 5000,
changeThreshold: 500,
minSaveInterval: 1000
autoSaveDelay: 5000
},
appearance: {
language: LanguageType.LangZhCN
@@ -338,9 +334,7 @@ export const useConfigStore = defineStore('config', () => {
fontFamily: (value: string) => safeCall(() => updateEditingConfig('fontFamily', value), 'config.saveFailed', 'config.saveSuccess'),
fontWeight: (value: string) => safeCall(() => updateEditingConfig('fontWeight', value), 'config.saveFailed', 'config.saveSuccess'),
defaultDataPath: (value: string) => safeCall(() => updateGeneralConfig('defaultDataPath', value), 'config.saveFailed', 'config.saveSuccess'),
autoSaveDelay: (value: number) => safeCall(() => updateEditingConfig('autoSaveDelay', value), 'config.saveFailed', 'config.saveSuccess'),
changeThreshold: (value: number) => safeCall(() => updateEditingConfig('changeThreshold', value), 'config.saveFailed', 'config.saveSuccess'),
minSaveInterval: (value: number) => safeCall(() => updateEditingConfig('minSaveInterval', value), 'config.saveFailed', 'config.saveSuccess')
autoSaveDelay: (value: number) => safeCall(() => updateEditingConfig('autoSaveDelay', value), 'config.saveFailed', 'config.saveSuccess')
};
return {
@@ -394,8 +388,6 @@ export const useConfigStore = defineStore('config', () => {
// 保存配置相关方法
setAutoSaveDelay: setters.autoSaveDelay,
setChangeThreshold: setters.changeThreshold,
setMinSaveInterval: setters.minSaveInterval,
// 热键配置相关方法
setEnableGlobalHotkey: (value: boolean) => safeCall(() => updateGeneralConfig('enableGlobalHotkey', value), 'config.saveFailed', 'config.saveSuccess'),

View File

@@ -12,6 +12,12 @@ export interface AutoSaveOptions {
/**
* 创建自动保存插件
*
* 新的简化保存策略:
* - 前端只负责将内容变更传递给后端
* - 后端使用定时保存机制,每隔配置的时间间隔自动保存(仅在有变更时)
* - 移除了复杂的阈值保存和最小间隔控制
*
* @param options 配置选项
* @returns EditorView.Plugin
*/

View File

@@ -138,27 +138,31 @@ const handleAutoSaveDelayChange = async (event: Event) => {
}
};
const handleChangeThresholdChange = async (event: Event) => {
const target = event.target as HTMLInputElement;
const value = parseInt(target.value, 10);
if (!isNaN(value) && value >= 10 && value <= 10000) {
await safeCall(
() => configStore.setChangeThreshold(value),
'config.changeThresholdUpdateFailed'
);
// 动态字体预览文本
const fontPreviewText = computed(() => {
const currentFont = configStore.config.editing.fontFamily;
// 根据字体类型返回不同的预览文本
if (currentFont.includes('HarmonyOS')) {
return '鸿蒙字体测试';
} else if (currentFont.includes('Microsoft YaHei')) {
return '微软雅黑测试';
} else if (currentFont.includes('PingFang')) {
return '苹方字体测试';
} else if (currentFont.includes('JetBrains')) {
return 'JetBrains Mono';
} else if (currentFont.includes('Fira Code')) {
return 'Fira Code Test';
} else if (currentFont.includes('Source Code')) {
return 'Source Code Pro';
} else if (currentFont.includes('Cascadia')) {
return 'Cascadia Code';
} else if (currentFont.includes('SF Mono') || currentFont.includes('Monaco')) {
return 'System Monospace';
} else {
return 'Font Preview';
}
};
const handleMinSaveIntervalChange = async (event: Event) => {
const target = event.target as HTMLInputElement;
const value = parseInt(target.value, 10);
if (!isNaN(value) && value >= 100 && value <= 10000) {
await safeCall(
() => configStore.setMinSaveInterval(value),
'config.minSaveIntervalUpdateFailed'
);
}
};
});
</script>
<template>
@@ -234,7 +238,7 @@ const handleMinSaveIntervalChange = async (event: Event) => {
<div class="preview-text">
<span>function example() {</span>
<span class="indent">console.log("Hello, 世界!");</span>
<span class="indent">const message = "鸿蒙字体测试";</span>
<span class="indent">const message = "{{ fontPreviewText }}";</span>
<span>}</span>
</div>
</div>
@@ -276,7 +280,7 @@ const handleMinSaveIntervalChange = async (event: Event) => {
</SettingSection>
<SettingSection :title="t('settings.saveOptions')">
<SettingItem :title="t('settings.autoSaveDelay')" :description="'单位:毫秒'">
<SettingItem :title="t('settings.autoSaveDelay')" :description="'定时保存间隔,每隔指定时间自动保存(仅在有变更时)'">
<input
type="number"
class="number-input"
@@ -284,24 +288,6 @@ const handleMinSaveIntervalChange = async (event: Event) => {
@change="handleAutoSaveDelayChange"
/>
</SettingItem>
<SettingItem :title="t('settings.changeThreshold')" :description="'变更字符超过此阈值时触发保存'">
<input
type="number"
class="number-input"
:value="configStore.config.editing.changeThreshold"
@change="handleChangeThresholdChange"
/>
</SettingItem>
<SettingItem :title="t('settings.minSaveInterval')" :description="'单位:毫秒'">
<input
type="number"
class="number-input"
:value="configStore.config.editing.minSaveInterval"
@change="handleMinSaveIntervalChange"
/>
</SettingItem>
</SettingSection>
</div>
</template>

View File

@@ -1,11 +1,12 @@
<script setup lang="ts">
import { useConfigStore } from '@/stores/configStore';
import { useI18n } from 'vue-i18n';
import { computed, ref, watch } from 'vue';
import { computed} from 'vue';
import SettingSection from '../components/SettingSection.vue';
import SettingItem from '../components/SettingItem.vue';
import ToggleSwitch from '../components/ToggleSwitch.vue';
import { useErrorHandler } from '@/utils/errorHandler';
import { DialogService } from '@/../bindings/voidraft/internal/services';
import * as runtime from '@wailsio/runtime';
const { t } = useI18n();
@@ -92,27 +93,28 @@ const useCustomDataPath = computed({
set: (value: boolean) => configStore.setUseCustomDataPath(value)
});
// 自定义路径输入
const customPathInput = ref('');
// 自定义路径显示
const customPathDisplay = computed(() => {
return configStore.config.general.customDataPath || '';
});
// 监听自定义路径的变化,同步到配置
const updateCustomPath = () => {
configStore.setCustomDataPath(customPathInput.value.trim());
};
// 当启用自定义路径时,初始化输入框的值
const initCustomPath = () => {
if (useCustomDataPath.value) {
customPathInput.value = configStore.config.general.customDataPath || '';
const selectDirectory = async () => {
// 只有开启自定义路径时才能选择
if (!useCustomDataPath.value) return;
try {
const selectedPath = await DialogService.SelectDirectory();
if (selectedPath && selectedPath.trim()) {
await configStore.setCustomDataPath(selectedPath.trim());
}
} catch (error) {
// 可以添加用户友好的错误提示
await safeCall(async () => {
throw error;
}, 'settings.selectDirectoryFailed');
}
};
// 监听useCustomDataPath的变化
watch(() => useCustomDataPath.value, (newVal) => {
if (newVal) {
initCustomPath();
}
}, { immediate: true });
</script>
<template>
@@ -163,20 +165,25 @@ watch(() => useCustomDataPath.value, (newVal) => {
<SettingItem :title="t('settings.useCustomDataPath')">
<ToggleSwitch v-model="useCustomDataPath" />
</SettingItem>
<div class="path-input-section" v-if="useCustomDataPath">
<input
type="text"
v-model="customPathInput"
@blur="updateCustomPath"
@keyup.enter="updateCustomPath"
:placeholder="t('settings.enterCustomPath')"
class="path-input"
/>
<div class="path-hint">{{ t('settings.pathHint') }}</div>
</div>
<div class="default-path-info" v-else>
<div class="path-display default">{{ configStore.config.general.defaultDataPath }}</div>
<span class="path-label">{{ t('settings.defaultDataPath') }}</span>
<!-- 路径显示区域 -->
<div class="path-section">
<div class="path-input-container">
<input
type="text"
:value="useCustomDataPath ? customPathDisplay : configStore.config.general.defaultDataPath"
readonly
:placeholder="useCustomDataPath ? t('settings.selectDataDirectory') : t('settings.defaultDataPath')"
:class="[
'path-display-input',
{ 'clickable': useCustomDataPath, 'disabled': !useCustomDataPath }
]"
@click="selectDirectory"
/>
<div class="path-label">
{{ useCustomDataPath ? t('settings.customDataPath') : t('settings.defaultDataPath') }}
</div>
</div>
</div>
</SettingSection>
@@ -305,70 +312,56 @@ watch(() => useCustomDataPath.value, (newVal) => {
}
}
.path-input-section {
.path-section {
margin-top: 10px;
padding-left: 20px;
padding-right: 20px;
padding: 0 20px;
.path-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
padding: 8px 12px;
background-color: #3a3a3a;
border: 1px solid #555555;
border-radius: 4px;
color: #e0e0e0;
font-size: 13px;
transition: all 0.2s ease;
&:focus {
outline: none;
border-color: #4a9eff;
background-color: #404040;
.path-input-container {
.path-display-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
padding: 8px 12px;
background-color: #3a3a3a;
border: 1px solid #555555;
border-radius: 4px;
color: #e0e0e0;
font-size: 13px;
transition: all 0.2s ease;
&.disabled {
opacity: 0.6;
cursor: not-allowed;
background-color: #2a2a2a;
border-color: #444444;
}
&.clickable {
cursor: pointer;
&:hover {
border-color: #4a9eff;
background-color: #404040;
}
&:focus {
outline: none;
border-color: #4a9eff;
background-color: #404040;
}
}
&::placeholder {
color: #888888;
}
}
&::placeholder {
.path-label {
margin-top: 4px;
color: #888888;
font-size: 12px;
}
}
.path-hint {
margin-top: 5px;
color: #888888;
font-size: 12px;
line-height: 1.4;
}
}
.default-path-info {
margin-top: 10px;
padding-left: 20px;
padding-right: 20px;
.path-display {
padding: 8px 12px;
background-color: #2a2a2a;
border: 1px solid #444444;
border-radius: 4px;
color: #888888;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
box-sizing: border-box;
&.default {
font-style: italic;
}
}
.path-label {
display: block;
margin-top: 4px;
color: #666666;
font-size: 12px;
}
}
.danger-zone {