feat: allow setting ace editor theme (#3826)

Co-authored-by: Henrique Dias <mail@hacdias.com>
This commit is contained in:
Adam 2025-09-25 16:47:00 +02:00 committed by GitHub
parent dec7a02737
commit b9787c78f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 104 additions and 35 deletions

View File

@ -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)

View File

@ -427,6 +427,7 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) error {
Scope: ".",
Locale: "en",
SingleClick: false,
AceEditorTheme: getStringParam(flags, "defaults.aceEditorTheme"),
Perm: users.Permissions{
Admin: false,
Execute: true,

View File

@ -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":

View 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>

View File

@ -158,6 +158,7 @@
"video": "Video"
},
"settings": {
"aceEditorTheme": "Ace editor theme",
"admin": "Admin",
"administrator": "Administrator",
"allowCommands": "Execute commands",

View File

@ -21,6 +21,7 @@ interface SettingsDefaults {
commands: any[];
hideDotfiles: boolean;
dateFormat: boolean;
aceEditorTheme: string;
}
interface SettingsBranding {

View File

@ -13,6 +13,7 @@ interface IUser {
dateFormat: boolean;
viewMode: ViewModeType;
sorting?: Sorting;
aceEditorTheme: string;
}
type ViewModeType = "list" | "mosaic" | "mosaic gallery";

View File

@ -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";
}
};

View File

@ -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 });
};

View File

@ -24,6 +24,13 @@
class="input input--block"
v-model:locale="locale"
></languages>
<h3>{{ t("settings.aceEditorTheme") }}</h3>
<AceEditorTheme
class="input input--block"
v-model:aceEditorTheme="aceEditorTheme"
id="aceTheme"
></AceEditorTheme>
</div>
<div class="card-action">
@ -81,6 +88,7 @@
import { useAuthStore } from "@/stores/auth";
import { useLayoutStore } from "@/stores/layout";
import { users as api } from "@/api";
import AceEditorTheme from "@/components/settings/AceEditorTheme.vue";
import Languages from "@/components/settings/Languages.vue";
import { computed, inject, onMounted, ref } from "vue";
import { useI18n } from "vue-i18n";
@ -98,6 +106,7 @@ const hideDotfiles = ref<boolean>(false);
const singleClick = ref<boolean>(false);
const dateFormat = ref<boolean>(false);
const locale = ref<string>("");
const aceEditorTheme = ref<string>("");
const passwordClass = computed(() => {
const baseClass = "input input--block";
@ -113,13 +122,14 @@ const passwordClass = computed(() => {
return `${baseClass} input--red`;
});
onMounted(() => {
onMounted(async () => {
layoutStore.loading = true;
if (authStore.user === null) return false;
locale.value = authStore.user.locale;
hideDotfiles.value = authStore.user.hideDotfiles;
singleClick.value = authStore.user.singleClick;
dateFormat.value = authStore.user.dateFormat;
aceEditorTheme.value = authStore.user.aceEditorTheme;
layoutStore.loading = false;
return true;
});
@ -163,6 +173,7 @@ const updateSettings = async (event: Event) => {
hideDotfiles: hideDotfiles.value,
singleClick: singleClick.value,
dateFormat: dateFormat.value,
aceEditorTheme: aceEditorTheme.value,
};
await api.update(data, [
@ -170,6 +181,7 @@ const updateSettings = async (event: Event) => {
"hideDotfiles",
"singleClick",
"dateFormat",
"aceEditorTheme",
]);
authStore.updateUser(data);
$showSuccess(t("settings.settingsUpdated"));

View File

@ -31,6 +31,7 @@ type userInfo struct {
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
Username string `json:"username"`
AceEditorTheme string `json:"aceEditorTheme"`
}
type authToken struct {
@ -200,6 +201,7 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use
HideDotfiles: user.HideDotfiles,
DateFormat: user.DateFormat,
Username: user.Username,
AceEditorTheme: user.AceEditorTheme,
},
RegisteredClaims: jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(time.Now()),

View File

@ -17,6 +17,7 @@ type UserDefaults struct {
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
}

View File

@ -3,5 +3,6 @@
"@commitlint/cli": "^15.0.0",
"@commitlint/config-conventional": "^15.0.0",
"standard-version": "^9.3.2"
}
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

View File

@ -35,6 +35,7 @@ type User struct {
Rules []rules.Rule `json:"rules"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
AceEditorTheme string `json:"aceEditorTheme"`
}
// GetRules implements rules.Provider.