Skip to content

Commit 08bf500

Browse files
feat(settings): seed stable editor launch commands
1 parent b0df2cd commit 08bf500

6 files changed

Lines changed: 56 additions & 41 deletions

File tree

src/constants/codeEditor.ts

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface CodeEditorOption {
2929
icon?: string
3030
descriptionKey?: string
3131
groupKey: string
32-
defaultCommand: string
32+
initialCommand?: string
3333
openInTerminal?: boolean
3434
monochromeIcon?: boolean
3535
}
@@ -40,7 +40,7 @@ export interface EditorCommandOption {
4040
icon?: string
4141
descriptionKey?: string
4242
groupKey: string
43-
defaultCommand: string
43+
initialCommand?: string
4444
openInTerminal: boolean
4545
monochromeIcon?: boolean
4646
editors: CodeEditorEnum[]
@@ -78,119 +78,108 @@ export const codeEditors: Record<CodeEditorEnum, CodeEditorOption> = {
7878
label: 'IntelliJ IDEA',
7979
descriptionKey: 'app.settings.editors.descriptions.intellij_idea',
8080
icon: 'i-custom:jetbrains-intellij-idea',
81-
defaultCommand: 'idea {project}',
8281
},
8382
[CodeEditorEnum.PyCharm]: {
8483
groupKey: 'app.settings.editors.groups.jetbrains',
8584
label: 'PyCharm',
8685
descriptionKey: 'app.settings.editors.descriptions.pycharm',
8786
icon: 'i-custom:jetbrains-pycharm',
88-
defaultCommand: 'pycharm {project}',
8987
},
9088
[CodeEditorEnum.PhpStorm]: {
9189
groupKey: 'app.settings.editors.groups.jetbrains',
9290
label: 'PhpStorm',
9391
descriptionKey: 'app.settings.editors.descriptions.phpstorm',
9492
icon: 'i-custom:jetbrains-phpstorm',
95-
defaultCommand: 'phpstorm {project}',
9693
},
9794
[CodeEditorEnum.GoLand]: {
9895
groupKey: 'app.settings.editors.groups.jetbrains',
9996
label: 'GoLand',
10097
descriptionKey: 'app.settings.editors.descriptions.goland',
10198
icon: 'i-custom:jetbrains-goland',
102-
defaultCommand: 'goland {project}',
10399
},
104100
[CodeEditorEnum.Rider]: {
105101
groupKey: 'app.settings.editors.groups.jetbrains',
106102
label: 'Rider',
107103
descriptionKey: 'app.settings.editors.descriptions.rider',
108104
icon: 'i-custom:jetbrains-rider',
109-
defaultCommand: 'rider {project}',
110105
},
111106
[CodeEditorEnum.Clion]: {
112107
groupKey: 'app.settings.editors.groups.jetbrains',
113108
label: 'CLion',
114109
descriptionKey: 'app.settings.editors.descriptions.clion',
115110
icon: 'i-custom:jetbrains-clion',
116-
defaultCommand: 'clion {project}',
117111
},
118112
[CodeEditorEnum.RustRover]: {
119113
groupKey: 'app.settings.editors.groups.jetbrains',
120114
label: 'RustRover',
121115
descriptionKey: 'app.settings.editors.descriptions.rust_rover',
122116
icon: 'i-custom:jetbrains-rustrover',
123-
defaultCommand: 'rustrover {project}',
124117
},
125118
[CodeEditorEnum.WebStorm]: {
126119
groupKey: 'app.settings.editors.groups.jetbrains',
127120
label: 'WebStorm',
128121
descriptionKey: 'app.settings.editors.descriptions.webstorm',
129122
icon: 'i-custom:jetbrains-webstorm',
130-
defaultCommand: 'webstorm {project}',
131123
},
132124
[CodeEditorEnum.RubyMine]: {
133125
groupKey: 'app.settings.editors.groups.jetbrains',
134126
label: 'RubyMine',
135127
descriptionKey: 'app.settings.editors.descriptions.rubymine',
136128
icon: 'i-custom:jetbrains-rubymine',
137-
defaultCommand: 'rubymine {project}',
138129
},
139130
[CodeEditorEnum.VisualStudio]: {
140131
groupKey: 'app.settings.editors.groups.microsoft',
141132
label: 'Visual Studio',
142133
icon: 'i-custom:visual-studio',
143-
defaultCommand: 'devenv {project}',
144134
},
145135
[CodeEditorEnum.VisualStudioCode]: {
146136
groupKey: 'app.settings.editors.groups.microsoft',
147137
label: 'Visual Studio Code',
148138
icon: 'i-custom:visual-studio-code',
149-
defaultCommand: 'code {project}',
139+
initialCommand: 'code {project}',
150140
},
151141
[CodeEditorEnum.Cursor]: {
152142
groupKey: 'app.settings.editors.groups.ai_editors',
153143
label: 'Cursor',
154144
descriptionKey: 'app.settings.editors.descriptions.cursor',
155145
icon: 'i-custom:cursor',
156-
defaultCommand: 'cursor {project}',
146+
initialCommand: 'cursor {project}',
157147
monochromeIcon: true,
158148
},
159149
[CodeEditorEnum.Windsurf]: {
160150
groupKey: 'app.settings.editors.groups.ai_editors',
161151
label: 'Windsurf',
162152
descriptionKey: 'app.settings.editors.descriptions.windsurf',
163153
icon: 'i-custom:windsurf',
164-
defaultCommand: 'windsurf {project}',
154+
initialCommand: 'windsurf {project}',
165155
monochromeIcon: true,
166156
},
167157
[CodeEditorEnum.Trae]: {
168158
groupKey: 'app.settings.editors.groups.ai_editors',
169159
label: 'Trae',
170160
descriptionKey: 'app.settings.editors.descriptions.trae',
171161
icon: 'i-custom:trae-color',
172-
defaultCommand: 'trae {project}',
173162
},
174163
[CodeEditorEnum.Zed]: {
175164
groupKey: 'app.settings.editors.groups.editors',
176165
label: 'Zed',
177166
descriptionKey: 'app.settings.editors.descriptions.zed',
178167
icon: 'i-custom:zed-industries',
179-
defaultCommand: 'zed {project}',
168+
initialCommand: 'zed {project}',
180169
monochromeIcon: true,
181170
},
182171
[CodeEditorEnum.SublimeText]: {
183172
groupKey: 'app.settings.editors.groups.editors',
184173
label: 'Sublime Text',
185174
icon: 'i-custom:sublime-text',
186-
defaultCommand: 'subl {project}',
175+
initialCommand: 'subl {project}',
187176
monochromeIcon: true,
188177
},
189178
[CodeEditorEnum.Neovim]: {
190179
groupKey: 'app.settings.editors.groups.terminal_editors',
191180
label: 'Neovim',
192181
icon: 'i-custom:neovim',
193-
defaultCommand: 'nvim .',
182+
initialCommand: 'nvim .',
194183
openInTerminal: true,
195184
monochromeIcon: true,
196185
},
@@ -199,30 +188,29 @@ export const codeEditors: Record<CodeEditorEnum, CodeEditorOption> = {
199188
label: 'Codex CLI',
200189
descriptionKey: 'app.settings.editors.descriptions.codex_cli',
201190
icon: 'i-custom:codex-color',
202-
defaultCommand: 'codex',
191+
initialCommand: 'codex',
203192
openInTerminal: true,
204193
},
205194
[CodeEditorEnum.ClaudeCode]: {
206195
groupKey: 'app.settings.editors.groups.ai_cli',
207196
label: 'Claude Code',
208197
descriptionKey: 'app.settings.editors.descriptions.claude_code',
209198
icon: 'i-custom:claude-color',
210-
defaultCommand: 'claude',
199+
initialCommand: 'claude',
211200
openInTerminal: true,
212201
},
213202
[CodeEditorEnum.GeminiCli]: {
214203
groupKey: 'app.settings.editors.groups.ai_cli',
215204
label: 'Gemini CLI',
216205
descriptionKey: 'app.settings.editors.descriptions.gemini_cli',
217206
icon: 'i-custom:gemini-color',
218-
defaultCommand: 'gemini',
207+
initialCommand: 'gemini',
219208
openInTerminal: true,
220209
},
221210
[CodeEditorEnum.AndroidStudio]: {
222211
groupKey: 'app.settings.editors.groups.google',
223212
label: 'Android Studio',
224213
icon: 'i-custom:android-studio-stable',
225-
defaultCommand: 'studio {project}',
226214
},
227215
}
228216

@@ -296,7 +284,7 @@ export const editorCommandOptions: EditorCommandOption[] = [
296284
icon: option.icon,
297285
descriptionKey: option.descriptionKey,
298286
groupKey: option.groupKey,
299-
defaultCommand: option.defaultCommand,
287+
initialCommand: option.initialCommand,
300288
openInTerminal: option.openInTerminal === true,
301289
monochromeIcon: option.monochromeIcon,
302290
editors: [editor],

src/locales/en.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ app:
180180
zh_tw: Traditional Chinese
181181
editors:
182182
title: Editors
183-
command_placeholder: Launch command for {editor}
183+
command_label: Launch command for {editor}
184+
command_placeholder: Command or executable path
184185
clear_command: Clear command
185186
browse_executable: Browse executable
186187
auto_detect: Auto detect

src/locales/zh-CN.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ app:
180180
zh_tw: 繁體中文
181181
editors:
182182
title: 编辑器
183-
command_placeholder: '{editor} 的启动命令'
183+
command_label: '{editor} 的启动命令'
184+
command_placeholder: 命令或可执行文件路径
184185
clear_command: 清空命令
185186
browse_executable: 浏览可执行文件
186187
auto_detect: 自动检测

src/locales/zh-TW.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ app:
180180
zh_tw: 繁體中文
181181
editors:
182182
title: 編輯器
183-
command_placeholder: '{editor} 的啟動指令'
183+
command_label: '{editor} 的啟動指令'
184+
command_placeholder: 指令或可執行檔路徑
184185
clear_command: 清空指令
185186
browse_executable: 瀏覽可執行檔案
186187
auto_detect: 自動偵測

src/stores/settingsStore.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ interface StoredSettings {
6666
customThemeColor?: string
6767
terminalCommand?: string
6868
language?: LanguageEnum
69+
editorInitialCommandsVersion?: number
6970
codeEditorsPath?: Partial<Record<EditorCommandKey | 'jetbrains', string>>
7071
codeEditorsOpenInTerminal?: Partial<Record<EditorCommandKey, boolean>>
7172
autoDetectedEditorCommands?: boolean
@@ -84,6 +85,7 @@ const settingsPersistence = createPersistence<StoredSettings>({
8485
})
8586

8687
const DEFAULT_CUSTOM_THEME_COLOR = '#4682fa'
88+
const EDITOR_INITIAL_COMMANDS_VERSION = 1
8789
const HEX_COLOR_RE = /^#[\da-f]{6}$/i
8890

8991
function isTheme(value: unknown): value is ThemeEnum {
@@ -124,6 +126,12 @@ function createDefaultCliEditorScanners(): Record<CliHistoryScannerEditor, CliEd
124126
) as Record<CliHistoryScannerEditor, CliEditorScannerConfig>
125127
}
126128

129+
function createInitialEditorCommands(): Record<EditorCommandKey, string> {
130+
return Object.fromEntries(
131+
editorCommandOptions.map(option => [option.key, option.initialCommand || '']),
132+
) as Record<EditorCommandKey, string>
133+
}
134+
127135
export const useSettingsStore = defineStore('settings', () => {
128136
// --- 基础配置项 ---
129137
const theme = ref<ThemeEnum>(ThemeEnum.System)
@@ -135,11 +143,10 @@ export const useSettingsStore = defineStore('settings', () => {
135143
const customThemeColor = ref(DEFAULT_CUSTOM_THEME_COLOR)
136144
const terminalCommand = ref('')
137145
const language = ref<LanguageEnum>(LanguageEnum.English)
146+
const editorInitialCommandsVersion = ref(EDITOR_INITIAL_COMMANDS_VERSION)
138147
const loaded = ref(false)
139148
let saveTimer: number | null = null
140-
const codeEditorsPath = reactive<Record<EditorCommandKey, string>>(
141-
Object.fromEntries(editorCommandKeys.map(key => [key, ''])) as Record<EditorCommandKey, string>,
142-
)
149+
const codeEditorsPath = reactive<Record<EditorCommandKey, string>>(createInitialEditorCommands())
143150
const codeEditorsOpenInTerminal = reactive<Record<EditorCommandKey, boolean>>(
144151
Object.fromEntries(editorCommandOptions.map(option => [option.key, option.openInTerminal])) as Record<EditorCommandKey, boolean>,
145152
)
@@ -269,10 +276,11 @@ export const useSettingsStore = defineStore('settings', () => {
269276
customThemeColor.value = DEFAULT_CUSTOM_THEME_COLOR
270277
terminalCommand.value = ''
271278
language.value = LanguageEnum.English
279+
editorInitialCommandsVersion.value = EDITOR_INITIAL_COMMANDS_VERSION
272280
autoDetectedEditorCommands.value = false
273281
savedWebDavPassword = ''
274282
for (const option of editorCommandOptions) {
275-
codeEditorsPath[option.key] = ''
283+
codeEditorsPath[option.key] = option.initialCommand || ''
276284
codeEditorsOpenInTerminal[option.key] = option.openInTerminal
277285
}
278286
webdav.endpoint = ''
@@ -294,12 +302,16 @@ export const useSettingsStore = defineStore('settings', () => {
294302
terminalCommand.value = loadedData.terminalCommand
295303
if (isLanguage(loadedData.language))
296304
language.value = loadedData.language
305+
editorInitialCommandsVersion.value = typeof loadedData.editorInitialCommandsVersion === 'number'
306+
? loadedData.editorInitialCommandsVersion
307+
: 0
297308

298309
// editor launch commands
299310
if (loadedData.codeEditorsPath) {
300311
for (const option of editorCommandOptions) {
301312
const command = loadedData.codeEditorsPath[option.key]
302-
codeEditorsPath[option.key] = typeof command === 'string' ? command : ''
313+
if (typeof command === 'string')
314+
codeEditorsPath[option.key] = command
303315
}
304316

305317
const legacyJetBrainsCommand = loadedData.codeEditorsPath.jetbrains
@@ -390,6 +402,18 @@ export const useSettingsStore = defineStore('settings', () => {
390402
return legacyWebDavPassword
391403
}
392404

405+
function applyInitialEditorCommandsMigration() {
406+
if (editorInitialCommandsVersion.value >= EDITOR_INITIAL_COMMANDS_VERSION)
407+
return false
408+
409+
for (const option of editorCommandOptions) {
410+
if (option.initialCommand && !codeEditorsPath[option.key].trim())
411+
codeEditorsPath[option.key] = option.initialCommand
412+
}
413+
editorInitialCommandsVersion.value = EDITOR_INITIAL_COMMANDS_VERSION
414+
return true
415+
}
416+
393417
function hydrateCachedSettings() {
394418
const cachedData = settingsPersistence.loadCached()
395419
if (!cachedData)
@@ -399,6 +423,7 @@ export const useSettingsStore = defineStore('settings', () => {
399423
loaded.value = false
400424
resetSettingsState()
401425
applyStoredSettings(cachedData)
426+
applyInitialEditorCommandsMigration()
402427
loaded.value = wasLoaded
403428
return true
404429
}
@@ -474,16 +499,18 @@ export const useSettingsStore = defineStore('settings', () => {
474499
const loadedData = await settingsPersistence.load()
475500
const isFirstRun = !loadedData
476501
let legacyWebDavPassword: string | null = null
502+
let shouldSaveInitialEditorCommands = false
477503
if (loadedData) {
478504
resetSettingsState()
479505
legacyWebDavPassword = applyStoredSettings(loadedData)
506+
shouldSaveInitialEditorCommands = applyInitialEditorCommandsMigration()
480507
}
481508
else {
482509
resetSettingsState()
483510
}
484511
const shouldSanitizeLegacyWebDavPassword = await loadWebDavPassword(legacyWebDavPassword)
485512
loaded.value = true
486-
if (shouldSanitizeLegacyWebDavPassword || cachedHadLegacyWebDavPassword)
513+
if (shouldSanitizeLegacyWebDavPassword || cachedHadLegacyWebDavPassword || shouldSaveInitialEditorCommands)
487514
await saveSettings()
488515
if (!isFirstRun && !autoDetectedEditorCommands.value) {
489516
autoDetectedEditorCommands.value = true
@@ -517,6 +544,7 @@ export const useSettingsStore = defineStore('settings', () => {
517544
customThemeColor: customThemeColor.value,
518545
terminalCommand: terminalCommand.value,
519546
language: language.value,
547+
editorInitialCommandsVersion: editorInitialCommandsVersion.value,
520548
codeEditorsPath: { ...codeEditorsPath },
521549
codeEditorsOpenInTerminal: { ...codeEditorsOpenInTerminal },
522550
autoDetectedEditorCommands: autoDetectedEditorCommands.value,
@@ -541,7 +569,7 @@ export const useSettingsStore = defineStore('settings', () => {
541569
}
542570

543571
watch(
544-
[theme, themeColor, customThemeColor, terminalCommand, language, codeEditorsPath, codeEditorsOpenInTerminal, autoDetectedEditorCommands, webdav, scanner],
572+
[theme, themeColor, customThemeColor, terminalCommand, language, editorInitialCommandsVersion, codeEditorsPath, codeEditorsOpenInTerminal, autoDetectedEditorCommands, webdav, scanner],
545573
queueSaveSettings,
546574
{ deep: true },
547575
)

src/views/SettingsView/pages/Ides.vue

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ function commandGroup(option: EditorCommandOption) {
3535
return t(option.groupKey)
3636
}
3737
38-
function commandPlaceholder(option: EditorCommandOption) {
39-
return option.defaultCommand
40-
}
41-
4238
function editorDescription(option: EditorCommandOption) {
4339
return option.descriptionKey ? t(option.descriptionKey) : commandGroup(option)
4440
}
@@ -259,9 +255,9 @@ function quoteCommand(command: string) {
259255
class="path-input"
260256
data-editor-control="command"
261257
spellcheck="false"
262-
:aria-label="t('app.settings.editors.command_placeholder', { editor: option.label })"
263-
:placeholder="commandPlaceholder(option)"
264-
:title="settings.codeEditorsPath[option.key] || commandPlaceholder(option)"
258+
:aria-label="t('app.settings.editors.command_label', { editor: option.label })"
259+
:placeholder="t('app.settings.editors.command_placeholder')"
260+
:title="settings.codeEditorsPath[option.key] || t('app.settings.editors.command_placeholder')"
265261
>
266262
<button
267263
v-if="settings.codeEditorsPath[option.key]"

0 commit comments

Comments
 (0)