- Apply the taskbar's max-height/overflow-y:auto treatment to #wpbody-content unconditionally, so tall native/Aurora pages (Add Plugins, Add User, Profile, Fonts, file editors, Settings, Activity Log, Database Explorer, Comments) scroll internally within their frame instead of growing past the viewport and scrolling the whole document. - Fix a Chrome scroll-anchoring bug this surfaced on Add Plugins (page loaded scrolled ~1700px into content instead of at the top) via overflow-anchor: none. - Fix a phantom document-level scrollbar with dead space below the frame by locking html/body's own overflow, since the sidebar/toolbar are fixed and content is only ever meant to scroll internally. - Make the sidebar's native-menu sections a single-open accordion: expanding one now collapses whichever else was open. Co-Authored-By: Claude Sonnet 5 <[email protected]>
551 lines
36 KiB
Markdown
551 lines
36 KiB
Markdown
# Aurora Admin v2 — Project Status
|
||
|
||
_Last updated: 2026-07-03 (latest session — universal content-area scroll cap, sidebar accordion, scroll-anchoring fix)_
|
||
|
||
## What this project is
|
||
|
||
A ground-up rebuild of the "uiXpress" WordPress admin-theme plugin, relaunched
|
||
as **Aurora Admin**. The original plugin's look/feel and feature set are the
|
||
target, but **no original code is reused** — all Vue components, PHP classes,
|
||
and build tooling are freshly written. This lives in `aurora-admin-v2/` (the
|
||
earlier in-place rebrand attempt, `aurora-admin-working/`, is superseded and
|
||
no longer the active codebase).
|
||
|
||
## Repo
|
||
|
||
Now on GitLab (self-hosted): `ssh://[email protected]:2289/NixReaper/aurora-wordpress-admin.git`
|
||
(HTTPS also available). Repo root = the contents of `aurora-admin-v2/`
|
||
(`admin/`, `app/`, `aurora-admin.php`), not the whole `uixpress/` folder — that
|
||
folder has a bunch of dead-end attempts (`aurora-admin-working`,
|
||
`aurora-admin-distribution`, `aurora-admin-rewrite`, `aurora-admin-GLM`,
|
||
`uixpress`) that are debugging history, not part of the project.
|
||
|
||
`.gitignore` excludes `app/node_modules/` and `app/dist/` (build output —
|
||
regenerate, don't version). To clone on a new machine: add an SSH key to the
|
||
GitLab account, then `git clone` the URL above, then `cd app && npm install`.
|
||
|
||
Claude Code conversation history does **not** sync across machines — it's
|
||
local per project-folder-per-machine. Point a fresh session at this file to
|
||
get back up to speed instead of relying on prior chat history.
|
||
|
||
## Workflow rules established
|
||
|
||
- **Always verify live in Chrome** before declaring a fix done. Use the
|
||
Chrome MCP tools (`tabs_context_mcp`, `navigate`, `computer`/screenshot,
|
||
`javascript_exec`) to inspect real DOM/computed styles rather than
|
||
guessing from source.
|
||
- **Zip packaging**: on macOS, build via `npm run build` in `app/`, `rsync`
|
||
the repo (excluding `node_modules`, `.git`, `/dist`) into a scratch staging
|
||
dir, then `zip -r -X` it into `dist/aurora-admin-v2.zip` — macOS `zip`
|
||
produces forward-slash paths natively, no path-separator corruption issue
|
||
like Windows `Compress-Archive`/PowerShell had. **Always delete the
|
||
existing zip first** (`rm -f dist/aurora-admin-v2.zip`) — `zip -r` merges
|
||
into an existing archive rather than replacing it, so stale entries (old
|
||
content hashes) silently survive otherwise. If building on Windows again,
|
||
the old PowerShell `System.IO.Compression.ZipFile` + `.Replace('\', '/')`
|
||
approach from earlier sessions still applies.
|
||
- **Fast local iteration**: the test site is LocalWP at
|
||
`/Users/kris/Local Sites/aurora/app/public/wp-content/plugins/aurora-admin`.
|
||
Instead of rezipping + manually reinstalling for every small CSS/JS tweak,
|
||
hot-patch the built files straight into that install
|
||
(`rm -rf app/dist && cp -R <repo>/app/dist app/dist`, or copy individual
|
||
files like `admin/assets/native-dark.css`) and just reload the page in
|
||
Chrome — near-instant feedback loop. Rezip properly (see above) once the
|
||
change is confirmed, so the zip artifact matches what's live-verified.
|
||
- User manually reinstalls the zip from a downloaded/attached copy when
|
||
actually deploying — **the Chrome MCP `file_upload` tool cannot push
|
||
arbitrary local files into the browser**, confirmed in an earlier session,
|
||
so that step can't be automated. (Hot-patching, above, sidesteps this for
|
||
local dev/verification.)
|
||
- **Old build caching gotcha**: WordPress serves whatever plugin files were
|
||
last installed — if the user says "didn't fix it," suspect a stale zip
|
||
still installed before suspecting the code. Verify the live script hash via
|
||
`document.querySelector('script[src*="shell"]')` + computed styles when in
|
||
doubt (done successfully this session to prove a spacing change was live).
|
||
- **Git**: local repo config only (`user.name`/`user.email` set per-repo, not
|
||
`--global`). Commit after each user-confirmed-working change, not before.
|
||
- Site under test: `http://admin.local/wp-admin/`.
|
||
|
||
## Architecture
|
||
|
||
- **PHP** (`admin/src/...`, namespaced `AuroraAdmin\...`), bootstrapped from
|
||
`aurora-admin.php` (manual `require` list, dependency order matters),
|
||
instantiated in `admin/src/App/AuroraAdmin.php`'s constructor.
|
||
- **Vue 3** app (`app/src/...`), Vite multi-entry build (`app/vite.config.js`
|
||
`rollupOptions.input`) → `manifest.json`, read by
|
||
`admin/src/Utility/Assets.php::enqueue()` which also walks the manifest's
|
||
`imports` chain to pull in shared-chunk CSS. Data reaches each Vue entry via
|
||
a `data-aurora-admin` JSON attribute on a mount div (never `wp_localize_script`).
|
||
- **Single global stylesheet**: `app/src/shell/shell-frame.css` — all
|
||
`--aurora-*` CSS variables (incl. `.aurora-dark` overrides) and the only
|
||
rules touching WP structural IDs (`#wpcontent`, `#wpbody-content`). No
|
||
element resets/utility classes leak onto native WP pages.
|
||
- **Immersive shell** (`admin/src/Shell/Shell.php` + `app/src/shell/SiteShell.vue`
|
||
+ `Sidebar.vue` + `Toolbar.vue` + `UserMenu.vue`): hides native WP chrome,
|
||
mounts a Vue sidebar/toolbar on every admin page.
|
||
- `MenuSerializer.php` converts WP's `$menu`/`$submenu` globals into JSON
|
||
for the sidebar's native-menu section — explicitly skips the
|
||
`aurora-admin` slug so Aurora's own pages aren't duplicated (they're
|
||
represented in the sidebar's own branded nav section instead).
|
||
- Sidebar has two sections: the WP-native menu list (icon-chip style,
|
||
28×28px rounded icon backgrounds, tight ~2px vertical padding per item)
|
||
and a bottom "Aurora" section (collapsible, expanded by default,
|
||
persisted via `localStorage['aurora-nav-expanded']`) styled like a
|
||
**native WP submenu** (plain indented text via `.sidebar__sublist`/
|
||
`.sidebar__sublink`, no icons) — deliberately matches how e.g. Posts'
|
||
own submenu (All Posts/Add Post/Categories/Tags) looks, per explicit user
|
||
request.
|
||
- Sidebar is `display:flex; flex-direction:column`, split into a
|
||
`.sidebar__scroll` (flex:1, scrollable — holds brand/native-list/Aurora
|
||
section) and a pinned `UserMenu` footer below it (not part of the
|
||
scrollable area, so the account card is always visible).
|
||
- `UserMenu.vue`: floating card that expands **upward** from a bottom-left
|
||
trigger (avatar+name+chevron). Card contains: user header (avatar/name/
|
||
email), links (Edit Profile → `profile.php`, Visit site, Site Settings →
|
||
`options-general.php`, Sign Out in red). No theme control here anymore —
|
||
see "Theme system" below; the old light/dark/system 3-way swatch picker
|
||
and the `dark_mode` setting it drove are both gone entirely.
|
||
- Old top-right toolbar user-dropdown and sun/moon toggle button are
|
||
**removed** — fully replaced by the bottom-left `UserMenu`.
|
||
- Sidebar is **inset**, not flush against the browser edge: fixed at
|
||
`top/left/bottom: var(--aurora-sidebar-gap)` (16px), fully rounded
|
||
corners, `background: var(--aurora-card-bg)` — floats as its own card
|
||
over the page gradient. `--aurora-sidebar-gap` is a shared CSS var
|
||
(`shell-frame.css`) that the toolbar's `left` offset and `#wpcontent`'s
|
||
`margin-left` both add too, so nothing overlaps when the gap changes.
|
||
- **Native-menu accordion**: only one WP-native-menu section's submenu is
|
||
open at a time (`Sidebar.vue`'s `openId` ref) — expanding one collapses
|
||
whichever else was open, instead of the list growing indefinitely as
|
||
more sections get expanded. Was previously a `Set` of independently
|
||
toggleable ids (each section remembered its own open/closed state);
|
||
replaced with a single id so opening any section closes the rest.
|
||
Starts on whichever section matches the current page (each nav is a
|
||
full page load in this multi-page admin, so this is computed once at
|
||
mount, not reactively). The separate "Aurora" branded nav section below
|
||
it keeps its own independent expand/collapse state
|
||
(`localStorage['aurora-nav-expanded']`) — not part of this accordion.
|
||
|
||
## Theme system (rewrite — supersedes the old dark_mode 3-state model)
|
||
|
||
Went through several rounds of user-directed rework this session, landing on:
|
||
**10 fixed, named color presets + 2 flat neutrals ("Black & Grey", "White")
|
||
— no custom color picker, no separate light/dark toggle.** Each preset is
|
||
fully self-contained (its own coordinated backgrounds/cards/borders/text),
|
||
not a hue applied on top of a shared light/dark base.
|
||
|
||
- `app/src/pages/settings/themePresets.js`: `themePresets` array (10 hue-based
|
||
presets like `{ id: 'indigo', label: 'Indigo', hue: 235 }`, plus `mono` and
|
||
`white` with an explicit `palette` object instead of a hue). `buildPalette(preset)`
|
||
returns all `--aurora-*` tokens — for hue presets, computed via HSL ramps;
|
||
for the two flat presets, returned as-is. `resolvePreset(id)` looks up by id.
|
||
- The main app background (`--aurora-app-bg`) is a **3-stop gradient**, not
|
||
flat: `linear-gradient(135deg, vivid1 0%, vivid2 55%, near-black 100%)` —
|
||
two bright, saturated stops of the same hue family carry most of the bar,
|
||
dipping to a near-black anchor only in the last stretch. This exact ratio
|
||
went through several corrections against a competing plugin's ("FleekDash")
|
||
reference screenshots — a 50/50 dark-to-vivid split reads "washed out", two
|
||
competing saturated hues ~70° apart reads "muddy"; this formula was the one
|
||
the user confirmed as clean. If revisiting the gradient formula, re-check
|
||
against those reference screenshots in the conversation history rather than
|
||
re-deriving from scratch.
|
||
- `--aurora-card-bg-deep`: a newer token, one step darker than
|
||
`--aurora-card-bg`, used only by the Dashboard's `AuroraCard.vue`. Needed
|
||
because the Dashboard got its own frame background using `--aurora-frame-bg`
|
||
(see "Window chrome" below), which is close in lightness to `--aurora-card-bg`
|
||
for most presets — cards would nearly disappear into the new frame without it.
|
||
- `applyThemePalette(themePresetId)` / `applyFontFamily(family)`
|
||
(`app/src/pages/settings/applyLiveTheme.js`) — shared between the settings
|
||
page's live preview (`Settings.vue`'s `watch()`) and the real per-page-load
|
||
application (`SiteShell.vue::onMounted`). Sets each token via
|
||
`document.documentElement.style.setProperty`.
|
||
- Google Fonts picker: `app/src/pages/settings/googleFonts.js` (curated ~70
|
||
family list, not the full catalog), searchable dropdown in
|
||
`SettingsField.vue`'s `font-picker` field type, applied by injecting a
|
||
Google Fonts CSS2 `<link>` tag + setting `--aurora-font-family`.
|
||
- **Recurring bug class this session — "independently-painted gradient"**:
|
||
several elements (sidebar, toolbar, `#wpbody-content` on the settings page,
|
||
`#wpbody-content` in general) each used to paint their *own* copy of
|
||
`var(--aurora-app-bg)`. Since it's a gradient, each element computes it
|
||
independently over *its own* bounding box — a narrow tall sidebar and a
|
||
wide short toolbar get visibly different-looking slices of "the same"
|
||
gradient, creating seams at their shared edges. Fixed by making `html`/`body`
|
||
the *only* thing that paints the gradient (`background-attachment: fixed`
|
||
so it doesn't scroll away from the fixed sidebar/toolbar), and leaving
|
||
every other element transparent so that one gradient plane shows through.
|
||
**If a new seam/mismatched-rectangle bug shows up, suspect this pattern
|
||
first** — grep for `var(--aurora-app-bg)` outside `shell-frame.css`'s
|
||
`html.aurora-ready, html.aurora-ready body` rule; it shouldn't be there.
|
||
- **Dashboard takeover** (`admin/src/Pages/DashboardTakeover.php` +
|
||
`app/src/pages/dashboard/Dashboard.vue`): hijacks native `index.php` via
|
||
`in_admin_header`, hides the native `.wrap`, renders the Aurora dashboard
|
||
in its place. 12-col grid; cards are single `<section class="aurora-card">`
|
||
elements with inline `grid-column: span N` (NOT wrapped — component
|
||
attribute fallthrough for styles was unreliable). Never use unprefixed
|
||
class names on Aurora components (`.card` collided with WP core once).
|
||
- **Settings page** (`admin/src/Pages/SettingsPage.php` + `app/src/pages/
|
||
settings/Settings.vue`): owns the top-level "Aurora Admin" WP admin menu
|
||
directly via `add_menu_page` (slug `aurora-admin`). Two-box layout:
|
||
`.settings__nav` (category list) + `.settings__content` (fields), both
|
||
boxed/bordered on a darker backdrop. Field schema is declarative in
|
||
`app/src/pages/settings/settingsConfig.js`, rendered by
|
||
`app/src/pages/settings/components/SettingsField.vue` (types: text, toggle,
|
||
color, image, repeater, multiselect, segmented, remote-sites).
|
||
- **6 sub-apps** (`admin/src/Pages/SubApps.php` + one PHP feature-class pair
|
||
each + one Vue page + one Vite entry each) — reachable only via the
|
||
sidebar's Aurora nav section, not the native WP menu:
|
||
- Registered via `add_submenu_page('aurora-admin', ...)` (or `'plugins.php'`
|
||
for Plugins) and **left registered** in WP's native `$submenu` global —
|
||
**no longer hidden via `remove_submenu_page()`**. An earlier version did
|
||
call it (the standard register-but-hide trick) but that corrupts WP
|
||
core's own `user_can_access_admin_page()` capability lookup, which partly
|
||
depends on the page still being present in `$submenu` to resolve its
|
||
required capability — removing it caused "Sorry, you are not allowed to
|
||
access this page" for every user, including admins, on Menu Creator/
|
||
Activity Log/Role Editor/Comments. Root-caused and fixed this session by
|
||
deleting `hide_from_native_submenu()` entirely; duplication in the
|
||
sidebar's native-menu section is instead prevented in
|
||
`MenuSerializer::serialize()` (see below), which has no such side effect.
|
||
- All slugs prefixed `aurora-admin-*` so `Shell::is_own_page()`'s existing
|
||
`strpos($page, "aurora-admin") === 0` check covers them for free (full
|
||
custom dark theme, `native-dark.css` skipped). `MenuSerializer::serialize()`
|
||
skips any submenu slug starting `aurora-admin-` (in addition to already
|
||
skipping the `aurora-admin` top-level slug) so none of them are
|
||
duplicated in the sidebar's native-menu section — needed because most are
|
||
parented under `aurora-admin` (already skipped wholesale) but
|
||
`aurora-admin-plugins` is parented under the real `plugins.php` menu.
|
||
- Each page uses the shared two-pane layout `app/src/pages/_shared/AppListShell.vue`
|
||
(generalized from Settings.vue's box-pair CSS) + `AppListItem.vue` for
|
||
list rows.
|
||
- **Menu Creator** (`admin/src/MenuCreator/`): DB table
|
||
`aurora_admin_menus` (name/status/roles JSON/items JSON). Runtime
|
||
`admin_menu` hook (priority `PHP_INT_MAX`) applies published configs
|
||
matching the current user's roles — hides/relabels native menu items by
|
||
slug. REST: `GET/POST /menus`, `GET/PUT/DELETE /menus/:id`,
|
||
`GET /menus/native-items` (real WP menu list to build the editor from,
|
||
via `MenuSerializer::serialize()`). Reordering is stored (`order` field)
|
||
but not yet applied at runtime — CRUD + hide/relabel only so far.
|
||
- **Admin Notices** (`admin/src/Notices/`): DB table `aurora_admin_notices`
|
||
(title/message/type/roles JSON/pages JSON/dismissible/active). Runtime
|
||
`admin_notices` hook renders active, role/page-matching rows via real WP
|
||
notice markup. REST: `GET/POST /notices`, `PUT/DELETE /notices/:id`.
|
||
- **Role Editor** (`admin/src/Roles/`): no custom table — wraps core
|
||
`get_editable_roles()`/`WP_Roles` directly. Hard-codes the 5 default
|
||
roles (`administrator, editor, author, contributor, subscriber`) as
|
||
delete-protected (WP has no built-in check for this). REST:
|
||
`GET/POST /roles`, `PUT/DELETE /roles/:slug`.
|
||
- **Activity Log** (`admin/src/ActivityLog/`): DB table
|
||
`aurora_admin_activity_log`. Hooked to `save_post` (guards
|
||
`wp_is_post_autosave()`/`wp_is_post_revision()`), `before_delete_post`,
|
||
`user_register`, `profile_update`, `wp_login`, `activated_plugin`,
|
||
`deactivated_plugin`, `switch_theme`,
|
||
`update_option_aurora_admin_settings`. REST:
|
||
`GET /activity-log?action_type=&object_type=&search=&page=`.
|
||
- **Plugins** (`admin/src/Rest/PluginsData.php` + `app/src/pages/plugins/Plugins.vue`):
|
||
custom plugin management list (activate/deactivate/delete), also using
|
||
`AppListShell.vue`.
|
||
- **Comments** (`admin/src/Rest/CommentsData.php` + `app/src/pages/comments/`):
|
||
newest sub-app, `edit_posts`-gated (not `manage_options` like the others).
|
||
`SubApps::redirect_native_pages()` (`admin_init`) redirects the native
|
||
`edit-comments.php` (when hit with no `?page=` param, i.e. a direct nav
|
||
or an old bookmark/menu link) to `admin.php?page=aurora-admin-comments`.
|
||
`MenuSerializer::resolve_url()` also maps the `edit-comments.php` slug
|
||
straight to the Aurora URL so the native-menu-derived sidebar entry (for
|
||
users who can `edit_posts` but not `manage_options`, so don't see the
|
||
Aurora nav section) still lands on the Aurora page. The Dashboard's
|
||
comments card (`DashboardData.php`'s `listUrl`) was updated to point at
|
||
the same Aurora URL instead of the native one.
|
||
- **Database Explorer** (`admin/src/DatabaseExplorer/`): no custom table —
|
||
reads real `$wpdb` schema. `GET /db-tables` (SHOW TABLE STATUS),
|
||
`GET /db-tables/:table` (columns + paginated rows — table name validated
|
||
against the real `$wpdb` table list before use, never interpolated
|
||
unchecked), `POST /db-query` (raw query runner, hard-restricted
|
||
server-side to queries starting `SELECT`/`SHOW`/`DESCRIBE`/`EXPLAIN`,
|
||
single-statement only, auto-`LIMIT 200` if none given). All 3 endpoints
|
||
`manage_options`-gated. Identifier allow-listing + backtick-escaping is
|
||
the actual safety boundary (`$wpdb->prepare()` only binds values, not
|
||
identifiers).
|
||
- All 5 backend classes + `SubApps.php` are `require`'d in
|
||
`aurora-admin.php` and instantiated in `AuroraAdmin.php`.
|
||
- **Settings persistence**: `admin/src/Options/Settings.php` — partial-payload
|
||
save via `merge_on_save()` sanitize callback (merges into existing option,
|
||
doesn't overwrite). `Settings::get_safe()` strips `license_key` and remote
|
||
site passwords for use on non-admin-gated pages; `Settings::get()` (full)
|
||
only used on the manage_options-gated settings page itself.
|
||
- **Global search**: `app/src/shell/components/SearchModal.vue`, Ctrl+K,
|
||
debounced `wp/v2/search` REST calls, gated on `disable_global_search` setting.
|
||
- **Remote site switcher**: toolbar dropdown, gated on `canSwitchSites` (from
|
||
`remote_site_switcher_capability` setting) + non-empty `remote_sites`.
|
||
- **Scheduled content**: `app/src/pages/dashboard/cards/ScheduledContent.vue`
|
||
calendar cells (squares, date top-left, count centered) are clickable —
|
||
navigate to `edit.php?post_status=future&aurora_day=YYYY-MM-DD` if that day
|
||
has scheduled posts, else show a toast ("no scheduled content"). Server side:
|
||
`admin/src/Posts/ScheduledDayFilter.php` filters the `edit.php` query via
|
||
`pre_get_posts` and prints an info notice.
|
||
- **Analytics**: `admin/src/Analytics/Analytics.php` (first-party pageview
|
||
tracker, own DB table `wp_aurora_admin_pageviews`, bot detection, gated on
|
||
`enable_analytics`/`track_admins` settings) + `admin/src/Rest/AnalyticsData.php`
|
||
(aggregation REST endpoint: totals, series, device/referrer/country
|
||
breakdowns, bounce rate, active users).
|
||
- **White label**: `admin/src/WhiteLabel/WhiteLabel.php` — custom favicon,
|
||
rename plugin in Plugins list, rename top-level menu item, gettext
|
||
find/replace via `text_replacements` setting.
|
||
- **Native page dark styling**: `admin/assets/native-dark.css` (large static
|
||
sheet, all rules gated `html.aurora-dark.aurora-ready`, scoped under
|
||
`#wpbody-content`) + `admin/assets/native-enhance.js` (progressive JS
|
||
enhancement — currently only `enhanceUpdates()`, which groups the flat
|
||
Updates page sections into `.aurora-native-card` wrapper divs on
|
||
`body.update-core-php`). This session also extended coverage to media/
|
||
thickbox modals, plugin/theme card grids, and the plugin-search filter bar
|
||
(all previously untouched, so still native white regardless of theme).
|
||
Known gap: the plugin "More Details" popup renders inside an `<iframe>`
|
||
pointing at a separate WP page — CSS can't reach into it; would need JS
|
||
injection into the iframe's own document if that's ever worth fixing.
|
||
This session also added dark-theme rules for the block editor's own chrome
|
||
(`.editor-header`/`.edit-post-header`, `.interface-interface-skeleton__sidebar`
|
||
and its nested `.components-panel`/`.editor-post-status`/
|
||
`.interface-complementary-area-header`) — found by inspecting the real
|
||
screen via `computer-use` and drilling into nested elements' computed
|
||
styles, since `.interface-complementary-area` (nested inside the sidebar)
|
||
had its own explicit white background the outer rule didn't reach, leaving
|
||
a white patch below the Tags panel until it was targeted directly. The
|
||
actual post content canvas is deliberately left untouched (WYSIWYG, matches
|
||
the front-end).
|
||
|
||
## Window chrome (macOS-style title bar + taskbar)
|
||
|
||
Added earlier this session, user-requested after seeing a competing plugin's
|
||
demo; extended later the same session to cover the block editor and the Site
|
||
Editor, and the taskbar was rewritten to support multiple minimized windows.
|
||
Every page gets a traffic-light title bar; minimizing persists across a full
|
||
page navigation via a taskbar.
|
||
|
||
- `app/src/shell/windowChrome.js`: `initWindowChrome(adminUrl)`, called
|
||
unconditionally from `SiteShell.vue::onMounted` (no longer skipped on the
|
||
block editor — that special case was removed once the editor got its own
|
||
handling). Three cases, checked in priority order:
|
||
1. **Dashboard** (`#aurora-admin-dashboard-root` present) — wraps the bar +
|
||
the dashboard root in a new `.aurora-dashboard-frame` div, giving it the
|
||
same card-frame treatment (`--aurora-frame-bg`, border, radius) every
|
||
other page gets from `#wpbody-content` itself.
|
||
2. **Block editor / Site Editor** (`isBlockEditorPage()`: body has
|
||
`block-editor-page`, or the path ends `/post.php` or `/post-new.php` —
|
||
`site-editor.php` also gets `block-editor-page` on its body, so this
|
||
already covers it with no JS change needed) — appends a
|
||
`.aurora-winbar--editor` bar (dots only, no bar surface) directly to
|
||
`document.body`, since neither editor's real content container is
|
||
`#wpbody-content`.
|
||
3. **Everything else** — native WP pages and all of Aurora's own Vue pages,
|
||
inserted as the first child of `#wpbody-content`.
|
||
- Red (close): `window.location.href = adminUrl`.
|
||
- Green (maximize): toggles `html.aurora-maximized` — hides `.sidebar`/
|
||
`.toolbar`, zeroes `#wpcontent`'s `margin-left` and the editor frame's
|
||
inset, so content fills the whole browser window. Toggle again (or
|
||
minimize) to restore. Nothing persisted — resets on next page load.
|
||
- Yellow (minimize): **on the Dashboard itself**, toggles `html.aurora-minimized`
|
||
which just collapses `#aurora-admin-dashboard-root` down to the bar in
|
||
place (nowhere further "home" to send it). **On every other page**, calls
|
||
`taskbar.js`'s `addMinimizedWindow(title, url)` then navigates to the
|
||
dashboard.
|
||
- **Taskbar rewritten from a single sessionStorage slot to a list**
|
||
(`app/src/shell/taskbar.js`): minimizing a second page used to silently
|
||
overwrite the first one's entry, so only the most recent minimize ever
|
||
showed up. Now `getMinimizedWindows()`/`addMinimizedWindow()` (dedupes by
|
||
URL)/`removeMinimizedWindow(id)` operate on an array under
|
||
`sessionStorage['aurora-minimized-windows']`; `SiteShell.vue` `v-for`s over
|
||
it, rendering one `.aurora-taskbar__item` pill per entry inside a
|
||
transparent, floating `.aurora-taskbar` bar (inset by `--aurora-sidebar-gap`
|
||
on all exposed edges, rounded, its own box-shadow — went through a design
|
||
iteration from a small centered pill back to full-length per explicit user
|
||
correction, keeping only the pill shape on the individual items). Each item
|
||
is independently restorable/closable. `currentPageTitle()` prefers the
|
||
page's own on-screen `<h1>` (`#wpbody-content h1, #aurora-admin-dashboard-root h1`)
|
||
over `document.title`, since WP's title-tag generation came up empty for
|
||
Aurora's sub-app pages back when they were still `remove_submenu_page()`'d
|
||
(see the sub-apps permissions bug above) — `document.title` is still the
|
||
fallback for pages where that isn't an issue.
|
||
- **Content-behind-taskbar fix**: `html.aurora-has-taskbar` no longer just
|
||
adds `margin-bottom` (that only guarantees clearance once scrolled to the
|
||
very bottom — content taller than the viewport still rendered underneath
|
||
the fixed taskbar on first paint). Instead it caps `#wpbody-content`'s/
|
||
`.aurora-dashboard-frame`'s own height via `max-height` + `overflow-y: auto`,
|
||
same principle as the toolbar's reserved space at the top. This appeared to
|
||
do nothing at first — `#wpbody-content`'s base rule sets `min-height:
|
||
calc(100vh - toolbar-h - 40px)`, and per spec `min-height` always wins over
|
||
a smaller `max-height`; fixed by adding `min-height: 0` to the
|
||
taskbar-specific override.
|
||
- **Universal content-area height cap** (this session): the taskbar-only
|
||
`max-height`/`overflow-y: auto` treatment above is now applied to
|
||
`#wpbody-content` unconditionally (not just when `html.aurora-has-taskbar`
|
||
is set), capped to `calc(100vh - toolbar-h - 40px)`. Several native and
|
||
Aurora pages with a lot of content (Add Plugins, Add User, Profile,
|
||
Appearance > Fonts, the file editors, every Settings page, Activity Log,
|
||
Database Explorer, Comments) used to grow taller than the viewport and
|
||
force the *whole document* to scroll — janky since the sidebar/toolbar
|
||
are fixed, so scrolling just moved the content away from them with a lot
|
||
of dead space. Now the content card's own box is capped and scrolls
|
||
internally, same as the toolbar/taskbar reservations already did.
|
||
- **Scroll-anchoring bug this surfaced**: making `#wpbody-content` a real
|
||
scroll container exposed a second, unrelated bug on Add Plugins — its
|
||
plugin-card grid renders via JS well after first paint, inserting a lot
|
||
of content *above* whatever was already visible in the now-scrollable
|
||
box. Chrome's scroll anchoring "corrects" for that by silently jumping
|
||
`scrollTop` down to compensate, so the page loaded already scrolled
|
||
~1700px into the content instead of at the top. Fixed with
|
||
`overflow-anchor: none` on `#wpbody-content` (and the taskbar/dashboard-
|
||
frame variant) — the standard fix for this class of bug, opting the
|
||
container out of anchoring entirely.
|
||
- **Phantom document-level scrollbar**: independently, Add Plugins also
|
||
left the outer `<html>`/`<body>` scrollable with ~1200px of empty dead
|
||
space below the frame, even though every element inside `#wpbody-content`
|
||
measured as properly capped to the viewport — narrowed down to some
|
||
interaction between the plugin grid's async rendering and the browser's
|
||
own scrollable-overflow bookkeeping (root cause not fully pinned down;
|
||
forcibly shrinking or hiding `#wpbody-content` reduced but didn't
|
||
eliminate it, and no single oversized/mispositioned descendant could be
|
||
found). Since the intended design already has the sidebar/toolbar fixed
|
||
and every page's real content scrolling *internally* within its own
|
||
frame, the document itself was never supposed to scroll at all — fixed
|
||
directly by adding `overflow: hidden` to `html.aurora-ready`/`body`,
|
||
which removes the dead scroll space outright regardless of what was
|
||
causing it.
|
||
- **Block editor / Site Editor framing**: neither editor renders inside
|
||
`#wpcontent`'s normal box model — each has its own fixed, full-viewport
|
||
root (`.interface-interface-skeleton` itself on `post.php`/`post-new.php`;
|
||
`#site-editor` on `site-editor.php`, where `.interface-interface-skeleton`
|
||
is merely `position: relative`, nested inside it — a reset rule puts the
|
||
nested skeleton back to a plain unpositioned child so the two frames don't
|
||
stack). `html.aurora-ready .interface-interface-skeleton, html.aurora-ready
|
||
#site-editor` in `shell-frame.css` frames whichever is the real fixed root
|
||
with the same insets/background/radius/shadow treatment `#wpbody-content`
|
||
gets elsewhere, plus `padding-top: var(--aurora-editor-winbar-h)` to
|
||
reserve a strip for `.aurora-winbar--editor`'s dots and `overflow: hidden`
|
||
so the rounded corners actually clip the editor's own white canvas
|
||
background (border-radius alone doesn't clip children). **Known
|
||
CSS-over-constraint gotcha**: WP core's `.edit-site { height: 100vh; }`
|
||
class (present on `#site-editor`) means `top`, `height`, and `bottom` are
|
||
all specified on that box at once — per spec this is over-constrained and
|
||
`bottom` silently gets recomputed from `top + height` instead of honoring
|
||
an explicit value, which is why the frame initially ran past the intended
|
||
bottom inset despite `bottom: 24px !important` being set. Fixed by adding
|
||
`height: auto !important` to the same rule so `top`/`bottom` actually
|
||
determine the box size again. **If a future feature needs to reach either
|
||
editor's own chrome**, the real containers are
|
||
`.interface-interface-skeleton` (post editor) or `#site-editor` (Site
|
||
Editor) — don't assume the normal `#wpbody-content` page structure there.
|
||
- `Shell.php::is_enabled()` no longer excludes the Site Editor screen
|
||
(`site-editor` used to be in the skip list alongside `customize`) — it
|
||
turns out to be built on the same `@wordpress/interface` package as the
|
||
block editor (`body.block-editor-page` is present on both), so the
|
||
existing block-editor handling covers it once `#site-editor` is framed.
|
||
|
||
## Key bugs fixed (root causes, in case they resurface)
|
||
|
||
- **`.card` global class collision**: original Dashboard cards used
|
||
`class="card"`, colliding with WP core's `.card { max-width: 520px }`.
|
||
Fixed by renaming to `.aurora-card*` everywhere. Never use unprefixed
|
||
class names on Aurora components.
|
||
- **Grid-column not applying on wrapped cards**: settled on unwrapped
|
||
`<section class="aurora-card" :style="{gridColumn: ...}">` — component
|
||
boundaries ate the style binding in earlier attempts.
|
||
- **Two-box settings layout**: took two wrong iterations before matching the
|
||
user's reference image. Re-confirm layout intent against a screenshot
|
||
before assuming.
|
||
- **Redundant outer frame border on settings page**: fixed with a
|
||
`body.toplevel_page_aurora-admin` override in `shell-frame.css`.
|
||
- **Dashboard submenu defaulting collapsed**: `Sidebar.vue`'s `normalize()`
|
||
URL helper didn't map the bare `/wp-admin/` root to `index.php`, so the
|
||
Dashboard section's active-state check (and thus its default-expanded
|
||
state) never matched on the dashboard itself. Fixed by normalizing empty/
|
||
query-only paths to `'index.php' + path`.
|
||
- **Aurora top-level menu appearing twice**: `SettingsPage.php`'s
|
||
`add_menu_page('aurora-admin', ...)` naturally appears in WP's native
|
||
`$menu` global. Since it's now also shown in the sidebar's dedicated Aurora
|
||
nav section, `MenuSerializer::serialize()` explicitly skips slug
|
||
`aurora-admin` to avoid the duplicate.
|
||
- **Browser automation can't install plugins**: the Chrome MCP `file_upload`
|
||
tool only accepts files already "shared with the session" (attachments/
|
||
connected folders) — local build output on disk doesn't qualify. Confirmed
|
||
this session; the user must manually reinstall the zip every time (though
|
||
see "Fast local iteration" above for the hot-patch workaround used for
|
||
verification during dev).
|
||
- **Independently-painted gradient seams** (sidebar/toolbar/settings-page
|
||
`#wpbody-content` each showing a visibly different slice of "the same"
|
||
background gradient): see the theme-system section above — root cause was
|
||
multiple elements each painting their own copy of a gradient-valued
|
||
`var(--aurora-app-bg)` instead of one element painting it and the rest
|
||
staying transparent.
|
||
- **Block editor content rendering underneath the sidebar**: Gutenberg's
|
||
`.interface-interface-skeleton` is `position: fixed; inset: 0`, completely
|
||
bypassing the `margin-left` that pushes every other page's `#wpcontent`
|
||
over — see the "Window chrome" block editor exception above for the full
|
||
mechanism and the fix.
|
||
- **`zip -r` merging into a stale archive**: rebuilding the deployable zip
|
||
without first deleting the old one left old-hash asset filenames mixed
|
||
into the new zip (macOS `zip -r` appends/merges rather than replacing).
|
||
Always `rm -f` the zip before rebuilding.
|
||
- **`remove_submenu_page()` corrupting WP's own capability check**: root
|
||
cause of "Sorry, you are not allowed to access this page" on Menu Creator/
|
||
Activity Log/Role Editor/Comments — see the sub-apps bullet above. Also
|
||
broke `document.title` on those pages, which is why `taskbar.js`'s
|
||
`currentPageTitle()` prefers the on-page `<h1>` instead. **If a future
|
||
custom admin page needs to be kept out of the native menu while staying
|
||
reachable and capability-checked, filter it out at the menu-serialization
|
||
layer (like `MenuSerializer.php` does), never with `remove_submenu_page()`.**
|
||
- **`min-height`/`max-height` conflict silently no-oping a fix**: per spec,
|
||
when `min-height` is larger than `max-height`, `min-height` always wins and
|
||
`max-height` is ignored outright — this is why the taskbar-overlap fix
|
||
first appeared to do nothing (see Window chrome section above).
|
||
- **`position: fixed` over-constraint silently ignoring an explicit `bottom`**:
|
||
when `top`, `height`, and `bottom` are all specified on a fixed-position
|
||
box, the spec has `bottom` get silently recomputed from `top + height`
|
||
rather than honored — root cause of the Site Editor frame running past the
|
||
viewport despite an explicit `bottom: 24px !important` (see Window chrome
|
||
section above).
|
||
|
||
## Not-yet-done (deferred, no active request)
|
||
|
||
- Full field sets for settings categories beyond General/Analytics/White
|
||
Label (original plugin had more).
|
||
- Extending `native-enhance.js`/`native-dark.css` coverage to more native
|
||
surfaces — the block editor's own chrome, Widgets screen, Customizer.
|
||
Media/thickbox modals and plugin/theme card grids were added this session;
|
||
the plugin "More Details" popup specifically can't be reached (it's an
|
||
`<iframe>` — see native page dark styling notes above).
|
||
- Porting the real license-key activation flow (built previously in the old
|
||
`aurora-admin-working` project, not yet ported to v2 — currently just a
|
||
text field).
|
||
- Menu Creator: stored item `order` field isn't applied at runtime yet
|
||
(hide/relabel work; reordering doesn't).
|
||
- No automated tests anywhere in the project — all verification has been
|
||
manual, live-in-Chrome, per the established workflow rule above.
|
||
|
||
## Current state
|
||
|
||
No task in progress. An earlier session rebuilt the entire theming system
|
||
(fixed color presets, gradient backgrounds, "independently-painted gradient"
|
||
seam bugs) and added the macOS-style window chrome + taskbar feature
|
||
end-to-end (pushed as commits `4a547d2`/`b0b0a99`). Later that same session:
|
||
root-caused and fixed the Menu Creator/Activity Log/Role Editor "Sorry, you
|
||
are not allowed to access this page" bug (was `remove_submenu_page()`
|
||
corrupting WP's own capability lookup); added a 6th sub-app, Comments; fixed
|
||
a white-patch bug in the block editor's dark-themed sidebar; rewrote the
|
||
taskbar from a single sessionStorage slot to a list supporting multiple
|
||
minimized windows; and extended the window-chrome/taskbar framing to the
|
||
Site Editor (`site-editor.php`), including tracking down a CSS
|
||
over-constraint bug (`top`+`height`+`bottom` all set on a fixed-position box
|
||
silently ignoring `bottom`) that was making its frame run past the viewport
|
||
(pushed as commit `8153d43`).
|
||
|
||
This session: capped every native/Aurora page's content area to the
|
||
viewport with internal scrolling instead of letting tall pages grow past it
|
||
and scroll the whole document (see "Universal content-area height cap"
|
||
above), which surfaced and fixed two more bugs along the way — a Chrome
|
||
scroll-anchoring jump on Add Plugins, and a phantom document-level
|
||
scrollbar with a large empty gap below the frame. Also made the sidebar's
|
||
native-menu sections a single-open accordion instead of letting multiple
|
||
stay expanded at once. All of it was live-verified in Chrome against the
|
||
LocalWP site (build hot-patched into
|
||
`/Users/kris/Local Sites/aurora/.../aurora-admin/app/dist` for iteration —
|
||
the repo's source files are the actual edits; that copy is just the
|
||
compiled build artifact, not a separate edit surface).
|