Initial commit: Aurora Admin plugin

This commit is contained in:
Kris Parish
2026-07-03 02:53:29 -05:00
commit 03cabe2fe4
73 changed files with 9591 additions and 0 deletions
+341
View File
@@ -0,0 +1,341 @@
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';
import SearchModal from './SearchModal.vue';
const props = defineProps({
serverData: {
type: Object,
required: true,
},
isDark: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['toggle-dark', 'toggle-menu']);
const menuOpen = ref(false);
const searchOpen = ref(false);
const sitesOpen = ref(false);
// Settings > General > "Disable global search" hides the search entirely.
const searchDisabled = computed(
() => !!props.serverData.settings?.disable_global_search
);
// Remote sites (secrets already stripped server-side). The switcher only
// shows for users holding the configured capability (canSwitchSites).
const remoteSites = computed(() => {
const sites = props.serverData.settings?.remote_sites;
return Array.isArray(sites) ? sites.filter((s) => s && s.url) : [];
});
const showSwitcher = computed(
() => !!props.serverData.canSwitchSites && remoteSites.value.length > 0
);
const siteAdminUrl = (site) => {
let url = (site.url || '').trim();
if (!/^https?:\/\//i.test(url)) url = 'https://' + url;
return url.replace(/\/+$/, '') + '/wp-admin/';
};
const siteHost = (site) => {
try {
return new URL(siteAdminUrl(site)).host;
} catch {
return site.url;
}
};
const onKeydown = (e) => {
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
if (searchDisabled.value) return;
e.preventDefault();
searchOpen.value = true;
}
};
onMounted(() => document.addEventListener('keydown', onKeydown));
onUnmounted(() => document.removeEventListener('keydown', onKeydown));
</script>
<template>
<header class="toolbar">
<div class="toolbar__left">
<button
type="button"
class="toolbar__icon-btn"
title="Toggle menu"
@click="emit('toggle-menu')"
>
<span class="dashicons dashicons-menu" />
</button>
<a
:href="serverData.siteUrl || '/'"
class="toolbar__home"
title="View website"
target="_blank"
rel="noopener noreferrer"
>
<span class="dashicons dashicons-admin-home" />
</a>
<a :href="serverData.adminUrl" class="toolbar__home" title="Dashboard">
<span class="dashicons dashicons-dashboard" />
</a>
<button
v-if="!searchDisabled"
type="button"
class="toolbar__search"
@click="searchOpen = true"
>
<span class="dashicons dashicons-search toolbar__search-icon" />
<span class="toolbar__search-text">Search…</span>
<span class="toolbar__search-kbd">Ctrl K</span>
</button>
</div>
<div class="toolbar__right">
<div v-if="showSwitcher" class="toolbar__sites">
<button
type="button"
class="toolbar__icon-btn"
title="Switch site"
@click="sitesOpen = !sitesOpen"
>
<span class="dashicons dashicons-admin-multisite" />
</button>
<div v-if="sitesOpen" class="toolbar__dropdown">
<div class="toolbar__dropdown-header">
<div class="toolbar__dropdown-name">Remote sites</div>
</div>
<a
v-for="(site, i) in remoteSites"
:key="i"
:href="siteAdminUrl(site)"
target="_blank"
rel="noopener noreferrer"
class="toolbar__dropdown-item"
>
{{ siteHost(site) }}
<span v-if="site.username" class="toolbar__site-user">{{ site.username }}</span>
</a>
</div>
</div>
<button
type="button"
class="toolbar__icon-btn toolbar__theme-toggle"
:title="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
@click="emit('toggle-dark')"
>
{{ isDark ? '☀' : '☾' }}
</button>
<div class="toolbar__user">
<button
type="button"
class="toolbar__user-btn"
@click="menuOpen = !menuOpen"
>
<img
v-if="serverData.avatar"
:src="serverData.avatar"
alt=""
class="toolbar__avatar"
/>
<span class="toolbar__user-name">{{ serverData.userName }}</span>
</button>
<div v-if="menuOpen" class="toolbar__dropdown">
<div class="toolbar__dropdown-header">
<div class="toolbar__dropdown-name">{{ serverData.userName }}</div>
<div class="toolbar__dropdown-email">{{ serverData.userEmail }}</div>
</div>
<a :href="serverData.logoutUrl" class="toolbar__dropdown-item">
Log out
</a>
</div>
</div>
</div>
<SearchModal
:server-data="serverData"
:open="searchOpen"
@close="searchOpen = false"
/>
</header>
</template>
<style scoped>
.toolbar {
position: fixed;
top: 0;
left: var(--aurora-sidebar-w);
right: 0;
height: var(--aurora-toolbar-h);
background: var(--aurora-app-bg);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
z-index: 99998;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.toolbar__left,
.toolbar__right {
display: flex;
align-items: center;
gap: 8px;
}
.toolbar__home {
display: flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
border-radius: 8px;
color: var(--aurora-text-muted);
text-decoration: none;
}
.toolbar__home:hover {
background: var(--aurora-bg-subtle);
color: var(--aurora-text);
}
.toolbar__search {
display: flex;
align-items: center;
gap: 8px;
height: 34px;
padding: 0 10px;
min-width: 240px;
border: 1px solid var(--aurora-border);
border-radius: 10px;
background: var(--aurora-bg-subtle);
color: var(--aurora-text-muted);
font-size: 0.8125rem;
cursor: text;
}
.toolbar__search-icon {
font-size: 16px;
width: 16px;
height: 16px;
}
.toolbar__search-text {
flex: 1;
text-align: left;
}
.toolbar__search-kbd {
font-size: 0.6875rem;
border: 1px solid var(--aurora-border);
border-radius: 5px;
padding: 1px 6px;
background: var(--aurora-bg);
}
.toolbar__icon-btn {
display: flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
border: none;
background: none;
border-radius: 8px;
cursor: pointer;
color: var(--aurora-text-muted);
}
.toolbar__icon-btn:hover {
background: var(--aurora-bg-subtle);
color: var(--aurora-text);
}
.toolbar__theme-toggle {
font-size: 1.05rem;
line-height: 1;
}
.toolbar__user,
.toolbar__sites {
position: relative;
}
.toolbar__site-user {
color: var(--aurora-text-muted);
font-size: 0.75rem;
margin-left: 8px;
}
.toolbar__user-btn {
display: flex;
align-items: center;
gap: 8px;
border: none;
background: none;
cursor: pointer;
padding: 4px 6px;
border-radius: 8px;
color: var(--aurora-text);
}
.toolbar__user-btn:hover {
background: var(--aurora-bg-subtle);
}
.toolbar__avatar {
width: 28px;
height: 28px;
border-radius: 50%;
}
.toolbar__user-name {
font-size: 0.875rem;
}
.toolbar__dropdown {
position: absolute;
top: calc(100% + 6px);
right: 0;
min-width: 200px;
background: var(--aurora-bg);
border: 1px solid var(--aurora-border);
border-radius: 10px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
overflow: hidden;
z-index: 100000;
}
.toolbar__dropdown-header {
padding: 12px 14px;
border-bottom: 1px solid var(--aurora-border);
}
.toolbar__dropdown-name {
font-size: 0.875rem;
font-weight: 600;
color: var(--aurora-text);
}
.toolbar__dropdown-email {
font-size: 0.75rem;
color: var(--aurora-text-muted);
}
.toolbar__dropdown-item {
display: block;
padding: 10px 14px;
text-decoration: none;
color: var(--aurora-text);
font-size: 0.875rem;
}
.toolbar__dropdown-item:hover {
background: var(--aurora-bg-subtle);
}
</style>