mirror of
https://github.com/filebrowser/filebrowser.git
synced 2025-09-26 16:20:28 +00:00
feat: allow setting ace editor theme (#3826)
Co-authored-by: Henrique Dias <mail@hacdias.com>
This commit is contained in:
parent
dec7a02737
commit
b9787c78f3
@ -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, "\tFile Creation Mode:\t%O\n", set.FileMode)
|
||||||
fmt.Fprintf(w, "\tDirectory Creation Mode:\t%O\n", set.DirMode)
|
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, "\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, "\tSorting:\n")
|
||||||
fmt.Fprintf(w, "\t\tBy:\t%s\n", set.Defaults.Sorting.By)
|
fmt.Fprintf(w, "\t\tBy:\t%s\n", set.Defaults.Sorting.By)
|
||||||
fmt.Fprintf(w, "\t\tAsc:\t%t\n", set.Defaults.Sorting.Asc)
|
fmt.Fprintf(w, "\t\tAsc:\t%t\n", set.Defaults.Sorting.Asc)
|
||||||
|
@ -424,9 +424,10 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) error {
|
|||||||
MinimumPasswordLength: settings.DefaultMinimumPasswordLength,
|
MinimumPasswordLength: settings.DefaultMinimumPasswordLength,
|
||||||
UserHomeBasePath: settings.DefaultUsersHomeBasePath,
|
UserHomeBasePath: settings.DefaultUsersHomeBasePath,
|
||||||
Defaults: settings.UserDefaults{
|
Defaults: settings.UserDefaults{
|
||||||
Scope: ".",
|
Scope: ".",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
SingleClick: false,
|
SingleClick: false,
|
||||||
|
AceEditorTheme: getStringParam(flags, "defaults.aceEditorTheme"),
|
||||||
Perm: users.Permissions{
|
Perm: users.Permissions{
|
||||||
Admin: false,
|
Admin: false,
|
||||||
Execute: true,
|
Execute: true,
|
||||||
|
@ -79,6 +79,7 @@ func addUserFlags(flags *pflag.FlagSet) {
|
|||||||
flags.Bool("singleClick", false, "use single clicks only")
|
flags.Bool("singleClick", false, "use single clicks only")
|
||||||
flags.Bool("dateFormat", false, "use date format (true for absolute time, false for relative)")
|
flags.Bool("dateFormat", false, "use date format (true for absolute time, false for relative)")
|
||||||
flags.Bool("hideDotfiles", false, "hide dotfiles")
|
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) {
|
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)
|
defaults.ViewMode, err = getViewMode(flags)
|
||||||
case "singleClick":
|
case "singleClick":
|
||||||
defaults.SingleClick, err = getBool(flags, flag.Name)
|
defaults.SingleClick, err = getBool(flags, flag.Name)
|
||||||
|
case "aceEditorTheme":
|
||||||
|
defaults.AceEditorTheme, err = getString(flags, flag.Name)
|
||||||
case "perm.admin":
|
case "perm.admin":
|
||||||
defaults.Perm.Admin, err = getBool(flags, flag.Name)
|
defaults.Perm.Admin, err = getBool(flags, flag.Name)
|
||||||
case "perm.execute":
|
case "perm.execute":
|
||||||
|
24
frontend/src/components/settings/AceEditorTheme.vue
Normal file
24
frontend/src/components/settings/AceEditorTheme.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<select name="selectAceEditorTheme" v-on:change="change" :value="aceEditorTheme">
|
||||||
|
<option v-for="theme in themes" :value="theme.theme" :key="theme.theme">
|
||||||
|
{{ theme.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type SelectHTMLAttributes } from "vue";
|
||||||
|
import { themes } from "ace-builds/src-noconflict/ext-themelist";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
aceEditorTheme: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: "update:aceEditorTheme", val: string | null): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const change = (event: Event) => {
|
||||||
|
emit("update:aceEditorTheme", (event.target as SelectHTMLAttributes)?.value);
|
||||||
|
};
|
||||||
|
</script>
|
@ -158,6 +158,7 @@
|
|||||||
"video": "Video"
|
"video": "Video"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
"aceEditorTheme": "Ace editor theme",
|
||||||
"admin": "Admin",
|
"admin": "Admin",
|
||||||
"administrator": "Administrator",
|
"administrator": "Administrator",
|
||||||
"allowCommands": "Execute commands",
|
"allowCommands": "Execute commands",
|
||||||
|
1
frontend/src/types/settings.d.ts
vendored
1
frontend/src/types/settings.d.ts
vendored
@ -21,6 +21,7 @@ interface SettingsDefaults {
|
|||||||
commands: any[];
|
commands: any[];
|
||||||
hideDotfiles: boolean;
|
hideDotfiles: boolean;
|
||||||
dateFormat: boolean;
|
dateFormat: boolean;
|
||||||
|
aceEditorTheme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SettingsBranding {
|
interface SettingsBranding {
|
||||||
|
1
frontend/src/types/user.d.ts
vendored
1
frontend/src/types/user.d.ts
vendored
@ -13,6 +13,7 @@ interface IUser {
|
|||||||
dateFormat: boolean;
|
dateFormat: boolean;
|
||||||
viewMode: ViewModeType;
|
viewMode: ViewModeType;
|
||||||
sorting?: Sorting;
|
sorting?: Sorting;
|
||||||
|
aceEditorTheme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ViewModeType = "list" | "mosaic" | "mosaic gallery";
|
type ViewModeType = "list" | "mosaic" | "mosaic gallery";
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { theme } from "./constants";
|
import { theme } from "./constants";
|
||||||
|
import "ace-builds";
|
||||||
|
import { themesByName } from "ace-builds/src-noconflict/ext-themelist";
|
||||||
|
|
||||||
export const getTheme = (): UserTheme => {
|
export const getTheme = (): UserTheme => {
|
||||||
return (document.documentElement.className as UserTheme) || theme;
|
return (document.documentElement.className as UserTheme) || theme;
|
||||||
@ -32,3 +34,17 @@ export const getMediaPreference = (): UserTheme => {
|
|||||||
return "light";
|
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";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -69,7 +69,7 @@ import HeaderBar from "@/components/header/HeaderBar.vue";
|
|||||||
import { useAuthStore } from "@/stores/auth";
|
import { useAuthStore } from "@/stores/auth";
|
||||||
import { useFileStore } from "@/stores/file";
|
import { useFileStore } from "@/stores/file";
|
||||||
import { useLayoutStore } from "@/stores/layout";
|
import { useLayoutStore } from "@/stores/layout";
|
||||||
import { getTheme } from "@/utils/theme";
|
import { getEditorTheme } from "@/utils/theme";
|
||||||
import { marked } from "marked";
|
import { marked } from "marked";
|
||||||
import { inject, onBeforeUnmount, onMounted, ref, watchEffect } from "vue";
|
import { inject, onBeforeUnmount, onMounted, ref, watchEffect } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
@ -122,7 +122,7 @@ onMounted(() => {
|
|||||||
value: fileContent,
|
value: fileContent,
|
||||||
showPrintMargin: false,
|
showPrintMargin: false,
|
||||||
readOnly: fileStore.req?.type === "textImmutable",
|
readOnly: fileStore.req?.type === "textImmutable",
|
||||||
theme: "ace/theme/chrome",
|
theme: getEditorTheme(authStore.user?.aceEditorTheme ?? ""),
|
||||||
mode: modelist.getModeForPath(fileStore.req!.name).mode,
|
mode: modelist.getModeForPath(fileStore.req!.name).mode,
|
||||||
wrap: true,
|
wrap: true,
|
||||||
enableBasicAutocompletion: true,
|
enableBasicAutocompletion: true,
|
||||||
@ -130,10 +130,6 @@ onMounted(() => {
|
|||||||
enableSnippets: true,
|
enableSnippets: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (getTheme() === "dark") {
|
|
||||||
editor.value!.setTheme("ace/theme/twilight");
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.value.setFontSize(fontSize.value);
|
editor.value.setFontSize(fontSize.value);
|
||||||
editor.value.focus();
|
editor.value.focus();
|
||||||
});
|
});
|
||||||
@ -219,6 +215,13 @@ const decreaseFontSize = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
|
if (!editor.value?.session.getUndoManager().isClean()) {
|
||||||
|
layoutStore.showHover("discardEditorChanges");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStore.updateRequest(null);
|
||||||
|
|
||||||
const uri = url.removeLastDir(route.path) + "/";
|
const uri = url.removeLastDir(route.path) + "/";
|
||||||
router.push({ path: uri });
|
router.push({ path: uri });
|
||||||
};
|
};
|
||||||
|
@ -24,6 +24,13 @@
|
|||||||
class="input input--block"
|
class="input input--block"
|
||||||
v-model:locale="locale"
|
v-model:locale="locale"
|
||||||
></languages>
|
></languages>
|
||||||
|
|
||||||
|
<h3>{{ t("settings.aceEditorTheme") }}</h3>
|
||||||
|
<AceEditorTheme
|
||||||
|
class="input input--block"
|
||||||
|
v-model:aceEditorTheme="aceEditorTheme"
|
||||||
|
id="aceTheme"
|
||||||
|
></AceEditorTheme>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
@ -81,6 +88,7 @@
|
|||||||
import { useAuthStore } from "@/stores/auth";
|
import { useAuthStore } from "@/stores/auth";
|
||||||
import { useLayoutStore } from "@/stores/layout";
|
import { useLayoutStore } from "@/stores/layout";
|
||||||
import { users as api } from "@/api";
|
import { users as api } from "@/api";
|
||||||
|
import AceEditorTheme from "@/components/settings/AceEditorTheme.vue";
|
||||||
import Languages from "@/components/settings/Languages.vue";
|
import Languages from "@/components/settings/Languages.vue";
|
||||||
import { computed, inject, onMounted, ref } from "vue";
|
import { computed, inject, onMounted, ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
@ -98,6 +106,7 @@ const hideDotfiles = ref<boolean>(false);
|
|||||||
const singleClick = ref<boolean>(false);
|
const singleClick = ref<boolean>(false);
|
||||||
const dateFormat = ref<boolean>(false);
|
const dateFormat = ref<boolean>(false);
|
||||||
const locale = ref<string>("");
|
const locale = ref<string>("");
|
||||||
|
const aceEditorTheme = ref<string>("");
|
||||||
|
|
||||||
const passwordClass = computed(() => {
|
const passwordClass = computed(() => {
|
||||||
const baseClass = "input input--block";
|
const baseClass = "input input--block";
|
||||||
@ -113,13 +122,14 @@ const passwordClass = computed(() => {
|
|||||||
return `${baseClass} input--red`;
|
return `${baseClass} input--red`;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
layoutStore.loading = true;
|
layoutStore.loading = true;
|
||||||
if (authStore.user === null) return false;
|
if (authStore.user === null) return false;
|
||||||
locale.value = authStore.user.locale;
|
locale.value = authStore.user.locale;
|
||||||
hideDotfiles.value = authStore.user.hideDotfiles;
|
hideDotfiles.value = authStore.user.hideDotfiles;
|
||||||
singleClick.value = authStore.user.singleClick;
|
singleClick.value = authStore.user.singleClick;
|
||||||
dateFormat.value = authStore.user.dateFormat;
|
dateFormat.value = authStore.user.dateFormat;
|
||||||
|
aceEditorTheme.value = authStore.user.aceEditorTheme;
|
||||||
layoutStore.loading = false;
|
layoutStore.loading = false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -163,6 +173,7 @@ const updateSettings = async (event: Event) => {
|
|||||||
hideDotfiles: hideDotfiles.value,
|
hideDotfiles: hideDotfiles.value,
|
||||||
singleClick: singleClick.value,
|
singleClick: singleClick.value,
|
||||||
dateFormat: dateFormat.value,
|
dateFormat: dateFormat.value,
|
||||||
|
aceEditorTheme: aceEditorTheme.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
await api.update(data, [
|
await api.update(data, [
|
||||||
@ -170,6 +181,7 @@ const updateSettings = async (event: Event) => {
|
|||||||
"hideDotfiles",
|
"hideDotfiles",
|
||||||
"singleClick",
|
"singleClick",
|
||||||
"dateFormat",
|
"dateFormat",
|
||||||
|
"aceEditorTheme",
|
||||||
]);
|
]);
|
||||||
authStore.updateUser(data);
|
authStore.updateUser(data);
|
||||||
$showSuccess(t("settings.settingsUpdated"));
|
$showSuccess(t("settings.settingsUpdated"));
|
||||||
|
@ -31,6 +31,7 @@ type userInfo struct {
|
|||||||
HideDotfiles bool `json:"hideDotfiles"`
|
HideDotfiles bool `json:"hideDotfiles"`
|
||||||
DateFormat bool `json:"dateFormat"`
|
DateFormat bool `json:"dateFormat"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
AceEditorTheme string `json:"aceEditorTheme"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type authToken struct {
|
type authToken struct {
|
||||||
@ -200,6 +201,7 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use
|
|||||||
HideDotfiles: user.HideDotfiles,
|
HideDotfiles: user.HideDotfiles,
|
||||||
DateFormat: user.DateFormat,
|
DateFormat: user.DateFormat,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
|
AceEditorTheme: user.AceEditorTheme,
|
||||||
},
|
},
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
|
@ -8,15 +8,16 @@ import (
|
|||||||
// UserDefaults is a type that holds the default values
|
// UserDefaults is a type that holds the default values
|
||||||
// for some fields on User.
|
// for some fields on User.
|
||||||
type UserDefaults struct {
|
type UserDefaults struct {
|
||||||
Scope string `json:"scope"`
|
Scope string `json:"scope"`
|
||||||
Locale string `json:"locale"`
|
Locale string `json:"locale"`
|
||||||
ViewMode users.ViewMode `json:"viewMode"`
|
ViewMode users.ViewMode `json:"viewMode"`
|
||||||
SingleClick bool `json:"singleClick"`
|
SingleClick bool `json:"singleClick"`
|
||||||
Sorting files.Sorting `json:"sorting"`
|
Sorting files.Sorting `json:"sorting"`
|
||||||
Perm users.Permissions `json:"perm"`
|
Perm users.Permissions `json:"perm"`
|
||||||
Commands []string `json:"commands"`
|
Commands []string `json:"commands"`
|
||||||
HideDotfiles bool `json:"hideDotfiles"`
|
HideDotfiles bool `json:"hideDotfiles"`
|
||||||
DateFormat bool `json:"dateFormat"`
|
DateFormat bool `json:"dateFormat"`
|
||||||
|
AceEditorTheme string `json:"aceEditorTheme"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply applies the default options to a user.
|
// Apply applies the default options to a user.
|
||||||
@ -30,4 +31,5 @@ func (d *UserDefaults) Apply(u *users.User) {
|
|||||||
u.Commands = d.Commands
|
u.Commands = d.Commands
|
||||||
u.HideDotfiles = d.HideDotfiles
|
u.HideDotfiles = d.HideDotfiles
|
||||||
u.DateFormat = d.DateFormat
|
u.DateFormat = d.DateFormat
|
||||||
|
u.AceEditorTheme = d.AceEditorTheme
|
||||||
}
|
}
|
||||||
|
@ -3,5 +3,6 @@
|
|||||||
"@commitlint/cli": "^15.0.0",
|
"@commitlint/cli": "^15.0.0",
|
||||||
"@commitlint/config-conventional": "^15.0.0",
|
"@commitlint/config-conventional": "^15.0.0",
|
||||||
"standard-version": "^9.3.2"
|
"standard-version": "^9.3.2"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||||
}
|
}
|
||||||
|
@ -20,21 +20,22 @@ const (
|
|||||||
|
|
||||||
// User describes a user.
|
// User describes a user.
|
||||||
type User struct {
|
type User struct {
|
||||||
ID uint `storm:"id,increment" json:"id"`
|
ID uint `storm:"id,increment" json:"id"`
|
||||||
Username string `storm:"unique" json:"username"`
|
Username string `storm:"unique" json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Scope string `json:"scope"`
|
Scope string `json:"scope"`
|
||||||
Locale string `json:"locale"`
|
Locale string `json:"locale"`
|
||||||
LockPassword bool `json:"lockPassword"`
|
LockPassword bool `json:"lockPassword"`
|
||||||
ViewMode ViewMode `json:"viewMode"`
|
ViewMode ViewMode `json:"viewMode"`
|
||||||
SingleClick bool `json:"singleClick"`
|
SingleClick bool `json:"singleClick"`
|
||||||
Perm Permissions `json:"perm"`
|
Perm Permissions `json:"perm"`
|
||||||
Commands []string `json:"commands"`
|
Commands []string `json:"commands"`
|
||||||
Sorting files.Sorting `json:"sorting"`
|
Sorting files.Sorting `json:"sorting"`
|
||||||
Fs afero.Fs `json:"-" yaml:"-"`
|
Fs afero.Fs `json:"-" yaml:"-"`
|
||||||
Rules []rules.Rule `json:"rules"`
|
Rules []rules.Rule `json:"rules"`
|
||||||
HideDotfiles bool `json:"hideDotfiles"`
|
HideDotfiles bool `json:"hideDotfiles"`
|
||||||
DateFormat bool `json:"dateFormat"`
|
DateFormat bool `json:"dateFormat"`
|
||||||
|
AceEditorTheme string `json:"aceEditorTheme"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRules implements rules.Provider.
|
// GetRules implements rules.Provider.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user