Update project status doc with sub-apps, sidebar restructure, and user menu work
This commit is contained in:
+179
-74
@@ -7,57 +7,168 @@ _Last updated: 2026-07-03_
|
|||||||
A ground-up rebuild of the "uiXpress" WordPress admin-theme plugin, relaunched
|
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
|
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,
|
target, but **no original code is reused** — all Vue components, PHP classes,
|
||||||
Tailwind config, and build tooling are freshly written. This lives in
|
and build tooling are freshly written. This lives in `aurora-admin-v2/` (the
|
||||||
`aurora-admin-v2/` (the earlier in-place rebrand attempt, `aurora-admin-working/`,
|
earlier in-place rebrand attempt, `aurora-admin-working/`, is superseded and
|
||||||
is superseded and no longer the active codebase).
|
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
|
## Workflow rules established
|
||||||
|
|
||||||
- **Always verify live in Chrome** before declaring a fix done — the user was
|
- **Always verify live in Chrome** before declaring a fix done. Use the
|
||||||
burned by blind-iteration guesses costing time/money. Use the Chrome MCP
|
Chrome MCP tools (`tabs_context_mcp`, `navigate`, `computer`/screenshot,
|
||||||
tools (`tabs_context_mcp`, `navigate`, `computer`/screenshot, `javascript_exec`)
|
`javascript_exec`) to inspect real DOM/computed styles rather than
|
||||||
to inspect real DOM/computed styles rather than guessing from source.
|
guessing from source.
|
||||||
- **Zip packaging**: Windows `Compress-Archive` corrupts the WP upload (backslash
|
- **Zip packaging**: Windows `Compress-Archive` corrupts the WP upload
|
||||||
path separators break PHP's unzip). Always build the zip via PowerShell
|
(backslash path separators break PHP's unzip). Always build the zip via
|
||||||
`System.IO.Compression.ZipFile`, normalizing every entry path with
|
PowerShell `System.IO.Compression.ZipFile`, normalizing every entry path
|
||||||
`.Replace('\', '/')`. Output goes to
|
with `.Replace('\', '/')`. Full sequence used every time:
|
||||||
`aurora-admin-v2-dist\aurora-admin\aurora-admin-v2.zip`.
|
1. `npm run build` in `app/`
|
||||||
- **Old build caching gotcha**: WordPress serves whatever plugin files were last
|
2. `robocopy` the whole `aurora-admin-v2/` tree into
|
||||||
installed — if the user says "didn't fix it," suspect a stale zip still
|
`aurora-admin-v2-dist/aurora-admin/` with `/MIR /XD node_modules .git`
|
||||||
installed before suspecting the code. Verify the live script hash via
|
(exit code 3 = success, not an error)
|
||||||
`document.querySelector('script[src*="dashboard"]')` when in doubt.
|
3. Rebuild `aurora-admin-v2-dist/aurora-admin-v2.zip` via the
|
||||||
|
`System.IO.Compression.ZipFile` PowerShell snippet (see any recent
|
||||||
|
commit's session for the exact script)
|
||||||
|
4. User manually reinstalls the zip into WordPress — **the Chrome MCP
|
||||||
|
`file_upload` tool cannot push arbitrary local files into the browser**,
|
||||||
|
confirmed this session, so this step can't be automated.
|
||||||
|
- **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/`.
|
- Site under test: `http://admin.local/wp-admin/`.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
- **PHP** (`admin/src/...`, namespaced `AuroraAdmin\...`), bootstrapped from
|
- **PHP** (`admin/src/...`, namespaced `AuroraAdmin\...`), bootstrapped from
|
||||||
`aurora-admin.php`, instantiated in `admin/src/App/AuroraAdmin.php`.
|
`aurora-admin.php` (manual `require` list, dependency order matters),
|
||||||
- **Vue 3** app (`app/src/...`), Vite multi-entry build → `manifest.json`,
|
instantiated in `admin/src/App/AuroraAdmin.php`'s constructor.
|
||||||
read by `admin/src/Utility/Assets.php::enqueue()` which also walks the
|
- **Vue 3** app (`app/src/...`), Vite multi-entry build (`app/vite.config.js`
|
||||||
manifest's `imports` chain to pull in shared-chunk CSS.
|
`rollupOptions.input`) → `manifest.json`, read by
|
||||||
- **Single global stylesheet**: `app/src/shell/shell-frame.css` — defines all
|
`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
|
`--aurora-*` CSS variables (incl. `.aurora-dark` overrides) and the only
|
||||||
rules that touch WP structural IDs (`#wpcontent`, `#wpbody-content`). No
|
rules touching WP structural IDs (`#wpcontent`, `#wpbody-content`). No
|
||||||
element resets/utility classes leak onto native WP pages (this was the
|
element resets/utility classes leak onto native WP pages.
|
||||||
root cause of the original rebrand's Tailwind-leakage bug — avoided here
|
|
||||||
by design).
|
|
||||||
- **Immersive shell** (`admin/src/Shell/Shell.php` + `app/src/shell/SiteShell.vue`
|
- **Immersive shell** (`admin/src/Shell/Shell.php` + `app/src/shell/SiteShell.vue`
|
||||||
+ `Sidebar.vue` + `Toolbar.vue`): hides native WP chrome, mounts a Vue
|
+ `Sidebar.vue` + `Toolbar.vue` + `UserMenu.vue`): hides native WP chrome,
|
||||||
sidebar/toolbar on every admin page. `MenuSerializer.php` converts WP's
|
mounts a Vue sidebar/toolbar on every admin page.
|
||||||
`$menu`/`$submenu` globals into JSON for the sidebar.
|
- `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), and a 3-way theme control.
|
||||||
|
- Theme is a **3-state model**: `'light' | 'dark' | 'system'`, stored as
|
||||||
|
that string in `settings.dark_mode` (old `'on'`/`'off'` values are
|
||||||
|
migrated on read via `normalizeStoredTheme()` in `SiteShell.vue`).
|
||||||
|
`system` tracks `matchMedia('(prefers-color-scheme: dark)')` live via a
|
||||||
|
change listener. The theme control renders as 3 actual color swatches
|
||||||
|
(white / near-black / diagonal split for light/dark/system) — **not**
|
||||||
|
icons; an earlier icon-based version (sun/moon/gear) was explicitly
|
||||||
|
replaced after user feedback that the gear "did nothing" visually.
|
||||||
|
- Old top-right toolbar user-dropdown and sun/moon toggle button are
|
||||||
|
**removed** — fully replaced by the bottom-left `UserMenu`.
|
||||||
- **Dashboard takeover** (`admin/src/Pages/DashboardTakeover.php` +
|
- **Dashboard takeover** (`admin/src/Pages/DashboardTakeover.php` +
|
||||||
`app/src/pages/dashboard/Dashboard.vue`): hijacks native `index.php` via
|
`app/src/pages/dashboard/Dashboard.vue`): hijacks native `index.php` via
|
||||||
`in_admin_header`, hides the native `.wrap`, renders the Aurora dashboard
|
`in_admin_header`, hides the native `.wrap`, renders the Aurora dashboard
|
||||||
in its place. 12-col grid; cards are single `<section class="aurora-card">`
|
in its place. 12-col grid; cards are single `<section class="aurora-card">`
|
||||||
elements with inline `grid-column: span N` (NOT wrapped — component
|
elements with inline `grid-column: span N` (NOT wrapped — component
|
||||||
attribute fallthrough for styles was unreliable).
|
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 page** (`admin/src/Pages/SettingsPage.php` + `app/src/pages/
|
||||||
settings/Settings.vue`): owns the top-level "Aurora Admin" admin menu
|
settings/Settings.vue`): owns the top-level "Aurora Admin" WP admin menu
|
||||||
directly (no submenu). Two-box layout: `.settings__nav` (category list) +
|
directly via `add_menu_page` (slug `aurora-admin`). Two-box layout:
|
||||||
`.settings__content` (fields), both boxed/bordered on a darker backdrop.
|
`.settings__nav` (category list) + `.settings__content` (fields), both
|
||||||
Field schema is declarative in `app/src/pages/settings/settingsConfig.js`,
|
boxed/bordered on a darker backdrop. Field schema is declarative in
|
||||||
rendered by `app/src/pages/settings/components/SettingsField.vue` (types:
|
`app/src/pages/settings/settingsConfig.js`, rendered by
|
||||||
text, toggle, color, image, repeater, multiselect, segmented, remote-sites).
|
`app/src/pages/settings/components/SettingsField.vue` (types: text, toggle,
|
||||||
|
color, image, repeater, multiselect, segmented, remote-sites).
|
||||||
|
- **5 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', ...)` then immediately
|
||||||
|
`remove_submenu_page()`'d on a late `admin_menu` hook — the
|
||||||
|
register-but-hide trick: real page/capability/URL, but never appears in
|
||||||
|
WP's native `$submenu` global (so `MenuSerializer` never sees them,
|
||||||
|
nothing to filter there).
|
||||||
|
- 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).
|
||||||
|
- 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=`.
|
||||||
|
- **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
|
- **Settings persistence**: `admin/src/Options/Settings.php` — partial-payload
|
||||||
save via `merge_on_save()` sanitize callback (merges into existing option,
|
save via `merge_on_save()` sanitize callback (merges into existing option,
|
||||||
doesn't overwrite). `Settings::get_safe()` strips `license_key` and remote
|
doesn't overwrite). `Settings::get_safe()` strips `license_key` and remote
|
||||||
@@ -91,48 +202,32 @@ is superseded and no longer the active codebase).
|
|||||||
## Key bugs fixed (root causes, in case they resurface)
|
## Key bugs fixed (root causes, in case they resurface)
|
||||||
|
|
||||||
- **`.card` global class collision**: original Dashboard cards used
|
- **`.card` global class collision**: original Dashboard cards used
|
||||||
`class="card"`, colliding with WP core's `.card { max-width: 520px }`,
|
`class="card"`, colliding with WP core's `.card { max-width: 520px }`.
|
||||||
capping card width regardless of grid span. Fixed by renaming to
|
Fixed by renaming to `.aurora-card*` everywhere. Never use unprefixed
|
||||||
`.aurora-card*` everywhere. Lesson: never use unprefixed class names on
|
class names on Aurora components.
|
||||||
Aurora components — always prefix `aurora-`.
|
|
||||||
- **Grid-column not applying on wrapped cards**: settled on unwrapped
|
- **Grid-column not applying on wrapped cards**: settled on unwrapped
|
||||||
`<section class="aurora-card" :style="{gridColumn: ...}">` — component
|
`<section class="aurora-card" :style="{gridColumn: ...}">` — component
|
||||||
boundaries ate the style binding in earlier attempts.
|
boundaries ate the style binding in earlier attempts.
|
||||||
- **Two-box settings layout**: took two wrong iterations (splitting
|
- **Two-box settings layout**: took two wrong iterations before matching the
|
||||||
header/fields instead of nav/content) before matching the user's reference
|
user's reference image. Re-confirm layout intent against a screenshot
|
||||||
image. If revisiting settings layout, re-confirm against a screenshot
|
before assuming.
|
||||||
before assuming intent.
|
- **Redundant outer frame border on settings page**: fixed with a
|
||||||
- **Redundant outer frame border on settings page**: `#wpbody-content`'s
|
`body.toplevel_page_aurora-admin` override in `shell-frame.css`.
|
||||||
frame border/radius doubled up with the new two-box layout; fixed with a
|
- **Dashboard submenu defaulting collapsed**: `Sidebar.vue`'s `normalize()`
|
||||||
`body.toplevel_page_aurora-admin` override in `shell-frame.css` that drops
|
URL helper didn't map the bare `/wp-admin/` root to `index.php`, so the
|
||||||
the outer frame there only.
|
Dashboard section's active-state check (and thus its default-expanded
|
||||||
|
state) never matched on the dashboard itself. Fixed by normalizing empty/
|
||||||
## Current / in-progress task
|
query-only paths to `'index.php' + path`.
|
||||||
|
- **Aurora top-level menu appearing twice**: `SettingsPage.php`'s
|
||||||
**User's last request: "now the updates page doesn't match the style as the
|
`add_menu_page('aurora-admin', ...)` naturally appears in WP's native
|
||||||
dashboard page."**
|
`$menu` global. Since it's now also shown in the sidebar's dedicated Aurora
|
||||||
|
nav section, `MenuSerializer::serialize()` explicitly skips slug
|
||||||
Status: diagnosis started but not finished. I navigated to
|
`aurora-admin` to avoid the duplicate.
|
||||||
`update-core.php` and confirmed the page renders with the shell (minify
|
- **Browser automation can't install plugins**: the Chrome MCP `file_upload`
|
||||||
button, View Website/Dashboard icons, search, dark toggle) and that
|
tool only accepts files already "shared with the session" (attachments/
|
||||||
`native-enhance.js` is already grouping the page into three
|
connected folders) — local build output on disk doesn't qualify. Confirmed
|
||||||
`.aurora-native-card` boxes (Current version / Plugins / Themes). I had
|
this session; the user must manually reinstall the zip every time. No
|
||||||
**not yet** done a side-by-side computed-style comparison against
|
workaround found — don't attempt this again.
|
||||||
`.aurora-card` (used on the Dashboard, e.g. "Recent content" / "Site Health"
|
|
||||||
cards) to find the actual mismatch — could be background shade, border
|
|
||||||
radius, padding, title typography, or hover state. No code changes made yet
|
|
||||||
for this specific request.
|
|
||||||
|
|
||||||
### Next step (concrete)
|
|
||||||
|
|
||||||
Via Chrome `javascript_exec`, pull `getComputedStyle()` for a live
|
|
||||||
`.aurora-card` element on the Dashboard and a live `.aurora-native-card`
|
|
||||||
element on the Updates page — compare `background-color`, `border-color`,
|
|
||||||
`border-radius`, `padding`, `box-shadow`, and heading font-size/weight. Then
|
|
||||||
patch `.aurora-native-card` rules in `admin/assets/native-dark.css` (and/or
|
|
||||||
structure in `native-enhance.js` if the mismatch is structural, e.g. missing
|
|
||||||
a title wrapper element) to match `.aurora-card` in `Dashboard.vue`. Verify
|
|
||||||
live, then rebuild/sync to dist, re-zip, and have the user reinstall.
|
|
||||||
|
|
||||||
## Not-yet-done (deferred, no active request)
|
## Not-yet-done (deferred, no active request)
|
||||||
|
|
||||||
@@ -146,3 +241,13 @@ live, then rebuild/sync to dist, re-zip, and have the user reinstall.
|
|||||||
- Light-mode boxing for native pages (`native-dark.css`/`native-enhance.js`
|
- Light-mode boxing for native pages (`native-dark.css`/`native-enhance.js`
|
||||||
currently only styled for dark mode; light mode native pages are untouched
|
currently only styled for dark mode; light mode native pages are untouched
|
||||||
WP default).
|
WP default).
|
||||||
|
- 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. Last completed and pushed: theme-control swatches in
|
||||||
|
the new floating `UserMenu`. Everything through commit `e1bd095` on
|
||||||
|
`origin/main` is confirmed live-verified and working by the user.
|
||||||
|
|||||||
Reference in New Issue
Block a user