From 650884cb85f02ae39b0121e2d98f84f95741590d Mon Sep 17 00:00:00 2001 From: landaiqing Date: Wed, 25 Jun 2025 17:50:50 +0800 Subject: [PATCH] :sparkles: Improve extension management --- .../voidraft/internal/models/models.ts | 5 - frontend/src/i18n/locales/en-US.ts | 28 +- frontend/src/i18n/locales/zh-CN.ts | 28 +- frontend/src/stores/editorStore.ts | 1 + frontend/src/stores/extensionStore.ts | 2 +- .../editor/extensions/codeblast/index.ts | 368 ------------------ .../editor/extensions/codeblast/styles.css | 52 --- .../views/editor/extensions/minimap/index.ts | 10 +- .../views/editor/manager/ExtensionManager.ts | 79 +++- .../src/views/editor/manager/factories.ts | 67 ++-- .../views/settings/pages/ExtensionsPage.vue | 367 +++++++++++++++-- internal/models/extensions.go | 15 +- 12 files changed, 455 insertions(+), 567 deletions(-) delete mode 100644 frontend/src/views/editor/extensions/codeblast/index.ts delete mode 100644 frontend/src/views/editor/extensions/codeblast/styles.css diff --git a/frontend/bindings/voidraft/internal/models/models.ts b/frontend/bindings/voidraft/internal/models/models.ts index 633f115..4ca364d 100644 --- a/frontend/bindings/voidraft/internal/models/models.ts +++ b/frontend/bindings/voidraft/internal/models/models.ts @@ -460,11 +460,6 @@ export enum ExtensionID { */ ExtensionMinimap = "minimap", - /** - * 代码爆炸效果 - */ - ExtensionCodeBlast = "codeBlast", - /** * 工具扩展 * 搜索功能 diff --git a/frontend/src/i18n/locales/en-US.ts b/frontend/src/i18n/locales/en-US.ts index 02c26a2..0729d65 100644 --- a/frontend/src/i18n/locales/en-US.ts +++ b/frontend/src/i18n/locales/en-US.ts @@ -157,28 +157,7 @@ export default { categoryTools: 'Tools', configuration: 'Configuration', resetToDefault: 'Reset to Default Configuration', - minimap: { - displayText: 'Display Text', - displayTextCharacters: 'Characters', - displayTextBlocks: 'Blocks', - showOverlay: 'Show Overlay', - showOverlayAlways: 'Always', - showOverlayHover: 'On Hover', - showOverlayNever: 'Never', - autohide: 'Auto Hide' - }, - codeBlast: { - effect: 'Effect Type', - effectParticles: 'Particle Effect', - effectExplosion: 'Explosion Effect', - shake: 'Shake Effect', - maxParticles: 'Max Particles', - shakeIntensity: 'Shake Intensity' - }, - codeBlock: { - showBackground: 'Show Background', - enableAutoDetection: 'Enable Auto Detection' - } + // Keep necessary extension interface translations, configuration items display in English directly } }, extensions: { @@ -198,10 +177,7 @@ export default { name: 'Minimap', description: 'Display minimap overview of the document' }, - codeBlast: { - name: 'Code Blast', - description: 'Animated effects while typing' - }, + search: { name: 'Search', description: 'Text search and replace functionality' diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index 44f0739..0ffc23d 100644 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -157,28 +157,7 @@ export default { categoryTools: '工具扩展', configuration: '配置', resetToDefault: '重置为默认配置', - minimap: { - displayText: '显示文本', - displayTextCharacters: '字符', - displayTextBlocks: '块', - showOverlay: '显示覆盖层', - showOverlayAlways: '始终', - showOverlayHover: '悬停时', - showOverlayNever: '从不', - autohide: '自动隐藏' - }, - codeBlast: { - effect: '效果类型', - effectParticles: '粒子效果', - effectExplosion: '爆炸效果', - shake: '震动效果', - maxParticles: '最大粒子数', - shakeIntensity: '震动强度' - }, - codeBlock: { - showBackground: '显示背景', - enableAutoDetection: '启用自动检测' - } + // 保留必要的扩展界面翻译,配置项直接显示英文 } }, extensions: { @@ -198,10 +177,7 @@ export default { name: '小地图', description: '显示小地图视图' }, - codeBlast: { - name: '爆炸效果', - description: '编写时的动画效果' - }, + search: { name: '搜索功能', description: '文本搜索和替换功能' diff --git a/frontend/src/stores/editorStore.ts b/frontend/src/stores/editorStore.ts index b13222c..b5238f2 100644 --- a/frontend/src/stores/editorStore.ts +++ b/frontend/src/stores/editorStore.ts @@ -270,6 +270,7 @@ export const useEditorStore = defineStore('editor', () => { // 如果需要更新配置 await ExtensionService.UpdateExtensionState(id, enabled, config) } + // 更新前端编辑器 const manager = getExtensionManager() if (manager) { diff --git a/frontend/src/stores/extensionStore.ts b/frontend/src/stores/extensionStore.ts index 523bda3..0c55040 100644 --- a/frontend/src/stores/extensionStore.ts +++ b/frontend/src/stores/extensionStore.ts @@ -19,7 +19,7 @@ export const useExtensionStore = defineStore('extension', () => { try { extensions.value = await ExtensionService.GetAllExtensions() } catch (err) { - console.error('Failed to load extensions:', err) + console.error('[ExtensionStore] Failed to load extensions:', err) } } diff --git a/frontend/src/views/editor/extensions/codeblast/index.ts b/frontend/src/views/editor/extensions/codeblast/index.ts deleted file mode 100644 index d259b40..0000000 --- a/frontend/src/views/editor/extensions/codeblast/index.ts +++ /dev/null @@ -1,368 +0,0 @@ -import { Extension } from '@codemirror/state'; -import { EditorView, ViewPlugin, ViewUpdate } from '@codemirror/view'; -import './styles.css'; - -// 粒子接口定义 -interface Particle { - x: number; - y: number; - vx: number; - vy: number; - alpha: number; - size: number; - color: number[]; - theta?: number; - drag?: number; - wander?: number; -} - -// 配置接口 -interface CodeBlastConfig { - effect?: 1 | 2; // effect 1: 随机粒子, effect 2: 追逐粒子 - shake?: boolean; // 启用震动效果 - maxParticles?: number; // 最大粒子数 - particleRange?: { min: number; max: number }; // 粒子大小范围 - shakeIntensity?: number; // 震动强度 - gravity?: number; // 重力加速度 (仅 effect: 1) - alphaFadeout?: number; // 粒子透明度衰减 - velocityRange?: { // 粒子速度范围 - x: [number, number]; // x轴方向速度范围 - y: [number, number]; // y轴方向速度范围 - }; -} - -class CodeBlastEffect { - private canvas: HTMLCanvasElement | null = null; - private ctx: CanvasRenderingContext2D | null = null; - private particles: Particle[] = []; - private particlePointer = 0; - private isActive = false; - private lastTime = 0; - private shakeTime = 0; - private shakeTimeMax = 0; - private animationId: number | null = null; - - // 配置参数 - private config: Required = { - effect: 2, - shake: true, - maxParticles: 500, - particleRange: { min: 5, max: 10 }, - shakeIntensity: 5, - gravity: 0.08, - alphaFadeout: 0.96, - velocityRange: { - x: [-1, 1], - y: [-3.5, -1.5] - } - }; - - constructor(config?: CodeBlastConfig) { - if (config) { - this.config = { ...this.config, ...config }; - } - this.particles = new Array(this.config.maxParticles); - } - - private getRGBComponents(element: Element): number[] { - try { - const style = getComputedStyle(element); - const color = style.color; - if (color) { - const match = color.match(/(\d+),\s*(\d+),\s*(\d+)/); - if (match) { - return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])]; - } - } - } catch (e) { - console.warn('Failed to get RGB components:', e); - } - return [255, 255, 255]; // 默认白色 - } - - private random(min: number, max?: number): number { - if (max === undefined) { - max = min; - min = 0; - } - return min + Math.floor(Math.random() * (max - min + 1)); - } - - private createParticle(x: number, y: number, color: number[]): Particle { - const particle: Particle = { - x, - y: y + 10, - alpha: 1, - color, - vx: 0, - vy: 0, - size: 0 - }; - - if (this.config.effect === 1) { - particle.size = this.random(2, 4); - particle.vx = this.config.velocityRange.x[0] + - Math.random() * (this.config.velocityRange.x[1] - this.config.velocityRange.x[0]); - particle.vy = this.config.velocityRange.y[0] + - Math.random() * (this.config.velocityRange.y[1] - this.config.velocityRange.y[0]); - } else if (this.config.effect === 2) { - particle.size = this.random(2, 8); - particle.drag = 0.92; - particle.vx = this.random(-3, 3); - particle.vy = this.random(-3, 3); - particle.wander = 0.15; - particle.theta = this.random(0, 360) * Math.PI / 180; - } - - return particle; - } - - private spawnParticles(view: EditorView): void { - if (!this.ctx) return; - - try { - // 获取光标位置 - const selection = view.state.selection.main; - const coords = view.coordsAtPos(selection.head); - if (!coords) return; - - // 获取光标处的元素来确定颜色 - const element = document.elementFromPoint(coords.left, coords.top); - const color = element ? this.getRGBComponents(element) : [255, 255, 255]; - - const numParticles = this.random( - this.config.particleRange.min, - this.config.particleRange.max - ); - - for (let i = 0; i < numParticles; i++) { - this.particles[this.particlePointer] = this.createParticle( - coords.left + 10, - coords.top, - color - ); - this.particlePointer = (this.particlePointer + 1) % this.config.maxParticles; - } - } catch (error) { - // 如果在更新期间无法读取坐标,静默忽略 - console.warn('Failed to spawn particles:', error); - } - } - - private effect1(particle: Particle): void { - if (!this.ctx) return; - - particle.vy += this.config.gravity; - particle.x += particle.vx; - particle.y += particle.vy; - particle.alpha *= this.config.alphaFadeout; - - this.ctx.fillStyle = `rgba(${particle.color[0]}, ${particle.color[1]}, ${particle.color[2]}, ${particle.alpha})`; - this.ctx.fillRect( - Math.round(particle.x - 1), - Math.round(particle.y - 1), - particle.size, - particle.size - ); - } - - private effect2(particle: Particle): void { - if (!this.ctx || particle.theta === undefined || particle.drag === undefined) return; - - particle.x += particle.vx; - particle.y += particle.vy; - particle.vx *= particle.drag; - particle.vy *= particle.drag; - particle.theta += this.random(-0.5, 0.5); - particle.vx += Math.sin(particle.theta) * 0.1; - particle.vy += Math.cos(particle.theta) * 0.1; - particle.size *= 0.96; - - this.ctx.fillStyle = `rgba(${particle.color[0]}, ${particle.color[1]}, ${particle.color[2]}, ${particle.alpha})`; - this.ctx.beginPath(); - this.ctx.arc( - Math.round(particle.x - 1), - Math.round(particle.y - 1), - particle.size, - 0, - 2 * Math.PI - ); - this.ctx.fill(); - } - - private drawParticles(): void { - if (!this.ctx) return; - - for (let i = 0; i < this.particles.length; i++) { - const particle = this.particles[i]; - if (!particle || particle.alpha < 0.01 || particle.size <= 0.5) { - continue; - } - - if (this.config.effect === 1) { - this.effect1(particle); - } else if (this.config.effect === 2) { - this.effect2(particle); - } - } - } - - private shake(view: EditorView, duration: number): void { - if (!this.config.shake) return; - - this.shakeTime = this.shakeTimeMax = duration; - const editorElement = view.dom; - - const shakeAnimation = () => { - if (this.shakeTime <= 0) { - editorElement.style.transform = ''; - return; - } - - const magnitude = (this.shakeTime / this.shakeTimeMax) * this.config.shakeIntensity; - const shakeX = this.random(-magnitude, magnitude); - const shakeY = this.random(-magnitude, magnitude); - editorElement.style.transform = `translate(${shakeX}px, ${shakeY}px)`; - - this.shakeTime -= 0.016; // ~60fps - requestAnimationFrame(shakeAnimation); - }; - - requestAnimationFrame(shakeAnimation); - } - - private loop = (): void => { - if (!this.isActive || !this.ctx || !this.canvas) return; - - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - this.drawParticles(); - this.animationId = requestAnimationFrame(this.loop); - }; - - public init(view: EditorView): void { - if (this.isActive) return; - - this.isActive = true; - - if (!this.canvas) { - this.canvas = document.createElement('canvas'); - this.ctx = this.canvas.getContext('2d'); - - if (!this.ctx) { - console.error('Failed to get canvas context'); - return; - } - - this.canvas.id = 'code-blast-canvas'; - this.canvas.style.position = 'absolute'; - this.canvas.style.top = '0'; - this.canvas.style.left = '0'; - this.canvas.style.zIndex = '1'; - this.canvas.style.pointerEvents = 'none'; - this.canvas.width = window.innerWidth; - this.canvas.height = window.innerHeight; - - document.body.appendChild(this.canvas); - this.loop(); - } - } - - public destroy(): void { - this.isActive = false; - - if (this.animationId) { - cancelAnimationFrame(this.animationId); - this.animationId = null; - } - - if (this.canvas) { - this.canvas.remove(); - this.canvas = null; - this.ctx = null; - } - } - - public onDocumentChange(view: EditorView): void { - if (this.config.shake) { - this.shake(view, 0.3); - } - // 使用 requestIdleCallback 或 setTimeout 延迟执行粒子生成 - // 确保在 DOM 更新完成后再读取坐标 - if (window.requestIdleCallback) { - window.requestIdleCallback(() => { - this.spawnParticles(view); - }); - } else { - setTimeout(() => { - this.spawnParticles(view); - }, 16); // ~60fps - } - } - - public updateCanvasSize(): void { - if (this.canvas) { - this.canvas.width = window.innerWidth; - this.canvas.height = window.innerHeight; - } - } -} - -// 节流函数 -function throttle void>( - func: T, - limit: number -): (...args: Parameters) => void { - let inThrottle: boolean; - return function(this: any, ...args: Parameters) { - if (!inThrottle) { - func.apply(this, args); - inThrottle = true; - setTimeout(() => inThrottle = false, limit); - } - }; -} - -// 创建 CodeBlast 扩展 -export function createCodeBlastExtension(config?: CodeBlastConfig): Extension { - let effect: CodeBlastEffect | null = null; - - const plugin = ViewPlugin.fromClass(class { - private throttledOnChange: (view: EditorView) => void; - - constructor(private view: EditorView) { - effect = new CodeBlastEffect(config); - effect.init(view); - - this.throttledOnChange = throttle((view: EditorView) => { - effect?.onDocumentChange(view); - }, 100); - - // 监听窗口大小变化 - window.addEventListener('resize', this.handleResize); - } - - update(update: ViewUpdate) { - if (update.docChanged) { - // 延迟执行,确保更新完成后再触发效果 - setTimeout(() => { - this.throttledOnChange(this.view); - }, 0); - } - } - - destroy() { - effect?.destroy(); - effect = null; - window.removeEventListener('resize', this.handleResize); - } - - private handleResize = () => { - effect?.updateCanvasSize(); - }; - }); - - return plugin; -} - -// 默认导出 -export const codeBlast = createCodeBlastExtension(); \ No newline at end of file diff --git a/frontend/src/views/editor/extensions/codeblast/styles.css b/frontend/src/views/editor/extensions/codeblast/styles.css deleted file mode 100644 index f48973e..0000000 --- a/frontend/src/views/editor/extensions/codeblast/styles.css +++ /dev/null @@ -1,52 +0,0 @@ -#code-blast-canvas { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - z-index: 1000; - pointer-events: none; - background: transparent; -} - -/* 确保编辑器在震动时不会影响布局 */ -.cm-editor { - transition: transform 0.1s ease-out; -} - -/* 打字机效果的粒子样式 */ -.code-blast-particle { - position: absolute; - pointer-events: none; - border-radius: 50%; - animation: particle-fade 2s ease-out forwards; -} - -@keyframes particle-fade { - 0% { - opacity: 1; - transform: scale(1); - } - 100% { - opacity: 0; - transform: scale(0.5); - } -} - -/* 震动效果的缓动 */ -.code-blast-shake { - animation: shake 0.3s ease-out; -} - -@keyframes shake { - 0%, 100% { transform: translate(0, 0); } - 10% { transform: translate(-2px, -1px); } - 20% { transform: translate(2px, 1px); } - 30% { transform: translate(-1px, 2px); } - 40% { transform: translate(1px, -2px); } - 50% { transform: translate(-2px, 1px); } - 60% { transform: translate(2px, -1px); } - 70% { transform: translate(-1px, -2px); } - 80% { transform: translate(1px, 2px); } - 90% { transform: translate(-2px, -1px); } -} \ No newline at end of file diff --git a/frontend/src/views/editor/extensions/minimap/index.ts b/frontend/src/views/editor/extensions/minimap/index.ts index e9d7bb9..8c1f510 100644 --- a/frontend/src/views/editor/extensions/minimap/index.ts +++ b/frontend/src/views/editor/extensions/minimap/index.ts @@ -122,6 +122,14 @@ const minimapClass = ViewPlugin.fromClass( } if (now) { + if (prev && this.dom && prev.autohide !== now.autohide) { + if (now.autohide) { + this.dom.classList.add('cm-minimap-autohide'); + } else { + this.dom.classList.remove('cm-minimap-autohide'); + } + } + this.text.update(update); this.selection.update(update); this.diagnostic.update(update); @@ -279,7 +287,7 @@ const minimapClass = ViewPlugin.fromClass( } ); -// 使用type定义,而不是interface +// 使用type定义 export type MinimapConfig = Omit & { /** * A function that creates the element that contains the minimap diff --git a/frontend/src/views/editor/manager/ExtensionManager.ts b/frontend/src/views/editor/manager/ExtensionManager.ts index 0a83dec..c9c88cb 100644 --- a/frontend/src/views/editor/manager/ExtensionManager.ts +++ b/frontend/src/views/editor/manager/ExtensionManager.ts @@ -47,6 +47,8 @@ export class ExtensionManager { private view: EditorView | null = null private compartments = new Map() private extensionFactories = new Map() + private updateQueue = new Map() + private debounceTimeout: number | null = null /** * 注册扩展工厂 @@ -110,7 +112,7 @@ export class ExtensionManager { compartmentInfo.currentConfig = config.config compartmentInfo.enabled = config.enabled } catch (error) { - console.error(`Failed to create extension ${config.id}:`, error) + console.error(`[ExtensionManager] Failed to create extension ${config.id}:`, error) } } @@ -126,13 +128,33 @@ export class ExtensionManager { } /** - * 动态更新单个扩展 + * 动态更新单个扩展(带防抖) * @param id 扩展ID * @param enabled 是否启用 * @param config 扩展配置 */ updateExtension(id: ExtensionID, enabled: boolean, config: any = {}): void { + // 添加到更新队列 + this.updateQueue.set(id, {enabled, config, timestamp: Date.now()}) + // 清除之前的防抖定时器 + if (this.debounceTimeout) { + clearTimeout(this.debounceTimeout) + } + + // 设置新的防抖定时器 + this.debounceTimeout = window.setTimeout(() => { + this.flushUpdateQueue() + }, 100) // 100ms 防抖 + } + + /** + * 立即更新扩展(无防抖) + * @param id 扩展ID + * @param enabled 是否启用 + * @param config 扩展配置 + */ + updateExtensionImmediate(id: ExtensionID, enabled: boolean, config: any = {}): void { if (!this.view) { return } @@ -166,6 +188,52 @@ export class ExtensionManager { } } + /** + * 处理更新队列中的所有更新 + */ + private flushUpdateQueue(): void { + if (!this.view) { + return + } + + const effects: StateEffect[] = [] + + for (const [id, update] of this.updateQueue) { + const compartmentInfo = this.compartments.get(id) + if (!compartmentInfo) { + continue + } + + try { + // 验证配置 + if (compartmentInfo.factory.validateConfig && + !compartmentInfo.factory.validateConfig(update.config)) { + continue + } + + const extension = update.enabled + ? compartmentInfo.factory.create(update.config) + : [] + + effects.push(compartmentInfo.compartment.reconfigure(extension)) + + // 更新状态 + compartmentInfo.currentConfig = update.config + compartmentInfo.enabled = update.enabled + } catch (error) { + console.error(`[ExtensionManager] Failed to update extension ${id}:`, error) + } + } + + if (effects.length > 0) { + this.view.dispatch({effects}) + } + + // 清空更新队列 + this.updateQueue.clear() + this.debounceTimeout = null + } + /** * 批量更新扩展 * @param updates 更新配置数组 @@ -251,8 +319,15 @@ export class ExtensionManager { * 销毁管理器 */ destroy(): void { + // 清除防抖定时器 + if (this.debounceTimeout) { + clearTimeout(this.debounceTimeout) + this.debounceTimeout = null + } + this.view = null this.compartments.clear() this.extensionFactories.clear() + this.updateQueue.clear() } } \ No newline at end of file diff --git a/frontend/src/views/editor/manager/factories.ts b/frontend/src/views/editor/manager/factories.ts index ca54f98..8932462 100644 --- a/frontend/src/views/editor/manager/factories.ts +++ b/frontend/src/views/editor/manager/factories.ts @@ -5,7 +5,7 @@ import i18n from '@/i18n' // 导入现有扩展的创建函数 import rainbowBracketsExtension from '../extensions/rainbowBracket/rainbowBracketsExtension' import {createTextHighlighter} from '../extensions/textHighlight/textHighlightExtension' -import {createCodeBlastExtension} from '../extensions/codeblast' + import {color} from '../extensions/colorSelector' import {hyperLink} from '../extensions/hyperlink' import {minimap} from '../extensions/minimap' @@ -73,35 +73,7 @@ export const minimapFactory: ExtensionFactory = { } } -/** - * 代码爆炸效果扩展工厂 - */ -export const codeBlastFactory: ExtensionFactory = { - create(config: any) { - const options = { - effect: config.effect || 1, - shake: config.shake !== false, - maxParticles: config.maxParticles || 300, - shakeIntensity: config.shakeIntensity || 3 - } - return createCodeBlastExtension(options) - }, - getDefaultConfig() { - return { - effect: 1, - shake: true, - maxParticles: 300, - shakeIntensity: 3 - } - }, - validateConfig(config: any) { - return typeof config === 'object' && - (!config.effect || [1, 2].includes(config.effect)) && - (!config.shake || typeof config.shake === 'boolean') && - (!config.maxParticles || typeof config.maxParticles === 'number') && - (!config.shakeIntensity || typeof config.shakeIntensity === 'number') - } -} + /** * 超链接扩展工厂 @@ -213,11 +185,7 @@ const EXTENSION_CONFIGS = { displayNameKey: 'extensions.minimap.name', descriptionKey: 'extensions.minimap.description' }, - [ExtensionID.ExtensionCodeBlast]: { - factory: codeBlastFactory, - displayNameKey: 'extensions.codeBlast.name', - descriptionKey: 'extensions.codeBlast.description' - }, + // 工具扩展 [ExtensionID.ExtensionSearch]: { @@ -272,6 +240,35 @@ export function getExtensionDescription(id: ExtensionID): string { return config?.descriptionKey ? i18n.global.t(config.descriptionKey) : '' } +/** + * 获取扩展工厂实例 + * @param id 扩展ID + * @returns 扩展工厂实例 + */ +export function getExtensionFactory(id: ExtensionID): ExtensionFactory | undefined { + return EXTENSION_CONFIGS[id as ExtensionID]?.factory +} + +/** + * 获取扩展的默认配置 + * @param id 扩展ID + * @returns 默认配置对象 + */ +export function getExtensionDefaultConfig(id: ExtensionID): any { + const factory = getExtensionFactory(id) + return factory?.getDefaultConfig() || {} +} + +/** + * 检查扩展是否有配置项 + * @param id 扩展ID + * @returns 是否有配置项 + */ +export function hasExtensionConfig(id: ExtensionID): boolean { + const defaultConfig = getExtensionDefaultConfig(id) + return Object.keys(defaultConfig).length > 0 +} + /** * 获取所有可用扩展的ID列表 * @returns 扩展ID数组 diff --git a/frontend/src/views/settings/pages/ExtensionsPage.vue b/frontend/src/views/settings/pages/ExtensionsPage.vue index 504de2c..c42ae88 100644 --- a/frontend/src/views/settings/pages/ExtensionsPage.vue +++ b/frontend/src/views/settings/pages/ExtensionsPage.vue @@ -1,11 +1,18 @@ @@ -83,25 +271,130 @@ const resetExtension = async (extensionId: ExtensionID) => { max-width: 1000px; } +.extension-item { + border-bottom: 1px solid var(--settings-input-border); + + &:last-child { + border-bottom: none; + } +} + .extension-controls { display: flex; align-items: center; - gap: 8px; + gap: 12px; + min-width: 140px; + justify-content: flex-end; } -.reset-button { - padding: 4px 8px; - font-size: 11px; - border: 1px solid var(--settings-input-border); - border-radius: 3px; - background-color: var(--settings-input-bg); +.config-button { + padding: 4px; + border: none; + background: none; color: var(--settings-text-secondary); cursor: pointer; transition: all 0.2s ease; - + border-radius: 4px; + flex-shrink: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + &:hover { background-color: var(--settings-hover); color: var(--settings-text); } + + &.expanded { + color: var(--settings-accent); + background-color: var(--settings-hover); + } + + svg { + transition: all 0.2s ease; + } +} + +.config-placeholder { + width: 24px; + height: 24px; + flex-shrink: 0; +} + +.extension-config { + background-color: var(--settings-input-bg); + border-left: 3px solid var(--settings-accent); + margin: 8px 0 16px 0; + padding: 12px; + border-radius: 6px; + font-size: 13px; /* 调整字体大小 */ +} + +.config-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; +} + +.config-title { + font-size: 13px; /* 调整字体大小 */ + font-weight: 600; + color: var(--settings-text); + margin: 0; +} + +.reset-button { + padding: 6px 12px; + font-size: 11px; /* 调整字体大小 */ + border: 1px solid var(--settings-input-border); + border-radius: 4px; + background-color: var(--settings-input-bg); + color: var(--settings-text-secondary); + cursor: pointer; + transition: all 0.2s ease; + white-space: nowrap; + + &:hover { + background-color: var(--settings-hover); + color: var(--settings-text); + border-color: var(--settings-accent); + } +} + +.config-item { + &:not(:last-child) { + margin-bottom: 12px; + } + + /* 配置项标题和描述字体大小 */ + :deep(.setting-item-title) { + font-size: 12px; + } + + :deep(.setting-item-description) { + font-size: 11px; + } +} + +.config-input, .config-select { + min-width: 120px; + padding: 4px 8px; + border: 1px solid var(--settings-input-border); + border-radius: 3px; + background-color: var(--settings-input-bg); + color: var(--settings-text); + font-size: 11px; /* 调整字体大小 */ + + &:focus { + outline: none; + border-color: var(--settings-accent); + } +} + +.config-select { + cursor: pointer; } \ No newline at end of file diff --git a/internal/models/extensions.go b/internal/models/extensions.go index 455fd06..9d4ca57 100644 --- a/internal/models/extensions.go +++ b/internal/models/extensions.go @@ -23,8 +23,7 @@ const ( ExtensionTextHighlight ExtensionID = "textHighlight" // UI增强扩展 - ExtensionMinimap ExtensionID = "minimap" // 小地图 - ExtensionCodeBlast ExtensionID = "codeBlast" // 代码爆炸效果 + ExtensionMinimap ExtensionID = "minimap" // 小地图 // 工具扩展 ExtensionSearch ExtensionID = "search" // 搜索功能 @@ -104,18 +103,6 @@ func NewDefaultExtensions() []Extension { "autohide": false, }, }, - { - ID: ExtensionCodeBlast, - Category: CategoryUI, - Enabled: true, - IsDefault: true, - Config: ExtensionConfig{ - "effect": 1, - "shake": true, - "maxParticles": 300, - "shakeIntensity": 3, - }, - }, // 工具扩展 {