3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-06-15 10:45:01 +00:00

Merge branch 'vue-3' of github.com:TheKodeToad/Zeppelin into TheKodeToad-vue-3

This commit is contained in:
Dragory 2025-06-01 15:26:14 +03:00
commit e313a91fbb
No known key found for this signature in database
17 changed files with 367 additions and 496 deletions

View file

@ -27,7 +27,7 @@
"source-map-loader": "^4.0.1", "source-map-loader": "^4.0.1",
"tailwindcss": "^1.9.6", "tailwindcss": "^1.9.6",
"ts-loader": "^9.4.3", "ts-loader": "^9.4.3",
"vue-loader": "^15.10.1", "vue-loader": "^17.4.2",
"vue-style-loader": "^4.1.3", "vue-style-loader": "^4.1.3",
"vue-template-compiler": "^2.7.14", "vue-template-compiler": "^2.7.14",
"webpack": "^5.94.0", "webpack": "^5.94.0",
@ -37,7 +37,6 @@
}, },
"dependencies": { "dependencies": {
"@fastify/static": "^7.0.1", "@fastify/static": "^7.0.1",
"@highlightjs/vue-plugin": "^1.0.2",
"fastify": "^4.26.2", "fastify": "^4.26.2",
"highlight.js": "^11.8.0", "highlight.js": "^11.8.0",
"humanize-duration": "^3.27.0", "humanize-duration": "^3.27.0",
@ -45,11 +44,12 @@
"marked": "^5.1.0", "marked": "^5.1.0",
"modern-css-reset": "^1.4.0", "modern-css-reset": "^1.4.0",
"moment": "^2.29.4", "moment": "^2.29.4",
"vue": "^2.7.14", "vue": "^3.5.13",
"vue-material-design-icons": "^4.1.0", "vue-material-design-icons": "^5.3.1",
"vue-router": "^3.6.5", "vue-router": "^4.5.0",
"vue2-ace-editor": "^0.0.15", "vue3-ace-editor": "^2.2.4",
"vuex": "^3.6.2" "vue3-highlightjs": "^1.0.5",
"vuex": "^4.1.0"
}, },
"browserslist": [ "browserslist": [
"last 2 Chrome versions" "last 2 Chrome versions"

View file

@ -21,9 +21,11 @@ export const loginCallbackGuard: NavigationGuard = async (to, from, next) => {
} else { } else {
window.location.href = `/?error=noAccess`; window.location.href = `/?error=noAccess`;
} }
return next();
}; };
export const authRedirectGuard: NavigationGuard = async (to, form, next) => { export const authRedirectGuard: NavigationGuard = async (to, form, next) => {
if (await isAuthenticated()) return next("/dashboard"); if (await isAuthenticated()) return next("/dashboard");
window.location.href = `${process.env.API_URL}/auth/login`; window.location.href = `${process.env.API_URL}/auth/login`;
return next();
}; };

View file

@ -72,11 +72,11 @@
.inline-code, .inline-code,
code:not([class]), code:not([class]),
>>> code:not([class]) { :deep(code:not([class])) {
@apply bg-gray-900; @apply bg-gray-900;
} }
.codeblock { :deep(.codeblock) {
box-shadow: none; box-shadow: none;
} }
</style> </style>

View file

@ -15,7 +15,7 @@
} }
} }
a { :deep(a) {
@apply block; @apply block;
@apply py-2; @apply py-2;
@apply px-4; @apply px-4;

View file

@ -175,7 +175,7 @@ export default {
expiresAt: null, expiresAt: null,
}); });
this.$set(perm, "permissions", new Set(perm.permissions)); perm.permissions = new Set(perm.permissions);
}, },
onTreeUpdate(targetPermissions) { onTreeUpdate(targetPermissions) {

View file

@ -19,13 +19,20 @@
</div> </div>
</div> </div>
<AceEditor class="rounded shadow-lg border border-gray-700 mt-4" <v-ace-editor class="rounded shadow-lg border border-gray-700 mt-4"
v-model="editableConfig" v-model:value="editableConfig"
@init="editorInit" @init="editorInit"
lang="yaml" lang="yaml"
theme="tomorrow_night" theme="tomorrow_night"
:height="editorHeight" ref="aceEditor"
ref="aceEditor" /> v-options="{
useSoftTabs: true,
tabSize: 2
}"
:style="{
width: editorWidth + 'px',
height: editorHeight + 'px',
}" />
</div> </div>
</template> </template>
@ -34,14 +41,19 @@
import {ApiError} from "../../api"; import {ApiError} from "../../api";
import { GuildState } from "../../store/types"; import { GuildState } from "../../store/types";
import AceEditor from "vue2-ace-editor"; import { VAceEditor } from "vue3-ace-editor";
import "ace-builds/src-noconflict/ext-language_tools";
import 'ace-builds/src-noconflict/ext-searchbox';
import "ace-builds/src-noconflict/mode-yaml";
import "ace-builds/src-noconflict/theme-tomorrow_night";
let editorKeybindListener; let editorKeybindListener;
let windowResizeListener; let windowResizeListener;
export default { export default {
components: { components: {
AceEditor, VAceEditor,
}, },
async mounted() { async mounted() {
try { try {
@ -101,16 +113,6 @@
}, },
methods: { methods: {
editorInit() { editorInit() {
require("brace/ext/language_tools");
require('brace/ext/searchbox');
require("brace/mode/yaml");
require("brace/theme/tomorrow_night");
this.$refs.aceEditor.editor.setOptions({
useSoftTabs: true,
tabSize: 2
});
// Add Ctrl+S/Cmd+S save shortcut // Add Ctrl+S/Cmd+S save shortcut
const isMac = /mac/i.test(navigator.platform); const isMac = /mac/i.test(navigator.platform);
const modKeyPressed = (ev: KeyboardEvent) => (isMac ? ev.metaKey : ev.ctrlKey); const modKeyPressed = (ev: KeyboardEvent) => (isMac ? ev.metaKey : ev.ctrlKey);
@ -131,7 +133,7 @@
if (shortcutModifierPressed(ev) && ev.key === "f") { if (shortcutModifierPressed(ev) && ev.key === "f") {
ev.preventDefault(); ev.preventDefault();
this.$refs.aceEditor.editor.execCommand("find"); this.$refs.aceEditor.getAceInstance().execCommand("find");
return; return;
} }
}; };
@ -171,7 +173,7 @@
this.editorHeight = newHeight; this.editorHeight = newHeight;
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.aceEditor.editor.resize(); this.$refs.aceEditor.getAceInstance().resize();
}); });
}, },
async save() { async save() {

View file

@ -31,7 +31,7 @@
<!-- Sidebar --> <!-- Sidebar -->
<nav class="docs-sidebar px-4 pt-2 pb-3 mr-8 mb-4 border border-gray-700 rounded bg-gray-800 shadow-md flex-full lg:flex-none lg:block" v-bind:class="{ closed: !mobileMenuOpen }"> <nav class="docs-sidebar px-4 pt-2 pb-3 mr-8 mb-4 border border-gray-700 rounded bg-gray-800 shadow-md flex-full lg:flex-none lg:block" v-bind:class="{ closed: !mobileMenuOpen }">
<div role="none" v-for="(group, index) in menu"> <div role="none" v-for="(group, index) in menu">
<h1 class="font-bold" :aria-owns="'menu-group-' + index" :class="{'mt-4': index !== 0}">{{ group.label }}</h1> <h1 class="font-bold" :aria-owns="'menu-group-' + index" :class="{'mt-4': typeof index === 'number' && index !== 0}">{{ group.label }}</h1>
<ul v-bind:id="'menu-group-' + index" role="group" class="list-none pl-2"> <ul v-bind:id="'menu-group-' + index" role="group" class="list-none pl-2">
<li role="none" v-for="item in group.items"> <li role="none" v-for="item in group.items">
<router-link role="menuitem" :to="item.to" class="text-gray-300 hover:text-gray-500" v-on:click.native="onChooseMenuItem()">{{ item.label }}</router-link> <router-link role="menuitem" :to="item.to" class="text-gray-300 hover:text-gray-500" v-on:click.native="onChooseMenuItem()">{{ item.label }}</router-link>

View file

@ -81,32 +81,33 @@
<h3>Summary Example</h3> <h3>Summary Example</h3>
<p> <p>
Employing what we have learnt so far, we can write a configuration that: Employing what we have learnt so far, we can write a configuration that:
<ul>
<li>Alerts members of their warns in a channel, instead of DMs.</li>
<li>Alerts members of kicks and bans in their DMs.</li>
<li>Makes use of multi-line strings to prepare a tidy message.</li>
<li>Includes the remaining ban time if a ban was temporary.</li>
</ul>
<CodeBlock code-lang="yaml" trim="start">
plugins:
mod_actions:
config:
dm_on_warn: false
message_on_warn: true
message_channel: "PRETEND-CHANNEL-ID"
dm_on_kick: true
dm_on_ban: true
tempban_message: |-
Dear {user.username},
As a result of {reason}, you have been banned from {guildName}
for {banTime}. We welcome you back provided you do not do this
again.
</CodeBlock>
</p> </p>
<ul>
<li>Alerts members of their warns in a channel, instead of DMs.</li>
<li>Alerts members of kicks and bans in their DMs.</li>
<li>Makes use of multi-line strings to prepare a tidy message.</li>
<li>Includes the remaining ban time if a ban was temporary.</li>
</ul>
<CodeBlock code-lang="yaml" trim="start">
plugins:
mod_actions:
config:
dm_on_warn: false
message_on_warn: true
message_channel: "PRETEND-CHANNEL-ID"
dm_on_kick: true
dm_on_ban: true
tempban_message: |-
Dear {user.username},
As a result of {reason}, you have been banned from {guildName}
for {banTime}. We welcome you back provided you do not do this
again.
</CodeBlock>
<h2>Alerts</h2> <h2>Alerts</h2>
<p> <p>
Alerts are a nifty way for moderators to be notified of members trying to Alerts are a nifty way for moderators to be notified of members trying to

View file

@ -49,7 +49,7 @@
class="command mb-4" class="command mb-4"
v-bind:ref="getCommandSlug(command)" v-bind:class="{target: targetCommandId === getCommandSlug(command)}"> v-bind:ref="getCommandSlug(command)" v-bind:class="{target: targetCommandId === getCommandSlug(command)}">
<h4 class="text-xl font-semibold mb-0"> <h4 class="text-xl font-semibold mb-0">
<span v-for="(trigger, index) in getTriggers(command)"> <span class="text-gray-600" v-if="index > 0">/</span> !{{ trigger }} </span> <span v-for="(trigger, index) in getTriggers(command)"> <span class="text-gray-600" v-if="typeof index === 'number' && index > 0">/</span> !{{ trigger }} </span>
</h4> </h4>
<MarkdownBlock v-if="command.description" <MarkdownBlock v-if="command.description"
:content="command.description" :content="command.description"
@ -88,7 +88,7 @@
<div v-if="command.signature"> <div v-if="command.signature">
<h5 class="font-semibold mb-2">Signatures:</h5> <h5 class="font-semibold mb-2">Signatures:</h5>
<ul class="list-none"> <ul class="list-none">
<li v-for="(signature, index) in getCommandSignatures(command)" v-bind:class="{'mt-8': index !== 0}"> <li v-for="(signature, index) in getCommandSignatures(command)" v-bind:class="{'mt-8': typeof index === 'number' && index !== 0}">
<code class="inline-code bg-gray-900"> <code class="inline-code bg-gray-900">
!{{ getTriggers(command)[0] }} !{{ getTriggers(command)[0] }}
<span v-for="paramInfo in getSignatureParameters(signature)">{{ renderParameterOrOption(paramInfo.name, paramInfo.param) }} </span> <span v-for="paramInfo in getSignatureParameters(signature)">{{ renderParameterOrOption(paramInfo.name, paramInfo.param) }} </span>

View file

@ -1,7 +1,7 @@
import Vue from "vue"; import { Directive } from "vue";
Vue.directive("trim-indents", { export const trimIndents: Directive = {
bind(el, binding) { beforeMount(el, binding) {
const withoutStartEndWhitespace = el.innerHTML.replace(/(^\n+|\n+$)/g, ""); const withoutStartEndWhitespace = el.innerHTML.replace(/(^\n+|\n+$)/g, "");
const mode = binding.value != null ? binding.value : "start"; const mode = binding.value != null ? binding.value : "start";
@ -22,4 +22,4 @@ Vue.directive("trim-indents", {
.map((line) => line.slice(spacesToTrim)) .map((line) => line.slice(spacesToTrim))
.join("\n"); .join("\n");
}, },
}); };

View file

@ -1,10 +1,8 @@
import "./style/app.pcss"; import "./style/app.pcss";
import Vue from "vue"; import { createApp } from "vue";
import VueHighlightJS from "@highlightjs/vue-plugin"; import VueHighlightJS from "vue3-highlightjs";
import hljs from "highlight.js/lib/core";
import hljsYaml from "highlight.js/lib/languages/yaml.js";
import "highlight.js/styles/base16/ocean.css"; import "highlight.js/styles/base16/ocean.css";
import { router } from "./routes"; import { router } from "./routes";
@ -13,9 +11,15 @@ import { RootStore } from "./store";
import "./directives/trim-indents"; import "./directives/trim-indents";
import App from "./components/App.vue"; import App from "./components/App.vue";
import { trimIndents } from "./directives/trim-indents";
const app = createApp(App);
app.use(router);
app.use(RootStore);
// Set up a read-only global variable to access specific env vars // Set up a read-only global variable to access specific env vars
Vue.mixin({ app.mixin({
data() { data() {
return { return {
get env() { get env() {
@ -27,14 +31,8 @@ Vue.mixin({
}, },
}); });
hljs.registerLanguage("yaml", hljsYaml); app.use(VueHighlightJS);
Vue.use(VueHighlightJS, { hljs });
const app = new Vue({ app.directive("trim-indents", trimIndents);
router,
store: RootStore, app.mount("#app");
el: "#app",
render(h) {
return h(App);
},
});

View file

@ -1,14 +1,11 @@
import Vue from "vue"; import { createRouter, createWebHistory } from "vue-router";
import VueRouter from "vue-router";
import { authGuard, authRedirectGuard, loginCallbackGuard } from "./auth"; import { authGuard, authRedirectGuard, loginCallbackGuard } from "./auth";
Vue.use(VueRouter); export const router = createRouter({
history: createWebHistory(),
export const router = new VueRouter({
mode: "history",
routes: [ routes: [
{ path: "/login", beforeEnter: authRedirectGuard }, { path: "/login", components: {}, beforeEnter: authRedirectGuard },
{ path: "/login-callback", beforeEnter: loginCallbackGuard }, { path: "/login-callback", component: {}, beforeEnter: loginCallbackGuard },
// Privacy policy // Privacy policy
{ {
@ -97,12 +94,12 @@ export const router = new VueRouter({
scrollBehavior(to, from, savedPosition) { scrollBehavior(to, from, savedPosition) {
if (to.hash) { if (to.hash) {
return { return {
selector: to.hash, el: to.hash,
}; };
} else if (savedPosition) { } else if (savedPosition) {
return savedPosition; return savedPosition;
} else { } else {
return { x: 0, y: 0 }; return { left: 0, top: 0 };
} }
}, },
}); });

View file

@ -1,4 +1,3 @@
import Vue from "vue";
import { Module } from "vuex"; import { Module } from "vuex";
import { get, post } from "../api"; import { get, post } from "../api";
import { GuildState, LoadStatus, RootState } from "./types"; import { GuildState, LoadStatus, RootState } from "./types";
@ -89,22 +88,18 @@ export const GuildStore: Module<GuildState, RootState> = {
}, },
setConfig(state: GuildState, { guildId, config }) { setConfig(state: GuildState, { guildId, config }) {
Vue.set(state.configs, guildId, config); state.configs[guildId] = config;
}, },
setGuildPermissionAssignments(state: GuildState, { guildId, permissionAssignments }) { setGuildPermissionAssignments(state: GuildState, { guildId, permissionAssignments }) {
if (!state.guildPermissionAssignments) { if (!state.guildPermissionAssignments) {
Vue.set(state, "guildPermissionAssignments", {}); state.guildPermissionAssignments = {};
} }
Vue.set( state.guildPermissionAssignments[guildId] = permissionAssignments.map((p) => ({
state.guildPermissionAssignments, ...p,
guildId, permissions: new Set(p.permissions),
permissionAssignments.map((p) => ({ }));
...p,
permissions: new Set(p.permissions),
})),
);
}, },
setTargetPermissions(state: GuildState, { guildId, targetId, type, permissions, expiresAt }) { setTargetPermissions(state: GuildState, { guildId, targetId, type, permissions, expiresAt }) {

View file

@ -1,14 +1,11 @@
import Vue from "vue"; import { createStore, Store } from "vuex";
import Vuex, { Store } from "vuex";
Vue.use(Vuex);
import { AuthStore } from "./auth"; import { AuthStore } from "./auth";
import { DocsStore } from "./docs"; import { DocsStore } from "./docs";
import { GuildStore } from "./guilds"; import { GuildStore } from "./guilds";
import { RootState } from "./types"; import { RootState } from "./types";
export const RootStore = new Vuex.Store<RootState>({ export const RootStore = createStore({
modules: { modules: {
auth: AuthStore, auth: AuthStore,
guilds: GuildStore, guilds: GuildStore,
@ -17,16 +14,8 @@ export const RootStore = new Vuex.Store<RootState>({
}); });
// Set up typings so Vue/our components know about the state's types // Set up typings so Vue/our components know about the state's types
declare module "vue/types/options" { declare module "vue" {
interface ComponentOptions<V extends Vue> { interface ComponentCustomProperties {
// @ts-ignore
store?: Store<RootState>;
}
}
declare module "vue/types/vue" {
interface Vue {
// @ts-ignore
$store: Store<RootState>; $store: Store<RootState>;
} }
} }

View file

@ -1,6 +1,7 @@
declare module "*.vue" { declare module "*.vue" {
import Vue from "vue"; import { DefineComponent } from "vue";
export default Vue; const component: DefineComponent;
export default component;
} }
declare module "*.png" { declare module "*.png" {

View file

@ -65,7 +65,12 @@ let config = {
// Vue / Babel / Typescript // Vue / Babel / Typescript
{ {
test: /\.vue$/, test: /\.vue$/,
use: ["vue-loader"], loader: "vue-loader",
options: {
compilerOptions: {
whitespace: 'preserve', // not the default despite the docs saying so
},
}
}, },
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
@ -158,6 +163,11 @@ let config = {
}, },
}), }),
new webpack.EnvironmentPlugin(["API_URL"]), new webpack.EnvironmentPlugin(["API_URL"]),
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
})
], ],
resolve: { resolve: {
extensions: [".ts", ".tsx", ".js", ".mjs", ".vue"], extensions: [".ts", ".tsx", ".js", ".mjs", ".vue"],

640
package-lock.json generated

File diff suppressed because it is too large Load diff