# 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://git@git.auroradragon.studio: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-Archive` corrupts the WP upload (backslash path separators break PHP's unzip). Always build the zip via PowerShell `System.IO.Compression.ZipFile`, normalizing every entry path with `.Replace('\', '/')`. Full sequence used every time: 1. `npm run build` in `app/` 2. `robocopy` the whole `aurora-admin-v2/` tree into `aurora-admin-v2-dist/aurora-admin/` with `/MIR /XD node_modules .git` (exit code 3 = success, not an error) 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/`. ## 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), 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` + `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 `
` 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). - **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 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`). ## 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 `
` — 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. 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.js` boxing to other native screens (Tools, etc.) beyond Updates. - 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). - Light-mode boxing for native pages (`native-dark.css`/`native-enhance.js` currently only styled for dark mode; light mode native pages are untouched 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.