Remove macOS window-chrome (traffic-light dots, minimize taskbar)
Strip the whole window metaphor as UI baggage: the traffic-light dots injected at the top of every page, the minimize taskbar, and the minimize/maximize state and all their CSS. windowChrome.js and taskbar.js are gone; the only structural DOM the shell still does — wrapping the takeover Dashboard in .aurora-dashboard-frame and reparenting its notices into the card — moved to a focused dashboardFrame.js (initDashboardFrame()). SiteShell.vue drops the taskbar rendering/state; shell-frame.css drops the .aurora-winbar*/.aurora-taskbar*/minimized/maximized rules and the --aurora-taskbar-h / --aurora-editor-winbar-h vars, and zeroes the editor and theme-install frame offsets that reserved a top strip for the dots. shell.js 16.4->13.4 kB. The editor "back to list" crumb lived in the bar and was removed with it (Gutenberg's own W-logo still navigates back). Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
dc2a4435c5
commit
53ca6f62b9
@@ -31,7 +31,7 @@ defineProps({
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
/* Deeper than the generic --aurora-card-bg — the Dashboard is now
|
||||
wrapped in its own frame (windowChrome.js's .aurora-dashboard-frame,
|
||||
wrapped in its own frame (dashboardFrame.js's .aurora-dashboard-frame,
|
||||
using --aurora-frame-bg), and that's close in tone to --aurora-card-bg
|
||||
for most presets, so cards would nearly disappear into it otherwise. */
|
||||
background: var(--aurora-card-bg-deep, var(--aurora-card-bg));
|
||||
|
||||
@@ -3,8 +3,7 @@ import { ref, onMounted } from 'vue';
|
||||
import Sidebar from './components/Sidebar.vue';
|
||||
import Toolbar from './components/Toolbar.vue';
|
||||
import { applyThemePalette, applyFontFamily } from '@/pages/settings/applyLiveTheme.js';
|
||||
import { initWindowChrome } from './windowChrome.js';
|
||||
import { getMinimizedWindows, removeMinimizedWindow } from './taskbar.js';
|
||||
import { initDashboardFrame } from './dashboardFrame.js';
|
||||
|
||||
const props = defineProps({
|
||||
serverData: {
|
||||
@@ -25,21 +24,7 @@ const toggleMenu = () => {
|
||||
localStorage.setItem('aurora-menu-collapsed', collapsed.value ? '1' : '0');
|
||||
};
|
||||
|
||||
// Every minimized window (taskbar.js persists the list in sessionStorage
|
||||
// across the page loads minimizing triggers). Rendered as taskbar items at
|
||||
// the bottom of every page since SiteShell mounts on all of them — clicking
|
||||
// one navigates back to it and removes just that entry.
|
||||
const minimizedWindows = ref([]);
|
||||
const restoreWindow = (win) => {
|
||||
removeMinimizedWindow(win.id);
|
||||
window.location.href = win.url;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
minimizedWindows.value = getMinimizedWindows();
|
||||
// Reserve bottom space for the taskbar (shell-frame.css) so page content
|
||||
// stops above it instead of running underneath.
|
||||
document.documentElement.classList.toggle('aurora-has-taskbar', minimizedWindows.value.length > 0);
|
||||
const settings = props.serverData?.settings || {};
|
||||
// Every theme preset is its own self-contained dark-toned palette now —
|
||||
// no separate light/dark mode, so this class is always on. It's kept
|
||||
@@ -49,7 +34,7 @@ onMounted(() => {
|
||||
applyThemePalette(settings.theme_preset || 'indigo');
|
||||
applyCollapsed();
|
||||
applyFontFamily(settings.font_family);
|
||||
initWindowChrome(props.serverData?.adminUrl);
|
||||
initDashboardFrame();
|
||||
// Signal the shell is live: reveals the framed content and commits to
|
||||
// keeping native chrome hidden (the PHP failsafe stops watching once
|
||||
// aurora-ready is present).
|
||||
@@ -73,74 +58,13 @@ onMounted(() => {
|
||||
:server-data="serverData"
|
||||
@toggle-menu="toggleMenu"
|
||||
/>
|
||||
<div v-if="minimizedWindows.length" class="aurora-taskbar">
|
||||
<button
|
||||
v-for="win in minimizedWindows"
|
||||
:key="win.id"
|
||||
type="button"
|
||||
class="aurora-taskbar__item"
|
||||
@click="restoreWindow(win)"
|
||||
>
|
||||
<span class="aurora-taskbar__dot" />
|
||||
{{ win.title }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Sidebar, Toolbar and the taskbar are all position:fixed, so this
|
||||
wrapper has no layout role — it just groups them under one mounted
|
||||
root. */
|
||||
/* Sidebar and Toolbar are both position:fixed, so this wrapper has no
|
||||
layout role — it just groups them under one mounted root. */
|
||||
.site-shell {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
/* Full-width docked bar, but inset with the same gap the sidebar floats
|
||||
with (not flush against the browser edges or the window above it) and
|
||||
fully rounded + shadowed so it reads as its own separate surface rather
|
||||
than an extension of the content frame it sits under. Page content
|
||||
reserves space above it via html.aurora-has-taskbar in shell-frame.css. */
|
||||
.aurora-taskbar {
|
||||
position: fixed;
|
||||
left: calc(var(--aurora-sidebar-w) + var(--aurora-sidebar-gap) * 2);
|
||||
right: var(--aurora-sidebar-gap);
|
||||
bottom: var(--aurora-sidebar-gap);
|
||||
height: var(--aurora-taskbar-h);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 0 16px;
|
||||
background: var(--aurora-card-bg);
|
||||
border: 1px solid var(--aurora-card-border);
|
||||
border-radius: var(--aurora-radius-lg);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
|
||||
box-sizing: border-box;
|
||||
z-index: 200001;
|
||||
overflow-x: auto;
|
||||
transition: left 0.2s ease;
|
||||
}
|
||||
.aurora-taskbar__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
background: var(--aurora-bg-subtle);
|
||||
border: 1px solid var(--aurora-border);
|
||||
border-radius: 999px;
|
||||
padding: 8px 18px 8px 10px;
|
||||
color: var(--aurora-text);
|
||||
font-size: 0.8125rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.aurora-taskbar__item:hover {
|
||||
border-color: var(--aurora-accent);
|
||||
}
|
||||
.aurora-taskbar__dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #febc2e;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Structural setup for the takeover Dashboard.
|
||||
*
|
||||
* DashboardTakeover.php renders the dashboard into a separate
|
||||
* #aurora-admin-dashboard-root sibling (above the hidden #wpbody), so unlike
|
||||
* every other page it never sits inside #wpbody-content and never got that
|
||||
* element's card frame. Wrap it in .aurora-dashboard-frame so it gets the
|
||||
* same card treatment, and pull the custom admin notices
|
||||
* (Notices::render_for_dashboard(), printed as a sibling just before the
|
||||
* mount point) into that frame so they render as part of the card.
|
||||
*
|
||||
* This is the only DOM restructuring the shell does — there is no window
|
||||
* chrome (title bars, traffic-light controls, minimize taskbar); that whole
|
||||
* macOS-window metaphor was removed.
|
||||
*/
|
||||
export function initDashboardFrame() {
|
||||
const dashboardRoot = document.getElementById('aurora-admin-dashboard-root');
|
||||
if (!dashboardRoot) {
|
||||
return;
|
||||
}
|
||||
// Dedupe: if SiteShell re-mounts, don't wrap a second time.
|
||||
if (dashboardRoot.parentNode.querySelector('.aurora-dashboard-frame')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const frame = document.createElement('div');
|
||||
frame.className = 'aurora-dashboard-frame';
|
||||
dashboardRoot.parentNode.insertBefore(frame, dashboardRoot);
|
||||
|
||||
const notices = document.getElementById('aurora-admin-dashboard-notices');
|
||||
if (notices) {
|
||||
frame.appendChild(notices);
|
||||
}
|
||||
frame.appendChild(dashboardRoot);
|
||||
}
|
||||
+11
-172
@@ -14,13 +14,6 @@
|
||||
:root {
|
||||
--aurora-sidebar-w: 264px;
|
||||
--aurora-toolbar-h: 56px;
|
||||
/* SiteShell.vue's taskbar, shown only while a window is minimized
|
||||
(html.aurora-has-taskbar toggles the bottom space reserved for it). */
|
||||
--aurora-taskbar-h: 56px;
|
||||
/* Height of the strip reserved at the top of the block editor's frame
|
||||
for .aurora-winbar--editor's traffic-light dots (a sibling element,
|
||||
not a child of the skeleton — see windowChrome.js). */
|
||||
--aurora-editor-winbar-h: 44px;
|
||||
/* Space between the sidebar card and the browser's left/top/bottom
|
||||
edges (Sidebar.vue) — the toolbar and #wpcontent offsets below add
|
||||
the same amount so nothing overlaps the floating sidebar. */
|
||||
@@ -170,12 +163,6 @@ html.aurora-ready #site-editor {
|
||||
border-radius: var(--aurora-radius-lg);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||
transition: left 0.2s ease;
|
||||
/* Reserves a strip at the top for .aurora-winbar--editor's dots (a
|
||||
sibling, not a child — see windowChrome.js) so they sit in their own
|
||||
clear space instead of overlapping the editor's own logo/toolbar row.
|
||||
border-box so the padding comes out of the top/left/right/bottom
|
||||
inset box already defined above, rather than adding to it. */
|
||||
padding-top: var(--aurora-editor-winbar-h);
|
||||
box-sizing: border-box;
|
||||
/* border-radius alone doesn't clip children — Gutenberg's own white
|
||||
canvas background ran straight past the rounded bottom corners as a
|
||||
@@ -250,48 +237,6 @@ html.aurora-ready body.post-php #wpbody-content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
html.aurora-ready body.block-editor-page #wpbody-content > .aurora-winbar,
|
||||
html.aurora-ready body.post-new-php #wpbody-content > .aurora-winbar,
|
||||
html.aurora-ready body.post-php #wpbody-content > .aurora-winbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* SiteShell.vue's taskbar floats along the bottom edge (inset by
|
||||
--aurora-sidebar-gap, same as the sidebar) only while a window is
|
||||
minimized. A plain margin-bottom only guarantees a gap once the page is
|
||||
scrolled all the way down — content taller than the viewport still
|
||||
renders underneath the fixed taskbar on first paint, since a fixed
|
||||
element doesn't reserve space from the rest of the page's flow. Instead,
|
||||
cap the content box's own height to whatever's actually left between the
|
||||
toolbar and the taskbar and let *it* scroll internally — the same way
|
||||
the toolbar's reserved space at the top never lets content start behind
|
||||
it, not just "eventually scroll clear of it". */
|
||||
html.aurora-has-taskbar #wpbody-content,
|
||||
html.aurora-has-taskbar .aurora-dashboard-frame {
|
||||
/* #wpbody-content's base rule sets min-height: calc(100vh - toolbar-h -
|
||||
40px) for the normal (no-taskbar) case — per spec, when min-height is
|
||||
larger than max-height, min-height always wins and max-height is
|
||||
silently ignored. Without this override the box kept sizing to that
|
||||
taller min-height regardless of the taskbar, which is why the
|
||||
max-height below appeared to do nothing. */
|
||||
min-height: 0;
|
||||
max-height: calc(
|
||||
100vh - var(--aurora-toolbar-h) - 16px - 24px - var(--aurora-taskbar-h) -
|
||||
var(--aurora-sidebar-gap) * 2
|
||||
);
|
||||
overflow-y: auto;
|
||||
overflow-anchor: none;
|
||||
}
|
||||
|
||||
/* The block editor's skeleton is already positioned with fixed insets
|
||||
(top/left/right/bottom) rather than margin, so reserving taskbar space
|
||||
is just moving its bottom inset in — no max-height/overflow trick
|
||||
needed there, unlike the margin-based elements above. */
|
||||
html.aurora-has-taskbar .interface-interface-skeleton,
|
||||
html.aurora-has-taskbar #site-editor {
|
||||
bottom: calc(24px + var(--aurora-taskbar-h) + var(--aurora-sidebar-gap) * 2) !important;
|
||||
}
|
||||
|
||||
/* Theme installer preview ("Add Themes" > click a theme > full "Preview &
|
||||
Install" overlay, theme-install.php). WP core's own .wp-full-overlay
|
||||
component (shared with the Customizer, but only .theme-install-overlay
|
||||
@@ -355,86 +300,21 @@ html.aurora-ready .theme-install-overlay.collapsed {
|
||||
|
||||
html.aurora-ready .theme-install-overlay .wp-full-overlay-sidebar {
|
||||
left: calc(var(--aurora-sidebar-w) + var(--aurora-sidebar-gap) * 2 + 24px) !important;
|
||||
top: calc(var(--aurora-toolbar-h) + 16px + var(--aurora-editor-winbar-h)) !important;
|
||||
top: calc(var(--aurora-toolbar-h) + 16px) !important;
|
||||
bottom: 24px !important;
|
||||
height: auto !important;
|
||||
border-top-left-radius: var(--aurora-radius-lg);
|
||||
}
|
||||
html.aurora-ready .theme-install-overlay .wp-full-overlay-main {
|
||||
top: var(--aurora-editor-winbar-h) !important;
|
||||
top: 0 !important;
|
||||
border-top-right-radius: var(--aurora-radius-lg);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* macOS-style title bar (windowChrome.js). Most pages get it injected as
|
||||
the first child of #wpbody-content — negative margins cancel the 32px
|
||||
padding above so it spans edge-to-edge like a real window title bar. */
|
||||
.aurora-winbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.aurora-winbar--editor {
|
||||
position: fixed;
|
||||
/* Centered vertically within the skeleton's reserved top strip
|
||||
(--aurora-editor-winbar-h), inset from its left edge to line up with
|
||||
the padding every other title bar uses. */
|
||||
top: calc(
|
||||
var(--aurora-toolbar-h) + 16px + (var(--aurora-editor-winbar-h) - 12px) / 2
|
||||
);
|
||||
left: calc(var(--aurora-sidebar-w) + var(--aurora-sidebar-gap) * 2 + 24px + 20px);
|
||||
height: 12px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
z-index: 100000;
|
||||
transition: left 0.2s ease;
|
||||
}
|
||||
#wpbody-content > .aurora-winbar {
|
||||
margin: -32px -32px 20px -32px;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid var(--aurora-frame-border);
|
||||
border-top-left-radius: var(--aurora-radius-lg);
|
||||
border-top-right-radius: var(--aurora-radius-lg);
|
||||
}
|
||||
.aurora-winbar__crumb {
|
||||
margin-left: 8px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: var(--aurora-text-muted);
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.aurora-winbar__crumb:hover {
|
||||
color: var(--aurora-text);
|
||||
}
|
||||
.aurora-winbar__dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.aurora-winbar__dot:hover {
|
||||
filter: brightness(1.15);
|
||||
}
|
||||
.aurora-winbar__dot--close {
|
||||
background: #ff5f57;
|
||||
}
|
||||
.aurora-winbar__dot--minimize {
|
||||
background: #febc2e;
|
||||
}
|
||||
.aurora-winbar__dot--maximize {
|
||||
background: #28c840;
|
||||
}
|
||||
|
||||
/* The Dashboard bypasses #wpbody-content (DashboardTakeover.php renders
|
||||
it into a separate sibling — see windowChrome.js), so it never got the
|
||||
card frame every other page has. windowChrome.js wraps the bar + the
|
||||
dashboard's mount point in this div to give it the same treatment. */
|
||||
it into a separate sibling — see dashboardFrame.js), so it never got the
|
||||
card frame every other page has. dashboardFrame.js wraps the dashboard's
|
||||
mount point in this div to give it the same treatment. */
|
||||
.aurora-dashboard-frame {
|
||||
background: var(--aurora-frame-bg);
|
||||
border: 1px solid var(--aurora-frame-border);
|
||||
@@ -444,25 +324,19 @@ html.aurora-ready .theme-install-overlay .wp-full-overlay-main {
|
||||
box-sizing: border-box;
|
||||
/* Same unconditional height cap as #wpbody-content's base rule — this
|
||||
element never got it, since the Dashboard bypasses #wpbody-content
|
||||
entirely (a separate sibling — see windowChrome.js) and only had the
|
||||
taskbar-present variant below. Once html/body were locked to
|
||||
overflow:hidden (to kill a phantom document-level scrollbar on
|
||||
another page), the dashboard's content past the first ~1500px became
|
||||
completely unreachable: no internal scroll here, and no document
|
||||
entirely (a separate sibling — see dashboardFrame.js). Once html/body
|
||||
were locked to overflow:hidden (to kill a phantom document-level
|
||||
scrollbar on another page), the dashboard's content past the first
|
||||
~1500px became completely unreachable: no internal scroll here, and
|
||||
no document
|
||||
scroll left to fall back on either. */
|
||||
max-height: calc(100vh - var(--aurora-toolbar-h) - 40px);
|
||||
overflow-y: auto;
|
||||
overflow-anchor: none;
|
||||
}
|
||||
.aurora-dashboard-frame > .aurora-winbar {
|
||||
padding: 14px 20px;
|
||||
border-bottom: 1px solid var(--aurora-frame-border);
|
||||
border-top-left-radius: var(--aurora-radius-lg);
|
||||
border-top-right-radius: var(--aurora-radius-lg);
|
||||
}
|
||||
|
||||
/* Custom admin notices (Notices::render_for_dashboard(), reparented in
|
||||
here by windowChrome.js) — native-dark.css already themes .notice
|
||||
here by dashboardFrame.js) — native-dark.css already themes .notice
|
||||
elsewhere via #wpbody-content, but that stylesheet isn't loaded on the
|
||||
Dashboard at all (Shell::is_own_page() treats index.php as one of
|
||||
Aurora's own pages), so it needs its own copy of the same treatment
|
||||
@@ -476,41 +350,6 @@ html.aurora-ready .theme-install-overlay .wp-full-overlay-main {
|
||||
margin: 16px 20px 0;
|
||||
}
|
||||
|
||||
/* Yellow dot: on the Dashboard itself there's nowhere further "home" to
|
||||
send it, so minimize just collapses the frame down to the title bar in
|
||||
place — everywhere else, minimize navigates to the dashboard instead
|
||||
(see windowChrome.js) and SiteShell.vue's taskbar offers a way back. */
|
||||
html.aurora-minimized #aurora-admin-dashboard-root {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Green dot: hide Aurora's own chrome (sidebar/toolbar) and let the
|
||||
content reclaim the full browser window, like restoring a real OS
|
||||
window to fullscreen. */
|
||||
html.aurora-maximized .sidebar,
|
||||
html.aurora-maximized .toolbar {
|
||||
display: none !important;
|
||||
}
|
||||
html.aurora-ready.aurora-maximized #wpcontent {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
html.aurora-ready.aurora-maximized .interface-interface-skeleton,
|
||||
html.aurora-ready.aurora-maximized #site-editor {
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
height: auto !important;
|
||||
border-radius: 0;
|
||||
}
|
||||
html.aurora-maximized .aurora-winbar--editor {
|
||||
top: calc((var(--aurora-editor-winbar-h) - 12px) / 2);
|
||||
left: 20px;
|
||||
}
|
||||
html.aurora-maximized .aurora-taskbar {
|
||||
left: 0 !important;
|
||||
}
|
||||
|
||||
/* The settings page renders its own two boxed panels, so drop the outer
|
||||
content frame there — leaving just those panels on the dark backdrop.
|
||||
No background here: #wpbody-content is inset from the sidebar/gutter,
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* Tracks every "minimized" window across page loads. Aurora is a normal
|
||||
* multi-page WP admin (each nav is a full page load), so a real taskbar
|
||||
* needs to survive navigation — sessionStorage (tab-scoped, clears when the
|
||||
* tab closes) is enough for that. Stored as a list, not a single slot —
|
||||
* minimizing a second page used to silently overwrite the first one's
|
||||
* entry, so only the most recent minimize ever showed up in the taskbar.
|
||||
*/
|
||||
const STORAGE_KEY = 'aurora-minimized-windows';
|
||||
|
||||
export function getMinimizedWindows() {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(STORAGE_KEY);
|
||||
const parsed = raw ? JSON.parse(raw) : [];
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function saveMinimizedWindows(list) {
|
||||
try {
|
||||
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(list));
|
||||
} catch {
|
||||
/* sessionStorage unavailable (e.g. private mode) — minimize just won't persist */
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds (or, if the same page is already minimized, replaces) an entry. */
|
||||
export function addMinimizedWindow(title, url) {
|
||||
const list = getMinimizedWindows().filter((w) => w.url !== url);
|
||||
list.push({ id: `${Date.now()}-${Math.random().toString(36).slice(2)}`, title, url });
|
||||
saveMinimizedWindows(list);
|
||||
}
|
||||
|
||||
export function removeMinimizedWindow(id) {
|
||||
saveMinimizedWindows(getMinimizedWindows().filter((w) => w.id !== id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Best-effort readable label for the current page, used as the taskbar
|
||||
* item's title. Prefers the page's own on-screen <h1> over document.title —
|
||||
* WordPress's title-tag lookup walks the $submenu global to find a page's
|
||||
* title, which comes up empty for Aurora's sub-app pages (SubApps.php
|
||||
* registers each one normally, then immediately remove_submenu_page()s it
|
||||
* to keep it out of the native menu — that breaks WP's own title lookup,
|
||||
* not just this). document.title is still the fallback for pages where
|
||||
* that isn't an issue (Settings, native WP pages, the Dashboard).
|
||||
*/
|
||||
export function currentPageTitle() {
|
||||
const heading = document.querySelector('#wpbody-content h1, #aurora-admin-dashboard-root h1');
|
||||
if (heading && heading.textContent.trim()) {
|
||||
return heading.textContent.trim();
|
||||
}
|
||||
const raw = document.title || '';
|
||||
return raw.split('‹')[0].trim() || 'Window';
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/**
|
||||
* Adds a macOS-style traffic-light title bar to the top of the page content.
|
||||
* Three cases, in priority order:
|
||||
* 1. The Dashboard — DashboardTakeover.php renders it into a separate
|
||||
* #aurora-admin-dashboard-root sibling, above #wpbody (which it hides),
|
||||
* to fully replace the native dashboard. Gets wrapped in a new
|
||||
* .aurora-dashboard-frame div to match the card treatment
|
||||
* #wpbody-content already has everywhere else.
|
||||
* 2. The block editor — gets only the traffic-light controls, overlaid on
|
||||
* the editor frame without a full-width titlebar surface.
|
||||
* 3. Everything else — native WP pages and all of Aurora's own Vue pages,
|
||||
* since each mounts its root div inside #wpbody-content. The bar is
|
||||
* inserted as its first child.
|
||||
*
|
||||
* Red closes (navigates back to the Aurora dashboard). Yellow minimizes —
|
||||
* on any page other than the dashboard, that means "save where I am and
|
||||
* send me home", with SiteShell.vue's taskbar offering a way back; on the
|
||||
* dashboard itself (already home) it just collapses in place instead,
|
||||
* since there's nowhere further back to send it. Green hides Aurora's own
|
||||
* sidebar/toolbar so the content fills the browser window. Minimized pages
|
||||
* are tracked as a list (taskbar.js), not a single slot, so minimizing
|
||||
* more than one shows up as separate taskbar items rather than the latest
|
||||
* silently replacing the previous one.
|
||||
*/
|
||||
import { addMinimizedWindow, currentPageTitle } from './taskbar.js';
|
||||
|
||||
function buildBar(adminUrl, isDashboard) {
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'aurora-winbar';
|
||||
|
||||
const makeDot = (variant, label, onClick) => {
|
||||
const dot = document.createElement('button');
|
||||
dot.type = 'button';
|
||||
dot.className = 'aurora-winbar__dot aurora-winbar__dot--' + variant;
|
||||
dot.setAttribute('aria-label', label);
|
||||
dot.title = label;
|
||||
dot.addEventListener('click', onClick);
|
||||
return dot;
|
||||
};
|
||||
|
||||
const root = document.documentElement;
|
||||
|
||||
bar.appendChild(
|
||||
makeDot('close', 'Close', () => {
|
||||
window.location.href = adminUrl || '/wp-admin/';
|
||||
})
|
||||
);
|
||||
bar.appendChild(
|
||||
makeDot('minimize', 'Minimize', () => {
|
||||
if (isDashboard) {
|
||||
root.classList.toggle('aurora-minimized');
|
||||
root.classList.remove('aurora-maximized');
|
||||
} else {
|
||||
addMinimizedWindow(currentPageTitle(), window.location.href);
|
||||
window.location.href = adminUrl || '/wp-admin/';
|
||||
}
|
||||
})
|
||||
);
|
||||
bar.appendChild(
|
||||
makeDot('maximize', 'Maximize', () => {
|
||||
root.classList.toggle('aurora-maximized');
|
||||
root.classList.remove('aurora-minimized');
|
||||
})
|
||||
);
|
||||
|
||||
return bar;
|
||||
}
|
||||
|
||||
function isBlockEditorPage() {
|
||||
const path = window.location.pathname;
|
||||
return (
|
||||
document.body.classList.contains('block-editor-page') ||
|
||||
path.endsWith('/post-new.php') ||
|
||||
path.endsWith('/post.php')
|
||||
);
|
||||
}
|
||||
|
||||
// WP core adds a "post-type-{slug}" class to <body> on every post edit
|
||||
// screen — the same signal used here to build a "back to the list" link,
|
||||
// since Posts ("edit.php") and Pages ("edit.php?post_type=page") are now
|
||||
// hijacked in place at their native URLs (see HijackedNativePages.php) and
|
||||
// there's no single admin.php?page=... URL to hardcode anymore.
|
||||
//
|
||||
// The Site Editor (site-editor.php) also carries the "block-editor-page"
|
||||
// body class isBlockEditorPage() matches on, but has no "post-type-*"
|
||||
// class (it edits templates/template parts, not a post) — returning null
|
||||
// here rather than guessing "Posts" is what keeps the crumb from showing
|
||||
// up somewhere it doesn't make sense.
|
||||
function editorBackLink(adminUrl) {
|
||||
const match = [...document.body.classList].find((c) => c.startsWith('post-type-'));
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const postType = match.replace('post-type-', '');
|
||||
const base = adminUrl || '/wp-admin/';
|
||||
if (postType === 'post') {
|
||||
return { label: 'Posts', href: base + 'edit.php' };
|
||||
}
|
||||
if (postType === 'page') {
|
||||
return { label: 'Pages', href: base + 'edit.php?post_type=page' };
|
||||
}
|
||||
return { label: postType, href: base + 'edit.php?post_type=' + encodeURIComponent(postType) };
|
||||
}
|
||||
|
||||
// theme-install.php's "Preview & Install" overlay (WP core's
|
||||
// .wp-full-overlay component) needs a permanently-running observer, not a
|
||||
// one-shot check, for two reasons: (1) core's own Backbone view creates it
|
||||
// lazily the first time a theme is clicked, so it's missing from the
|
||||
// initial DOM at initWindowChrome()'s call time; (2) every subsequent
|
||||
// theme selection re-renders the overlay's contents from scratch (its own
|
||||
// view.render() replaces all children to show the newly-selected theme's
|
||||
// details), wiping out anything inserted into it — confirmed live: the bar
|
||||
// was there after the very first click, gone after picking a second theme.
|
||||
// Kept running for the page's whole lifetime rather than disconnecting
|
||||
// after the first successful insert. Independent of the rest of this
|
||||
// file's dedupe guard (a normal page winbar already exists in
|
||||
// #wpbody-content here too; this is a second, additional bar for the
|
||||
// overlay specifically, not a replacement for it).
|
||||
function watchForThemeOverlay(adminUrl) {
|
||||
const ensureInserted = () => {
|
||||
const overlay = document.querySelector('.theme-install-overlay');
|
||||
if (overlay && !overlay.querySelector('.aurora-winbar')) {
|
||||
const bar = buildBar(adminUrl, false);
|
||||
bar.classList.add('aurora-winbar--editor');
|
||||
overlay.insertBefore(bar, overlay.firstChild);
|
||||
}
|
||||
};
|
||||
|
||||
ensureInserted();
|
||||
new MutationObserver(ensureInserted).observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
export function initWindowChrome(adminUrl) {
|
||||
if (document.body.classList.contains('theme-install-php')) {
|
||||
watchForThemeOverlay(adminUrl);
|
||||
}
|
||||
|
||||
if (document.querySelector('.aurora-winbar')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dashboardRoot = document.getElementById('aurora-admin-dashboard-root');
|
||||
if (dashboardRoot) {
|
||||
const bar = buildBar(adminUrl, true);
|
||||
const frame = document.createElement('div');
|
||||
frame.className = 'aurora-dashboard-frame';
|
||||
dashboardRoot.parentNode.insertBefore(frame, dashboardRoot);
|
||||
frame.appendChild(bar);
|
||||
// Custom admin notices (Notices::render_for_dashboard(), printed as a
|
||||
// sibling right before the mount point) — reparented into the frame
|
||||
// here too, so they render as part of the dashboard's own card
|
||||
// instead of sitting outside it unstyled. Normally these reach the
|
||||
// user via the plain admin_notices hook, but that renders inside
|
||||
// #wpbody, which the Dashboard hides entirely to hide the *native*
|
||||
// dashboard widgets — this is the replacement path for that one page.
|
||||
const notices = document.getElementById('aurora-admin-dashboard-notices');
|
||||
if (notices) frame.appendChild(notices);
|
||||
frame.appendChild(dashboardRoot);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBlockEditorPage()) {
|
||||
const bar = buildBar(adminUrl, false);
|
||||
bar.classList.add('aurora-winbar--editor');
|
||||
|
||||
const back = editorBackLink(adminUrl);
|
||||
if (back) {
|
||||
const crumb = document.createElement('a');
|
||||
crumb.className = 'aurora-winbar__crumb';
|
||||
crumb.href = back.href;
|
||||
crumb.textContent = '← ' + back.label;
|
||||
bar.appendChild(crumb);
|
||||
}
|
||||
|
||||
document.body.appendChild(bar);
|
||||
return;
|
||||
}
|
||||
|
||||
const wpbodyContent = document.getElementById('wpbody-content');
|
||||
if (wpbodyContent) {
|
||||
const bar = buildBar(adminUrl, false);
|
||||
wpbodyContent.insertBefore(bar, wpbodyContent.firstChild);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user