15 KiB
Aurora Admin v2 — Project Status
Last updated: 2026-07-03
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: Windows
Compress-Archivecorrupts the WP upload (backslash path separators break PHP's unzip). Always build the zip via PowerShellSystem.IO.Compression.ZipFile, normalizing every entry path with.Replace('\', '/'). Full sequence used every time:npm run buildinapp/robocopythe wholeaurora-admin-v2/tree intoaurora-admin-v2-dist/aurora-admin/with/MIR /XD node_modules .git(exit code 3 = success, not an error)- Rebuild
aurora-admin-v2-dist/aurora-admin-v2.zipvia theSystem.IO.Compression.ZipFilePowerShell snippet (see any recent commit's session for the exact script) - User manually reinstalls the zip into WordPress — the Chrome MCP
file_uploadtool 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.emailset 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/..., namespacedAuroraAdmin\...), bootstrapped fromaurora-admin.php(manualrequirelist, dependency order matters), instantiated inadmin/src/App/AuroraAdmin.php's constructor. - Vue 3 app (
app/src/...), Vite multi-entry build (app/vite.config.jsrollupOptions.input) →manifest.json, read byadmin/src/Utility/Assets.php::enqueue()which also walks the manifest'simportschain to pull in shared-chunk CSS. Data reaches each Vue entry via adata-aurora-adminJSON attribute on a mount div (neverwp_localize_script). - Single global stylesheet:
app/src/shell/shell-frame.css— all--aurora-*CSS variables (incl..aurora-darkoverrides) 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.vueSidebar.vue+Toolbar.vue+UserMenu.vue): hides native WP chrome, mounts a Vue sidebar/toolbar on every admin page.
MenuSerializer.phpconverts WP's$menu/$submenuglobals into JSON for the sidebar's native-menu section — explicitly skips theaurora-adminslug 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 pinnedUserMenufooter 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 insettings.dark_mode(old'on'/'off'values are migrated on read vianormalizeStoredTheme()inSiteShell.vue).systemtracksmatchMedia('(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+app/src/pages/dashboard/Dashboard.vue): hijacks nativeindex.phpviain_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 inlinegrid-column: span N(NOT wrapped — component attribute fallthrough for styles was unreliable). Never use unprefixed class names on Aurora components (.cardcollided 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 viaadd_menu_page(slugaurora-admin). Two-box layout:.settings__nav(category list) +.settings__content(fields), both boxed/bordered on a darker backdrop. Field schema is declarative inapp/src/pages/settings/settingsConfig.js, rendered byapp/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 immediatelyremove_submenu_page()'d on a lateadmin_menuhook — the register-but-hide trick: real page/capability/URL, but never appears in WP's native$submenuglobal (soMenuSerializernever sees them, nothing to filter there). - All slugs prefixed
aurora-admin-*soShell::is_own_page()'s existingstrpos($page, "aurora-admin") === 0check covers them for free (full custom dark theme,native-dark.cssskipped). - Each page uses the shared two-pane layout
app/src/pages/_shared/AppListShell.vue(generalized from Settings.vue's box-pair CSS) +AppListItem.vuefor list rows. - Menu Creator (
admin/src/MenuCreator/): DB tableaurora_admin_menus(name/status/roles JSON/items JSON). Runtimeadmin_menuhook (priorityPHP_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, viaMenuSerializer::serialize()). Reordering is stored (orderfield) but not yet applied at runtime — CRUD + hide/relabel only so far. - Admin Notices (
admin/src/Notices/): DB tableaurora_admin_notices(title/message/type/roles JSON/pages JSON/dismissible/active). Runtimeadmin_noticeshook 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 coreget_editable_roles()/WP_Rolesdirectly. 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 tableaurora_admin_activity_log. Hooked tosave_post(guardswp_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$wpdbschema.GET /db-tables(SHOW TABLE STATUS),GET /db-tables/:table(columns + paginated rows — table name validated against the real$wpdbtable list before use, never interpolated unchecked),POST /db-query(raw query runner, hard-restricted server-side to queries startingSELECT/SHOW/DESCRIBE/EXPLAIN, single-statement only, auto-LIMIT 200if none given). All 3 endpointsmanage_options-gated. Identifier allow-listing + backtick-escaping is the actual safety boundary ($wpdb->prepare()only binds values, not identifiers). - All 5 backend classes +
SubApps.phparerequire'd inaurora-admin.phpand instantiated inAuroraAdmin.php.
- Registered via
- Settings persistence:
admin/src/Options/Settings.php— partial-payload save viamerge_on_save()sanitize callback (merges into existing option, doesn't overwrite).Settings::get_safe()stripslicense_keyand 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, debouncedwp/v2/searchREST calls, gated ondisable_global_searchsetting. - Remote site switcher: toolbar dropdown, gated on
canSwitchSites(fromremote_site_switcher_capabilitysetting) + non-emptyremote_sites. - Scheduled content:
app/src/pages/dashboard/cards/ScheduledContent.vuecalendar cells (squares, date top-left, count centered) are clickable — navigate toedit.php?post_status=future&aurora_day=YYYY-MM-DDif that day has scheduled posts, else show a toast ("no scheduled content"). Server side:admin/src/Posts/ScheduledDayFilter.phpfilters theedit.phpquery viapre_get_postsand prints an info notice. - Analytics:
admin/src/Analytics/Analytics.php(first-party pageview tracker, own DB tablewp_aurora_admin_pageviews, bot detection, gated onenable_analytics/track_adminssettings) +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 viatext_replacementssetting. - Native page dark styling:
admin/assets/native-dark.css(large static sheet, all rules gatedhtml.aurora-dark.aurora-ready, scoped under#wpbody-content) +admin/assets/native-enhance.js(progressive JS enhancement — currently onlyenhanceUpdates(), which groups the flat Updates page sections into.aurora-native-cardwrapper divs onbody.update-core-php).
Key bugs fixed (root causes, in case they resurface)
.cardglobal class collision: original Dashboard cards usedclass="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-adminoverride inshell-frame.css. - Dashboard submenu defaulting collapsed:
Sidebar.vue'snormalize()URL helper didn't map the bare/wp-admin/root toindex.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'sadd_menu_page('aurora-admin', ...)naturally appears in WP's native$menuglobal. Since it's now also shown in the sidebar's dedicated Aurora nav section,MenuSerializer::serialize()explicitly skips slugaurora-adminto avoid the duplicate. - Browser automation can't install plugins: the Chrome MCP
file_uploadtool 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. No workaround found — don't attempt this again.
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.jsboxing to other native screens (Tools, etc.) beyond Updates. - Porting the real license-key activation flow (built previously in the old
aurora-admin-workingproject, not yet ported to v2 — currently just a text field). - Light-mode boxing for native pages (
native-dark.css/native-enhance.jscurrently only styled for dark mode; light mode native pages are untouched WP default). - Menu Creator: stored item
orderfield 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.