Author SHA1 Message Date
NixReaper 19831cd2f3 Trim uixpress references from changelog and project status docs 2026-08-29 21:59:23 -05:00
NixReaperandClaude Opus 4.8 faf9f35992 Expand login customizer: background, colors, custom CSS
Extend Settings > Login (beyond the existing style toggle + logo) toward
Loginfy parity: background image or color, form-panel background, button/
focus-accent color, and a custom login CSS field — all dependent on
"Style login page" and all falling back to the original fixed dark theme
when unset. LoginStyle.php sanitizes colors via sanitize_hex_color(),
esc_url()s the background image, and appends custom CSS (</style> stripped).
Verified on wp-login.php.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 14:49:03 -05:00
NixReaperandClaude Opus 4.8 f5c48bced9 Add Code settings group: custom admin CSS/JS + header/footer scripts
New Settings > Code group (Custom CSS & JS + Header & footer scripts) driven
by admin/src/Code/CodeInjection.php:
- Admin CSS  → <style> in the admin <head> (</style> stripped to prevent breakout)
- Admin JS   → <script> in the admin footer (</script> neutralized)
- Header scripts → printed verbatim in the front-end <head>
- Footer scripts → printed verbatim before the front-end </body>

Header/footer scripts are raw admin-authored markup by design (analytics,
meta, etc.), settable only by manage_options users on the settings page.
SettingsField gains a `mono` flag for monospace textareas (no CodeMirror
dependency). All four injection points verified on PHP 8.5.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 14:44:10 -05:00
NixReaperandClaude Opus 4.8 f0bc04a110 Add Security & performance settings group + hardening module
New Settings > Security & performance group (Hardening + Performance) with
toggles wired by admin/src/Security/Security.php — each gated on its own
setting, adding/removing core hooks so anything left off behaves exactly like
stock WordPress:

- Disable XML-RPC (xmlrpc_enabled false + drop X-Pingback header)
- Disable comments site-wide (close everywhere, strip post-type support,
  remove the Comments menu/dashboard-widget/toolbar node)
- Clean up <head> (generator, RSD, WLW manifest, shortlink)
- Disable emoji scripts/styles + the s.w.org dns-prefetch
- Disable oEmbeds (discovery links, host js, embed REST route, wp-embed) —
  discovery links are removed at whatever priority core hooks them (loops,
  since they can be registered at more than one)
- Heartbeat API: default / slow (60s) / disabled

New setting keys need no schema change (Settings::merge_on_save +
additionalProperties). Every toggle verified at the hook level on PHP 8.5.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 14:26:59 -05:00
NixReaperandClaude Opus 4.8 2fec4cf34b Add first-run setup wizard
Full-screen onboarding at admin.php?page=aurora-admin-setup: Welcome →
Appearance (theme + font) → Branding (logo/favicon/rename) → Features
(replacement-screen toggles) → Done. Reuses SettingsField for every control
and applyLiveTheme for instant preview; finish/skip saves via wp/v2/settings
and marks completion.

- admin/src/Pages/SetupWizard.php: hidden submenu page, disables the shell on
  its own page (aurora_admin_shell_enabled filter) + hides native chrome,
  register_activation_hook sets a one-time redirect flag, admin_init redirects
  once, and a POST /aurora-admin/v1/setup/complete route marks it done.
- app/src/pages/setup-wizard/SetupWizard.vue + entries/setup-wizard.js + vite
  input.
- Wired into aurora-admin.php + AuroraAdmin.php.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 13:55:03 -05:00
NixReaperandClaude Opus 4.8 2da3883d20 Indent settings category items under their group header
Add a small left indent to .settings__list so each group's categories read
as a nested submenu rather than sitting flush with the group header.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 13:24:17 -05:00
NixReaperandClaude Opus 4.8 b3ff365483 Round corners a notch; tokenize all radii onto an sm/md/lg/xl scale
Consolidate ~140 scattered hardcoded border-radius values (and the old
--aurora-radius/--aurora-radius-lg vars) onto one four-step scale defined in
shell-frame.css:
  --aurora-radius-sm 10px  (chips, small/icon controls; was 5-8px)
  --aurora-radius-md 14px  (buttons, inputs, search, list rows; was 9-14px)
  --aurora-radius-lg 20px  (cards, panels, larger buttons; was 15-20px)
  --aurora-radius-xl 28px  (page frames: content card, dashboard, sidebar,
                            editor; was the 24px --aurora-radius-lg)
Everything is one notch rounder, and the whole UI's roundness is now tunable
from four lines instead of 35 files.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 13:21:22 -05:00
NixReaperandClaude Opus 4.8 53ca6f62b9 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]>
2026-07-12 11:44:57 -05:00
NixReaperandClaude Opus 4.8 dc2a4435c5 Remove unused CodeMirror dependencies
@codemirror/* (8 language packages) and codemirror were declared but
imported nowhere in src — leftovers from the removed File Manager and
Database Explorer. Dropped all nine; removes 30 packages from the install
(node_modules ~44 MB -> ~39 MB). Not bundled before (unused), so the built
output is unchanged. If the future custom CSS/JS editor needs an in-browser
code editor, add back the specific packages it uses at that point.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 09:57:59 -05:00
NixReaperandClaude Opus 4.8 da856dc853 Split Vue into a stable vendor chunk; make sourcemaps opt-out
- Pin `vue` to a named `vendor-vue` chunk (manualChunks) so its hash stays
  stable across app-code changes — returning users keep it cached across
  plugin releases instead of re-downloading ~31 kB gzip each update. Also
  replaces the misleadingly-named `_plugin-vue_export-helper` shared chunk.
- Gate sourcemap emission on AURORA_SOURCEMAP (default on for the local
  hot-patch dev loop; the distribution build sets it to false). Cuts a
  from-scratch dist build's app/dist from ~1.7 MB to ~502 kB by dropping
  27 .map files that ship no value to end users.
- Refresh the stale header comment (was "one entry in this first slice").

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 09:38:16 -05:00
NixReaperandClaude Opus 4.8 1c8c351654 Regroup settings into collapsible sections
Restructure the flat settingsCategories list into five top-level groups
(General, Appearance, Screens, Productivity, Add-ons), each a collapsible
nav section with persisted open/closed state. Consolidate the six
single-toggle modern_* categories (posts/pages/media/users/comments/
plugins) into one "Content tables" screen. Search now matches on group +
category + subtitle and hides empty groups. Field definitions, save flow,
and setting keys are unchanged.

settingsConfig.js exports settingsGroups plus a flattened settingsCategories
for default-seeding and deep-link lookups.

(package-lock.json: version field synced 2.16.0 -> 3.1.0 to match package.json.)

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 09:27:09 -05:00
NixReaperandClaude Opus 4.8 ce8fcc8fdd Remove uiXpress origin framing from PROJECT_STATUS
Reworded "What this project is" to describe Aurora on its own terms and
dropped the stale uixpress/ folder archaeology from the Repo section.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 09:26:34 -05:00
53 changed files with 1586 additions and 1384 deletions
+1 -2
View File
@@ -1053,8 +1053,7 @@ and closed the one remaining documented security gap.
- First phase of the "full admin takeover" initiative: Posts, Pages, Media - First phase of the "full admin takeover" initiative: Posts, Pages, Media
Library, Users, and Comments no longer redirect to a separate Library, Users, and Comments no longer redirect to a separate
`admin.php?page=...` URL — they now hijack their native screens in place `admin.php?page=...` URL — they now hijack their native screens in place
(`ScreenHijack.php`, registered via `HijackedNativePages.php`), the same (`ScreenHijack.php`, registered via `HijackedNativePages.php`). WordPress's
approach the original uixpress reference plugin used. WordPress's
`load-{page}.php` hook fires before the native screen prints anything, so `load-{page}.php` hook fires before the native screen prints anything, so
Aurora buffers and discards the native output and mounts its own Vue page Aurora buffers and discards the native output and mounts its own Vue page
in its place — the URL never changes, so there's no separate slug that can in its place — the URL never changes, so there's no separate slug that can
+7 -13
View File
@@ -85,21 +85,15 @@ unless explicitly marked otherwise).)_
## What this project is ## What this project is
A ground-up rebuild of the "uiXpress" WordPress admin-theme plugin, relaunched **Aurora Admin** — a modern, fast WordPress admin interface. Real replacement
as **Aurora Admin**. The original plugin's look/feel and feature set are the screens for the core admin (Posts, Pages, Media, Users, Comments, and more),
target, but **no original code is reused** — all Vue components, PHP classes, a full theme system with dark mode, and admin power-tools, all freshly written
and build tooling are freshly written. This lives in `aurora-admin-v2/` (the from scratch (Vue components, PHP classes, and build tooling).
earlier in-place rebrand attempt, `aurora-admin-working/`, is superseded and
no longer the active codebase).
## Repo ## Repo
Now on GitLab (self-hosted): `ssh://[email protected]:2289/NixReaper/aurora-wordpress-admin.git` 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/` (HTTPS also available). Repo root holds `admin/`, `app/`, and `aurora-admin.php`.
(`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 — `.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 regenerate, don't version). To clone on a new machine: add an SSH key to the
@@ -293,8 +287,8 @@ URL with no rewrite needed.
**Plugins is the one exception, deliberately left on the old redirect **Plugins is the one exception, deliberately left on the old redirect
mechanism** — `plugins.php` prints output in a way that can't be cleanly mechanism** — `plugins.php` prints output in a way that can't be cleanly
buffered (confirmed by the old uixpress reference plugin's own abandoned buffered (confirmed via a prior abandoned attempt at hijacking it the same
attempt at hijacking it the same way). It still has a real way). It still has a real
`aurora-admin-plugins` submenu under `plugins.php` and its own `aurora-admin-plugins` submenu under `plugins.php` and its own
`redirect_native_pages()` branch in `SubApps.php`. `redirect_native_pages()` branch in `SubApps.php`.
+6
View File
@@ -13,6 +13,7 @@ use AuroraAdmin\Options\Settings;
use AuroraAdmin\Shell\Shell; use AuroraAdmin\Shell\Shell;
use AuroraAdmin\Pages\DashboardTakeover; use AuroraAdmin\Pages\DashboardTakeover;
use AuroraAdmin\Pages\SettingsPage; use AuroraAdmin\Pages\SettingsPage;
use AuroraAdmin\Pages\SetupWizard;
use AuroraAdmin\Rest\DashboardData; use AuroraAdmin\Rest\DashboardData;
use AuroraAdmin\Analytics\Analytics; use AuroraAdmin\Analytics\Analytics;
use AuroraAdmin\Rest\AnalyticsData; use AuroraAdmin\Rest\AnalyticsData;
@@ -45,6 +46,8 @@ use AuroraAdmin\Login\LoginStyle;
use AuroraAdmin\Media\SvgUploads; use AuroraAdmin\Media\SvgUploads;
use AuroraAdmin\Media\MediaFolders; use AuroraAdmin\Media\MediaFolders;
use AuroraAdmin\ImageFormats\ImageFormats; use AuroraAdmin\ImageFormats\ImageFormats;
use AuroraAdmin\Security\Security;
use AuroraAdmin\Code\CodeInjection;
use AuroraAdmin\Rest\MediaFolderData; use AuroraAdmin\Rest\MediaFolderData;
use AuroraAdmin\Rest\BugReportData; use AuroraAdmin\Rest\BugReportData;
use AuroraAdmin\Rest\CompanionPluginData; use AuroraAdmin\Rest\CompanionPluginData;
@@ -63,6 +66,7 @@ class AuroraAdmin
new Shell(); new Shell();
new DashboardTakeover(); new DashboardTakeover();
new SettingsPage(); new SettingsPage();
new SetupWizard();
new DashboardData(); new DashboardData();
new Analytics(); new Analytics();
new AnalyticsData(); new AnalyticsData();
@@ -95,6 +99,8 @@ class AuroraAdmin
new SvgUploads(); new SvgUploads();
new MediaFolders(); new MediaFolders();
new ImageFormats(); new ImageFormats();
new Security();
new CodeInjection();
new MediaFolderData(); new MediaFolderData();
new BugReportData(); new BugReportData();
new CompanionPluginData(); new CompanionPluginData();
+72
View File
@@ -0,0 +1,72 @@
<?php
namespace AuroraAdmin\Code;
use AuroraAdmin\Options\Settings;
defined("ABSPATH") || exit();
/**
* Custom code injection (Settings > Code).
*
* Four admin-authored fields, only settable by manage_options users on the
* settings page:
* - Admin CSS → wrapped in a <style> in the admin <head>
* - Admin JS → wrapped in a <script> in the admin footer
* - Header scripts → printed raw in the front-end <head>
* - Footer scripts → printed raw before the front-end </body>
*
* Header/footer scripts are printed verbatim (they're meant to hold full
* <script>/<meta> tags — analytics, verification, etc.), the same trusted-
* admin model as any "insert headers and footers" tool. Admin CSS/JS is the
* user's own code too, but is wrapped in the right tag for them and guarded
* against accidentally breaking out of that tag.
*/
class CodeInjection
{
public function __construct()
{
add_action("admin_head", [self::class, "admin_css"], 999);
add_action("admin_print_footer_scripts", [self::class, "admin_js"], 999);
add_action("wp_head", [self::class, "header_scripts"], 999);
add_action("wp_footer", [self::class, "footer_scripts"], 999);
}
public static function admin_css()
{
$css = trim((string) Settings::get("custom_admin_css", ""));
if ($css === "") {
return;
}
// Keep the CSS from breaking out of its own <style> element.
$css = str_ireplace("</style>", "", $css);
echo "\n<style id=\"aurora-custom-admin-css\">\n" . $css . "\n</style>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin-authored stylesheet, output raw inside a <style> tag by design; </style> stripped above to prevent breakout.
}
public static function admin_js()
{
$js = trim((string) Settings::get("custom_admin_js", ""));
if ($js === "") {
return;
}
$js = str_ireplace("</script>", "<\\/script>", $js);
echo "\n<script id=\"aurora-custom-admin-js\">\n" . $js . "\n</script>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin-authored script, output raw inside a <script> tag by design.
}
public static function header_scripts()
{
$code = trim((string) Settings::get("header_scripts", ""));
if ($code === "") {
return;
}
echo "\n" . $code . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin-authored header markup (scripts/meta), printed verbatim by design.
}
public static function footer_scripts()
{
$code = trim((string) Settings::get("footer_scripts", ""));
if ($code === "") {
return;
}
echo "\n" . $code . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin-authored footer markup (scripts), printed verbatim by design.
}
}
+24 -9
View File
@@ -42,10 +42,25 @@ class LoginStyle
} }
$logo = trim((string) Settings::get("login_logo", "")); $logo = trim((string) Settings::get("login_logo", ""));
$bg_image = trim((string) Settings::get("login_bg_image", ""));
// Colors fall back to the original fixed dark theme when unset; invalid
// values are dropped by sanitize_hex_color (returns null) then fall back.
$bg_color = sanitize_hex_color((string) Settings::get("login_bg_color", "")) ?: "";
$form_bg = sanitize_hex_color((string) Settings::get("login_form_bg", "")) ?: "#1c2333";
$accent = sanitize_hex_color((string) Settings::get("login_button_color", "")) ?: "#4f7cff";
$custom_css = str_ireplace("</style>", "", (string) Settings::get("login_custom_css", ""));
if ($bg_image) {
$background = "url(" . esc_url($bg_image) . ") center / cover no-repeat";
} elseif ($bg_color) {
$background = $bg_color;
} else {
$background = "linear-gradient(135deg, #1b2333 0%, #0f1420 100%)";
}
?> ?>
<style> <style>
body.login { body.login {
background: linear-gradient(135deg, #1b2333 0%, #0f1420 100%); background: <?php echo $background; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- composed from a sanitize_hex_color() color or esc_url()'d image above. ?>;
} }
body.login #login h1 a { body.login #login h1 a {
<?php if ($logo): ?> <?php if ($logo): ?>
@@ -64,7 +79,7 @@ class LoginStyle
body.login form#loginform, body.login form#loginform,
body.login form#registerform, body.login form#registerform,
body.login form#lostpasswordform { body.login form#lostpasswordform {
background: #1c2333; background: <?php echo esc_attr($form_bg); ?>;
border: 1px solid rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px; border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35);
@@ -84,8 +99,8 @@ class LoginStyle
body.login input[type="text"]:focus, body.login input[type="text"]:focus,
body.login input[type="password"]:focus, body.login input[type="password"]:focus,
body.login input[type="email"]:focus { body.login input[type="email"]:focus {
border-color: #4f7cff; border-color: <?php echo esc_attr($accent); ?>;
box-shadow: 0 0 0 1px #4f7cff; box-shadow: 0 0 0 1px <?php echo esc_attr($accent); ?>;
} }
body.login .forgetmenot label { body.login .forgetmenot label {
color: #9fb0d0; color: #9fb0d0;
@@ -99,23 +114,23 @@ class LoginStyle
color: #fff; color: #fff;
} }
body.login .wp-core-ui .button-primary { body.login .wp-core-ui .button-primary {
background: #4f7cff; background: <?php echo esc_attr($accent); ?>;
border-color: #4f7cff; border-color: <?php echo esc_attr($accent); ?>;
box-shadow: none; box-shadow: none;
text-shadow: none; text-shadow: none;
} }
body.login .wp-core-ui .button-primary:hover, body.login .wp-core-ui .button-primary:hover,
body.login .wp-core-ui .button-primary:focus { body.login .wp-core-ui .button-primary:focus {
background: #3b63e0; filter: brightness(0.92);
border-color: #3b63e0;
} }
body.login #login_error, body.login #login_error,
body.login .message, body.login .message,
body.login .success { body.login .success {
background: #232b40; background: #232b40;
border-left-color: #4f7cff; border-left-color: <?php echo esc_attr($accent); ?>;
color: #e2e8f4; color: #e2e8f4;
} }
<?php echo $custom_css; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin-authored login CSS, output raw by design; </style> stripped above. ?>
</style> </style>
<?php <?php
} }
+151
View File
@@ -0,0 +1,151 @@
<?php
namespace AuroraAdmin\Pages;
use AuroraAdmin\Utility\Assets;
use AuroraAdmin\Options\Settings;
defined("ABSPATH") || exit();
/**
* First-run onboarding wizard.
*
* A full-screen, self-contained page (no Aurora sidebar/toolbar shell, no
* native WP chrome) walking a new user through appearance, branding, and
* which replacement screens to enable. Registered as a hidden submenu of the
* "aurora-admin" menu — MenuSerializer skips that slug, so it never shows in
* the Aurora sidebar, and the native menu it does technically live under is
* hidden by the shell on every other page anyway.
*
* First run: on activation an option flags a one-time redirect here; the
* wizard clears it and sets a "complete" option when finished or skipped, so
* it never auto-opens again (but stays reachable at its URL).
*/
class SetupWizard
{
const PAGE = "aurora-admin-setup";
const OPT_COMPLETE = "aurora_admin_setup_complete";
const OPT_REDIRECT = "aurora_admin_setup_redirect";
private static $hook_suffix = null;
public function __construct()
{
register_activation_hook(AURORA_ADMIN_PATH . "aurora-admin.php", [self::class, "on_activate"]);
add_action("admin_menu", [self::class, "register_menu"]);
add_action("admin_enqueue_scripts", [self::class, "maybe_enqueue"]);
add_action("admin_init", [self::class, "maybe_redirect"]);
add_action("rest_api_init", [self::class, "register_rest"]);
// Turn the immersive shell off on this page so the wizard owns the whole
// viewport; native chrome is hidden separately (printed below).
add_filter("aurora_admin_shell_enabled", [self::class, "disable_shell_here"]);
add_action("admin_head", [self::class, "hide_native_chrome"]);
}
/** Flag a one-time redirect to the wizard, unless setup was already done. */
public static function on_activate()
{
if (!get_option(self::OPT_COMPLETE)) {
update_option(self::OPT_REDIRECT, 1);
}
}
private static function is_wizard_page()
{
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page match; no state change.
return isset($_GET["page"]) && sanitize_text_field(wp_unslash($_GET["page"])) === self::PAGE;
}
public static function disable_shell_here($enabled)
{
return self::is_wizard_page() ? false : $enabled;
}
public static function register_menu()
{
self::$hook_suffix = add_submenu_page(
"aurora-admin",
__("Setup", "aurora-admin"),
__("Setup", "aurora-admin"),
"manage_options",
self::PAGE,
[self::class, "render"]
);
}
public static function maybe_redirect()
{
if (!get_option(self::OPT_REDIRECT)) {
return;
}
if (wp_doing_ajax() || !current_user_can("manage_options") || self::is_wizard_page()) {
return;
}
delete_option(self::OPT_REDIRECT);
wp_safe_redirect(admin_url("admin.php?page=" . self::PAGE));
exit();
}
public static function maybe_enqueue($hook_suffix)
{
if ($hook_suffix !== self::$hook_suffix) {
return;
}
// Image fields (logo, dark logo, favicon) use the real media library.
wp_enqueue_media();
Assets::enqueue("aurora-admin-setup", "entries/setup-wizard.js");
}
/** Hide native WP chrome on the wizard page (the shell, which normally does
* this, is turned off here). The Vue app also renders as a full-screen
* overlay, but this keeps the admin bar/menu from flashing first. */
public static function hide_native_chrome()
{
if (!self::is_wizard_page()) {
return;
}
?>
<style id="aurora-setup-hide-chrome">
#adminmenumain, #wpadminbar, #wpfooter { display: none !important; }
html.wp-toolbar { padding-top: 0 !important; }
#wpcontent { margin-left: 0 !important; }
#wpbody-content { padding: 0 !important; float: none; width: auto; }
</style>
<?php
}
public static function register_rest()
{
register_rest_route("aurora-admin/v1", "/setup/complete", [
"methods" => "POST",
"callback" => [self::class, "rest_complete"],
"permission_callback" => function () {
return current_user_can("manage_options");
},
]);
}
public static function rest_complete()
{
update_option(self::OPT_COMPLETE, 1);
delete_option(self::OPT_REDIRECT);
return ["success" => true];
}
public static function render()
{
$data = [
"restUrl" => esc_url_raw(get_rest_url()),
"restNonce" => wp_create_nonce("wp_rest"),
"userName" => wp_get_current_user()->display_name,
"settings" => Settings::get(),
"dashboardUrl" => esc_url_raw(admin_url("index.php")),
"settingsUrl" => esc_url_raw(admin_url("admin.php?page=aurora-admin")),
];
?>
<div
id="aurora-admin-setup-root"
data-aurora-admin="<?php echo esc_attr(wp_json_encode($data)); ?>"
></div>
<?php
}
}
+151
View File
@@ -0,0 +1,151 @@
<?php
namespace AuroraAdmin\Security;
use AuroraAdmin\Options\Settings;
defined("ABSPATH") || exit();
/**
* Security + performance hardening toggles (Settings > Security & performance).
*
* Each feature is gated on its own setting and wired here by adding/removing
* core hooks — no output is buffered or rewritten, so anything left off
* behaves exactly like stock WordPress. Settings are read once at construct
* time (plugins_loaded), the same pattern the rest of the plugin uses.
*/
class Security
{
public function __construct()
{
if (Settings::get("disable_xmlrpc")) {
self::disable_xmlrpc();
}
if (Settings::get("disable_comments")) {
self::disable_comments();
}
if (Settings::get("remove_head_junk")) {
self::clean_head();
}
if (Settings::get("disable_emojis")) {
self::disable_emojis();
}
if (Settings::get("disable_oembed")) {
self::disable_oembed();
}
$heartbeat = Settings::get("heartbeat_mode", "default");
if ($heartbeat === "disabled") {
add_action("init", function () {
wp_deregister_script("heartbeat");
}, 1);
} elseif ($heartbeat === "slow") {
add_filter("heartbeat_settings", function ($settings) {
$settings["interval"] = 60;
return $settings;
});
}
}
private static function disable_xmlrpc()
{
add_filter("xmlrpc_enabled", "__return_false");
// Drop the X-Pingback header that advertises the endpoint.
add_filter("wp_headers", function ($headers) {
unset($headers["X-Pingback"]);
return $headers;
});
add_filter("pings_open", "__return_false", 20);
}
private static function disable_comments()
{
// Front end: closed everywhere, and existing comments never render.
add_filter("comments_open", "__return_false", 20);
add_filter("pings_open", "__return_false", 20);
add_filter("comments_array", "__return_empty_array", 20);
// Strip comment support from every post type.
add_action("init", function () {
foreach (get_post_types() as $post_type) {
if (post_type_supports($post_type, "comments")) {
remove_post_type_support($post_type, "comments");
remove_post_type_support($post_type, "trackbacks");
}
}
}, 100);
// Admin: remove the Comments menu, the dashboard widget, and the toolbar
// bubble so there's no dangling entry point.
add_action("admin_menu", function () {
remove_menu_page("edit-comments.php");
});
add_action("wp_dashboard_setup", function () {
remove_meta_box("dashboard_recent_comments", "dashboard", "normal");
});
add_action("admin_bar_menu", function ($bar) {
$bar->remove_node("comments");
}, 999);
}
private static function clean_head()
{
remove_action("wp_head", "rsd_link");
remove_action("wp_head", "wlwmanifest_link");
remove_action("wp_head", "wp_generator");
remove_action("wp_head", "wp_shortlink_wp_head");
add_filter("the_generator", "__return_empty_string");
}
private static function disable_emojis()
{
remove_action("wp_head", "print_emoji_detection_script", 7);
remove_action("admin_print_scripts", "print_emoji_detection_script");
remove_action("wp_print_styles", "print_emoji_styles");
remove_action("admin_print_styles", "print_emoji_styles");
remove_filter("the_content_feed", "wp_staticize_emoji");
remove_filter("comment_text_rss", "wp_staticize_emoji");
remove_filter("wp_mail", "wp_staticize_emoji_for_email");
add_filter("tiny_mce_plugins", function ($plugins) {
return is_array($plugins) ? array_diff($plugins, ["wpemoji"]) : [];
});
// Drop the s.w.org DNS-prefetch the emoji script adds.
add_filter("wp_resource_hints", function ($hints, $relation_type) {
if ($relation_type === "dns-prefetch") {
$emoji_svg = apply_filters("emoji_svg_url", "https://s.w.org/images/core/emoji/");
$hints = array_filter($hints, function ($h) use ($emoji_svg) {
return strpos((string) $h, $emoji_svg) === false && strpos((string) $h, "s.w.org") === false;
});
}
return $hints;
}, 10, 2);
}
private static function disable_oembed()
{
// Core registers these on wp_head at a non-default priority (the
// discovery links sit at priority 4, not 10), so match whatever priority
// they're actually hooked at rather than assuming the default.
self::remove_wp_head_action("wp_oembed_add_discovery_links");
self::remove_wp_head_action("wp_oembed_add_host_js");
add_filter("embed_oembed_discover", "__return_false");
remove_action("rest_api_init", "wp_oembed_register_route");
// Stop the front-end wp-embed.min.js from loading.
add_action("wp_footer", function () {
wp_dequeue_script("wp-embed");
});
}
/**
* Remove a wp_head callback at whatever priority (or priorities) it's
* registered at — core hooks the oEmbed discovery links at a non-default
* priority, and it can end up registered at more than one, so loop until
* none remain (bounded, so a stubborn callback can't spin forever).
*/
private static function remove_wp_head_action($function)
{
$guard = 0;
while (($priority = has_action("wp_head", $function)) !== false && $guard++ < 20) {
remove_action("wp_head", $function, $priority);
}
}
}
+2 -342
View File
@@ -1,22 +1,13 @@
{ {
"name": "aurora-admin-app", "name": "aurora-admin-app",
"version": "2.16.0", "version": "3.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "aurora-admin-app", "name": "aurora-admin-app",
"version": "2.16.0", "version": "3.1.0",
"dependencies": { "dependencies": {
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.11",
"@codemirror/lang-javascript": "^6.2.5",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-markdown": "^6.5.0",
"@codemirror/lang-php": "^6.0.2",
"@codemirror/lang-sql": "^6.10.0",
"@codemirror/lang-xml": "^6.1.0",
"codemirror": "^6.0.2",
"vue": "^3.5.26" "vue": "^3.5.26"
}, },
"devDependencies": { "devDependencies": {
@@ -70,198 +61,6 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@codemirror/autocomplete": {
"version": "6.20.3",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz",
"integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0"
}
},
"node_modules/@codemirror/commands": {
"version": "6.10.4",
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.4.tgz",
"integrity": "sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.7.0",
"@codemirror/view": "^6.27.0",
"@lezer/common": "^1.1.0"
}
},
"node_modules/@codemirror/lang-css": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.0.2",
"@lezer/css": "^1.1.7"
}
},
"node_modules/@codemirror/lang-html": {
"version": "6.4.11",
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz",
"integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/lang-css": "^6.0.0",
"@codemirror/lang-javascript": "^6.0.0",
"@codemirror/language": "^6.4.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0",
"@lezer/css": "^1.1.0",
"@lezer/html": "^1.3.12"
}
},
"node_modules/@codemirror/lang-javascript": {
"version": "6.2.5",
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz",
"integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.6.0",
"@codemirror/lint": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0",
"@lezer/javascript": "^1.0.0"
}
},
"node_modules/@codemirror/lang-json": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz",
"integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@lezer/json": "^1.0.0"
}
},
"node_modules/@codemirror/lang-markdown": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.5.0.tgz",
"integrity": "sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.7.1",
"@codemirror/lang-html": "^6.0.0",
"@codemirror/language": "^6.3.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.2.1",
"@lezer/markdown": "^1.0.0"
}
},
"node_modules/@codemirror/lang-php": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.2.tgz",
"integrity": "sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==",
"license": "MIT",
"dependencies": {
"@codemirror/lang-html": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.0.0",
"@lezer/php": "^1.0.0"
}
},
"node_modules/@codemirror/lang-sql": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.10.0.tgz",
"integrity": "sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"node_modules/@codemirror/lang-xml": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz",
"integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.4.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.0.0",
"@lezer/xml": "^1.0.0"
}
},
"node_modules/@codemirror/language": {
"version": "6.12.4",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz",
"integrity": "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
"@lezer/common": "^1.5.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"style-mod": "^4.0.0"
}
},
"node_modules/@codemirror/lint": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz",
"integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.42.0",
"crelt": "^1.0.5"
}
},
"node_modules/@codemirror/search": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.1.tgz",
"integrity": "sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.37.0",
"crelt": "^1.0.5"
}
},
"node_modules/@codemirror/state": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.1.tgz",
"integrity": "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==",
"license": "MIT",
"dependencies": {
"@marijn/find-cluster-break": "^1.0.0"
}
},
"node_modules/@codemirror/view": {
"version": "6.43.5",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.5.tgz",
"integrity": "sha512-7uT/vUgH6dfXWn3WqOe23KneILMvGy5wQjNMEcRXLKzziJ9NOktpW6tGoyQpwVkBgE5Gj6hKkCcsddbnkaWrOQ==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.7.0",
"crelt": "^1.0.6",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.25.12", "version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
@@ -710,112 +509,6 @@
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@lezer/common": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz",
"integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==",
"license": "MIT"
},
"node_modules/@lezer/css": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.4.tgz",
"integrity": "sha512-N+tn9tej2hPvyKgHEApMOQfHczDJCwxrRFS3SPn9QjYN+uwHvEDnCgKRrb3mxDYxRS8sKMM8fhC3+lc04Abz5Q==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.3.0"
}
},
"node_modules/@lezer/highlight": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz",
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.3.0"
}
},
"node_modules/@lezer/html": {
"version": "1.3.13",
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz",
"integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"node_modules/@lezer/javascript": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz",
"integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.1.3",
"@lezer/lr": "^1.3.0"
}
},
"node_modules/@lezer/json": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz",
"integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"node_modules/@lezer/lr": {
"version": "1.4.10",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz",
"integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
"node_modules/@lezer/markdown": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.4.tgz",
"integrity": "sha512-N0SxazMj4k65DBfaf1azqtMZd6u7MqluP84/NZnB/io8Td9aleFmAhz9hcbvSfsxT5tdYlJ5qgv5aMJGY4zEtA==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.5.0",
"@lezer/highlight": "^1.0.0"
}
},
"node_modules/@lezer/php": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.5.tgz",
"integrity": "sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.1.0"
}
},
"node_modules/@lezer/xml": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz",
"integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"node_modules/@marijn/find-cluster-break": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.3.tgz",
"integrity": "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==",
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.62.2", "version": "4.62.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",
@@ -1326,27 +1019,6 @@
"integrity": "sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==", "integrity": "sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/codemirror": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/commands": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/lint": "^6.0.0",
"@codemirror/search": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0"
}
},
"node_modules/crelt": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz",
"integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==",
"license": "MIT"
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@@ -1574,12 +1246,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/style-mod": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",
"license": "MIT"
},
"node_modules/tinyglobby": { "node_modules/tinyglobby": {
"version": "0.2.17", "version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
@@ -1692,12 +1358,6 @@
"optional": true "optional": true
} }
} }
},
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
"license": "MIT"
} }
} }
} }
-9
View File
@@ -8,15 +8,6 @@
"build": "vite build" "build": "vite build"
}, },
"dependencies": { "dependencies": {
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.11",
"@codemirror/lang-javascript": "^6.2.5",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-markdown": "^6.5.0",
"@codemirror/lang-php": "^6.0.2",
"@codemirror/lang-sql": "^6.10.0",
"@codemirror/lang-xml": "^6.1.0",
"codemirror": "^6.0.2",
"vue": "^3.5.26" "vue": "^3.5.26"
}, },
"devDependencies": { "devDependencies": {
+10
View File
@@ -0,0 +1,10 @@
import { createApp } from 'vue';
import '@/shell/shell-frame.css';
import SetupWizard from '@/pages/setup-wizard/SetupWizard.vue';
const mountEl = document.getElementById('aurora-admin-setup-root');
if (mountEl) {
const data = JSON.parse(mountEl.dataset.auroraAdmin || '{}');
createApp(SetupWizard, { serverData: data }).mount(mountEl);
}
+2 -2
View File
@@ -36,7 +36,7 @@ defineEmits(['click']);
border: none; border: none;
background: none; background: none;
cursor: pointer; cursor: pointer;
border-radius: 12px; border-radius: var(--aurora-radius-md);
text-align: left; text-align: left;
} }
.app-list-item:hover { .app-list-item:hover {
@@ -49,7 +49,7 @@ defineEmits(['click']);
width: 32px; width: 32px;
height: 32px; height: 32px;
flex-shrink: 0; flex-shrink: 0;
border-radius: 9px; border-radius: var(--aurora-radius-md);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
display: flex; display: flex;
+5 -5
View File
@@ -94,7 +94,7 @@ const search = defineModel('search', { type: String, default: '' });
flex-shrink: 0; flex-shrink: 0;
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
border-radius: 16px; border-radius: var(--aurora-radius-lg);
padding: 24px 20px; padding: 24px 20px;
box-sizing: border-box; box-sizing: border-box;
} }
@@ -106,7 +106,7 @@ const search = defineModel('search', { type: String, default: '' });
.app-list-shell__actions { display: flex; gap: 6px; align-items: center; } .app-list-shell__actions { display: flex; gap: 6px; align-items: center; }
.app-list-shell__actions :deep(button) { .app-list-shell__actions :deep(button) {
display: inline-flex; align-items: center; justify-content: center; display: inline-flex; align-items: center; justify-content: center;
height: 30px; padding: 0 10px; border: none; border-radius: 7px; height: 30px; padding: 0 10px; border: none; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); color: var(--aurora-text-muted); background: var(--aurora-bg-subtle); color: var(--aurora-text-muted);
font-size: 0.75rem; font-weight: 500; cursor: pointer; gap: 6px; font-size: 0.75rem; font-weight: 500; cursor: pointer; gap: 6px;
} }
@@ -119,7 +119,7 @@ const search = defineModel('search', { type: String, default: '' });
display: flex; align-items: center; gap: 8px; display: flex; align-items: center; gap: 8px;
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 10px; padding: 8px 12px; margin-bottom: 14px; border-radius: var(--aurora-radius-md); padding: 8px 12px; margin-bottom: 14px;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
} }
.app-list-shell__search input { .app-list-shell__search input {
@@ -161,7 +161,7 @@ const search = defineModel('search', { type: String, default: '' });
flex: 1; min-width: 0; flex: 1; min-width: 0;
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
border-radius: 16px; border-radius: var(--aurora-radius-lg);
padding: 28px 32px; padding: 28px 32px;
min-height: 480px; min-height: 480px;
box-sizing: border-box; box-sizing: border-box;
@@ -174,7 +174,7 @@ const search = defineModel('search', { type: String, default: '' });
text-align: center; gap: 6px; text-align: center; gap: 6px;
} }
.app-list-shell__empty-icon { .app-list-shell__empty-icon {
width: 56px; height: 56px; border-radius: 14px; width: 56px; height: 56px; border-radius: var(--aurora-radius-md);
background: var(--aurora-bg-subtle); color: var(--aurora-text-muted); background: var(--aurora-bg-subtle); color: var(--aurora-text-muted);
display: flex; align-items: center; justify-content: center; display: flex; align-items: center; justify-content: center;
font-size: 22px; margin-bottom: 10px; font-size: 22px; margin-bottom: 10px;
+2 -2
View File
@@ -139,7 +139,7 @@ const formatDate = (s) => (s ? new Date(s.replace(' ', 'T')).toLocaleString() :
<style scoped> <style scoped>
.activity-log__select { .activity-log__select {
padding: 5px 10px; border-radius: 8px; border: 1px solid var(--aurora-border); padding: 5px 10px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.75rem; background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.75rem;
} }
.activity-detail h2 { margin: 0 0 16px; font-size: 1.1rem; color: var(--aurora-text); } .activity-detail h2 { margin: 0 0 16px; font-size: 1.1rem; color: var(--aurora-text); }
@@ -148,6 +148,6 @@ const formatDate = (s) => (s ? new Date(s.replace(' ', 'T')).toLocaleString() :
.activity-detail dd { margin: 0; color: var(--aurora-text); } .activity-detail dd { margin: 0; color: var(--aurora-text); }
.activity-detail pre { .activity-detail pre {
margin-top: 18px; padding: 12px; background: var(--aurora-bg-subtle); margin-top: 18px; padding: 12px; background: var(--aurora-bg-subtle);
border-radius: 8px; font-size: 0.75rem; overflow-x: auto; color: var(--aurora-text); border-radius: var(--aurora-radius-sm); font-size: 0.75rem; overflow-x: auto; color: var(--aurora-text);
} }
</style> </style>
+3 -3
View File
@@ -190,11 +190,11 @@ const remove = async () => {
.notice-editor__row { display: flex; gap: 10px; } .notice-editor__row { display: flex; gap: 10px; }
.notice-editor__title { .notice-editor__title {
flex: 1; font-size: 1.05rem; font-weight: 700; padding: 8px 10px; flex: 1; font-size: 1.05rem; font-weight: 700; padding: 8px 10px;
border: 1px solid var(--aurora-border); border-radius: 8px; border: 1px solid var(--aurora-border); border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); color: var(--aurora-text); background: var(--aurora-bg-subtle); color: var(--aurora-text);
} }
.notice-editor select, .notice-editor input[type='text'], .notice-editor textarea { .notice-editor select, .notice-editor input[type='text'], .notice-editor textarea {
padding: 8px 10px; border-radius: 8px; border: 1px solid var(--aurora-border); padding: 8px 10px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.8125rem; background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.8125rem;
font-family: inherit; width: 100%; box-sizing: border-box; font-family: inherit; width: 100%; box-sizing: border-box;
} }
@@ -208,7 +208,7 @@ const remove = async () => {
.notice-editor__toggles label { display: flex; align-items: center; gap: 6px; } .notice-editor__toggles label { display: flex; align-items: center; gap: 6px; }
.notice-editor__actions { display: flex; justify-content: flex-end; gap: 8px; } .notice-editor__actions { display: flex; justify-content: flex-end; gap: 8px; }
.notice-editor__actions button { .notice-editor__actions button {
border: none; border-radius: 8px; padding: 9px 18px; font-size: 0.875rem; cursor: pointer; border: none; border-radius: var(--aurora-radius-sm); padding: 9px 18px; font-size: 0.875rem; cursor: pointer;
background: var(--aurora-bg-subtle); color: var(--aurora-text); background: var(--aurora-bg-subtle); color: var(--aurora-text);
} }
.notice-editor__actions .is-primary { background: var(--aurora-accent); color: #fff; } .notice-editor__actions .is-primary { background: var(--aurora-accent); color: #fff; }
+3 -3
View File
@@ -98,7 +98,7 @@ const send = async () => {
.bug-report-page h1 { margin: 0; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); } .bug-report-page h1 { margin: 0; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); }
.bug-report-page__sub { margin: 8px 0 0; font-size: 0.875rem; color: var(--aurora-text-muted); } .bug-report-page__sub { margin: 8px 0 0; font-size: 0.875rem; color: var(--aurora-text-muted); }
.bug-report-notice { padding: 10px 14px; border-radius: 8px; margin-bottom: 16px; font-size: 0.875rem; } .bug-report-notice { padding: 10px 14px; border-radius: var(--aurora-radius-sm); margin-bottom: 16px; font-size: 0.875rem; }
.bug-report-notice--success { background: rgba(34, 197, 94, 0.12); color: #22c55e; } .bug-report-notice--success { background: rgba(34, 197, 94, 0.12); color: #22c55e; }
.bug-report-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; } .bug-report-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; }
@@ -106,14 +106,14 @@ const send = async () => {
.bug-report-field { display: flex; flex-direction: column; gap: 6px; } .bug-report-field { display: flex; flex-direction: column; gap: 6px; }
.bug-report-field span { font-size: 0.8125rem; font-weight: 600; color: var(--aurora-text); } .bug-report-field span { font-size: 0.8125rem; font-weight: 600; color: var(--aurora-text); }
.bug-report-field textarea { .bug-report-field textarea {
padding: 10px 12px; border-radius: 8px; border: 1px solid var(--aurora-border); padding: 10px 12px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.875rem; background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.875rem;
font-family: inherit; resize: vertical; font-family: inherit; resize: vertical;
} }
.bug-report-diagnostics { .bug-report-diagnostics {
background: var(--aurora-bg-subtle); border: 1px solid var(--aurora-border); background: var(--aurora-bg-subtle); border: 1px solid var(--aurora-border);
border-radius: 10px; padding: 14px 16px; border-radius: var(--aurora-radius-md); padding: 14px 16px;
} }
.bug-report-diagnostics h2 { .bug-report-diagnostics h2 {
margin: 0 0 10px; font-size: 0.75rem; text-transform: uppercase; margin: 0 0 10px; font-size: 0.75rem; text-transform: uppercase;
+5 -5
View File
@@ -398,7 +398,7 @@ const rowRange = computed(() => {
width: min(320px, 100%); width: min(320px, 100%);
min-height: 38px; min-height: 38px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: flex; display: flex;
align-items: center; align-items: center;
@@ -423,7 +423,7 @@ const rowRange = computed(() => {
width: 42px; width: 42px;
min-height: 42px; min-height: 42px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
color: var(--aurora-text); color: var(--aurora-text);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: inline-flex; display: inline-flex;
@@ -451,7 +451,7 @@ const rowRange = computed(() => {
.comments-notice { .comments-notice {
margin: 0 0 12px; margin: 0 0 12px;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text); color: var(--aurora-text);
font-size: 0.78rem; font-size: 0.78rem;
@@ -460,7 +460,7 @@ const rowRange = computed(() => {
.comments-table { .comments-table {
overflow: hidden; overflow: hidden;
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
} }
.comments-table__head, .comments-table__head,
@@ -636,7 +636,7 @@ const rowRange = computed(() => {
width: 28px; width: 28px;
height: 28px; height: 28px;
border: 0; border: 0;
border-radius: 6px; border-radius: var(--aurora-radius-sm);
background: transparent; background: transparent;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
cursor: pointer; cursor: pointer;
+5 -5
View File
@@ -176,7 +176,7 @@ onMounted(() => reload(range.value[0], range.value[1]));
background: var(--aurora-card-bg); background: var(--aurora-card-bg);
border: 1px solid var(--aurora-card-border); border: 1px solid var(--aurora-card-border);
color: var(--aurora-text); color: var(--aurora-text);
border-radius: 10px; border-radius: var(--aurora-radius-md);
padding: 8px 12px; padding: 8px 12px;
font-size: 0.8125rem; font-size: 0.8125rem;
cursor: pointer; cursor: pointer;
@@ -186,7 +186,7 @@ onMounted(() => reload(range.value[0], range.value[1]));
position: absolute; right: 0; top: calc(100% + 6px); position: absolute; right: 0; top: calc(100% + 6px);
background: var(--aurora-card-bg); background: var(--aurora-card-bg);
border: 1px solid var(--aurora-card-border); border: 1px solid var(--aurora-card-border);
border-radius: 10px; border-radius: var(--aurora-radius-md);
padding: 6px; padding: 6px;
z-index: 10; z-index: 10;
min-width: 160px; min-width: 160px;
@@ -195,14 +195,14 @@ onMounted(() => reload(range.value[0], range.value[1]));
.dash__presets button { .dash__presets button {
display: block; width: 100%; text-align: left; display: block; width: 100%; text-align: left;
background: none; border: none; color: var(--aurora-text); background: none; border: none; color: var(--aurora-text);
padding: 8px 10px; border-radius: 6px; cursor: pointer; font-size: 0.8125rem; padding: 8px 10px; border-radius: var(--aurora-radius-sm); cursor: pointer; font-size: 0.8125rem;
} }
.dash__presets button:hover { background: var(--aurora-bg-subtle); } .dash__presets button:hover { background: var(--aurora-bg-subtle); }
.dash__tabs { .dash__tabs {
display: inline-flex; display: inline-flex;
gap: 2px; gap: 2px;
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border-radius: 10px; border-radius: var(--aurora-radius-md);
padding: 3px; padding: 3px;
margin-bottom: 20px; margin-bottom: 20px;
} }
@@ -214,7 +214,7 @@ onMounted(() => reload(range.value[0], range.value[1]));
} }
.dash__tab { .dash__tab {
border: none; background: none; cursor: pointer; border: none; background: none; cursor: pointer;
padding: 6px 16px; border-radius: 8px; padding: 6px 16px; border-radius: var(--aurora-radius-sm);
font-size: 0.8125rem; font-weight: 500; font-size: 0.8125rem; font-weight: 500;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
} }
@@ -52,7 +52,7 @@ defineProps({ data: { type: Object, default: () => ({}) } });
.ma__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } .ma__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.ma__box { .ma__box {
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border-radius: 14px; border-radius: var(--aurora-radius-md);
padding: 14px; padding: 14px;
text-align: center; text-align: center;
} }
@@ -102,7 +102,7 @@ const onDayClick = (cell) => {
border: 1px solid var(--aurora-card-border); border: 1px solid var(--aurora-card-border);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
cursor: pointer; cursor: pointer;
line-height: 1; line-height: 1;
} }
@@ -120,7 +120,7 @@ const onDayClick = (cell) => {
position: relative; position: relative;
width: 100%; height: 100%; width: 100%; height: 100%;
display: flex; align-items: center; justify-content: center; display: flex; align-items: center; justify-content: center;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
border: 1px solid var(--aurora-card-border); border: 1px solid var(--aurora-card-border);
background: transparent; background: transparent;
padding: 0; padding: 0;
@@ -156,7 +156,7 @@ const onDayClick = (cell) => {
background: var(--aurora-card-bg, #1f1f23); background: var(--aurora-card-bg, #1f1f23);
color: var(--aurora-text, #f4f4f5); color: var(--aurora-text, #f4f4f5);
border: 1px solid var(--aurora-border, #2e2e33); border: 1px solid var(--aurora-border, #2e2e33);
border-radius: 10px; border-radius: var(--aurora-radius-md);
padding: 12px 18px; padding: 12px 18px;
font-size: 0.875rem; font-size: 0.875rem;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
@@ -58,7 +58,7 @@ defineProps({ data: { type: Object, default: () => ({}) } });
.sh__versions { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 14px; } .sh__versions { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 14px; }
.sh__ver { .sh__ver {
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border-radius: 12px; border-radius: var(--aurora-radius-md);
padding: 12px 14px; padding: 12px 14px;
} }
.sh__ver-label { font-size: 0.6875rem; color: var(--aurora-text-muted); } .sh__ver-label { font-size: 0.6875rem; color: var(--aurora-text-muted); }
@@ -31,12 +31,12 @@ defineProps({
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
/* Deeper than the generic --aurora-card-bg — the Dashboard is now /* 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 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. */ for most presets, so cards would nearly disappear into it otherwise. */
background: var(--aurora-card-bg-deep, var(--aurora-card-bg)); background: var(--aurora-card-bg-deep, var(--aurora-card-bg));
border: 1px solid var(--aurora-card-border); border: 1px solid var(--aurora-card-border);
border-radius: 20px; border-radius: var(--aurora-radius-lg);
padding: 20px; padding: 20px;
box-sizing: border-box; box-sizing: border-box;
} }
@@ -30,7 +30,7 @@ defineProps({
<style scoped> <style scoped>
.pbar { .pbar {
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border-radius: 12px; border-radius: var(--aurora-radius-md);
padding: 12px 14px; padding: 12px 14px;
} }
.pbar__top { .pbar__top {
@@ -24,7 +24,7 @@ defineProps({
} }
.statrow--boxed { .statrow--boxed {
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border-radius: 14px; border-radius: var(--aurora-radius-md);
padding: 16px 8px; padding: 16px 8px;
} }
.statrow__value { .statrow__value {
+2 -2
View File
@@ -122,7 +122,7 @@ const clearDone = () => {
display: flex; flex-direction: column; align-items: center; justify-content: center; display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 10px; padding: 48px 24px; text-align: center; cursor: pointer; gap: 10px; padding: 48px 24px; text-align: center; cursor: pointer;
border: 2px dashed var(--aurora-border); border: 2px dashed var(--aurora-border);
border-radius: 14px; border-radius: var(--aurora-radius-md);
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
transition: border-color 0.15s, color 0.15s; transition: border-color 0.15s, color 0.15s;
} }
@@ -135,7 +135,7 @@ const clearDone = () => {
.media-upload-item { .media-upload-item {
display: flex; align-items: center; justify-content: space-between; display: flex; align-items: center; justify-content: space-between;
padding: 10px 14px; border-radius: 8px; padding: 10px 14px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
margin-bottom: 6px; font-size: 0.875rem; margin-bottom: 6px; font-size: 0.875rem;
+11 -11
View File
@@ -479,7 +479,7 @@ const typeIcon = (mimeType) => {
align-items: center; align-items: center;
min-height: 38px; min-height: 38px;
padding: 0 16px; padding: 0 16px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-accent); background: var(--aurora-accent);
color: var(--aurora-accent-text); color: var(--aurora-accent-text);
font-size: 0.84rem; font-size: 0.84rem;
@@ -529,7 +529,7 @@ const typeIcon = (mimeType) => {
width: min(320px, 100%); width: min(320px, 100%);
min-height: 38px; min-height: 38px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: flex; display: flex;
align-items: center; align-items: center;
@@ -554,7 +554,7 @@ const typeIcon = (mimeType) => {
width: 42px; width: 42px;
min-height: 42px; min-height: 42px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
color: var(--aurora-text); color: var(--aurora-text);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: inline-flex; display: inline-flex;
@@ -581,7 +581,7 @@ const typeIcon = (mimeType) => {
.media-notice { .media-notice {
margin: 0 0 12px; margin: 0 0 12px;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text); color: var(--aurora-text);
font-size: 0.78rem; font-size: 0.78rem;
@@ -590,7 +590,7 @@ const typeIcon = (mimeType) => {
.media-grid__empty { .media-grid__empty {
padding: 60px 18px; padding: 60px 18px;
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
font-size: 0.85rem; font-size: 0.85rem;
@@ -603,7 +603,7 @@ const typeIcon = (mimeType) => {
} }
.media-card { .media-card {
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
border-radius: 10px; border-radius: var(--aurora-radius-md);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
overflow: hidden; overflow: hidden;
} }
@@ -688,7 +688,7 @@ const typeIcon = (mimeType) => {
width: 28px; width: 28px;
height: 28px; height: 28px;
border: 0; border: 0;
border-radius: 6px; border-radius: var(--aurora-radius-sm);
background: transparent; background: transparent;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
cursor: pointer; cursor: pointer;
@@ -733,7 +733,7 @@ const typeIcon = (mimeType) => {
width: 220px; width: 220px;
padding: 12px; padding: 12px;
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
border-radius: 10px; border-radius: var(--aurora-radius-md);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
} }
.media-page__main { .media-page__main {
@@ -753,7 +753,7 @@ const typeIcon = (mimeType) => {
align-items: center; align-items: center;
gap: 8px; gap: 8px;
border: none; border: none;
border-radius: 6px; border-radius: var(--aurora-radius-sm);
background: none; background: none;
padding: 6px 6px; padding: 6px 6px;
color: var(--aurora-text); color: var(--aurora-text);
@@ -815,7 +815,7 @@ const typeIcon = (mimeType) => {
min-width: 150px; min-width: 150px;
overflow-y: auto; overflow-y: auto;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
padding: 4px; padding: 4px;
@@ -826,7 +826,7 @@ const typeIcon = (mimeType) => {
padding: 6px 10px; padding: 6px 10px;
border: none; border: none;
background: none; background: none;
border-radius: 6px; border-radius: var(--aurora-radius-sm);
color: var(--aurora-text); color: var(--aurora-text);
font-size: 0.78rem; font-size: 0.78rem;
text-align: left; text-align: left;
+3 -3
View File
@@ -129,7 +129,7 @@ const runFromMenu = (fn) => {
.mf-tree { list-style: none; margin: 0; padding-left: 14px; } .mf-tree { list-style: none; margin: 0; padding-left: 14px; }
.mf-tree:first-of-type { padding-left: 0; } .mf-tree:first-of-type { padding-left: 0; }
.mf-tree__row { .mf-tree__row {
display: flex; align-items: center; gap: 2px; border-radius: 6px; display: flex; align-items: center; gap: 2px; border-radius: var(--aurora-radius-sm);
} }
.mf-tree__row:hover { background: var(--aurora-bg-subtle); } .mf-tree__row:hover { background: var(--aurora-bg-subtle); }
.mf-tree__row--active { background: var(--aurora-accent-soft); } .mf-tree__row--active { background: var(--aurora-accent-soft); }
@@ -163,7 +163,7 @@ const runFromMenu = (fn) => {
padding: 4px; padding: 4px;
list-style: none; list-style: none;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
} }
@@ -175,7 +175,7 @@ const runFromMenu = (fn) => {
padding: 7px 10px; padding: 7px 10px;
border: none; border: none;
background: none; background: none;
border-radius: 6px; border-radius: var(--aurora-radius-sm);
color: var(--aurora-text); color: var(--aurora-text);
font-size: 0.8rem; font-size: 0.8rem;
text-align: left; text-align: left;
+4 -4
View File
@@ -225,11 +225,11 @@ const toggleRole = (slug) => {
.menu-editor__row { display: flex; gap: 10px; } .menu-editor__row { display: flex; gap: 10px; }
.menu-editor__name { .menu-editor__name {
flex: 1; font-size: 1.1rem; font-weight: 700; padding: 8px 10px; flex: 1; font-size: 1.1rem; font-weight: 700; padding: 8px 10px;
border: 1px solid var(--aurora-border); border-radius: 8px; border: 1px solid var(--aurora-border); border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); color: var(--aurora-text); background: var(--aurora-bg-subtle); color: var(--aurora-text);
} }
.menu-editor__status { .menu-editor__status {
padding: 8px 10px; border-radius: 8px; border: 1px solid var(--aurora-border); padding: 8px 10px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg-subtle); color: var(--aurora-text); background: var(--aurora-bg-subtle); color: var(--aurora-text);
} }
.menu-editor__section h3 { .menu-editor__section h3 {
@@ -244,13 +244,13 @@ const toggleRole = (slug) => {
border-bottom: 1px solid var(--aurora-frame-border); border-bottom: 1px solid var(--aurora-frame-border);
} }
.menu-editor__item input[type='text'] { .menu-editor__item input[type='text'] {
flex: 1; padding: 6px 8px; border-radius: 6px; border: 1px solid var(--aurora-border); flex: 1; padding: 6px 8px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.8125rem; background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.8125rem;
} }
.menu-editor__hide { display: flex; align-items: center; gap: 4px; font-size: 0.75rem; color: var(--aurora-text-muted); } .menu-editor__hide { display: flex; align-items: center; gap: 4px; font-size: 0.75rem; color: var(--aurora-text-muted); }
.menu-editor__actions { display: flex; justify-content: flex-end; gap: 8px; } .menu-editor__actions { display: flex; justify-content: flex-end; gap: 8px; }
.menu-editor__actions button { .menu-editor__actions button {
border: none; border-radius: 8px; padding: 9px 18px; font-size: 0.875rem; cursor: pointer; border: none; border-radius: var(--aurora-radius-sm); padding: 9px 18px; font-size: 0.875rem; cursor: pointer;
background: var(--aurora-bg-subtle); color: var(--aurora-text); background: var(--aurora-bg-subtle); color: var(--aurora-text);
} }
.menu-editor__actions .is-primary { background: var(--aurora-accent); color: #fff; } .menu-editor__actions .is-primary { background: var(--aurora-accent); color: #fff; }
@@ -173,7 +173,7 @@ const save = async () => {
.native-settings__saved { font-size: 0.8125rem; color: var(--aurora-text-muted); } .native-settings__saved { font-size: 0.8125rem; color: var(--aurora-text-muted); }
.native-settings__save { .native-settings__save {
background: var(--aurora-accent); color: #fff; border: none; background: var(--aurora-accent); color: #fff; border: none;
border-radius: 9px; padding: 9px 20px; font-size: 0.875rem; font-weight: 500; cursor: pointer; border-radius: var(--aurora-radius-md); padding: 9px 20px; font-size: 0.875rem; font-weight: 500; cursor: pointer;
} }
.native-settings__save:disabled { opacity: 0.6; cursor: default; } .native-settings__save:disabled { opacity: 0.6; cursor: default; }
.native-settings__fields > :last-child { border-bottom: none; } .native-settings__fields > :last-child { border-bottom: none; }
@@ -210,6 +210,6 @@ const save = async () => {
font-size: 0.8125rem; font-size: 0.8125rem;
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
padding: 2px 8px; padding: 2px 8px;
border-radius: 5px; border-radius: var(--aurora-radius-sm);
} }
</style> </style>
+9 -9
View File
@@ -361,21 +361,21 @@ const assignLocation = async (slug, menuId) => {
} }
.nav-menus-page h1 { margin: 0; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); } .nav-menus-page h1 { margin: 0; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); }
.nav-menus-notice { padding: 10px 14px; border-radius: 8px; margin-bottom: 16px; font-size: 0.875rem; } .nav-menus-notice { padding: 10px 14px; border-radius: var(--aurora-radius-sm); margin-bottom: 16px; font-size: 0.875rem; }
.nav-menus-notice--success { background: rgba(34, 197, 94, 0.12); color: #22c55e; } .nav-menus-notice--success { background: rgba(34, 197, 94, 0.12); color: #22c55e; }
.nav-menus-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; } .nav-menus-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; }
.nav-menus-layout { display: grid; grid-template-columns: 260px 300px 1fr; gap: 20px; align-items: start; } .nav-menus-layout { display: grid; grid-template-columns: 260px 300px 1fr; gap: 20px; align-items: start; }
.nav-menus-side, .nav-menus-add, .nav-menus-structure { .nav-menus-side, .nav-menus-add, .nav-menus-structure {
background: var(--aurora-bg-subtle); border: 1px solid var(--aurora-border); background: var(--aurora-bg-subtle); border: 1px solid var(--aurora-border);
border-radius: 14px; padding: 18px; border-radius: var(--aurora-radius-md); padding: 18px;
} }
.nav-menus-side h2, .nav-menus-add h2, .nav-menus-structure h2 { .nav-menus-side h2, .nav-menus-add h2, .nav-menus-structure h2 {
font-size: 0.9375rem; margin: 0 0 10px; color: var(--aurora-text); font-size: 0.9375rem; margin: 0 0 10px; color: var(--aurora-text);
} }
.nav-menus-list { list-style: none; margin: 0 0 14px; padding: 0; } .nav-menus-list { list-style: none; margin: 0 0 14px; padding: 0; }
.nav-menus-list__item { .nav-menus-list__item {
width: 100%; text-align: left; padding: 8px 10px; border-radius: 8px; width: 100%; text-align: left; padding: 8px 10px; border-radius: var(--aurora-radius-sm);
border: none; background: none; color: var(--aurora-text-muted); cursor: pointer; border: none; background: none; color: var(--aurora-text-muted); cursor: pointer;
font-size: 0.875rem; font-size: 0.875rem;
} }
@@ -385,7 +385,7 @@ const assignLocation = async (slug, menuId) => {
.nav-menus-create { display: flex; gap: 6px; margin-bottom: 20px; } .nav-menus-create { display: flex; gap: 6px; margin-bottom: 20px; }
.nav-menus-create input { .nav-menus-create input {
flex: 1; padding: 7px 10px; border-radius: 7px; border: 1px solid var(--aurora-border); flex: 1; padding: 7px 10px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg); color: var(--aurora-text); font-size: 0.8125rem; background: var(--aurora-bg); color: var(--aurora-text); font-size: 0.8125rem;
} }
@@ -393,20 +393,20 @@ const assignLocation = async (slug, menuId) => {
.nav-menus-location label { font-size: 0.8125rem; color: var(--aurora-text-muted); } .nav-menus-location label { font-size: 0.8125rem; color: var(--aurora-text-muted); }
.nav-menus-location select, .nav-menus-location select,
.nav-menus-add__custom input { .nav-menus-add__custom input {
padding: 7px 10px; border-radius: 7px; border: 1px solid var(--aurora-border); padding: 7px 10px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg); color: var(--aurora-text); font-size: 0.8125rem; background: var(--aurora-bg); color: var(--aurora-text); font-size: 0.8125rem;
} }
.nav-menus-add__tabs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; } .nav-menus-add__tabs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.nav-menus-add__tabs button { .nav-menus-add__tabs button {
padding: 6px 10px; border-radius: 7px; border: 1px solid var(--aurora-border); padding: 6px 10px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg); color: var(--aurora-text-muted); font-size: 0.75rem; cursor: pointer; background: var(--aurora-bg); color: var(--aurora-text-muted); font-size: 0.75rem; cursor: pointer;
} }
.nav-menus-add__tabs button.is-active { background: var(--aurora-accent-soft); color: var(--aurora-accent); border-color: transparent; } .nav-menus-add__tabs button.is-active { background: var(--aurora-accent-soft); color: var(--aurora-accent); border-color: transparent; }
.nav-menus-add__list { max-height: 320px; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; } .nav-menus-add__list { max-height: 320px; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
.nav-menus-add__row { .nav-menus-add__row {
display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--aurora-radius-sm);
font-size: 0.8125rem; color: var(--aurora-text); font-size: 0.8125rem; color: var(--aurora-text);
} }
.nav-menus-add__row:hover { background: var(--aurora-bg); } .nav-menus-add__row:hover { background: var(--aurora-bg); }
@@ -417,14 +417,14 @@ const assignLocation = async (slug, menuId) => {
.nav-menus-structure__head { display: flex; align-items: center; justify-content: space-between; } .nav-menus-structure__head { display: flex; align-items: center; justify-content: space-between; }
.nav-menus-item { .nav-menus-item {
display: flex; align-items: center; justify-content: space-between; display: flex; align-items: center; justify-content: space-between;
padding: 8px 12px; border-radius: 8px; margin-bottom: 4px; padding: 8px 12px; border-radius: var(--aurora-radius-sm); margin-bottom: 4px;
background: var(--aurora-bg); border: 1px solid var(--aurora-border); background: var(--aurora-bg); border: 1px solid var(--aurora-border);
font-size: 0.8125rem; color: var(--aurora-text); font-size: 0.8125rem; color: var(--aurora-text);
} }
.nav-menus-item__actions { display: flex; gap: 2px; } .nav-menus-item__actions { display: flex; gap: 2px; }
.nav-menus-item__actions button { .nav-menus-item__actions button {
border: none; background: none; color: var(--aurora-text-muted); cursor: pointer; border: none; background: none; color: var(--aurora-text-muted); cursor: pointer;
width: 26px; height: 26px; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: var(--aurora-radius-sm); display: inline-flex; align-items: center; justify-content: center;
} }
.nav-menus-item__actions button:hover { background: var(--aurora-bg-subtle); color: var(--aurora-text); } .nav-menus-item__actions button:hover { background: var(--aurora-bg-subtle); color: var(--aurora-text); }
.nav-menus-item__actions button:disabled { opacity: 0.35; cursor: default; } .nav-menus-item__actions button:disabled { opacity: 0.35; cursor: default; }
+6 -6
View File
@@ -340,7 +340,7 @@ const rowRange = computed(() => {
align-items: center; align-items: center;
min-height: 38px; min-height: 38px;
padding: 0 16px; padding: 0 16px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-accent); background: var(--aurora-accent);
color: var(--aurora-accent-text); color: var(--aurora-accent-text);
font-size: 0.84rem; font-size: 0.84rem;
@@ -390,7 +390,7 @@ const rowRange = computed(() => {
width: min(320px, 100%); width: min(320px, 100%);
min-height: 38px; min-height: 38px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: flex; display: flex;
align-items: center; align-items: center;
@@ -415,7 +415,7 @@ const rowRange = computed(() => {
width: 42px; width: 42px;
min-height: 42px; min-height: 42px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
color: var(--aurora-text); color: var(--aurora-text);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: inline-flex; display: inline-flex;
@@ -442,7 +442,7 @@ const rowRange = computed(() => {
.pages-notice { .pages-notice {
margin: 0 0 12px; margin: 0 0 12px;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text); color: var(--aurora-text);
font-size: 0.78rem; font-size: 0.78rem;
@@ -451,7 +451,7 @@ const rowRange = computed(() => {
.pages-table { .pages-table {
overflow: hidden; overflow: hidden;
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
} }
.pages-table__head, .pages-table__head,
@@ -559,7 +559,7 @@ const rowRange = computed(() => {
width: 28px; width: 28px;
height: 28px; height: 28px;
border: 0; border: 0;
border-radius: 6px; border-radius: var(--aurora-radius-sm);
background: transparent; background: transparent;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
cursor: pointer; cursor: pointer;
+5 -5
View File
@@ -364,7 +364,7 @@ const versionLine = (plugin) => {
width: min(320px, 100%); width: min(320px, 100%);
min-height: 38px; min-height: 38px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: flex; display: flex;
align-items: center; align-items: center;
@@ -388,7 +388,7 @@ const versionLine = (plugin) => {
.primary-button, .primary-button,
.icon-button { .icon-button {
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
min-height: 42px; min-height: 42px;
color: var(--aurora-text); color: var(--aurora-text);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
@@ -439,7 +439,7 @@ const versionLine = (plugin) => {
.plugins-notice { .plugins-notice {
margin: 0 0 12px; margin: 0 0 12px;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
font-size: 0.78rem; font-size: 0.78rem;
font-weight: 750; font-weight: 750;
} }
@@ -454,7 +454,7 @@ const versionLine = (plugin) => {
.plugins-table { .plugins-table {
overflow: hidden; overflow: hidden;
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
box-shadow: none; box-shadow: none;
} }
@@ -505,7 +505,7 @@ const versionLine = (plugin) => {
.plugin-row__mark { .plugin-row__mark {
width: 46px; width: 46px;
height: 46px; height: 46px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
+6 -6
View File
@@ -342,7 +342,7 @@ const rowRange = computed(() => {
align-items: center; align-items: center;
min-height: 38px; min-height: 38px;
padding: 0 16px; padding: 0 16px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-accent); background: var(--aurora-accent);
color: var(--aurora-accent-text); color: var(--aurora-accent-text);
font-size: 0.84rem; font-size: 0.84rem;
@@ -392,7 +392,7 @@ const rowRange = computed(() => {
width: min(320px, 100%); width: min(320px, 100%);
min-height: 38px; min-height: 38px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: flex; display: flex;
align-items: center; align-items: center;
@@ -417,7 +417,7 @@ const rowRange = computed(() => {
width: 42px; width: 42px;
min-height: 42px; min-height: 42px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
color: var(--aurora-text); color: var(--aurora-text);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: inline-flex; display: inline-flex;
@@ -444,7 +444,7 @@ const rowRange = computed(() => {
.posts-notice { .posts-notice {
margin: 0 0 12px; margin: 0 0 12px;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text); color: var(--aurora-text);
font-size: 0.78rem; font-size: 0.78rem;
@@ -453,7 +453,7 @@ const rowRange = computed(() => {
.posts-table { .posts-table {
overflow: hidden; overflow: hidden;
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
} }
.posts-table__head, .posts-table__head,
@@ -561,7 +561,7 @@ const rowRange = computed(() => {
width: 28px; width: 28px;
height: 28px; height: 28px;
border: 0; border: 0;
border-radius: 6px; border-radius: var(--aurora-radius-sm);
background: transparent; background: transparent;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
cursor: pointer; cursor: pointer;
+4 -4
View File
@@ -172,7 +172,7 @@ const changePassword = async () => {
.profile-page h1 { margin: 0; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); } .profile-page h1 { margin: 0; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); }
.profile-empty { color: var(--aurora-text-muted); font-size: 0.8125rem; } .profile-empty { color: var(--aurora-text-muted); font-size: 0.8125rem; }
.profile-notice { padding: 10px 14px; border-radius: 8px; margin-bottom: 16px; font-size: 0.875rem; } .profile-notice { padding: 10px 14px; border-radius: var(--aurora-radius-sm); margin-bottom: 16px; font-size: 0.875rem; }
.profile-notice--success { background: rgba(34, 197, 94, 0.12); color: #22c55e; } .profile-notice--success { background: rgba(34, 197, 94, 0.12); color: #22c55e; }
.profile-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; } .profile-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; }
.profile-notice--info { background: var(--aurora-bg-subtle); color: var(--aurora-text-muted); } .profile-notice--info { background: var(--aurora-bg-subtle); color: var(--aurora-text-muted); }
@@ -182,7 +182,7 @@ const changePassword = async () => {
display: flex; flex-direction: column; gap: 18px; display: flex; flex-direction: column; gap: 18px;
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 14px; border-radius: var(--aurora-radius-md);
padding: 24px; padding: 24px;
margin-bottom: 20px; margin-bottom: 20px;
} }
@@ -190,7 +190,7 @@ const changePassword = async () => {
.profile-field span { font-size: 0.8125rem; font-weight: 600; color: var(--aurora-text); } .profile-field span { font-size: 0.8125rem; font-weight: 600; color: var(--aurora-text); }
.profile-field input, .profile-field input,
.profile-field textarea { .profile-field textarea {
padding: 9px 12px; border-radius: 8px; border: 1px solid var(--aurora-border); padding: 9px 12px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg); color: var(--aurora-text); font-size: 0.875rem; background: var(--aurora-bg); color: var(--aurora-text); font-size: 0.875rem;
font-family: inherit; resize: vertical; font-family: inherit; resize: vertical;
} }
@@ -199,7 +199,7 @@ const changePassword = async () => {
.profile-password h2 { margin: 0 0 4px; font-size: 0.9375rem; color: var(--aurora-text); } .profile-password h2 { margin: 0 0 4px; font-size: 0.9375rem; color: var(--aurora-text); }
.profile-password__row { display: flex; gap: 8px; } .profile-password__row { display: flex; gap: 8px; }
.profile-password__row input { .profile-password__row input {
flex: 1; padding: 9px 12px; border-radius: 8px; border: 1px solid var(--aurora-border); flex: 1; padding: 9px 12px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg); color: var(--aurora-text); font-size: 0.875rem; background: var(--aurora-bg); color: var(--aurora-text); font-size: 0.875rem;
} }
</style> </style>
+3 -3
View File
@@ -182,7 +182,7 @@ const remove = async () => {
.role-editor { display: flex; flex-direction: column; gap: 18px; } .role-editor { display: flex; flex-direction: column; gap: 18px; }
.role-editor__row { display: flex; gap: 10px; } .role-editor__row { display: flex; gap: 10px; }
.role-editor__slug, .role-editor__name { .role-editor__slug, .role-editor__name {
padding: 8px 10px; border-radius: 8px; border: 1px solid var(--aurora-border); padding: 8px 10px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.875rem; background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.875rem;
} }
.role-editor__name { flex: 1; font-weight: 700; } .role-editor__name { flex: 1; font-weight: 700; }
@@ -225,7 +225,7 @@ const remove = async () => {
z-index: 20; z-index: 20;
width: 260px; width: 260px;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
color: var(--aurora-text); color: var(--aurora-text);
@@ -246,7 +246,7 @@ const remove = async () => {
} }
.role-editor__actions { display: flex; justify-content: flex-end; gap: 8px; } .role-editor__actions { display: flex; justify-content: flex-end; gap: 8px; }
.role-editor__actions button { .role-editor__actions button {
border: none; border-radius: 8px; padding: 9px 18px; font-size: 0.875rem; cursor: pointer; border: none; border-radius: var(--aurora-radius-sm); padding: 9px 18px; font-size: 0.875rem; cursor: pointer;
background: var(--aurora-bg-subtle); color: var(--aurora-text); background: var(--aurora-bg-subtle); color: var(--aurora-text);
} }
.role-editor__actions .is-primary { background: var(--aurora-accent); color: #fff; } .role-editor__actions .is-primary { background: var(--aurora-accent); color: #fff; }
+102 -30
View File
@@ -1,6 +1,6 @@
<script setup> <script setup>
import { ref, reactive, computed, watch, nextTick } from 'vue'; import { ref, reactive, computed, watch, nextTick } from 'vue';
import { settingsCategories } from './settingsConfig.js'; import { settingsGroups, settingsCategories } from './settingsConfig.js';
import SettingsField from './components/SettingsField.vue'; import SettingsField from './components/SettingsField.vue';
import { applyThemePalette, applyFontFamily } from './applyLiveTheme.js'; import { applyThemePalette, applyFontFamily } from './applyLiveTheme.js';
@@ -45,6 +45,29 @@ const activeCategory = computed(
() => visibleCategories.find((c) => c.id === activeId.value) || visibleCategories[0] () => visibleCategories.find((c) => c.id === activeId.value) || visibleCategories[0]
); );
const groupOfCategory = (catId) =>
settingsGroups.find((g) => g.categories.some((c) => c.id === catId));
// Collapsible nav groups. Default open; collapse state persisted per group.
const OPEN_KEY = 'aurora-settings-groups-open';
const loadOpen = () => {
try {
return JSON.parse(localStorage.getItem(OPEN_KEY)) || {};
} catch {
return {};
}
};
const openState = reactive(loadOpen());
// Keep the active category's group open so its item is always reachable.
const activeGroup = groupOfCategory(activeId.value);
if (activeGroup) openState[activeGroup.id] = true;
const isGroupOpen = (id) => openState[id] !== false;
const toggleGroup = (id) => {
openState[id] = !isGroupOpen(id);
localStorage.setItem(OPEN_KEY, JSON.stringify(openState));
};
// The content panel scrolls independently of the nav (see .settings__content // The content panel scrolls independently of the nav (see .settings__content
// below) so picking a category from a scrolled-down spot in the nav doesn't // below) so picking a category from a scrolled-down spot in the nav doesn't
// require scrolling the whole page back up to see it. But the panel is one // require scrolling the whole page back up to see it. But the panel is one
@@ -56,6 +79,8 @@ const activeCategory = computed(
const contentEl = ref(null); const contentEl = ref(null);
const selectCategory = (id) => { const selectCategory = (id) => {
activeId.value = id; activeId.value = id;
const g = groupOfCategory(id);
if (g) openState[g.id] = true;
const url = new URL(window.location.href); const url = new URL(window.location.href);
url.searchParams.set('category', id); url.searchParams.set('category', id);
window.history.replaceState({}, '', url); window.history.replaceState({}, '', url);
@@ -64,15 +89,25 @@ const selectCategory = (id) => {
}); });
}; };
// Search filter over the nav. // Search filter over the nav — matches on group title, category title, or
// subtitle; groups with no surviving categories drop out entirely.
const query = ref(''); const query = ref('');
const filteredCategories = computed(() => { const searching = computed(() => query.value.trim().length > 0);
const filteredGroups = computed(() => {
const q = query.value.trim().toLowerCase(); const q = query.value.trim().toLowerCase();
if (!q) return visibleCategories; if (!q) return settingsGroups;
return visibleCategories.filter( return settingsGroups
.map((g) => {
const groupMatch = g.title.toLowerCase().includes(q);
const categories = g.categories.filter(
(c) => (c) =>
c.title.toLowerCase().includes(q) || c.subtitle.toLowerCase().includes(q) groupMatch ||
c.title.toLowerCase().includes(q) ||
c.subtitle.toLowerCase().includes(q)
); );
return { ...g, categories };
})
.filter((g) => g.categories.length > 0);
}); });
// Options for multiselect fields, resolved from server-provided lists. // Options for multiselect fields, resolved from server-provided lists.
@@ -172,22 +207,39 @@ const importSettings = (e) => {
<input v-model="query" type="text" placeholder="Search settings…" /> <input v-model="query" type="text" placeholder="Search settings…" />
</div> </div>
<ul class="settings__list"> <ul class="settings__groups">
<li v-for="cat in filteredCategories" :key="cat.id"> <li v-for="group in filteredGroups" :key="group.id" class="settings__group">
<button
type="button"
class="settings__group-head"
:aria-expanded="isGroupOpen(group.id) || searching"
@click="toggleGroup(group.id)"
>
<span class="settings__group-icon dashicons" :class="group.icon" />
<span class="settings__group-title">{{ group.title }}</span>
<span
class="settings__group-caret dashicons dashicons-arrow-down-alt2"
:class="{ 'is-collapsed': !isGroupOpen(group.id) && !searching }"
/>
</button>
<ul v-show="isGroupOpen(group.id) || searching" class="settings__list">
<li v-for="cat in group.categories" :key="cat.id">
<button <button
type="button" type="button"
class="settings__cat" class="settings__cat"
:class="{ 'settings__cat--active': cat.id === activeId }" :class="{ 'settings__cat--active': cat.id === activeId }"
@click="selectCategory(cat.id)" @click="selectCategory(cat.id)"
> >
<span class="settings__cat-icon"><span class="dashicons" :class="cat.icon" /></span> <span class="settings__cat-icon dashicons" :class="cat.icon" />
<span class="settings__cat-text">
<span class="settings__cat-title">{{ cat.title }}</span> <span class="settings__cat-title">{{ cat.title }}</span>
<span class="settings__cat-sub">{{ cat.subtitle }}</span>
</span>
</button> </button>
</li> </li>
</ul> </ul>
</li>
<li v-if="!filteredGroups.length" class="settings__empty">
No settings match “{{ query }}”.
</li>
</ul>
</aside> </aside>
<!-- Right content panel --> <!-- Right content panel -->
@@ -250,7 +302,7 @@ const importSettings = (e) => {
flex-shrink: 0; flex-shrink: 0;
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
border-radius: 16px; border-radius: var(--aurora-radius-lg);
padding: 24px 20px; padding: 24px 20px;
box-sizing: border-box; box-sizing: border-box;
} }
@@ -262,7 +314,7 @@ const importSettings = (e) => {
.settings__actions { display: flex; gap: 4px; } .settings__actions { display: flex; gap: 4px; }
.settings__actions button { .settings__actions button {
width: 30px; height: 30px; border: none; background: none; width: 30px; height: 30px; border: none; background: none;
color: var(--aurora-text-muted); cursor: pointer; border-radius: 7px; color: var(--aurora-text-muted); cursor: pointer; border-radius: var(--aurora-radius-sm);
display: inline-flex; align-items: center; justify-content: center; display: inline-flex; align-items: center; justify-content: center;
} }
.settings__actions button:hover { background: var(--aurora-bg-subtle); color: var(--aurora-text); } .settings__actions button:hover { background: var(--aurora-bg-subtle); color: var(--aurora-text); }
@@ -272,7 +324,7 @@ const importSettings = (e) => {
display: flex; align-items: center; gap: 8px; display: flex; align-items: center; gap: 8px;
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 10px; padding: 8px 12px; margin-bottom: 16px; border-radius: var(--aurora-radius-md); padding: 8px 12px; margin-bottom: 16px;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
} }
.settings__search input { .settings__search input {
@@ -280,27 +332,47 @@ const importSettings = (e) => {
font-size: 0.8125rem; width: 100%; font-size: 0.8125rem; width: 100%;
} }
.settings__list { list-style: none; margin: 0; padding: 0; } .settings__groups { list-style: none; margin: 0; padding: 0; }
.settings__group { margin-bottom: 4px; }
.settings__group-head {
display: flex; align-items: center; gap: 8px; width: 100%;
padding: 6px 8px; border: none; background: none; cursor: pointer;
border-radius: var(--aurora-radius-sm); color: var(--aurora-text-muted);
}
.settings__group-head:hover { color: var(--aurora-text); }
.settings__group-icon { font-size: 15px; width: 16px; height: 16px; }
.settings__group-title {
font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.04em;
text-transform: uppercase;
}
.settings__group-caret {
margin-left: auto; font-size: 16px; width: 16px; height: 16px;
transition: transform 0.15s ease;
}
.settings__group-caret.is-collapsed { transform: rotate(-90deg); }
/* Indent the category items so they read as a submenu nested under their
group header, rather than sitting flush at the same level. */
.settings__list { list-style: none; margin: 2px 0 8px; padding: 0 0 0 14px; }
.settings__cat { .settings__cat {
display: flex; align-items: center; gap: 12px; width: 100%; display: flex; align-items: center; gap: 10px; width: 100%;
padding: 10px; border: none; background: none; cursor: pointer; padding: 8px 10px; border: none; background: none; cursor: pointer;
border-radius: 12px; text-align: left; margin-bottom: 2px; border-radius: var(--aurora-radius-md); text-align: left; margin-bottom: 1px;
color: var(--aurora-text);
} }
.settings__cat:hover { background: var(--aurora-bg-subtle); } .settings__cat:hover { background: var(--aurora-bg-subtle); }
.settings__cat--active { background: var(--aurora-accent-soft); } .settings__cat--active { background: var(--aurora-accent-soft); }
.settings__cat-icon { .settings__cat-icon {
width: 34px; height: 34px; flex-shrink: 0; font-size: 16px; width: 18px; height: 18px; flex-shrink: 0;
border-radius: 9px; background: var(--aurora-bg-subtle);
display: flex; align-items: center; justify-content: center;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
} }
.settings__cat--active .settings__cat-icon { color: var(--aurora-accent); } .settings__cat--active .settings__cat-icon { color: var(--aurora-accent); }
.settings__cat-text { display: flex; flex-direction: column; min-width: 0; } .settings__cat-title { font-size: 0.8125rem; font-weight: 500; }
.settings__cat-title { font-size: 0.875rem; font-weight: 600; color: var(--aurora-text); } .settings__cat--active .settings__cat-title { color: var(--aurora-accent); font-weight: 600; }
.settings__cat--active .settings__cat-title { color: var(--aurora-accent); }
.settings__cat-sub { .settings__empty {
font-size: 0.75rem; color: var(--aurora-text-muted); padding: 12px 10px; font-size: 0.8125rem; color: var(--aurora-text-muted);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
} }
/* Right content — no background/border of its own, same reasoning. /* Right content — no background/border of its own, same reasoning.
@@ -313,7 +385,7 @@ const importSettings = (e) => {
flex: 1; min-width: 0; flex: 1; min-width: 0;
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
border-radius: 16px; border-radius: var(--aurora-radius-lg);
padding: 28px 32px; padding: 28px 32px;
box-sizing: border-box; box-sizing: border-box;
} }
@@ -330,7 +402,7 @@ const importSettings = (e) => {
.settings__saved { font-size: 0.8125rem; color: var(--aurora-text-muted); } .settings__saved { font-size: 0.8125rem; color: var(--aurora-text-muted); }
.settings__save { .settings__save {
background: var(--aurora-accent); color: #fff; border: none; background: var(--aurora-accent); color: #fff; border: none;
border-radius: 9px; padding: 9px 20px; font-size: 0.875rem; font-weight: 500; cursor: pointer; border-radius: var(--aurora-radius-md); padding: 9px 20px; font-size: 0.875rem; font-weight: 500; cursor: pointer;
} }
.settings__save:disabled { opacity: 0.6; cursor: default; } .settings__save:disabled { opacity: 0.6; cursor: default; }
@@ -138,6 +138,7 @@ const installCompanion = async () => {
<textarea <textarea
v-else-if="field.type === 'textarea'" v-else-if="field.type === 'textarea'"
class="field__input field__textarea" class="field__input field__textarea"
:class="{ 'field__textarea--mono': field.mono }"
:placeholder="field.placeholder || ''" :placeholder="field.placeholder || ''"
:rows="field.rows || 6" :rows="field.rows || 6"
:value="val || ''" :value="val || ''"
@@ -340,24 +341,29 @@ const installCompanion = async () => {
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
color: var(--aurora-text); color: var(--aurora-text);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
padding: 9px 12px; padding: 9px 12px;
font-size: 0.875rem; font-size: 0.875rem;
} }
.field__input:focus { outline: none; border-color: var(--aurora-accent); } .field__input:focus { outline: none; border-color: var(--aurora-accent); }
.field__textarea { resize: vertical; font-family: inherit; line-height: 1.5; } .field__textarea { resize: vertical; font-family: inherit; line-height: 1.5; }
.field__textarea--mono {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
font-size: 0.8125rem;
tab-size: 2;
}
.field__color { display: flex; align-items: center; gap: 10px; } .field__color { display: flex; align-items: center; gap: 10px; }
.field__color input[type='color'] { .field__color input[type='color'] {
width: 40px; height: 32px; border: 1px solid var(--aurora-border); width: 40px; height: 32px; border: 1px solid var(--aurora-border);
border-radius: 8px; background: none; cursor: pointer; padding: 2px; border-radius: var(--aurora-radius-sm); background: none; cursor: pointer; padding: 2px;
} }
.field__color-hex { font-size: 0.8125rem; color: var(--aurora-text-muted); } .field__color-hex { font-size: 0.8125rem; color: var(--aurora-text-muted); }
.field__image { .field__image {
width: 160px; height: 120px; width: 160px; height: 120px;
border: 1px dashed var(--aurora-border); border: 1px dashed var(--aurora-border);
border-radius: 12px; border-radius: var(--aurora-radius-md);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: flex; flex-direction: column; align-items: center; justify-content: center; display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 8px; cursor: pointer; color: var(--aurora-text-muted); gap: 8px; cursor: pointer; color: var(--aurora-text-muted);
@@ -376,13 +382,13 @@ const installCompanion = async () => {
.field__repeater-remove { .field__repeater-remove {
flex-shrink: 0; width: 34px; border: 1px solid var(--aurora-border); flex-shrink: 0; width: 34px; border: 1px solid var(--aurora-border);
background: var(--aurora-bg-subtle); color: var(--aurora-text-muted); background: var(--aurora-bg-subtle); color: var(--aurora-text-muted);
border-radius: 8px; cursor: pointer; font-size: 1.1rem; border-radius: var(--aurora-radius-sm); cursor: pointer; font-size: 1.1rem;
} }
.field__newitem { .field__newitem {
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text); color: var(--aurora-text);
border-radius: 8px; padding: 8px 16px; font-size: 0.8125rem; cursor: pointer; border-radius: var(--aurora-radius-sm); padding: 8px 16px; font-size: 0.8125rem; cursor: pointer;
} }
.field__newitem:hover { border-color: var(--aurora-accent); color: var(--aurora-accent); } .field__newitem:hover { border-color: var(--aurora-accent); color: var(--aurora-accent); }
@@ -400,13 +406,13 @@ const installCompanion = async () => {
display: inline-flex; display: inline-flex;
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 9px; border-radius: var(--aurora-radius-md);
padding: 3px; padding: 3px;
gap: 2px; gap: 2px;
} }
.field__seg button { .field__seg button {
border: none; background: none; cursor: pointer; border: none; background: none; cursor: pointer;
padding: 6px 26px; border-radius: 7px; padding: 6px 26px; border-radius: var(--aurora-radius-sm);
font-size: 0.8125rem; color: var(--aurora-text-muted); font-size: 0.8125rem; color: var(--aurora-text-muted);
} }
.field__seg--active { background: var(--aurora-accent); color: #fff !important; } .field__seg--active { background: var(--aurora-accent); color: #fff !important; }
@@ -417,13 +423,13 @@ const installCompanion = async () => {
display: flex; flex-direction: column; align-items: center; gap: 6px; display: flex; flex-direction: column; align-items: center; gap: 6px;
width: 76px; padding: 10px 6px; width: 76px; padding: 10px 6px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 10px; border-radius: var(--aurora-radius-md);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
cursor: pointer; cursor: pointer;
} }
.field__theme-card--active { border-color: var(--aurora-accent); box-shadow: 0 0 0 1px var(--aurora-accent); } .field__theme-card--active { border-color: var(--aurora-accent); box-shadow: 0 0 0 1px var(--aurora-accent); }
.field__theme-swatch { .field__theme-swatch {
width: 36px; height: 36px; border-radius: 10px; width: 36px; height: 36px; border-radius: var(--aurora-radius-md);
border: 1px solid var(--aurora-card-border); border: 1px solid var(--aurora-card-border);
display: flex; align-items: flex-end; justify-content: flex-end; display: flex; align-items: flex-end; justify-content: flex-end;
padding: 4px; padding: 4px;
@@ -441,14 +447,14 @@ const installCompanion = async () => {
max-height: 260px; overflow-y: auto; z-index: 20; max-height: 260px; overflow-y: auto; z-index: 20;
background: var(--aurora-card-bg); background: var(--aurora-card-bg);
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 10px; border-radius: var(--aurora-radius-md);
padding: 6px; padding: 6px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
} }
.field__font-item { .field__font-item {
display: block; width: 100%; text-align: left; display: block; width: 100%; text-align: left;
border: none; background: none; cursor: pointer; border: none; background: none; cursor: pointer;
padding: 8px 10px; border-radius: 7px; padding: 8px 10px; border-radius: var(--aurora-radius-sm);
font-size: 0.875rem; color: var(--aurora-text); font-size: 0.875rem; color: var(--aurora-text);
} }
.field__font-item:hover { background: var(--aurora-bg-subtle); } .field__font-item:hover { background: var(--aurora-bg-subtle); }
@@ -457,7 +463,7 @@ const installCompanion = async () => {
/* Companion plugin install card */ /* Companion plugin install card */
.field__companion-status { font-size: 0.8125rem; color: var(--aurora-text-muted); } .field__companion-status { font-size: 0.8125rem; color: var(--aurora-text-muted); }
.field__companion-install { .field__companion-install {
border: none; border-radius: 8px; background: var(--aurora-accent); color: #fff; border: none; border-radius: var(--aurora-radius-sm); background: var(--aurora-accent); color: #fff;
padding: 8px 16px; font-size: 0.8125rem; font-weight: 600; cursor: pointer; padding: 8px 16px; font-size: 0.8125rem; font-weight: 600; cursor: pointer;
} }
.field__companion-install:disabled { opacity: 0.6; cursor: default; } .field__companion-install:disabled { opacity: 0.6; cursor: default; }
+303 -115
View File
@@ -1,10 +1,18 @@
/** /**
* Declarative settings schema for the Aurora Admin settings page. * Declarative settings schema for the Aurora Admin settings page.
* Each category renders as an item in the left nav; its fields render in *
* the right panel. Field `key`s map directly into the aurora_admin_settings * Categories are organized into top-level groups. Each group renders as a
* option, saved via the wp/v2/settings REST route. * collapsible section in the left nav; each category within it renders as a
* nav item, and its fields render in the right panel. Field `key`s map
* directly into the aurora_admin_settings option, saved via the
* wp/v2/settings REST route.
*/ */
export const settingsCategories = [ export const settingsGroups = [
{
id: 'general',
title: 'General',
icon: 'dashicons-admin-settings',
categories: [
{ {
id: 'general', id: 'general',
title: 'General', title: 'General',
@@ -47,25 +55,33 @@ export const settingsCategories = [
], ],
}, },
{ {
id: 'login', id: 'analytics',
title: 'Login', title: 'Analytics',
subtitle: 'Customize login page appearance and security', subtitle: 'Built-in analytics and tracking settings',
icon: 'dashicons-lock', icon: 'dashicons-chart-bar',
fields: [ fields: [
{ {
key: 'style_login', key: 'enable_analytics',
type: 'toggle', type: 'toggle',
label: 'Style login page', label: 'Enable analytics',
description: 'Apply the Aurora theme to the WordPress login screen.', description: 'Track first-party visitor analytics for the dashboard.',
default: true,
}, },
{ {
key: 'login_logo', key: 'track_admins',
type: 'image', type: 'toggle',
label: 'Login logo', label: 'Track logged-in admins',
description: 'Replace the WordPress logo on the login screen.', description: 'Include administrator visits in analytics (off by default).',
}, },
], ],
}, },
],
},
{
id: 'appearance',
title: 'Appearance',
icon: 'dashicons-admin-appearance',
categories: [
{ {
id: 'theme', id: 'theme',
title: 'Theme', title: 'Theme',
@@ -104,7 +120,7 @@ export const settingsCategories = [
type: 'text', type: 'text',
label: 'Rename Aurora Admin', label: 'Rename Aurora Admin',
description: description:
"This will rename Aurora Admin in the plugins list as well as renaming its settings link.", 'This will rename Aurora Admin in the plugins list as well as renaming its settings link.',
placeholder: 'Aurora Admin', placeholder: 'Aurora Admin',
}, },
{ {
@@ -117,6 +133,68 @@ export const settingsCategories = [
}, },
], ],
}, },
{
id: 'login',
title: 'Login',
subtitle: 'Customize login page appearance and security',
icon: 'dashicons-lock',
fields: [
{
key: 'style_login',
type: 'toggle',
label: 'Style login page',
description: 'Apply the Aurora theme to the WordPress login screen.',
},
{
key: 'login_logo',
type: 'image',
label: 'Login logo',
description: 'Replace the WordPress logo on the login screen.',
dependsOn: 'style_login',
},
{
key: 'login_bg_image',
type: 'image',
label: 'Background image',
description: 'Full-screen background for the login page. Overrides the background color below.',
dependsOn: 'style_login',
},
{
key: 'login_bg_color',
type: 'color',
label: 'Background color',
description: 'Solid background color for the login page (used when no background image is set).',
default: '#0f1420',
dependsOn: 'style_login',
},
{
key: 'login_form_bg',
type: 'color',
label: 'Form background',
description: 'Background color of the login form panel.',
default: '#1c2333',
dependsOn: 'style_login',
},
{
key: 'login_button_color',
type: 'color',
label: 'Button color',
description: 'Color of the primary “Log In” button and the field focus accent.',
default: '#4f7cff',
dependsOn: 'style_login',
},
{
key: 'login_custom_css',
type: 'textarea',
mono: true,
rows: 8,
label: 'Custom login CSS',
description: 'Extra CSS applied to the login screen, after Aurora’s own styles.',
placeholder: 'body.login { … }',
dependsOn: 'style_login',
},
],
},
{ {
id: 'menu', id: 'menu',
title: 'Menu', title: 'Menu',
@@ -131,51 +209,13 @@ export const settingsCategories = [
}, },
], ],
}, },
{
id: 'posts',
title: 'Posts',
subtitle: 'Post management and display preferences',
icon: 'dashicons-admin-post',
fields: [
{
key: 'modern_posts',
type: 'toggle',
label: 'Modern posts table',
description: 'Use the Aurora posts management interface.',
default: true,
},
], ],
}, },
{ {
id: 'pages', id: 'screens',
title: 'Pages', title: 'Screens',
subtitle: 'Page management and display preferences', icon: 'dashicons-desktop',
icon: 'dashicons-admin-page', categories: [
fields: [
{
key: 'modern_pages',
type: 'toggle',
label: 'Modern pages table',
description: 'Use the Aurora pages management interface.',
default: true,
},
],
},
{
id: 'plugins',
title: 'Plugins',
subtitle: 'Plugin management interface options',
icon: 'dashicons-admin-plugins',
fields: [
{
key: 'modern_plugins',
type: 'toggle',
label: 'Modern plugins page',
description: 'Use the Aurora plugins management interface.',
default: true,
},
],
},
{ {
id: 'dashboard', id: 'dashboard',
title: 'Dashboard', title: 'Dashboard',
@@ -192,77 +232,78 @@ export const settingsCategories = [
], ],
}, },
{ {
id: 'analytics', id: 'content-tables',
title: 'Analytics', title: 'Content tables',
subtitle: 'Built-in analytics and tracking settings', subtitle: 'Choose which admin list screens use the Aurora interface',
icon: 'dashicons-chart-bar', icon: 'dashicons-editor-table',
fields: [ fields: [
{ {
key: 'enable_analytics', key: 'modern_posts',
type: 'toggle', type: 'toggle',
label: 'Enable analytics', label: 'Posts',
description: 'Track first-party visitor analytics for the dashboard.', description: 'Use the Aurora posts management interface.',
default: true, default: true,
}, },
{ {
key: 'track_admins', key: 'modern_pages',
type: 'toggle', type: 'toggle',
label: 'Track logged-in admins', label: 'Pages',
description: 'Include administrator visits in analytics (off by default).', description: 'Use the Aurora pages management interface.',
default: true,
}, },
],
},
{
id: 'media',
title: 'Media',
subtitle: 'Media library and upload management settings',
icon: 'dashicons-admin-media',
fields: [
{ {
key: 'modern_media', key: 'modern_media',
type: 'toggle', type: 'toggle',
label: 'Modern media library', label: 'Media library',
description: 'Use the Aurora media library interface.', description: 'Use the Aurora media library interface.',
default: true, default: true,
}, },
{
key: 'enable_svg',
type: 'toggle',
label: 'Allow SVG uploads',
description: 'Permit SVG files to be uploaded to the media library.',
},
],
},
{
id: 'users',
title: 'Users',
subtitle: 'User management and administration settings',
icon: 'dashicons-admin-users',
fields: [
{ {
key: 'modern_users', key: 'modern_users',
type: 'toggle', type: 'toggle',
label: 'Modern users page', label: 'Users',
description: 'Use the Aurora users management interface.', description: 'Use the Aurora users management interface.',
default: true, default: true,
}, },
],
},
{
id: 'comments',
title: 'Comments',
subtitle: 'Comment management and administration',
icon: 'dashicons-admin-comments',
fields: [
{ {
key: 'modern_comments', key: 'modern_comments',
type: 'toggle', type: 'toggle',
label: 'Modern comments page', label: 'Comments',
description: 'Use the Aurora comments management interface.', description: 'Use the Aurora comments management interface.',
default: true, default: true,
}, },
{
key: 'modern_plugins',
type: 'toggle',
label: 'Plugins',
description: 'Use the Aurora plugins management interface.',
default: true,
},
], ],
}, },
{
id: 'admin-takeover',
title: 'Admin Takeover',
subtitle: 'Replace the remaining native WordPress admin screens with Aurora',
icon: 'dashicons-shield',
fields: [
{
key: 'full_admin_takeover',
type: 'toggle',
label: 'Full admin takeover',
description:
'Replace Media Upload, Add User, Profile, Appearance, Tools, and native Settings screens with Aurora equivalents. Off by default — these cover more ground than Posts/Pages/Media/Users/Comments, so it’s opt-in rather than on by default.',
default: false,
},
],
},
],
},
{
id: 'productivity',
title: 'Productivity',
icon: 'dashicons-superhero-alt',
categories: [
{ {
id: 'image-formats', id: 'image-formats',
title: 'Modern Image Formats', title: 'Modern Image Formats',
@@ -302,6 +343,161 @@ export const settingsCategories = [
}, },
], ],
}, },
{
id: 'media',
title: 'Media',
subtitle: 'Media library and upload options',
icon: 'dashicons-admin-media',
fields: [
{
key: 'enable_svg',
type: 'toggle',
label: 'Allow SVG uploads',
description: 'Permit SVG files to be uploaded to the media library.',
},
],
},
],
},
{
id: 'security',
title: 'Security & performance',
icon: 'dashicons-shield',
categories: [
{
id: 'security-hardening',
title: 'Hardening',
subtitle: 'Reduce your site’s attack surface and trim unnecessary output',
icon: 'dashicons-shield-alt',
fields: [
{
key: 'disable_xmlrpc',
type: 'toggle',
label: 'Disable XML-RPC',
description:
'Turn off the XML-RPC endpoint, a common brute-force and pingback-DDoS vector. Leave on only if you rely on the WordPress mobile app or remote publishing.',
},
{
key: 'disable_comments',
type: 'toggle',
label: 'Disable comments',
description:
'Turn comments off site-wide — closes them on every post type, hides existing ones, and removes the Comments admin screen and dashboard widget.',
},
{
key: 'remove_head_junk',
type: 'toggle',
label: 'Clean up <head>',
description:
'Remove the WordPress generator/version tag, RSD link, WLW manifest, and shortlink meta from your site’s <head>.',
},
],
},
{
id: 'performance-tweaks',
title: 'Performance',
subtitle: 'Trim requests and scripts WordPress loads by default',
icon: 'dashicons-performance',
fields: [
{
key: 'disable_emojis',
type: 'toggle',
label: 'Disable emoji scripts',
description:
'Stop WordPress loading its emoji-detection script and styles on every front-end and admin page.',
},
{
key: 'disable_oembed',
type: 'toggle',
label: 'Disable oEmbeds',
description:
'Remove oEmbed discovery links and the wp-embed script (stops auto-embedding of pasted URLs and the embed REST route).',
},
{
key: 'heartbeat_mode',
type: 'select',
label: 'Heartbeat API',
description:
'The Heartbeat API polls the server on a timer (autosave, post-lock notices). Slow it down or disable it to cut admin-ajax load.',
default: 'default',
options: [
{ value: 'default', label: 'Default (15–60s)' },
{ value: 'slow', label: 'Slow (60s)' },
{ value: 'disabled', label: 'Disabled' },
],
},
],
},
],
},
{
id: 'code',
title: 'Code',
icon: 'dashicons-editor-code',
categories: [
{
id: 'custom-code',
title: 'Custom CSS & JS',
subtitle: 'Inject your own CSS and JavaScript into the WordPress admin',
icon: 'dashicons-admin-customizer',
fields: [
{
key: 'custom_admin_css',
type: 'textarea',
mono: true,
rows: 10,
label: 'Admin CSS',
description:
'CSS added to every wp-admin page, inside a <style> tag in the admin <head>. Enter CSS only — no <style> tags needed.',
placeholder: '.wrap { max-width: 1200px; }',
},
{
key: 'custom_admin_js',
type: 'textarea',
mono: true,
rows: 10,
label: 'Admin JavaScript',
description:
'JavaScript added to the admin footer, inside a <script> tag. Enter JS only — no <script> tags needed.',
placeholder: "console.log('Hello from Aurora');",
},
],
},
{
id: 'header-footer',
title: 'Header & footer scripts',
subtitle: 'Inject raw code into your site’s front-end <head> and footer',
icon: 'dashicons-editor-code',
fields: [
{
key: 'header_scripts',
type: 'textarea',
mono: true,
rows: 8,
label: 'Header scripts',
description:
'Raw HTML/JS printed in the front-end <head> — analytics snippets, verification meta tags, etc. Include the full <script>/<meta> tags.',
placeholder: '<!-- e.g. analytics -->\n<script>…</script>',
},
{
key: 'footer_scripts',
type: 'textarea',
mono: true,
rows: 8,
label: 'Footer scripts',
description:
'Raw HTML/JS printed just before </body> on the front end. Include the full <script> tags.',
placeholder: '<script>…</script>',
},
],
},
],
},
{
id: 'add-ons',
title: 'Add-ons',
icon: 'dashicons-admin-plugins',
categories: [
{ {
id: 'add-ons', id: 'add-ons',
title: 'Add-ons', title: 'Add-ons',
@@ -340,20 +536,12 @@ export const settingsCategories = [
}, },
], ],
}, },
{
id: 'admin-takeover',
title: 'Admin Takeover',
subtitle: 'Replace the remaining native WordPress admin screens with Aurora',
icon: 'dashicons-shield',
fields: [
{
key: 'full_admin_takeover',
type: 'toggle',
label: 'Full admin takeover',
description:
'Replace Media Upload, Add User, Profile, Appearance, Tools, and native Settings screens with Aurora equivalents. Off by default — these cover more ground than Posts/Pages/Media/Users/Comments, so it’s opt-in rather than on by default.',
default: false,
},
], ],
}, },
]; ];
/**
* Flattened category list — every category across all groups, in order.
* Used for default-seeding, active-category lookup, and URL deep-links.
*/
export const settingsCategories = settingsGroups.flatMap((g) => g.categories);
+310
View File
@@ -0,0 +1,310 @@
<script setup>
import { ref, reactive, computed, watch, onMounted } from 'vue';
import SettingsField from '@/pages/settings/components/SettingsField.vue';
import { applyThemePalette, applyFontFamily } from '@/pages/settings/applyLiveTheme.js';
const props = defineProps({
serverData: { type: Object, required: true },
});
// Step definitions. Welcome and Done are custom content; the middle three
// render their `fields` through the shared SettingsField component (same
// controls as the settings page — theme picker, font picker, media-library
// image fields, toggles).
const steps = [
{ id: 'welcome', label: 'Welcome' },
{
id: 'appearance',
label: 'Appearance',
title: 'Choose your look',
subtitle:
'Pick a color theme and font for the whole admin. You can change these any time in Settings.',
fields: [
{
key: 'theme_preset',
type: 'theme-picker',
label: 'Theme',
description: 'The accent and surface colors used across the admin.',
default: 'indigo',
},
{
key: 'font_family',
type: 'font-picker',
label: 'Font family',
description: 'A Google Font used across the Aurora interface.',
},
],
},
{
id: 'branding',
label: 'Branding',
title: 'Add your branding',
subtitle:
'All optional — drop in a logo and favicon, or rename Aurora for a fully white-labeled admin.',
fields: [
{ key: 'logo', type: 'image', label: 'Logo', description: 'Shown at the top of the sidebar menu.' },
{
key: 'dark_logo',
type: 'image',
label: 'Dark mode logo',
description: 'Optional alternative logo; falls back to the standard one.',
},
{
key: 'admin_favicon',
type: 'image',
label: 'Admin favicon',
description: 'Replaces the WordPress favicon in the admin.',
},
{
key: 'plugin_name',
type: 'text',
label: 'Rename Aurora Admin',
description: 'White-labels the plugin name in menus and the plugins list.',
placeholder: 'Aurora Admin',
},
],
},
{
id: 'features',
label: 'Features',
title: 'Choose your features',
subtitle:
'Aurora replaces these WordPress admin screens with faster versions of its own. Turn off any you’d rather keep native.',
fields: [
{ key: 'use_custom_dashboard', type: 'toggle', label: 'Dashboard', description: 'Replace the WordPress dashboard with Aurora’s.', default: true },
{ key: 'modern_posts', type: 'toggle', label: 'Posts', description: 'Aurora posts management screen.', default: true },
{ key: 'modern_pages', type: 'toggle', label: 'Pages', description: 'Aurora pages management screen.', default: true },
{ key: 'modern_media', type: 'toggle', label: 'Media library', description: 'Aurora media library screen.', default: true },
{ key: 'modern_users', type: 'toggle', label: 'Users', description: 'Aurora users management screen.', default: true },
{ key: 'modern_comments', type: 'toggle', label: 'Comments', description: 'Aurora comments management screen.', default: true },
{ key: 'modern_plugins', type: 'toggle', label: 'Plugins', description: 'Aurora plugins management screen.', default: true },
{
key: 'full_admin_takeover',
type: 'toggle',
label: 'Full admin takeover',
description: 'Also replace Appearance, Tools, and the native Settings screens. Off by default.',
default: false,
},
],
},
{ id: 'done', label: 'Done', title: 'You’re all set' },
];
// Working settings, seeded from the server + each field's default.
const settings = reactive({ ...(props.serverData.settings || {}) });
steps.forEach((s) =>
(s.fields || []).forEach((f) => {
if (settings[f.key] === undefined && f.default !== undefined) settings[f.key] = f.default;
})
);
const stepIndex = ref(0);
const current = computed(() => steps[stepIndex.value]);
const isFirst = computed(() => stepIndex.value === 0);
const isDone = computed(() => current.value.id === 'done');
const goNext = () => {
if (stepIndex.value < steps.length - 1) stepIndex.value++;
};
const goBack = () => {
if (stepIndex.value > 0) stepIndex.value--;
};
// Live preview of theme + font as they're picked (nothing is persisted until
// the wizard is finished, but the whole admin previews immediately).
watch(() => settings.theme_preset, (p) => applyThemePalette(p || 'indigo'), { immediate: true });
watch(() => settings.font_family, (f) => applyFontFamily(f), { immediate: true });
onMounted(() => {
// The shell (which normally does this) is disabled on this page.
document.documentElement.classList.add('aurora-dark');
applyThemePalette(settings.theme_preset || 'indigo');
applyFontFamily(settings.font_family);
});
const saving = ref(false);
const finish = async (target) => {
if (saving.value) return;
saving.value = true;
try {
await fetch(`${props.serverData.restUrl}wp/v2/settings`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': props.serverData.restNonce },
body: JSON.stringify({ aurora_admin_settings: { ...settings } }),
});
await fetch(`${props.serverData.restUrl}aurora-admin/v1/setup/complete`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': props.serverData.restNonce },
});
} catch {
/* Even if persistence fails, don't trap the user in the wizard. */
}
window.location.href = target || props.serverData.dashboardUrl || '/wp-admin/';
};
</script>
<template>
<div class="wiz">
<div class="wiz__inner">
<div class="wiz__brand">
<span class="wiz__mark">A</span>
<span class="wiz__brandname">Aurora Admin</span>
</div>
<!-- Progress -->
<ol class="wiz__steps">
<template v-for="(s, i) in steps" :key="s.id">
<li
class="wiz__step"
:class="{ 'is-active': i === stepIndex, 'is-done': i < stepIndex }"
>
<span class="wiz__dot">
<span v-if="i < stepIndex" class="dashicons dashicons-yes" />
<span v-else>{{ i + 1 }}</span>
</span>
<span class="wiz__step-label">{{ s.label }}</span>
</li>
<li v-if="i < steps.length - 1" class="wiz__connector" :class="{ 'is-on': i < stepIndex }" />
</template>
</ol>
<!-- Card -->
<section class="wiz__card">
<!-- Welcome -->
<div v-if="current.id === 'welcome'" class="wiz__welcome">
<h1 class="wiz__h1">Welcome to Aurora Admin<span v-if="serverData.userName">, {{ serverData.userName }}</span></h1>
<p class="wiz__lead">
A modern, fast redesign of the WordPress admin. This quick setup takes about a
minute — choose a look, add your branding, and pick which screens Aurora takes over.
You can skip it and change everything later in Settings.
</p>
</div>
<!-- Done -->
<div v-else-if="current.id === 'done'" class="wiz__welcome">
<span class="wiz__done-icon dashicons dashicons-yes-alt" />
<h1 class="wiz__h1">You’re all set</h1>
<p class="wiz__lead">
Aurora is configured and ready. Head to your dashboard, or fine-tune anything in
Settings.
</p>
<div class="wiz__done-actions">
<button type="button" class="wiz__btn wiz__btn--primary" :disabled="saving" @click="finish(serverData.dashboardUrl)">
{{ saving ? 'Finishing…' : 'Go to dashboard' }}
</button>
<button type="button" class="wiz__btn" :disabled="saving" @click="finish(serverData.settingsUrl)">
Open settings
</button>
</div>
</div>
<!-- Field steps -->
<div v-else>
<h1 class="wiz__h1">{{ current.title }}</h1>
<p v-if="current.subtitle" class="wiz__lead">{{ current.subtitle }}</p>
<div class="wiz__fields">
<SettingsField
v-for="f in current.fields"
:key="f.key"
:field="f"
:api="null"
v-model="settings[f.key]"
/>
</div>
</div>
</section>
<!-- Footer nav (hidden on the Done step, which has its own actions) -->
<div v-if="!isDone" class="wiz__nav">
<button type="button" class="wiz__btn wiz__btn--ghost" :disabled="saving" @click="finish(serverData.dashboardUrl)">
Skip setup
</button>
<div class="wiz__nav-right">
<button v-if="!isFirst" type="button" class="wiz__btn" @click="goBack">Back</button>
<button type="button" class="wiz__btn wiz__btn--primary" @click="goNext">
{{ isFirst ? 'Get started' : 'Continue' }}
</button>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.wiz {
position: fixed;
inset: 0;
z-index: 100000;
overflow-y: auto;
background:
radial-gradient(120% 90% at 50% 0%, color-mix(in srgb, var(--aurora-accent) 12%, var(--aurora-app-bg-solid)) 0%, var(--aurora-app-bg-solid) 65%);
color: var(--aurora-text);
font-family: var(--aurora-font-family);
}
.wiz__inner {
max-width: 760px;
margin: 0 auto;
padding: 48px 24px 64px;
}
.wiz__brand { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 30px; }
.wiz__mark {
width: 30px; height: 30px; border-radius: var(--aurora-radius-md);
background: var(--aurora-accent); color: #fff;
display: flex; align-items: center; justify-content: center; font-weight: 700;
}
.wiz__brandname { font-size: 1rem; font-weight: 500; }
/* Progress */
.wiz__steps { display: flex; align-items: center; justify-content: center; list-style: none; margin: 0 0 30px; padding: 0; }
.wiz__step { display: flex; flex-direction: column; align-items: center; gap: 7px; flex: 0 0 auto; }
.wiz__dot {
width: 28px; height: 28px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 0.75rem; font-weight: 500;
border: 1.5px solid var(--aurora-border); background: var(--aurora-card-bg); color: var(--aurora-text-muted);
}
.wiz__dot .dashicons { font-size: 16px; width: 16px; height: 16px; }
.wiz__step.is-done .wiz__dot { background: var(--aurora-accent); border-color: var(--aurora-accent); color: #fff; }
.wiz__step.is-active .wiz__dot { border-color: var(--aurora-accent); color: var(--aurora-accent); }
.wiz__step-label { font-size: 0.6875rem; color: var(--aurora-text-muted); }
.wiz__step.is-active .wiz__step-label, .wiz__step.is-done .wiz__step-label { color: var(--aurora-text); }
.wiz__connector { flex: 1 1 auto; height: 1.5px; min-width: 22px; background: var(--aurora-border); margin: 0 6px; position: relative; top: -10px; }
.wiz__connector.is-on { background: var(--aurora-accent); }
/* Card */
.wiz__card {
background: var(--aurora-card-bg);
border: 1px solid var(--aurora-border);
border-radius: var(--aurora-radius-xl);
padding: 30px 32px;
}
.wiz__h1 { font-size: 1.5rem; font-weight: 600; margin: 0; }
.wiz__lead { margin: 10px 0 0; font-size: 0.9rem; color: var(--aurora-text-muted); line-height: 1.6; }
.wiz__welcome { text-align: center; padding: 20px 8px; }
.wiz__welcome .wiz__lead { max-width: 520px; margin: 12px auto 0; }
.wiz__done-icon { font-size: 52px; width: 52px; height: 52px; color: var(--aurora-accent); }
.wiz__done-actions { display: flex; gap: 12px; justify-content: center; margin-top: 26px; }
/* Fields: SettingsField renders its own rows; drop the first row's top pad
and the last row's divider so they sit cleanly inside the card. */
.wiz__fields { margin-top: 8px; }
.wiz__fields > :deep(.field):first-child { padding-top: 12px; }
.wiz__fields > :deep(.field):last-child { border-bottom: none; padding-bottom: 4px; }
/* Nav */
.wiz__nav { display: flex; align-items: center; justify-content: space-between; margin-top: 22px; }
.wiz__nav-right { display: flex; gap: 10px; }
.wiz__btn {
font-size: 0.875rem; font-weight: 500;
padding: 10px 20px; border-radius: var(--aurora-radius-md); cursor: pointer;
border: 1px solid var(--aurora-border); background: var(--aurora-card-bg); color: var(--aurora-text);
}
.wiz__btn:hover { border-color: var(--aurora-accent); }
.wiz__btn:disabled { opacity: 0.6; cursor: default; }
.wiz__btn--ghost { border-color: transparent; color: var(--aurora-text-muted); }
.wiz__btn--ghost:hover { color: var(--aurora-text); border-color: transparent; }
.wiz__btn--primary { background: var(--aurora-accent); color: #fff; border-color: var(--aurora-accent); }
.wiz__btn--primary:hover { filter: brightness(1.08); }
</style>
+2 -2
View File
@@ -131,13 +131,13 @@ const remove = async (theme) => {
} }
.themes-page h1 { margin: 0; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); } .themes-page h1 { margin: 0; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); }
.themes-empty { color: var(--aurora-text-muted); font-size: 0.8125rem; } .themes-empty { color: var(--aurora-text-muted); font-size: 0.8125rem; }
.themes-notice { padding: 10px 14px; border-radius: 8px; margin-bottom: 16px; font-size: 0.875rem; } .themes-notice { padding: 10px 14px; border-radius: var(--aurora-radius-sm); margin-bottom: 16px; font-size: 0.875rem; }
.themes-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; } .themes-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; }
.themes-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 18px; } .themes-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 18px; }
.theme-card { .theme-card {
background: var(--aurora-bg-subtle); border: 1px solid var(--aurora-border); background: var(--aurora-bg-subtle); border: 1px solid var(--aurora-border);
border-radius: 14px; overflow: hidden; display: flex; flex-direction: column; border-radius: var(--aurora-radius-md); overflow: hidden; display: flex; flex-direction: column;
} }
.theme-card--active { border-color: var(--aurora-accent); } .theme-card--active { border-color: var(--aurora-accent); }
.theme-card__shot { .theme-card__shot {
+3 -3
View File
@@ -126,7 +126,7 @@ const submit = async () => {
.add-user-page h1 { margin: 0; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); } .add-user-page h1 { margin: 0; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); }
.add-user-notice { .add-user-notice {
padding: 10px 14px; border-radius: 8px; margin-bottom: 16px; font-size: 0.875rem; padding: 10px 14px; border-radius: var(--aurora-radius-sm); margin-bottom: 16px; font-size: 0.875rem;
} }
.add-user-notice--success { background: rgba(34, 197, 94, 0.12); color: #22c55e; } .add-user-notice--success { background: rgba(34, 197, 94, 0.12); color: #22c55e; }
.add-user-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; } .add-user-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; }
@@ -135,7 +135,7 @@ const submit = async () => {
display: flex; flex-direction: column; gap: 18px; display: flex; flex-direction: column; gap: 18px;
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 14px; border-radius: var(--aurora-radius-md);
padding: 24px; padding: 24px;
} }
.add-user-field { display: flex; flex-direction: column; gap: 6px; } .add-user-field { display: flex; flex-direction: column; gap: 6px; }
@@ -143,7 +143,7 @@ const submit = async () => {
.add-user-field em { font-weight: 400; font-style: normal; color: var(--aurora-text-muted); } .add-user-field em { font-weight: 400; font-style: normal; color: var(--aurora-text-muted); }
.add-user-field input, .add-user-field input,
.add-user-field select { .add-user-field select {
padding: 9px 12px; border-radius: 8px; border: 1px solid var(--aurora-border); padding: 9px 12px; border-radius: var(--aurora-radius-sm); border: 1px solid var(--aurora-border);
background: var(--aurora-bg); color: var(--aurora-text); font-size: 0.875rem; background: var(--aurora-bg); color: var(--aurora-text); font-size: 0.875rem;
} }
.add-user-field__row { display: flex; gap: 8px; } .add-user-field__row { display: flex; gap: 8px; }
+6 -6
View File
@@ -281,7 +281,7 @@ const initials = (name) =>
align-items: center; align-items: center;
min-height: 38px; min-height: 38px;
padding: 0 16px; padding: 0 16px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-accent); background: var(--aurora-accent);
color: var(--aurora-accent-text); color: var(--aurora-accent-text);
font-size: 0.84rem; font-size: 0.84rem;
@@ -331,7 +331,7 @@ const initials = (name) =>
width: min(320px, 100%); width: min(320px, 100%);
min-height: 38px; min-height: 38px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: flex; display: flex;
align-items: center; align-items: center;
@@ -356,7 +356,7 @@ const initials = (name) =>
width: 42px; width: 42px;
min-height: 42px; min-height: 42px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
color: var(--aurora-text); color: var(--aurora-text);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
display: inline-flex; display: inline-flex;
@@ -383,7 +383,7 @@ const initials = (name) =>
.users-notice { .users-notice {
margin: 0 0 12px; margin: 0 0 12px;
padding: 10px 12px; padding: 10px 12px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text); color: var(--aurora-text);
font-size: 0.78rem; font-size: 0.78rem;
@@ -392,7 +392,7 @@ const initials = (name) =>
.users-table { .users-table {
overflow: hidden; overflow: hidden;
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
} }
.users-table__head, .users-table__head,
@@ -507,7 +507,7 @@ const initials = (name) =>
width: 28px; width: 28px;
height: 28px; height: 28px;
border: 0; border: 0;
border-radius: 6px; border-radius: var(--aurora-radius-sm);
background: transparent; background: transparent;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
cursor: pointer; cursor: pointer;
+5 -5
View File
@@ -111,20 +111,20 @@ const remove = async (widget) => {
.widgets-page h1 { margin: 0 0 6px; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); } .widgets-page h1 { margin: 0 0 6px; font-size: 1.6rem; font-weight: 700; color: var(--aurora-text); }
.widgets-page__sub { margin: 0; font-size: 0.8125rem; color: var(--aurora-text-muted); max-width: 640px; } .widgets-page__sub { margin: 0; font-size: 0.8125rem; color: var(--aurora-text-muted); max-width: 640px; }
.widgets-notice { padding: 10px 14px; border-radius: 8px; margin-bottom: 16px; font-size: 0.875rem; } .widgets-notice { padding: 10px 14px; border-radius: var(--aurora-radius-sm); margin-bottom: 16px; font-size: 0.875rem; }
.widgets-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; } .widgets-notice--error { background: rgba(239, 68, 68, 0.12); color: #ef4444; }
.widgets-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; } .widgets-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; }
.widgets-sidebar { .widgets-sidebar {
background: var(--aurora-bg-subtle); border: 1px solid var(--aurora-border); background: var(--aurora-bg-subtle); border: 1px solid var(--aurora-border);
border-radius: 14px; padding: 18px; border-radius: var(--aurora-radius-md); padding: 18px;
} }
.widgets-sidebar h2 { font-size: 0.9375rem; margin: 0 0 10px; color: var(--aurora-text); } .widgets-sidebar h2 { font-size: 0.9375rem; margin: 0 0 10px; color: var(--aurora-text); }
.widgets-empty { color: var(--aurora-text-muted); font-size: 0.8125rem; } .widgets-empty { color: var(--aurora-text-muted); font-size: 0.8125rem; }
.widgets-item { .widgets-item {
display: flex; align-items: center; justify-content: space-between; gap: 10px; display: flex; align-items: center; justify-content: space-between; gap: 10px;
padding: 8px 10px; border-radius: 8px; margin-bottom: 6px; padding: 8px 10px; border-radius: var(--aurora-radius-sm); margin-bottom: 6px;
background: var(--aurora-bg); border: 1px solid var(--aurora-border); background: var(--aurora-bg); border: 1px solid var(--aurora-border);
font-size: 0.8125rem; font-size: 0.8125rem;
} }
@@ -134,11 +134,11 @@ const remove = async (widget) => {
.widgets-item__actions { display: flex; align-items: center; gap: 2px; flex-shrink: 0; } .widgets-item__actions { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
.widgets-item__actions button { .widgets-item__actions button {
border: none; background: none; color: var(--aurora-text-muted); cursor: pointer; border: none; background: none; color: var(--aurora-text-muted); cursor: pointer;
width: 26px; height: 26px; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: var(--aurora-radius-sm); display: inline-flex; align-items: center; justify-content: center;
} }
.widgets-item__actions button:hover { background: var(--aurora-bg-subtle); color: var(--aurora-text); } .widgets-item__actions button:hover { background: var(--aurora-bg-subtle); color: var(--aurora-text); }
.widgets-item__actions select { .widgets-item__actions select {
font-size: 0.75rem; padding: 4px 6px; border-radius: 6px; font-size: 0.75rem; padding: 4px 6px; border-radius: var(--aurora-radius-sm);
border: 1px solid var(--aurora-border); background: var(--aurora-bg); color: var(--aurora-text); border: 1px solid var(--aurora-border); background: var(--aurora-bg); color: var(--aurora-text);
} }
</style> </style>
+4 -80
View File
@@ -3,8 +3,7 @@ import { ref, onMounted } from 'vue';
import Sidebar from './components/Sidebar.vue'; import Sidebar from './components/Sidebar.vue';
import Toolbar from './components/Toolbar.vue'; import Toolbar from './components/Toolbar.vue';
import { applyThemePalette, applyFontFamily } from '@/pages/settings/applyLiveTheme.js'; import { applyThemePalette, applyFontFamily } from '@/pages/settings/applyLiveTheme.js';
import { initWindowChrome } from './windowChrome.js'; import { initDashboardFrame } from './dashboardFrame.js';
import { getMinimizedWindows, removeMinimizedWindow } from './taskbar.js';
const props = defineProps({ const props = defineProps({
serverData: { serverData: {
@@ -25,21 +24,7 @@ const toggleMenu = () => {
localStorage.setItem('aurora-menu-collapsed', collapsed.value ? '1' : '0'); 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(() => { 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 || {}; const settings = props.serverData?.settings || {};
// Every theme preset is its own self-contained dark-toned palette now — // 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 // no separate light/dark mode, so this class is always on. It's kept
@@ -49,7 +34,7 @@ onMounted(() => {
applyThemePalette(settings.theme_preset || 'indigo'); applyThemePalette(settings.theme_preset || 'indigo');
applyCollapsed(); applyCollapsed();
applyFontFamily(settings.font_family); applyFontFamily(settings.font_family);
initWindowChrome(props.serverData?.adminUrl); initDashboardFrame();
// Signal the shell is live: reveals the framed content and commits to // Signal the shell is live: reveals the framed content and commits to
// keeping native chrome hidden (the PHP failsafe stops watching once // keeping native chrome hidden (the PHP failsafe stops watching once
// aurora-ready is present). // aurora-ready is present).
@@ -73,74 +58,13 @@ onMounted(() => {
:server-data="serverData" :server-data="serverData"
@toggle-menu="toggleMenu" @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> </div>
</template> </template>
<style scoped> <style scoped>
/* Sidebar, Toolbar and the taskbar are all position:fixed, so this /* Sidebar and Toolbar are both position:fixed, so this wrapper has no
wrapper has no layout role — it just groups them under one mounted layout role — it just groups them under one mounted root. */
root. */
.site-shell { .site-shell {
display: contents; 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> </style>
+3 -3
View File
@@ -146,7 +146,7 @@ onUnmounted(() => document.removeEventListener('keydown', onKeydown));
align-self: flex-start; align-self: flex-start;
background: var(--aurora-frame-bg, #1c1c20); background: var(--aurora-frame-bg, #1c1c20);
border: 1px solid var(--aurora-frame-border, #34343b); border: 1px solid var(--aurora-frame-border, #34343b);
border-radius: 14px; border-radius: var(--aurora-radius-md);
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.45); box-shadow: 0 24px 64px rgba(0, 0, 0, 0.45);
overflow: hidden; overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
@@ -171,7 +171,7 @@ onUnmounted(() => document.removeEventListener('keydown', onKeydown));
font-size: 0.6875rem; font-size: 0.6875rem;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
border: 1px solid var(--aurora-border, #27272a); border: 1px solid var(--aurora-border, #27272a);
border-radius: 5px; border-radius: var(--aurora-radius-sm);
padding: 2px 6px; padding: 2px 6px;
} }
.smodal__state { .smodal__state {
@@ -197,7 +197,7 @@ onUnmounted(() => document.removeEventListener('keydown', onKeydown));
cursor: pointer; cursor: pointer;
text-align: left; text-align: left;
padding: 10px 12px; padding: 10px 12px;
border-radius: 9px; border-radius: var(--aurora-radius-md);
} }
.smodal__result--active { background: var(--aurora-accent-soft); } .smodal__result--active { background: var(--aurora-accent-soft); }
.smodal__result-title { .smodal__result-title {
+5 -5
View File
@@ -276,7 +276,7 @@ const filteredAuroraNav = computed(() => {
flush against the viewport edges. */ flush against the viewport edges. */
background: var(--aurora-card-bg); background: var(--aurora-card-bg);
border: 1px solid var(--aurora-card-border); border: 1px solid var(--aurora-card-border);
border-radius: 20px; border-radius: var(--aurora-radius-lg);
transition: width 0.2s ease; transition: width 0.2s ease;
z-index: 99999; z-index: 99999;
box-sizing: border-box; box-sizing: border-box;
@@ -316,7 +316,7 @@ const filteredAuroraNav = computed(() => {
gap: 8px; gap: 8px;
margin: 0 12px 10px; margin: 0 12px 10px;
padding: 6px 10px; padding: 6px 10px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
} }
@@ -361,7 +361,7 @@ const filteredAuroraNav = computed(() => {
.sidebar__row { .sidebar__row {
display: flex; display: flex;
align-items: center; align-items: center;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
margin: 0 8px; margin: 0 8px;
} }
@@ -393,7 +393,7 @@ const filteredAuroraNav = computed(() => {
width: 28px; width: 28px;
height: 28px; height: 28px;
flex-shrink: 0; flex-shrink: 0;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
transition: background 0.12s ease, color 0.12s ease; transition: background 0.12s ease, color 0.12s ease;
@@ -467,7 +467,7 @@ const filteredAuroraNav = computed(() => {
text-decoration: none; text-decoration: none;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
font-size: 0.8125rem; font-size: 0.8125rem;
border-radius: 6px; border-radius: var(--aurora-radius-sm);
margin: 0 8px; margin: 0 8px;
} }
+4 -4
View File
@@ -102,7 +102,7 @@ onUnmounted(() => document.removeEventListener('keydown', onKeydown));
justify-content: center; justify-content: center;
width: 34px; width: 34px;
height: 34px; height: 34px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
text-decoration: none; text-decoration: none;
} }
@@ -120,7 +120,7 @@ onUnmounted(() => document.removeEventListener('keydown', onKeydown));
padding: 0 10px; padding: 0 10px;
min-width: 240px; min-width: 240px;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 10px; border-radius: var(--aurora-radius-md);
background: var(--aurora-bg-subtle); background: var(--aurora-bg-subtle);
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
font-size: 0.8125rem; font-size: 0.8125rem;
@@ -141,7 +141,7 @@ onUnmounted(() => document.removeEventListener('keydown', onKeydown));
.toolbar__search-kbd { .toolbar__search-kbd {
font-size: 0.6875rem; font-size: 0.6875rem;
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 5px; border-radius: var(--aurora-radius-sm);
padding: 1px 6px; padding: 1px 6px;
background: var(--aurora-bg); background: var(--aurora-bg);
} }
@@ -154,7 +154,7 @@ onUnmounted(() => document.removeEventListener('keydown', onKeydown));
height: 34px; height: 34px;
border: none; border: none;
background: none; background: none;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
cursor: pointer; cursor: pointer;
color: var(--aurora-text-muted); color: var(--aurora-text-muted);
} }
+3 -3
View File
@@ -82,7 +82,7 @@ onUnmounted(() => document.removeEventListener('click', onDocClick));
background: none; background: none;
cursor: pointer; cursor: pointer;
padding: 6px 8px; padding: 6px 8px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
color: var(--aurora-text); color: var(--aurora-text);
} }
.user-menu__trigger:hover { .user-menu__trigger:hover {
@@ -125,7 +125,7 @@ onUnmounted(() => document.removeEventListener('click', onDocClick));
width: 240px; width: 240px;
background: var(--aurora-bg); background: var(--aurora-bg);
border: 1px solid var(--aurora-border); border: 1px solid var(--aurora-border);
border-radius: 12px; border-radius: var(--aurora-radius-md);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
overflow: hidden; overflow: hidden;
z-index: 100000; z-index: 100000;
@@ -170,7 +170,7 @@ onUnmounted(() => document.removeEventListener('click', onDocClick));
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding: 8px 10px; padding: 8px 10px;
border-radius: 8px; border-radius: var(--aurora-radius-sm);
text-decoration: none; text-decoration: none;
color: var(--aurora-text); color: var(--aurora-text);
font-size: 0.8125rem; font-size: 0.8125rem;
+35
View File
@@ -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);
}
+22 -179
View File
@@ -14,13 +14,6 @@
:root { :root {
--aurora-sidebar-w: 264px; --aurora-sidebar-w: 264px;
--aurora-toolbar-h: 56px; --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 /* Space between the sidebar card and the browser's left/top/bottom
edges (Sidebar.vue) — the toolbar and #wpcontent offsets below add edges (Sidebar.vue) — the toolbar and #wpcontent offsets below add
the same amount so nothing overlaps the floating sidebar. */ the same amount so nothing overlaps the floating sidebar. */
@@ -28,8 +21,12 @@
/* Breathing room between page content and the right browser edge. */ /* Breathing room between page content and the right browser edge. */
--aurora-gutter-r: 44px; --aurora-gutter-r: 44px;
--aurora-radius: 14px; /* Corner-radius scale, one notch rounder across the UI — tune here and
--aurora-radius-lg: 24px; every surface follows (all radii are tokenized onto this scale). */
--aurora-radius-sm: 10px; /* chips, badges, small/icon controls */
--aurora-radius-md: 14px; /* buttons, inputs, search, list rows */
--aurora-radius-lg: 20px; /* cards, panels, larger buttons */
--aurora-radius-xl: 28px; /* page frames: content card, dashboard, sidebar, editor */
/* Light theme */ /* Light theme */
--aurora-app-bg: #fafafa; /* zinc-50 */ --aurora-app-bg: #fafafa; /* zinc-50 */
@@ -167,15 +164,9 @@ html.aurora-ready #site-editor {
position produced a visible double-edge rendering artifact right at position produced a visible double-edge rendering artifact right at
the rounded corners. The background/gradient contrast alone is the rounded corners. The background/gradient contrast alone is
enough to read as a distinct surface without it. */ enough to read as a distinct surface without it. */
border-radius: var(--aurora-radius-lg); border-radius: var(--aurora-radius-xl);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
transition: left 0.2s ease; 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; box-sizing: border-box;
/* border-radius alone doesn't clip children — Gutenberg's own white /* border-radius alone doesn't clip children — Gutenberg's own white
canvas background ran straight past the rounded bottom corners as a canvas background ran straight past the rounded bottom corners as a
@@ -203,7 +194,7 @@ html.aurora-ready #wpbody-content {
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
color: var(--aurora-text); color: var(--aurora-text);
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
border-radius: var(--aurora-radius-lg); border-radius: var(--aurora-radius-xl);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
margin: 16px var(--aurora-gutter-r) 24px 24px; margin: 16px var(--aurora-gutter-r) 24px 24px;
padding: 32px; padding: 32px;
@@ -250,48 +241,6 @@ html.aurora-ready body.post-php #wpbody-content {
padding: 0 !important; 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 & /* Theme installer preview ("Add Themes" > click a theme > full "Preview &
Install" overlay, theme-install.php). WP core's own .wp-full-overlay Install" overlay, theme-install.php). WP core's own .wp-full-overlay
component (shared with the Customizer, but only .theme-install-overlay component (shared with the Customizer, but only .theme-install-overlay
@@ -355,114 +304,43 @@ html.aurora-ready .theme-install-overlay.collapsed {
html.aurora-ready .theme-install-overlay .wp-full-overlay-sidebar { html.aurora-ready .theme-install-overlay .wp-full-overlay-sidebar {
left: calc(var(--aurora-sidebar-w) + var(--aurora-sidebar-gap) * 2 + 24px) !important; 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; bottom: 24px !important;
height: auto !important; height: auto !important;
border-top-left-radius: var(--aurora-radius-lg); border-top-left-radius: var(--aurora-radius-xl);
} }
html.aurora-ready .theme-install-overlay .wp-full-overlay-main { 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); border-top-right-radius: var(--aurora-radius-xl);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); 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 /* The Dashboard bypasses #wpbody-content (DashboardTakeover.php renders
it into a separate sibling — see windowChrome.js), so it never got the it into a separate sibling — see dashboardFrame.js), so it never got the
card frame every other page has. windowChrome.js wraps the bar + the card frame every other page has. dashboardFrame.js wraps the dashboard's
dashboard's mount point in this div to give it the same treatment. */ mount point in this div to give it the same treatment. */
.aurora-dashboard-frame { .aurora-dashboard-frame {
background: var(--aurora-frame-bg); background: var(--aurora-frame-bg);
border: 1px solid var(--aurora-frame-border); border: 1px solid var(--aurora-frame-border);
border-radius: var(--aurora-radius-lg); border-radius: var(--aurora-radius-xl);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
margin: 16px var(--aurora-gutter-r) 24px 24px; margin: 16px var(--aurora-gutter-r) 24px 24px;
box-sizing: border-box; box-sizing: border-box;
/* Same unconditional height cap as #wpbody-content's base rule — this /* Same unconditional height cap as #wpbody-content's base rule — this
element never got it, since the Dashboard bypasses #wpbody-content element never got it, since the Dashboard bypasses #wpbody-content
entirely (a separate sibling — see windowChrome.js) and only had the entirely (a separate sibling — see dashboardFrame.js). Once html/body
taskbar-present variant below. Once html/body were locked to were locked to overflow:hidden (to kill a phantom document-level
overflow:hidden (to kill a phantom document-level scrollbar on scrollbar on another page), the dashboard's content past the first
another page), the dashboard's content past the first ~1500px became ~1500px became completely unreachable: no internal scroll here, and
completely unreachable: no internal scroll here, and no document no document
scroll left to fall back on either. */ scroll left to fall back on either. */
max-height: calc(100vh - var(--aurora-toolbar-h) - 40px); max-height: calc(100vh - var(--aurora-toolbar-h) - 40px);
overflow-y: auto; overflow-y: auto;
overflow-anchor: none; 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 /* 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 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 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 Aurora's own pages), so it needs its own copy of the same treatment
@@ -476,41 +354,6 @@ html.aurora-ready .theme-install-overlay .wp-full-overlay-main {
margin: 16px 20px 0; 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 /* The settings page renders its own two boxed panels, so drop the outer
content frame there — leaving just those panels on the dark backdrop. content frame there — leaving just those panels on the dark backdrop.
No background here: #wpbody-content is inset from the sidebar/gutter, No background here: #wpbody-content is inset from the sidebar/gutter,
-57
View File
@@ -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';
}
-185
View File
@@ -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);
}
}
+18 -4
View File
@@ -2,9 +2,20 @@ import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'node:url'; import { fileURLToPath, URL } from 'node:url';
// Deliberately simple: one entry in this first slice (dashboard). Add more // Multi-entry build: one entry per admin screen (see rollupOptions.input).
// entries here as additional pages get rebuilt — same flat pattern each // Each is enqueued as its own ES module by admin/src/Utility/Assets.php,
// time, no shared scoping/theme-remap layer to keep in sync across files. // which also walks the manifest's `imports` chain for shared-chunk CSS.
// Rollup automatically splits code shared by 2+ entries into its own chunk;
// `vue` is pinned to a named `vendor-vue` chunk below so its hash stays
// stable across app-code changes (returning users keep it cached across
// plugin updates instead of re-downloading it every release).
//
// Sourcemaps are on by default for local debugging (the dev loop hot-patches
// app/dist into a LocalWP install). The distribution build sets
// AURORA_SOURCEMAP=false so the shipped zip doesn't carry ~1.2 MB of .map
// files that are useless to end users.
const emitSourcemaps = process.env.AURORA_SOURCEMAP !== 'false';
export default defineConfig({ export default defineConfig({
root: fileURLToPath(new URL('./src', import.meta.url)), root: fileURLToPath(new URL('./src', import.meta.url)),
plugins: [vue()], plugins: [vue()],
@@ -16,13 +27,14 @@ export default defineConfig({
build: { build: {
outDir: fileURLToPath(new URL('./dist', import.meta.url)), outDir: fileURLToPath(new URL('./dist', import.meta.url)),
emptyOutDir: true, emptyOutDir: true,
sourcemap: true, sourcemap: emitSourcemaps,
manifest: true, manifest: true,
rollupOptions: { rollupOptions: {
input: { input: {
shell: fileURLToPath(new URL('./src/entries/shell.js', import.meta.url)), shell: fileURLToPath(new URL('./src/entries/shell.js', import.meta.url)),
dashboard: fileURLToPath(new URL('./src/entries/dashboard.js', import.meta.url)), dashboard: fileURLToPath(new URL('./src/entries/dashboard.js', import.meta.url)),
settings: fileURLToPath(new URL('./src/entries/settings.js', import.meta.url)), settings: fileURLToPath(new URL('./src/entries/settings.js', import.meta.url)),
'setup-wizard': fileURLToPath(new URL('./src/entries/setup-wizard.js', import.meta.url)),
'menu-creator': fileURLToPath(new URL('./src/entries/menu-creator.js', import.meta.url)), 'menu-creator': fileURLToPath(new URL('./src/entries/menu-creator.js', import.meta.url)),
'admin-notices': fileURLToPath(new URL('./src/entries/admin-notices.js', import.meta.url)), 'admin-notices': fileURLToPath(new URL('./src/entries/admin-notices.js', import.meta.url)),
'role-editor': fileURLToPath(new URL('./src/entries/role-editor.js', import.meta.url)), 'role-editor': fileURLToPath(new URL('./src/entries/role-editor.js', import.meta.url)),
@@ -43,6 +55,8 @@ export default defineConfig({
'bug-report': fileURLToPath(new URL('./src/entries/bug-report.js', import.meta.url)), 'bug-report': fileURLToPath(new URL('./src/entries/bug-report.js', import.meta.url)),
}, },
output: { output: {
// Keep Vue in its own long-lived vendor chunk (see header note).
manualChunks: { 'vendor-vue': ['vue'] },
entryFileNames: '[name].[hash].js', entryFileNames: '[name].[hash].js',
chunkFileNames: 'assets/[name].[hash].js', chunkFileNames: 'assets/[name].[hash].js',
assetFileNames: 'assets/[name].[hash][extname]', assetFileNames: 'assets/[name].[hash][extname]',
+3
View File
@@ -26,6 +26,7 @@ require AURORA_ADMIN_PATH . "admin/src/Shell/MenuSerializer.php";
require AURORA_ADMIN_PATH . "admin/src/Shell/Shell.php"; require AURORA_ADMIN_PATH . "admin/src/Shell/Shell.php";
require AURORA_ADMIN_PATH . "admin/src/Pages/DashboardTakeover.php"; require AURORA_ADMIN_PATH . "admin/src/Pages/DashboardTakeover.php";
require AURORA_ADMIN_PATH . "admin/src/Pages/SettingsPage.php"; require AURORA_ADMIN_PATH . "admin/src/Pages/SettingsPage.php";
require AURORA_ADMIN_PATH . "admin/src/Pages/SetupWizard.php";
require AURORA_ADMIN_PATH . "admin/src/Rest/DashboardData.php"; require AURORA_ADMIN_PATH . "admin/src/Rest/DashboardData.php";
require AURORA_ADMIN_PATH . "admin/src/Analytics/Analytics.php"; require AURORA_ADMIN_PATH . "admin/src/Analytics/Analytics.php";
require AURORA_ADMIN_PATH . "admin/src/Rest/AnalyticsData.php"; require AURORA_ADMIN_PATH . "admin/src/Rest/AnalyticsData.php";
@@ -59,6 +60,8 @@ require AURORA_ADMIN_PATH . "admin/src/Login/LoginStyle.php";
require AURORA_ADMIN_PATH . "admin/src/Media/SvgUploads.php"; require AURORA_ADMIN_PATH . "admin/src/Media/SvgUploads.php";
require AURORA_ADMIN_PATH . "admin/src/Media/MediaFolders.php"; require AURORA_ADMIN_PATH . "admin/src/Media/MediaFolders.php";
require AURORA_ADMIN_PATH . "admin/src/ImageFormats/ImageFormats.php"; require AURORA_ADMIN_PATH . "admin/src/ImageFormats/ImageFormats.php";
require AURORA_ADMIN_PATH . "admin/src/Security/Security.php";
require AURORA_ADMIN_PATH . "admin/src/Code/CodeInjection.php";
require AURORA_ADMIN_PATH . "admin/src/Rest/MediaFolderData.php"; require AURORA_ADMIN_PATH . "admin/src/Rest/MediaFolderData.php";
require AURORA_ADMIN_PATH . "admin/src/Rest/BugReportData.php"; require AURORA_ADMIN_PATH . "admin/src/Rest/BugReportData.php";
require AURORA_ADMIN_PATH . "admin/src/Rest/CompanionPluginData.php"; require AURORA_ADMIN_PATH . "admin/src/Rest/CompanionPluginData.php";