Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions agent/utils/terminal/ai/config_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ func ResolveGeneratorConfigFromAgentSettings() (GeneratorConfig, uint, time.Dura
return config, uint(accountID), timeout, err
}

// ResolveGeneratorConfigFromFileSettings loads file-management AI search settings (independent from terminal AI).
func ResolveGeneratorConfigFromFileSettings() (GeneratorConfig, uint, time.Duration, error) {
status, err := loadAgentSettingValue("FileAIStatus")
if err != nil {
Expand All @@ -151,7 +150,6 @@ func ResolveGeneratorConfigFromFileSettings() (GeneratorConfig, uint, time.Durat
return config, uint(accountID), timeout, err
}

// LoadFileAIRuntimeConfig returns LLM client config for file-management AI search.
func LoadFileAIRuntimeConfig() (GeneratorConfig, time.Duration, error) {
cfg, _, timeout, err := ResolveGeneratorConfigFromFileSettings()
return cfg, timeout, err
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/global/mimetype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export const Languages = [
label: 'markdown',
value: ['md'],
},
{
label: 'dockerfile',
value: ['dockerfile'],
},
{
label: 'yaml',
value: ['yml', 'yaml'],
Expand Down
48 changes: 48 additions & 0 deletions frontend/src/utils/file.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Languages } from '@/global/mimetype';

const icons = new Map([
['.zip', 'p-file-zip'],
['.gz', 'p-file-zip'],
Expand Down Expand Up @@ -152,3 +154,49 @@ export function downloadWithContent(content: string, fileName: string) {
const event = new MouseEvent('click');
a.dispatchEvent(event);
}

const editorLanguages = new Map(
Languages.flatMap((language) => language.value.map((ext) => [ext.toLowerCase(), language.label] as const)),
);

const specialEditorFileNames = ['dockerfile'];

const normalizeEditorExtension = (value: string) => {
const trimmed = value.trim().toLowerCase();
if (!trimmed) {
return '';
}
return trimmed.startsWith('.') ? trimmed.slice(1) : trimmed;
};

const isSpecialEditorFileName = (value: string) => {
const normalized = value.trim().toLowerCase();
if (!normalized) {
return false;
}
return specialEditorFileNames.some((filename) => normalized === filename || normalized.startsWith(`${filename}.`));
};

export const resolveEditorLanguage = (path: string, extension = '', name = '') => {
if (isSpecialEditorFileName(name)) {
return 'dockerfile';
}

const candidates = [extension, path.split('/').pop() || '']
.map((value) => normalizeEditorExtension(value))
.filter(Boolean);

for (const ext of candidates) {
const language = editorLanguages.get(ext);
if (language) {
return language;
}
}

const fileName = path.split('/').pop() || '';
if (isSpecialEditorFileName(fileName)) {
return 'dockerfile';
}

return 'yaml';
};
42 changes: 15 additions & 27 deletions frontend/src/views/host/file-management/code-editor/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
<span
v-else
style="display: inline-flex; align-items: center"
@click="getContent(data.path, data.extension)"
@click="getContent(data.path)"
>
<template v-if="isCreate == 'file' && data.id == 'new-file'">
<div class="flex justify-between items-center gap-0.5 pr-2">
Expand Down Expand Up @@ -405,6 +405,7 @@ import { MsgError, MsgSuccess, MsgWarning } from '@/utils/message';
import { loadMonacoLanguageSupport, setupMonacoEnvironment } from '@/utils/monaco';
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
import { Languages } from '@/global/mimetype';
import { resolveEditorLanguage } from '@/utils/file';

import type { TabPaneName } from 'element-plus';
import { ElMessageBox, ElTreeV2 } from 'element-plus';
Expand Down Expand Up @@ -636,20 +637,20 @@ const removeTab = (targetPath: TabPaneName) => {
updateTabs();
if (fileTabs.value.length > 0) {
saveContent();
getContent(selectTab.value, '');
getContent(selectTab.value);
}
})
.catch(() => {
updateTabs();
isEdit.value = false;
if (fileTabs.value.length > 0) {
getContent(selectTab.value, '');
getContent(selectTab.value);
}
});
} else {
updateTabs();
if (fileTabs.value.length > 0) {
getContent(selectTab.value, '');
getContent(selectTab.value);
}
}
};
Expand Down Expand Up @@ -678,7 +679,7 @@ const removeAllTab = (targetPath: string, type: 'left' | 'right' | 'all') => {
selectTab.value = '';
disposeEditor();
} else if (newTabs.length > 0) {
getContent(activeName, '');
getContent(activeName);
}
};

Expand Down Expand Up @@ -719,7 +720,7 @@ const removeOtherTab = (targetPath: string) => {
fileTabs.value = [targetTab];
selectTab.value = targetTab.path;
saveTabsToStorage();
getContent(targetTab.path, '');
getContent(targetTab.path);
};

const onConfirm = () => {
Expand Down Expand Up @@ -749,7 +750,7 @@ const removeOtherTab = (targetPath: string) => {

const changeTab = (targetPath: TabPaneName) => {
selectTab.value = targetPath.toString();
getContent(targetPath.toString(), '');
getContent(targetPath.toString());
};

const eols = computed(() => [
Expand Down Expand Up @@ -1040,6 +1041,7 @@ const acceptParams = async (props: EditProps) => {
directoryPath.value = getDirectoryPath(props.path);
fileExtension.value = props.extension;
fileName.value = props.name;
config.language = resolveEditorLanguage(props.path, props.extension, props.name);

let savedTabs = loadTabsFromStorage();
const withoutCurrent = savedTabs.filter((tab) => tab.path !== props.path);
Expand All @@ -1059,7 +1061,9 @@ const acceptParams = async (props: EditProps) => {
fileTabs.value = merged.slice(-maxTabs);
selectTab.value = props.path;

config.language = props.language;
if (props.language) {
config.language = props.language;
}
config.eol = monaco.editor.EndOfLineSequence.LF;
config.theme = localStorage.getItem(codeThemeKey) || 'vs-dark';
config.wordWrap = (localStorage.getItem(warpKey) as WordWrapOptions) || 'on';
Expand Down Expand Up @@ -1140,7 +1144,7 @@ const getRefresh = (path: string) => {
}
};

const getContent = (path: string, extension: string, forceReload = false) => {
const getContent = (path: string, forceReload = false) => {
if (!forceReload && (form.value.path === path || isCreate.value == 'file')) {
return;
}
Expand All @@ -1152,31 +1156,15 @@ const getContent = (path: string, extension: string, forceReload = false) => {
codeReq.path = path;
codeReq.expand = true;

if (extension !== '') {
Languages.forEach((language) => {
const ext = extension.substring(1);
if (language.value.indexOf(ext) > -1) {
config.language = language.label;
}
});
}

getFileContent(codeReq)
.then((res) => {
form.value.content = res.data.content;
oldFileContent.value = res.data.content;
form.value.path = res.data.path;
fileExtension.value = res.data.extension;
fileName.value = res.data.name;
config.language = resolveEditorLanguage(res.data.path, res.data.extension, res.data.name);
initEditor();
if (extension == '') {
Languages.forEach((language) => {
const ext = fileExtension.value.substring(1);
if (language.value.indexOf(ext) > -1) {
config.language = language.label;
}
});
}
const exists = fileTabs.value.some((tab) => tab.path === path);
if (exists) {
const tab = fileTabs.value.find((t) => t.path === path);
Expand Down Expand Up @@ -1215,7 +1203,7 @@ const getContent = (path: string, extension: string, forceReload = false) => {

const handleHistoryRestored = (path: string) => {
if (path === form.value.path) {
getContent(path, '', true);
getContent(path, true);
loadHistoryVersionCount(path);
}
};
Expand Down
31 changes: 7 additions & 24 deletions frontend/src/views/host/file-management/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,8 @@ import { dateFormat } from '@/utils/date';
import { downloadFile, getFileType, getIcon, isConvertible } from '@/utils/file';
import { getRandomStr } from '@/utils/id';
import { File } from '@/api/interface/file';
import { Languages, Mimetypes } from '@/global/mimetype';
import { Mimetypes } from '@/global/mimetype';
import { resolveEditorLanguage } from '@/utils/file';
import { useRouter } from 'vue-router';
import { MsgSuccess, MsgWarning } from '@/utils/message';
import { useMultipleSearchable } from './hooks/searchable';
Expand Down Expand Up @@ -1383,10 +1384,10 @@ const openView = (item: File.File) => {
}

const actionMap = {
text: () => openCodeEditor(path, item.extension),
text: () => openCodeEditor(path),
};

return actionMap[fileType] ? actionMap[fileType](item) : openCodeEditor(path, item.extension);
return actionMap[fileType] ? actionMap[fileType](item) : openCodeEditor(path);
};

const openPreview = (item: File.File, fileType: string) => {
Expand All @@ -1404,37 +1405,18 @@ const openPreview = (item: File.File, fileType: string) => {
previewRef.value.acceptParams(filePreview);
};

const extensionFromPath = (p: string) => {
const i = p.lastIndexOf('.');
if (i <= 0 || i === p.length - 1) {
return '';
}
return p.slice(i);
};

const openPathInCodeEditor = (
path: string,
opts?: {
extension?: string;
initialLine?: number;
},
) => {
if (!path) {
return;
}
const extension = opts?.extension && opts.extension !== '' ? opts.extension : extensionFromPath(path);
codeReq.path = path;
codeReq.expand = true;

if (extension !== '') {
Languages.forEach((language) => {
const ext = extension.substring(1);
if (language.value.indexOf(ext) > -1) {
fileEdit.language = language.label;
}
});
}

const line = opts?.initialLine && opts.initialLine > 0 ? Math.floor(opts.initialLine) : undefined;

getFileContent(codeReq)
Expand All @@ -1443,6 +1425,7 @@ const openPathInCodeEditor = (
fileEdit.path = res.data.path;
fileEdit.name = res.data.name;
fileEdit.extension = res.data.extension;
fileEdit.language = resolveEditorLanguage(res.data.path, res.data.extension, res.data.name);
fileEdit.initialLine = line;
codeEditorRef.value.acceptParams(fileEdit);
fileEdit.initialLine = undefined;
Expand All @@ -1454,8 +1437,8 @@ const onAiSearchOpenEditor = (payload: { path: string; initialLine?: number }) =
openPathInCodeEditor(payload.path, { initialLine: payload.initialLine });
};

const openCodeEditor = (path: string, extension: string) => {
openPathInCodeEditor(path, { extension });
const openCodeEditor = (path: string) => {
openPathInCodeEditor(path);
};

const openTextPreview = (path: string, name: string) => {
Expand Down
Loading