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
14 changed files with 104 additions and 35 deletions

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"));