diff --git a/cmd/config.go b/cmd/config.go index 5ce54ad9..ebb9c69f 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -221,6 +221,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut fmt.Fprintf(w, "\tFile Creation Mode:\t%O\n", set.FileMode) fmt.Fprintf(w, "\tDirectory Creation Mode:\t%O\n", set.DirMode) fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " ")) + fmt.Fprintf(w, "\tAce editor syntax highlighting theme:\t%s\n", set.Defaults.AceEditorTheme) fmt.Fprintf(w, "\tSorting:\n") fmt.Fprintf(w, "\t\tBy:\t%s\n", set.Defaults.Sorting.By) fmt.Fprintf(w, "\t\tAsc:\t%t\n", set.Defaults.Sorting.Asc) diff --git a/cmd/root.go b/cmd/root.go index 286f6343..a9704cf4 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -424,9 +424,10 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) error { MinimumPasswordLength: settings.DefaultMinimumPasswordLength, UserHomeBasePath: settings.DefaultUsersHomeBasePath, Defaults: settings.UserDefaults{ - Scope: ".", - Locale: "en", - SingleClick: false, + Scope: ".", + Locale: "en", + SingleClick: false, + AceEditorTheme: getStringParam(flags, "defaults.aceEditorTheme"), Perm: users.Permissions{ Admin: false, Execute: true, diff --git a/cmd/users.go b/cmd/users.go index dd3d1f52..5b458a5a 100644 --- a/cmd/users.go +++ b/cmd/users.go @@ -79,6 +79,7 @@ func addUserFlags(flags *pflag.FlagSet) { flags.Bool("singleClick", false, "use single clicks only") flags.Bool("dateFormat", false, "use date format (true for absolute time, false for relative)") flags.Bool("hideDotfiles", false, "hide dotfiles") + flags.String("aceEditorTheme", "", "ace editor's syntax highlighting theme for users") } func getViewMode(flags *pflag.FlagSet) (users.ViewMode, error) { @@ -110,6 +111,8 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all defaults.ViewMode, err = getViewMode(flags) case "singleClick": defaults.SingleClick, err = getBool(flags, flag.Name) + case "aceEditorTheme": + defaults.AceEditorTheme, err = getString(flags, flag.Name) case "perm.admin": defaults.Perm.Admin, err = getBool(flags, flag.Name) case "perm.execute": diff --git a/frontend/src/components/settings/AceEditorTheme.vue b/frontend/src/components/settings/AceEditorTheme.vue new file mode 100644 index 00000000..09efe48b --- /dev/null +++ b/frontend/src/components/settings/AceEditorTheme.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 9411d91e..b6325511 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -158,6 +158,7 @@ "video": "Video" }, "settings": { + "aceEditorTheme": "Ace editor theme", "admin": "Admin", "administrator": "Administrator", "allowCommands": "Execute commands", diff --git a/frontend/src/types/settings.d.ts b/frontend/src/types/settings.d.ts index 90ca6ae7..ba56c612 100644 --- a/frontend/src/types/settings.d.ts +++ b/frontend/src/types/settings.d.ts @@ -21,6 +21,7 @@ interface SettingsDefaults { commands: any[]; hideDotfiles: boolean; dateFormat: boolean; + aceEditorTheme: string; } interface SettingsBranding { diff --git a/frontend/src/types/user.d.ts b/frontend/src/types/user.d.ts index b81806fc..40c453c5 100644 --- a/frontend/src/types/user.d.ts +++ b/frontend/src/types/user.d.ts @@ -13,6 +13,7 @@ interface IUser { dateFormat: boolean; viewMode: ViewModeType; sorting?: Sorting; + aceEditorTheme: string; } type ViewModeType = "list" | "mosaic" | "mosaic gallery"; diff --git a/frontend/src/utils/theme.ts b/frontend/src/utils/theme.ts index 8058356a..0244b044 100644 --- a/frontend/src/utils/theme.ts +++ b/frontend/src/utils/theme.ts @@ -1,4 +1,6 @@ import { theme } from "./constants"; +import "ace-builds"; +import { themesByName } from "ace-builds/src-noconflict/ext-themelist"; export const getTheme = (): UserTheme => { return (document.documentElement.className as UserTheme) || theme; @@ -32,3 +34,17 @@ export const getMediaPreference = (): UserTheme => { return "light"; } }; + +export const getEditorTheme = (themeName: string) => { + if (!themeName.startsWith("ace/theme/")) { + themeName = `ace/theme/${themeName}`; + } + const themeKey = themeName.replace("ace/theme/", ""); + if (themesByName[themeKey] !== undefined) { + return themeName; + } else if (getTheme() === "dark") { + return "ace/theme/twilight"; + } else { + return "ace/theme/chrome"; + } +}; diff --git a/frontend/src/views/files/Editor.vue b/frontend/src/views/files/Editor.vue index 13013a9f..f3d9aa94 100644 --- a/frontend/src/views/files/Editor.vue +++ b/frontend/src/views/files/Editor.vue @@ -69,7 +69,7 @@ import HeaderBar from "@/components/header/HeaderBar.vue"; import { useAuthStore } from "@/stores/auth"; import { useFileStore } from "@/stores/file"; import { useLayoutStore } from "@/stores/layout"; -import { getTheme } from "@/utils/theme"; +import { getEditorTheme } from "@/utils/theme"; import { marked } from "marked"; import { inject, onBeforeUnmount, onMounted, ref, watchEffect } from "vue"; import { useI18n } from "vue-i18n"; @@ -122,7 +122,7 @@ onMounted(() => { value: fileContent, showPrintMargin: false, readOnly: fileStore.req?.type === "textImmutable", - theme: "ace/theme/chrome", + theme: getEditorTheme(authStore.user?.aceEditorTheme ?? ""), mode: modelist.getModeForPath(fileStore.req!.name).mode, wrap: true, enableBasicAutocompletion: true, @@ -130,10 +130,6 @@ onMounted(() => { enableSnippets: true, }); - if (getTheme() === "dark") { - editor.value!.setTheme("ace/theme/twilight"); - } - editor.value.setFontSize(fontSize.value); editor.value.focus(); }); @@ -219,6 +215,13 @@ const decreaseFontSize = () => { }; const close = () => { + if (!editor.value?.session.getUndoManager().isClean()) { + layoutStore.showHover("discardEditorChanges"); + return; + } + + fileStore.updateRequest(null); + const uri = url.removeLastDir(route.path) + "/"; router.push({ path: uri }); }; diff --git a/frontend/src/views/settings/Profile.vue b/frontend/src/views/settings/Profile.vue index c677092f..1b079473 100644 --- a/frontend/src/views/settings/Profile.vue +++ b/frontend/src/views/settings/Profile.vue @@ -24,6 +24,13 @@ class="input input--block" v-model:locale="locale" > + +