feat: 完成移动端集成CodeMirror
This commit is contained in:
@@ -55,6 +55,9 @@ let terminalTextStrokeEnabledFound = false;
|
|||||||
case 'editorFontSize':
|
case 'editorFontSize':
|
||||||
settings.editorFontSize = parseInt(row.value, 10);
|
settings.editorFontSize = parseInt(row.value, 10);
|
||||||
break;
|
break;
|
||||||
|
case 'mobileEditorFontSize':
|
||||||
|
settings.mobileEditorFontSize = parseInt(row.value, 10);
|
||||||
|
break;
|
||||||
case 'terminalBackgroundImage':
|
case 'terminalBackgroundImage':
|
||||||
settings.terminalBackgroundImage = row.value || undefined;
|
settings.terminalBackgroundImage = row.value || undefined;
|
||||||
break;
|
break;
|
||||||
@@ -122,6 +125,7 @@ case 'terminalTextStrokeEnabled':
|
|||||||
terminalFontSize: settings.terminalFontSize ?? defaults.terminalFontSize,
|
terminalFontSize: settings.terminalFontSize ?? defaults.terminalFontSize,
|
||||||
terminalFontSizeMobile: settings.terminalFontSizeMobile ?? defaults.terminalFontSizeMobile,
|
terminalFontSizeMobile: settings.terminalFontSizeMobile ?? defaults.terminalFontSizeMobile,
|
||||||
editorFontSize: settings.editorFontSize ?? defaults.editorFontSize,
|
editorFontSize: settings.editorFontSize ?? defaults.editorFontSize,
|
||||||
|
mobileEditorFontSize: settings.mobileEditorFontSize ?? defaults.mobileEditorFontSize,
|
||||||
editorFontFamily: settings.editorFontFamily ?? defaults.editorFontFamily,
|
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,
|
||||||
@@ -172,6 +176,7 @@ const getDefaultAppearanceSettings = (): Omit<AppearanceSettings, '_id'> => {
|
|||||||
terminalFontSize: 14,
|
terminalFontSize: 14,
|
||||||
terminalFontSizeMobile: 14, // 移动端默认字体大小
|
terminalFontSizeMobile: 14, // 移动端默认字体大小
|
||||||
editorFontSize: 14,
|
editorFontSize: 14,
|
||||||
|
mobileEditorFontSize: 16, //移动端编辑器默认字体大小
|
||||||
editorFontFamily: 'Consolas, "Noto Sans SC", "Microsoft YaHei"',
|
editorFontFamily: 'Consolas, "Noto Sans SC", "Microsoft YaHei"',
|
||||||
terminalBackgroundImage: undefined,
|
terminalBackgroundImage: undefined,
|
||||||
pageBackgroundImage: undefined,
|
pageBackgroundImage: undefined,
|
||||||
@@ -213,6 +218,7 @@ export const ensureDefaultSettingsExist = async (db: sqlite3.Database): Promise<
|
|||||||
{ key: 'terminalFontSize', value: defaults.terminalFontSize },
|
{ key: 'terminalFontSize', value: defaults.terminalFontSize },
|
||||||
{ key: 'terminalFontSizeMobile', value: defaults.terminalFontSizeMobile },
|
{ key: 'terminalFontSizeMobile', value: defaults.terminalFontSizeMobile },
|
||||||
{ key: 'editorFontSize', value: defaults.editorFontSize },
|
{ key: 'editorFontSize', value: defaults.editorFontSize },
|
||||||
|
{ key: 'mobileEditorFontSize', value: defaults.mobileEditorFontSize },
|
||||||
{ key: 'editorFontFamily', value: defaults.editorFontFamily },
|
{ 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 ?? '' }, // 数据库中使用空字符串
|
||||||
|
|||||||
@@ -114,6 +114,16 @@ export const updateSettings = async (settingsDto: UpdateAppearanceDto): Promise<
|
|||||||
settingsDto.editorFontSize = size;
|
settingsDto.editorFontSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证 mobileEditorFontSize (如果提供了)
|
||||||
|
if (settingsDto.mobileEditorFontSize !== undefined && settingsDto.mobileEditorFontSize !== null) {
|
||||||
|
const size = Number(settingsDto.mobileEditorFontSize);
|
||||||
|
if (isNaN(size) || size <= 0) {
|
||||||
|
throw new Error(`无效的移动端编辑器字体大小: ${settingsDto.mobileEditorFontSize}。必须是一个正数。`);
|
||||||
|
}
|
||||||
|
// 确保类型正确传递给仓库层
|
||||||
|
settingsDto.mobileEditorFontSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
// 验证 editorFontFamily (如果提供了)
|
// 验证 editorFontFamily (如果提供了)
|
||||||
if (settingsDto.hasOwnProperty('editorFontFamily')) {
|
if (settingsDto.hasOwnProperty('editorFontFamily')) {
|
||||||
if (settingsDto.editorFontFamily === null) {
|
if (settingsDto.editorFontFamily === null) {
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onBeforeUnmount, watch, shallowRef } from 'vue';
|
import { ref, onMounted, onBeforeUnmount, watch, shallowRef, computed } from 'vue';
|
||||||
import { EditorState, Compartment } from '@codemirror/state';
|
import { EditorState, Compartment } from '@codemirror/state';
|
||||||
|
import { useAppearanceStore } from '../stores/appearance.store';
|
||||||
import { EditorView, keymap } from '@codemirror/view';
|
import { EditorView, keymap } from '@codemirror/view';
|
||||||
import { basicSetup } from 'codemirror'; // Use basicSetup from the main 'codemirror' package
|
import { basicSetup } from 'codemirror'; // Use basicSetup from the main 'codemirror' package
|
||||||
|
|
||||||
@@ -21,14 +22,18 @@ const props = defineProps({
|
|||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'request-save']);
|
const emit = defineEmits(['update:modelValue', 'request-save']);
|
||||||
|
|
||||||
|
const appearanceStore = useAppearanceStore();
|
||||||
const editorRef = ref<HTMLDivElement | null>(null);
|
const editorRef = ref<HTMLDivElement | null>(null);
|
||||||
const view = shallowRef<EditorView | null>(null);
|
const view = shallowRef<EditorView | null>(null);
|
||||||
const languageCompartment = new Compartment(); // For dynamic language switching
|
const languageCompartment = new Compartment(); // For dynamic language switching
|
||||||
// Pinch to zoom state and handlers
|
// Pinch to zoom state and handlers
|
||||||
const currentFontSize = ref(16); // Initial font size in pixels
|
// Initialize with a default, will be overwritten by store value in onMounted
|
||||||
|
const currentFontSize = ref(appearanceStore.currentMobileEditorFontSize);
|
||||||
const MIN_FONT_SIZE = 8;
|
const MIN_FONT_SIZE = 8;
|
||||||
const MAX_FONT_SIZE = 40;
|
const MAX_FONT_SIZE = 40;
|
||||||
let lastPinchDistance = 0;
|
let lastPinchDistance = 0;
|
||||||
|
const debounceTimeout = ref<number | null>(null);
|
||||||
|
const DEBOUNCE_DELAY = 500; // 500ms 防抖延迟
|
||||||
|
|
||||||
const getDistance = (touches: TouchList): number => {
|
const getDistance = (touches: TouchList): number => {
|
||||||
if (touches.length < 2) return 0;
|
if (touches.length < 2) return 0;
|
||||||
@@ -49,6 +54,15 @@ const onTouchStart = (event: TouchEvent) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const debouncedSetMobileEditorFontSize = (size: number) => {
|
||||||
|
if (debounceTimeout.value !== null) {
|
||||||
|
clearTimeout(debounceTimeout.value);
|
||||||
|
}
|
||||||
|
debounceTimeout.value = window.setTimeout(() => {
|
||||||
|
appearanceStore.setMobileEditorFontSize(size);
|
||||||
|
}, DEBOUNCE_DELAY);
|
||||||
|
};
|
||||||
|
|
||||||
const onTouchMove = (event: TouchEvent) => {
|
const onTouchMove = (event: TouchEvent) => {
|
||||||
if (editorRef.value && editorRef.value.contains(event.target as Node)) {
|
if (editorRef.value && editorRef.value.contains(event.target as Node)) {
|
||||||
if (event.touches.length === 2) {
|
if (event.touches.length === 2) {
|
||||||
@@ -61,6 +75,8 @@ const onTouchMove = (event: TouchEvent) => {
|
|||||||
|
|
||||||
if (Math.abs(currentFontSize.value - newFontSize) > 0.1) { // Only update if change is meaningful
|
if (Math.abs(currentFontSize.value - newFontSize) > 0.1) { // Only update if change is meaningful
|
||||||
currentFontSize.value = newFontSize;
|
currentFontSize.value = newFontSize;
|
||||||
|
// Persist the new font size to the store with debounce
|
||||||
|
debouncedSetMobileEditorFontSize(newFontSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newPinchDistance > 0) {
|
if (newPinchDistance > 0) {
|
||||||
@@ -114,6 +130,9 @@ const getLanguageExtension = async (lang: string) => {
|
|||||||
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
// Initialize font size from store
|
||||||
|
currentFontSize.value = appearanceStore.currentMobileEditorFontSize;
|
||||||
|
|
||||||
if (editorRef.value) {
|
if (editorRef.value) {
|
||||||
const langExt = await getLanguageExtension(props.language);
|
const langExt = await getLanguageExtension(props.language);
|
||||||
const startState = createEditorState(props.modelValue, langExt);
|
const startState = createEditorState(props.modelValue, langExt);
|
||||||
@@ -140,6 +159,10 @@ onBeforeUnmount(() => {
|
|||||||
editorRef.value.removeEventListener('touchmove', onTouchMove);
|
editorRef.value.removeEventListener('touchmove', onTouchMove);
|
||||||
editorRef.value.removeEventListener('touchend', onTouchEnd);
|
editorRef.value.removeEventListener('touchend', onTouchEnd);
|
||||||
}
|
}
|
||||||
|
// Clear debounce timeout if it exists
|
||||||
|
if (debounceTimeout.value !== null) {
|
||||||
|
clearTimeout(debounceTimeout.value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(() => props.modelValue, (newValue) => {
|
watch(() => props.modelValue, (newValue) => {
|
||||||
@@ -160,6 +183,13 @@ watch(() => props.language, async (newLanguage, oldLanguage) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Watch for changes from the store (e.g., if changed in settings panel)
|
||||||
|
watch(() => appearanceStore.currentMobileEditorFontSize, (newSize) => {
|
||||||
|
if (newSize !== currentFontSize.value) {
|
||||||
|
currentFontSize.value = newSize;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
focus: () => view.value?.focus(),
|
focus: () => view.value?.focus(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'Consolas, "Courier New", monospace',
|
default: 'Consolas, "Courier New", monospace',
|
||||||
},
|
},
|
||||||
fontSize: { // 新增 prop
|
fontSize: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 14, // 默认字体大小
|
default: 14, // 默认字体大小
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -130,6 +130,12 @@ export const useAppearanceStore = defineStore('appearance', () => {
|
|||||||
return appearanceSettings.value.editorFontFamily || 'Consolas, "Noto Sans SC", "Microsoft YaHei"'; // 提供默认值
|
return appearanceSettings.value.editorFontFamily || 'Consolas, "Noto Sans SC", "Microsoft YaHei"'; // 提供默认值
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 当前移动端编辑器字体大小
|
||||||
|
const currentMobileEditorFontSize = computed<number>(() => {
|
||||||
|
const size = appearanceSettings.value.mobileEditorFontSize;
|
||||||
|
return typeof size === 'number' && size > 0 ? size : 16; // 默认 16
|
||||||
|
});
|
||||||
|
|
||||||
// 终端背景是否启用
|
// 终端背景是否启用
|
||||||
const isTerminalBackgroundEnabled = computed<boolean>(() => {
|
const isTerminalBackgroundEnabled = computed<boolean>(() => {
|
||||||
// 提供默认值 true,如果后端没有设置或设置无效
|
// 提供默认值 true,如果后端没有设置或设置无效
|
||||||
@@ -343,6 +349,14 @@ export const useAppearanceStore = defineStore('appearance', () => {
|
|||||||
await updateAppearanceSettings({ editorFontFamily: fontFamily });
|
await updateAppearanceSettings({ editorFontFamily: fontFamily });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置移动端编辑器字体大小
|
||||||
|
* @param size 字体大小 (数字)
|
||||||
|
*/
|
||||||
|
async function setMobileEditorFontSize(size: number) {
|
||||||
|
await updateAppearanceSettings({ mobileEditorFontSize: size });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置终端背景是否启用
|
* 设置终端背景是否启用
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
@@ -890,7 +904,8 @@ export const useAppearanceStore = defineStore('appearance', () => {
|
|||||||
terminalFontSizeDesktop, // + 用于在设置中分别显示/设置桌面端字号
|
terminalFontSizeDesktop, // + 用于在设置中分别显示/设置桌面端字号
|
||||||
terminalFontSizeMobile, // + 用于在设置中分别显示/设置移动端字号
|
terminalFontSizeMobile, // + 用于在设置中分别显示/设置移动端字号
|
||||||
currentEditorFontSize,
|
currentEditorFontSize,
|
||||||
currentEditorFontFamily, // 新增
|
currentMobileEditorFontSize, // 移动端编辑器字号 getter
|
||||||
|
currentEditorFontFamily,
|
||||||
pageBackgroundImage,
|
pageBackgroundImage,
|
||||||
terminalBackgroundImage,
|
terminalBackgroundImage,
|
||||||
currentTerminalBackgroundOverlayOpacity,
|
currentTerminalBackgroundOverlayOpacity,
|
||||||
@@ -904,7 +919,8 @@ export const useAppearanceStore = defineStore('appearance', () => {
|
|||||||
setTerminalFontSize, // 设置桌面端字体大小
|
setTerminalFontSize, // 设置桌面端字体大小
|
||||||
setTerminalFontSizeMobile, // + 设置移动端字体大小
|
setTerminalFontSizeMobile, // + 设置移动端字体大小
|
||||||
setEditorFontSize,
|
setEditorFontSize,
|
||||||
setEditorFontFamily, // 新增
|
setMobileEditorFontSize, // 设置移动端编辑器字号 action
|
||||||
|
setEditorFontFamily,
|
||||||
setTerminalBackgroundEnabled,
|
setTerminalBackgroundEnabled,
|
||||||
createTerminalTheme,
|
createTerminalTheme,
|
||||||
updateTerminalTheme,
|
updateTerminalTheme,
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ export interface AppearanceSettings {
|
|||||||
terminalFontSizeMobile?: number; // 移动端字体大小
|
terminalFontSizeMobile?: number; // 移动端字体大小
|
||||||
terminalBackgroundImage?: string;
|
terminalBackgroundImage?: string;
|
||||||
pageBackgroundImage?: string;
|
pageBackgroundImage?: string;
|
||||||
editorFontSize?: number;
|
editorFontSize?: number; // 桌面端编辑器字号
|
||||||
|
mobileEditorFontSize?: number; // 移动端编辑器字号
|
||||||
editorFontFamily?: string | null; // Monaco Editor 字体偏好
|
editorFontFamily?: string | null; // Monaco Editor 字体偏好
|
||||||
terminalBackgroundEnabled?: boolean; // 终端背景是否启用
|
terminalBackgroundEnabled?: boolean; // 终端背景是否启用
|
||||||
terminalBackgroundOverlayOpacity?: number; // 终端背景蒙版透明度 (0-1)
|
terminalBackgroundOverlayOpacity?: number; // 终端背景蒙版透明度 (0-1)
|
||||||
|
|||||||
Reference in New Issue
Block a user