diff --git a/cmd/config.go b/cmd/config.go index de55c28e..98c467c6 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -169,6 +169,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut fmt.Fprintf(w, "\tView mode:\t%s\n", set.Defaults.ViewMode) fmt.Fprintf(w, "\tSingle Click:\t%t\n", set.Defaults.SingleClick) 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 59329c5c..039ed5f6 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -323,9 +323,10 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) { CreateUserDir: false, UserHomeBasePath: settings.DefaultUsersHomeBasePath, Defaults: settings.UserDefaults{ - Scope: ".", - Locale: "en", - SingleClick: false, + Scope: ".", + Locale: "en", + SingleClick: false, + AceEditorTheme: getParam(flags, "defaults.aceEditorTheme"), Perm: users.Permissions{ Admin: false, Execute: true, diff --git a/cmd/users.go b/cmd/users.go index d3f97da6..5cd62d45 100644 --- a/cmd/users.go +++ b/cmd/users.go @@ -77,6 +77,7 @@ func addUserFlags(flags *pflag.FlagSet) { flags.String("locale", "en", "locale for users") flags.String("viewMode", string(users.ListViewMode), "view mode for users") flags.Bool("singleClick", false, "use single clicks only") + flags.String("aceEditorTheme", "", "ace editor's syntax highlighting theme for users") } func getViewMode(flags *pflag.FlagSet) users.ViewMode { @@ -99,6 +100,8 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all defaults.ViewMode = getViewMode(flags) case "singleClick": defaults.SingleClick = mustGetBool(flags, flag.Name) + case "aceEditorTheme": + defaults.AceEditorTheme = mustGetString(flags, flag.Name) case "perm.admin": defaults.Perm.Admin = mustGetBool(flags, flag.Name) case "perm.execute": diff --git a/frontend/src/types/settings.d.ts b/frontend/src/types/settings.d.ts index a2c19f76..0775b549 100644 --- a/frontend/src/types/settings.d.ts +++ b/frontend/src/types/settings.d.ts @@ -20,6 +20,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 9e9b6b68..655720ee 100644 --- a/frontend/src/views/files/Editor.vue +++ b/frontend/src/views/files/Editor.vue @@ -51,7 +51,7 @@ import { useLayoutStore } from "@/stores/layout"; import { inject, onBeforeUnmount, onMounted, ref, watchEffect } from "vue"; import { useRoute, useRouter } from "vue-router"; import { useI18n } from "vue-i18n"; -import { getTheme } from "@/utils/theme"; +import { getEditorTheme } from "@/utils/theme"; import { marked } from "marked"; const $showError = inject("$showError")!; @@ -107,7 +107,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, @@ -115,10 +115,6 @@ onMounted(() => { enableSnippets: true, }); - if (getTheme() === "dark") { - editor.value!.setTheme("ace/theme/twilight"); - } - editor.value.focus(); }); diff --git a/http/auth.go b/http/auth.go index 23dc7b77..dfcdaf29 100644 --- a/http/auth.go +++ b/http/auth.go @@ -21,15 +21,16 @@ const ( ) type userInfo struct { - ID uint `json:"id"` - Locale string `json:"locale"` - ViewMode users.ViewMode `json:"viewMode"` - SingleClick bool `json:"singleClick"` - Perm users.Permissions `json:"perm"` - Commands []string `json:"commands"` - LockPassword bool `json:"lockPassword"` - HideDotfiles bool `json:"hideDotfiles"` - DateFormat bool `json:"dateFormat"` + ID uint `json:"id"` + Locale string `json:"locale"` + ViewMode users.ViewMode `json:"viewMode"` + SingleClick bool `json:"singleClick"` + Perm users.Permissions `json:"perm"` + Commands []string `json:"commands"` + LockPassword bool `json:"lockPassword"` + HideDotfiles bool `json:"hideDotfiles"` + DateFormat bool `json:"dateFormat"` + AceEditorTheme string `json:"aceEditorTheme"` } type authToken struct { @@ -186,15 +187,16 @@ func renewHandler(tokenExpireTime time.Duration) handleFunc { func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User, tokenExpirationTime time.Duration) (int, error) { claims := &authToken{ User: userInfo{ - ID: user.ID, - Locale: user.Locale, - ViewMode: user.ViewMode, - SingleClick: user.SingleClick, - Perm: user.Perm, - LockPassword: user.LockPassword, - Commands: user.Commands, - HideDotfiles: user.HideDotfiles, - DateFormat: user.DateFormat, + ID: user.ID, + Locale: user.Locale, + ViewMode: user.ViewMode, + SingleClick: user.SingleClick, + Perm: user.Perm, + LockPassword: user.LockPassword, + Commands: user.Commands, + HideDotfiles: user.HideDotfiles, + DateFormat: user.DateFormat, + AceEditorTheme: user.AceEditorTheme, }, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(time.Now()), diff --git a/settings/defaults.go b/settings/defaults.go index d60e36dc..5b6c3f2a 100644 --- a/settings/defaults.go +++ b/settings/defaults.go @@ -8,15 +8,16 @@ import ( // UserDefaults is a type that holds the default values // for some fields on User. type UserDefaults struct { - Scope string `json:"scope"` - Locale string `json:"locale"` - ViewMode users.ViewMode `json:"viewMode"` - SingleClick bool `json:"singleClick"` - Sorting files.Sorting `json:"sorting"` - Perm users.Permissions `json:"perm"` - Commands []string `json:"commands"` - HideDotfiles bool `json:"hideDotfiles"` - DateFormat bool `json:"dateFormat"` + Scope string `json:"scope"` + Locale string `json:"locale"` + ViewMode users.ViewMode `json:"viewMode"` + SingleClick bool `json:"singleClick"` + Sorting files.Sorting `json:"sorting"` + Perm users.Permissions `json:"perm"` + Commands []string `json:"commands"` + HideDotfiles bool `json:"hideDotfiles"` + DateFormat bool `json:"dateFormat"` + AceEditorTheme string `json:"aceEditorTheme"` } // Apply applies the default options to a user. @@ -30,4 +31,5 @@ func (d *UserDefaults) Apply(u *users.User) { u.Commands = d.Commands u.HideDotfiles = d.HideDotfiles u.DateFormat = d.DateFormat + u.AceEditorTheme = d.AceEditorTheme } diff --git a/users/users.go b/users/users.go index ec613856..b31819ed 100644 --- a/users/users.go +++ b/users/users.go @@ -21,21 +21,22 @@ const ( // User describes a user. type User struct { - ID uint `storm:"id,increment" json:"id"` - Username string `storm:"unique" json:"username"` - Password string `json:"password"` - Scope string `json:"scope"` - Locale string `json:"locale"` - LockPassword bool `json:"lockPassword"` - ViewMode ViewMode `json:"viewMode"` - SingleClick bool `json:"singleClick"` - Perm Permissions `json:"perm"` - Commands []string `json:"commands"` - Sorting files.Sorting `json:"sorting"` - Fs afero.Fs `json:"-" yaml:"-"` - Rules []rules.Rule `json:"rules"` - HideDotfiles bool `json:"hideDotfiles"` - DateFormat bool `json:"dateFormat"` + ID uint `storm:"id,increment" json:"id"` + Username string `storm:"unique" json:"username"` + Password string `json:"password"` + Scope string `json:"scope"` + Locale string `json:"locale"` + LockPassword bool `json:"lockPassword"` + ViewMode ViewMode `json:"viewMode"` + SingleClick bool `json:"singleClick"` + Perm Permissions `json:"perm"` + Commands []string `json:"commands"` + Sorting files.Sorting `json:"sorting"` + Fs afero.Fs `json:"-" yaml:"-"` + Rules []rules.Rule `json:"rules"` + HideDotfiles bool `json:"hideDotfiles"` + DateFormat bool `json:"dateFormat"` + AceEditorTheme string `json:"aceEditorTheme"` } // GetRules implements rules.Provider.