Add Dark Mode public plugin source
@@ -0,0 +1,6 @@
|
|||||||
|
* text=auto
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.webp binary
|
||||||
|
*.gif binary
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.old/
|
||||||
|
*.zip
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
After Width: | Height: | Size: 662 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
@@ -0,0 +1,267 @@
|
|||||||
|
## 4.8.18 public-track development - 2026-08-23
|
||||||
|
|
||||||
|
- Established the dedicated public source repository at https://git.christit.com/gracepress/dark-mode-by-gracepress and added the canonical public plugin page to the plugin metadata.
|
||||||
|
- Prepared the established GracePress Dark Mode engine for WordPress.org distribution without removing any Dark Mode control, option, or runtime behavior.
|
||||||
|
- Preserved optional local OmniLog event hooks and conditional GracePress Central Hub routing.
|
||||||
|
- Removed the anonymous ecosystem ping system and private ChristIT update engine from the public track.
|
||||||
|
- Compacted and reorganized the existing four-part settings interface while retaining every field and function.
|
||||||
|
- Replaced the remotely loaded administration background with an original bundled sunrise image.
|
||||||
|
|
||||||
|
The public development track remains at 4.8.18. Version 5.0.0 is reserved for the deliberate first WordPress.org release.
|
||||||
|
|
||||||
|
## 4.8.18 - 2026-08-03
|
||||||
|
|
||||||
|
- Corrected GracePress Builder hero backgrounds in dark mode: CSS-backed hero artwork now receives the same counter-inversion as normal media, preserving its original color, contrast, and full opacity.
|
||||||
|
|
||||||
|
## 4.8.17 - 2026-07-04
|
||||||
|
|
||||||
|
- Changed time-based dark mode to use the WordPress site timezone/current site hour instead of browser clock time.
|
||||||
|
- Stopped stale browser localStorage from overriding the scheduled auto mode on page load while preserving explicit URL and manual toggle behavior.
|
||||||
|
|
||||||
|
## 4.8.16 - 2026-07-03
|
||||||
|
|
||||||
|
- Added Chrome fullscreen video exceptions for dark-mode media filters so live and recorded videos do not render inverted or washed out in fullscreen.
|
||||||
|
|
||||||
|
## 4.8.15 - 2026-06-25
|
||||||
|
|
||||||
|
- Stopped applying sub-100% brightness and contrast compression to the root dark-mode page filter because it permanently muted final-composited brand art such as the ChristP2P header logo.
|
||||||
|
- Returned the header logo to a normal media-style counter-correction now that the root filter no longer caps its rendered saturation and brightness.
|
||||||
|
- Raised and isolated the GracePress UI header logo stacking context so local overlay layers cannot sit on top of the brand mark in dark mode.
|
||||||
|
|
||||||
|
## 4.8.14 - 2026-06-25
|
||||||
|
|
||||||
|
- Strengthened the GracePress UI header logo dark-mode correction after screenshot comparison showed the logo was still losing saturation and brightness under the root dark-mode filter.
|
||||||
|
- Kept the stronger compensation scoped to the top-left header logo only, without changing thumbnail, video, or general media handling.
|
||||||
|
|
||||||
|
## 4.8.13 - 2026-06-25
|
||||||
|
|
||||||
|
- Added a logo-specific dark-mode correction for the GracePress UI header logo so the root brightness/contrast filter no longer leaves the site mark washed out after media reinversion.
|
||||||
|
- Kept the change scoped to the top-left header logo without changing thumbnail or general media correction behavior.
|
||||||
|
|
||||||
|
## 4.8.12 - 2026-06-25
|
||||||
|
|
||||||
|
- Restored the GracePress UI header logo to full opacity in dark mode so it is no longer dimmed by the global media opacity rule.
|
||||||
|
- Kept the fix scoped to the shared Dark Mode media correction path without changing GracePress UI header layout.
|
||||||
|
|
||||||
|
## 4.8.11 - 2026-06-25
|
||||||
|
|
||||||
|
- Raised the specificity of GracePress Blocks media counter-correction so thumbnail images are no longer dimmed by the global dark-mode media opacity rule.
|
||||||
|
- Kept hover preview videos and static thumbnails on the same full-strength correction path in dark mode.
|
||||||
|
|
||||||
|
## 4.8.10 - 2026-06-25
|
||||||
|
|
||||||
|
- Fixed frontend Plyr video playback in dark mode by no longer forcing the poster overlay visible after playback starts.
|
||||||
|
- Preserved dark-mode media correction while allowing the active video frame to render above the poster layer.
|
||||||
|
|
||||||
|
## 4.8.9 - 2026-06-25
|
||||||
|
|
||||||
|
- Forward rollback release: restored the known-good pre-convention package behavior and republished it as a newer GracePress update.
|
||||||
|
|
||||||
|
# GracePress: Dark Mode Changelog
|
||||||
|
|
||||||
|
## 4.8.7 - 2026-06-24
|
||||||
|
- Removed GracePress Blocks overlay and text counter-correction rules from Dark Mode.
|
||||||
|
- Kept Dark Mode focused on document inversion, media pixel restoration, and transition effects while Blocks owns its own presentation.
|
||||||
|
|
||||||
|
## 4.8.6 - 2026-06-24
|
||||||
|
- Fixed mode switching so the veil now performs a real two-phase transition: cover the page, flip the mode while covered, then reveal the page smoothly.
|
||||||
|
- Added configurable transition styles for Fade, Side Wipe, Vertical Wipe, Iris, and Zoom Fade without adding a frontend animation dependency.
|
||||||
|
- Added the selected transition style to saved settings and OmniLog context.
|
||||||
|
|
||||||
|
## 4.8.5 - 2026-06-24
|
||||||
|
- Corrected GracePress Blocks overlay-card text colors and shadows under the global invert engine so titles render as white text with dark shadows instead of black text with pale glow.
|
||||||
|
- Kept media correction intact while targeting only Blocks overlay, Bento, Masonry, and post feature title layers.
|
||||||
|
|
||||||
|
## 4.8.4 - 2026-06-24
|
||||||
|
- Added dark-mode counter-correction for GracePress Blocks overlay and shade layers so card title gradients stay dark instead of inverting into pale thumbnail washout.
|
||||||
|
- Preserved media double-inversion behavior while correcting only the readability layers used by video cards, Bento cards, shorts overlays, and post feature heroes.
|
||||||
|
|
||||||
|
## 4.8.3 - 2026-06-24
|
||||||
|
- Removed the global filter transition that caused visible polarity flashing during light/dark switches.
|
||||||
|
- Changed the mode switch veil into a two-phase quick fade so the page flips instantly while covered, then fades back in.
|
||||||
|
|
||||||
|
## 4.8.2 - 2026-06-24
|
||||||
|
- Added full current GracePress release metadata, including public-release flags, author URI, license, and minimum WordPress version.
|
||||||
|
- Routed activation, deactivation, privacy toggle, filter reset, and settings-save events through structured OmniLog events with `gp_log` fallback context.
|
||||||
|
- Removed the unused legacy file logger class so Dark Mode has one active logging path.
|
||||||
|
- Updated stale admin version text and aligned the admin footer with the shared GracePress support links.
|
||||||
|
|
||||||
|
## 4.8.1 - 2026-06-24
|
||||||
|
- Added a quick black/white mode-switch veil so light/dark toggles hide transient global-filter flashes on thumbnails and media blocks.
|
||||||
|
- Tightened Gutenberg, GracePress hero, card thumbnail, widget, avatar, and player media counter-correction so images remain natural and full-strength in dark mode.
|
||||||
|
- Added current GracePress stage/release headers and a configurable blink-duration control for the transition veil.
|
||||||
|
|
||||||
|
## 4.8.0 - 2026-05-27
|
||||||
|
- Removed the admin-bar boolean output that printed a stray selectable `1` after the GracePress Dark Mode toolbar item.
|
||||||
|
- Kept dark-mode video rules scoped to actual players instead of card thumbnail hover previews.
|
||||||
|
|
||||||
|
## 4.7.9 - 2026-05-27
|
||||||
|
|
||||||
|
- Kept the current Dark Mode source in place and advanced the updater-visible version after the rollback test sequence.
|
||||||
|
- Leaves thumbnail behavior under Blocks/Video Management while giving GracePress Central a clean package to install.
|
||||||
|
|
||||||
|
## 4.7.8 - 2026-05-27
|
||||||
|
|
||||||
|
- Removed dark-mode CSS rules that forced GracePress Blocks preview videos and thumbnail images into a lazy-preview state.
|
||||||
|
- Kept the playback-safe rules for actual video players while leaving card thumbnails under Blocks control.
|
||||||
|
- Advanced the updater metadata past the already-installed 4.7.6/4.7.7 line.
|
||||||
|
|
||||||
|
## 4.7.6 - 2026-05-27
|
||||||
|
|
||||||
|
- Updated video-card dark mode preview rules to match lazy hover/focus loading and avoid hiding thumbnails before animated previews are ready.
|
||||||
|
|
||||||
|
## 4.7.5 - 2026-05-25
|
||||||
|
|
||||||
|
- Restored a backend admin-bar dark mode toggle so wp-admin can switch modes without returning to the frontend.
|
||||||
|
- Ensured admin dark mode support is enabled for existing installs once after upgrade.
|
||||||
|
- Updated the module version metadata used by the GracePress updater.
|
||||||
|
|
||||||
|
## Reconstruction Notes
|
||||||
|
- Changelog initialized during reconstruction.
|
||||||
|
|
||||||
|
<!-- GP-RECONSTRUCTED-CHANGELOG:START -->
|
||||||
|
|
||||||
|
## Reconstructed History
|
||||||
|
|
||||||
|
These entries were inferred from local distribution archives, module headers, and `.old` backups. They are intentionally conservative and may not capture the full intent of each change.
|
||||||
|
|
||||||
|
### 4.7.4 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.7.4.zip` (2026-05-21).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Header advertised WordPress compatibility through 7.0.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.7.3 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.7.3.zip` (2026-05-21).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.7.2 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.7.2.zip` (2026-05-20).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.7.1 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.7.1.zip` (2026-05-20).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.7.0 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.7.0.zip` (2026-05-20).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.6.9 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.6.9.zip` (2026-05-20).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.6.8 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.6.8.zip` (2026-05-16).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.6.7 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.6.7.zip` (2026-05-16).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.6.6 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.6.6.zip` (2026-05-16).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.6.5 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.6.5.zip` (2026-04-01).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.6.4 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.6.4.zip` (2026-04-01).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.6.3 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.6.3.zip` (2026-04-01).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.6.2 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.6.2.zip` (2026-04-01).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.6.1 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.6.1.zip` (2026-04-01).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Catalog/category marker: User Interface.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.6.0 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.6.0.zip` (2026-04-01).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, variable dimming, and keyboard shortcuts.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.5.0 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.5.0.zip` (2026-03-31).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, filter tuning, and keyboard shortcuts.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.4.0 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.4.0.zip` (2026-03-30).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, keyboard shortcuts, and image dimming.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.3.1 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.3.1.zip` (2026-03-30).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, granular exclusions, and image dimming.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.3.0 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.3.0.zip` (2026-03-30).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, granular exclusions, and image dimming.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.2.0 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.2.0.zip` (2026-03-29).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, OS-level sync, hardware-accelerated CSS inversion, and image dimming.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.1.0 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.1.0.zip` (2026-03-27).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC synchronization, hardware-accelerated CSS inversion, and image dimming.
|
||||||
|
- Header advertised WordPress compatibility through 6.4.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 4.0.0 - reconstructed
|
||||||
|
- Reconstructed from archive `dark-mode-v4.0.0.zip` (2026-03-27).
|
||||||
|
- Header description at this version: A premium-grade Dark Mode engine. Features absolute Anti-FOUC (white flash) blocking, OS-level preference detection, and granular CSS exclusion rules.
|
||||||
|
- Header advertised WordPress compatibility through 6.4.
|
||||||
|
- Header icon marker: 🌓.
|
||||||
|
|
||||||
|
### 3.1.0 - reconstructed
|
||||||
|
- Reconstructed from 1 file backup(s) in `plugins/dark-mode/.old`.
|
||||||
|
- Exact intent unknown; this confirms a recoverable historical version existed.
|
||||||
|
|
||||||
|
### 3.0.0 - reconstructed
|
||||||
|
- Reconstructed from 1 file backup(s) in `plugins/dark-mode/.old`.
|
||||||
|
- Exact intent unknown; this confirms a recoverable historical version existed.
|
||||||
|
|
||||||
|
### 2.2.2 - reconstructed
|
||||||
|
- Reconstructed from 3 file backup(s) in `plugins/dark-mode/.old`.
|
||||||
|
- Exact intent unknown; this confirms a recoverable historical version existed.
|
||||||
|
|
||||||
|
<!-- GP-RECONSTRUCTED-CHANGELOG:END -->
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
<https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Moe Ghoul>, 1 April 1989
|
||||||
|
Moe Ghoul, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Dark Mode by GracePress
|
||||||
|
|
||||||
|
A configurable dark mode for WordPress websites and the WordPress administration area.
|
||||||
|
|
||||||
|
Dark Mode by GracePress includes anti-flash loading, operating-system preference support, visual tuning, media correction, transitions, and accessible controls that can be placed where a site needs them.
|
||||||
|
|
||||||
|
## Project status
|
||||||
|
|
||||||
|
Version 4.8.18 is the current public-development baseline. The plugin is being prepared, documented, and tested for submission to WordPress.org. Version 5.0.0 is reserved for the deliberate first WordPress.org release.
|
||||||
|
|
||||||
|
There is no official public download yet. Candidate packages are used only for controlled testing until the public release is approved.
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- [Public plugin page](https://christit.com/gracepress/plugins/dark-mode-by-gracepress/)
|
||||||
|
- [GracePress project](https://christit.com/gracepress/)
|
||||||
|
- [GracePress plugin catalog](https://christit.com/gracepress/plugins/)
|
||||||
|
- [Christ IT](https://christit.com/)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Device-theme, scheduled, light, or dark first-visit behavior.
|
||||||
|
- Remembered visitor choice with anti-flash startup synchronization.
|
||||||
|
- Brightness, contrast, and media-dimming controls.
|
||||||
|
- Media correction for images, video, and fullscreen playback.
|
||||||
|
- Fade, horizontal wipe, vertical wipe, iris, and zoom transitions.
|
||||||
|
- Floating, menu, shortcode, administrator-bar, and keyboard controls.
|
||||||
|
- Independent frontend and WordPress administration enablement.
|
||||||
|
- Page, selector, and custom-CSS compatibility controls.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- WordPress 6.0 or newer.
|
||||||
|
- PHP 7.4 or newer.
|
||||||
|
|
||||||
|
## Privacy and integrations
|
||||||
|
|
||||||
|
The public plugin contains no telemetry, private update connection, license server, or required external service.
|
||||||
|
|
||||||
|
GracePress Central Hub routing and OmniLog events are optional local integrations. They are used only when those plugins are already installed and active; neither is required.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The official installation route will be published here and on the public plugin page after the WordPress.org release is approved. See `readme.txt` for the current controlled-testing instructions and WordPress plugin documentation.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Dark Mode by GracePress is licensed under [GPL-2.0-or-later](LICENSE.txt).
|
||||||
|
|
||||||
|
Published by Jason Eugene Garrison through [Christ IT](https://christit.com/). Questions may be sent to [jason@christit.com](mailto:jason@christit.com).
|
||||||
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,332 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: Dark Mode by GracePress
|
||||||
|
* Plugin URI: https://christit.com/gracepress/plugins/dark-mode-by-gracepress/
|
||||||
|
* Description: A complete dark mode engine with anti-flash loading, OS preference support, visual tuning, transitions, and flexible controls.
|
||||||
|
* Version: 4.8.18
|
||||||
|
* Stage: Beta
|
||||||
|
* Release Channel: WordPress.org
|
||||||
|
* Public Release: No
|
||||||
|
* Public Candidate: Yes
|
||||||
|
* WordPress.org Slug: dark-mode-by-gracepress
|
||||||
|
* WordPress.org Status: Draft
|
||||||
|
* Internal Source Slug: dark-mode
|
||||||
|
* Conversion Status: Lab Testing
|
||||||
|
* Tested up to: 7.1
|
||||||
|
* Category: User Interface
|
||||||
|
* Settings Slug: gracepress-dark-mode
|
||||||
|
* Author: Jason Eugene Garrison
|
||||||
|
* Author URI: https://christit.com
|
||||||
|
* License: GPL-2.0-or-later
|
||||||
|
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||||
|
* Text Domain: dark-mode-by-gracepress
|
||||||
|
* Requires at least: 6.0
|
||||||
|
* Requires PHP: 7.4
|
||||||
|
* Privacy: No telemetry
|
||||||
|
* External Services: None
|
||||||
|
* Optional Integrations: GracePress Central Hub, OmniLog
|
||||||
|
* Plugin Check Status: Passed
|
||||||
|
* Plugin Check Date: 2026-08-23
|
||||||
|
* Icon: 🌓
|
||||||
|
*/
|
||||||
|
if(!defined('ABSPATH'))exit;
|
||||||
|
|
||||||
|
if(!defined('GP_DARKMODE_VERSION')) define('GP_DARKMODE_VERSION','4.8.18');
|
||||||
|
|
||||||
|
function gp_darkmode_log($message,$event='message',$context=[],$level='INFO'){
|
||||||
|
$context=is_array($context)?$context:['value'=>$context];
|
||||||
|
$context['version']=GP_DARKMODE_VERSION;
|
||||||
|
$context['level']=$level;
|
||||||
|
try{
|
||||||
|
if(function_exists('gp_omnilog')) gp_omnilog('dark-mode',$event,array_merge(['message'=>$message],$context));
|
||||||
|
}catch(\Throwable $e){}
|
||||||
|
try{
|
||||||
|
if(function_exists('gp_log')) gp_log($message,'DARKMODE',array_merge(['event'=>$event],$context));
|
||||||
|
}catch(\Throwable $e){}
|
||||||
|
}
|
||||||
|
|
||||||
|
register_deactivation_hook(__FILE__,function(){
|
||||||
|
gp_darkmode_log('Dark Mode engine deactivated.','deactivated');
|
||||||
|
});
|
||||||
|
|
||||||
|
register_activation_hook(__FILE__, function() {
|
||||||
|
if(get_option('gp_dm_brightness') === false) update_option('gp_dm_brightness', 95);
|
||||||
|
if(get_option('gp_dm_contrast') === false) update_option('gp_dm_contrast', 90);
|
||||||
|
if(get_option('gp_dm_dim_level') === false) update_option('gp_dm_dim_level', 85);
|
||||||
|
if(get_option('gracepress_dark_admin_enable') === false) update_option('gracepress_dark_admin_enable', '1');
|
||||||
|
if(get_option('gp_dm_veil_ms') === false) update_option('gp_dm_veil_ms', 160);
|
||||||
|
if(get_option('gp_dm_veil_effect') === false) update_option('gp_dm_veil_effect', 'fade');
|
||||||
|
gp_darkmode_log('Dark Mode engine activated.','activated');
|
||||||
|
});
|
||||||
|
|
||||||
|
add_action('admin_init', function() {
|
||||||
|
if (get_option('gp_dm_adminbar_toggle_restored') === '1') return;
|
||||||
|
update_option('gracepress_dark_admin_enable', '1');
|
||||||
|
update_option('gp_dm_adminbar_toggle_restored', '1');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dynamic Self-Aware Router
|
||||||
|
add_action('admin_menu',function(){
|
||||||
|
$s='gracepress-dark-mode';
|
||||||
|
$t='Dark Mode';
|
||||||
|
$is_theme_active = (get_template() === 'gracepress-theme');
|
||||||
|
|
||||||
|
$is_theme_required = false;
|
||||||
|
if (!function_exists('get_plugin_data')) include_once(ABSPATH . 'wp-admin/includes/plugin.php');
|
||||||
|
$plugin_data = get_plugin_data(__FILE__);
|
||||||
|
if (isset($plugin_data['Category']) && strtolower(trim($plugin_data['Category'])) === 'theme required') {
|
||||||
|
$is_theme_required = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(class_exists('GracePress_Central_Hub')) {
|
||||||
|
$parent_slug = ($is_theme_active && $is_theme_required) ? 'gracepress-plugins' : 'gracepress-central';
|
||||||
|
add_submenu_page($parent_slug, $t, $t, 'manage_options', $s, 'gp_darkmode_admin_page');
|
||||||
|
} else {
|
||||||
|
add_options_page($t, $t, 'manage_options', $s, 'gp_darkmode_admin_page');
|
||||||
|
}
|
||||||
|
},100);
|
||||||
|
|
||||||
|
function gp_darkmode_is_excluded(){
|
||||||
|
$ex_pages = get_option('gracepress_dark_exclude_pages','');
|
||||||
|
if(!empty($ex_pages) && (is_page() || is_single())){
|
||||||
|
$pid = get_the_ID();
|
||||||
|
$arr = array_map('trim', explode(',', $ex_pages));
|
||||||
|
if(in_array($pid, $arr)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gp_darkmode_icons($s=''){
|
||||||
|
$s=$s?$s:get_option('gracepress_dark_icon_style','classic');
|
||||||
|
if($s=='minimal')return['l'=>'⚫','d'=>'⚪'];
|
||||||
|
if($s=='bulbs')return['l'=>'🌑','d'=>'💡'];
|
||||||
|
if($s=='text')return['l'=>'DARK','d'=>'LIGHT'];
|
||||||
|
return['l'=>'🌙','d'=>'☀️'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function gp_darkmode_btn($f=false,$s=''){
|
||||||
|
$sz=get_option('gracepress_dark_button_size','normal');
|
||||||
|
$s=$s?$s:get_option('gracepress_dark_icon_style','classic');
|
||||||
|
$i=gp_darkmode_icons($s);$l=$i['l'];$d=$i['d'];$c="gracepress-dark-toggle-trigger";
|
||||||
|
if($f){$p=get_option('gracepress_dark_button_position','bottom-right');$c.=" gracepress-floating gracepress-pos-$p";}
|
||||||
|
if($s==='switch'){
|
||||||
|
$c.=" gracepress-pill-switch gracepress-size-$sz";
|
||||||
|
return "<div class='$c' data-light='$l' data-dark='$d'><div class='gracepress-pill-knob'>$l</div></div>";
|
||||||
|
}else{
|
||||||
|
$c.=" gracepress-dark-toggle-btn gracepress-size-$sz".($s==='text'?' gracepress-text-btn':'');
|
||||||
|
return "<button class='$c' data-light='$l' data-dark='$d'><span class='gracepress-icon-wrapper'>$l</span></button>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action('wp_head','gp_darkmode_head',1); add_action('admin_head','gp_darkmode_head',1);
|
||||||
|
function gp_darkmode_head(){
|
||||||
|
if(!is_admin()&&gp_darkmode_is_excluded())return;
|
||||||
|
|
||||||
|
$ds=get_option('gracepress_dark_default_state','light');$sp=get_option('gracepress_dark_transition_speed','0.3s');$pl=get_option('gracepress_dark_pulse_effect','0');$ex=get_option('gracepress_dark_exclusions','');$cus=get_option('gp_dm_css','');
|
||||||
|
if(!preg_match('/^(?:\d+(?:\.\d+)?|\.\d+)(?:ms|s)$/',$sp))$sp='0.3s';
|
||||||
|
|
||||||
|
$br=get_option('gp_dm_brightness',95);
|
||||||
|
$ct=get_option('gp_dm_contrast',90);
|
||||||
|
$dim=get_option('gp_dm_dim_level',85);
|
||||||
|
$root_br = max(100, (int)$br);
|
||||||
|
$root_ct = max(100, (int)$ct);
|
||||||
|
|
||||||
|
$xstr="";if(!empty($ex)){$xp=explode(',',$ex);$xc=[];foreach($xp as $p){$p=trim($p);if(!empty($p)&&!preg_match('/[{};<>]|\/\*/',$p))$xc[]="html.gracepress-dark-active ".$p;}if(!empty($xc))$xstr=",".implode(',',$xc);}
|
||||||
|
$veil_ms=max(0,min(800,absint(get_option('gp_dm_veil_ms',160))));
|
||||||
|
$veil_effect=sanitize_key(get_option('gp_dm_veil_effect','fade'));
|
||||||
|
if(!in_array($veil_effect,['fade','wipe-right','wipe-down','iris','zoom'],true))$veil_effect='fade';
|
||||||
|
$site_dt=function_exists('current_datetime')?current_datetime():new DateTimeImmutable('now',function_exists('wp_timezone')?wp_timezone():null);
|
||||||
|
$site_hour=(int)$site_dt->format('G');
|
||||||
|
$site_tz=wp_json_encode(function_exists('wp_timezone_string')?wp_timezone_string():'');
|
||||||
|
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Values are JSON encoded before entering this early inline state script.
|
||||||
|
echo "<script>!function(){try{var p=new URLSearchParams(window.location.search),stored=localStorage.getItem('gracepressDarkMode'),d=".wp_json_encode($ds).",siteHour=".wp_json_encode($site_hour).",siteTimezone=".$site_tz.",s=null;if(p.get('darkmode')==='1')s='enabled';else if(p.get('lightmode')==='1')s='disabled';else if(d==='auto'){s=(siteHour>=18||siteHour<=6)?'enabled':'disabled';}else if(stored){s=stored;}else if(d==='os')s=window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches?'enabled':'disabled';else s=(d==='dark')?'enabled':'disabled';document.documentElement.setAttribute('data-gp-dark-timezone',siteTimezone||'site');document.documentElement.setAttribute('data-gp-dark-hour',String(siteHour));if(s==='enabled'){document.documentElement.classList.add('gracepress-dark-active');}}catch(e){}}();</script>";
|
||||||
|
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Numeric, enum and selector values are validated; administrator-authored CSS is an intentional manage_options feature.
|
||||||
|
echo "<style>:root{--gp-dm-media-opacity:".esc_html((string)($dim/100)).";--gp-dm-veil-ms:".esc_html((string)$veil_ms)."ms;}html{transition:background-color $sp ease;}html.gracepress-dark-active{filter:invert(100%) hue-rotate(180deg) brightness($root_br%) contrast($root_ct%)!important;background-color:#fff!important;}html.gracepress-dark-active body{background-color:#fff!important;}html.gracepress-dark-active img,html.gracepress-dark-active video,html.gracepress-dark-active iframe,html.gracepress-dark-active picture,html.gracepress-dark-active .gracepress-no-invert,html.gracepress-dark-active #adminmenuwrap,html.gracepress-dark-active #adminmenuback,html.gracepress-dark-active #wpadminbar$xstr{filter:invert(100%) hue-rotate(180deg)!important;opacity:var(--gp-dm-media-opacity)!important;}.gracepress-darkmode-veil{position:fixed;inset:0;z-index:2147483647;pointer-events:none;opacity:0;background:#000;clip-path:inset(0);transform:none;transform-origin:center;transition:opacity var(--gp-dm-veil-ms) ease,clip-path var(--gp-dm-veil-ms) cubic-bezier(.65,0,.35,1),transform var(--gp-dm-veil-ms) ease;will-change:opacity,clip-path,transform;}.gracepress-darkmode-veil.is-active{opacity:1;}.gracepress-darkmode-veil[data-effect='wipe-right']{opacity:1;clip-path:inset(0 100% 0 0);}.gracepress-darkmode-veil[data-effect='wipe-right'].is-active{clip-path:inset(0);}.gracepress-darkmode-veil[data-effect='wipe-down']{opacity:1;clip-path:inset(0 0 100% 0);}.gracepress-darkmode-veil[data-effect='wipe-down'].is-active{clip-path:inset(0);}.gracepress-darkmode-veil[data-effect='iris']{opacity:1;clip-path:circle(0 at 50% 50%);}.gracepress-darkmode-veil[data-effect='iris'].is-active{clip-path:circle(150% at 50% 50%);}.gracepress-darkmode-veil[data-effect='zoom']{opacity:0;transform:scale(.92);}.gracepress-darkmode-veil[data-effect='zoom'].is-active{opacity:1;transform:scale(1);}html.gracepress-dark-active .gracepress-darkmode-veil{filter:invert(100%) hue-rotate(180deg)!important;}.gracepress-dark-toggle-btn{background:#222!important;color:#fff!important;border:2px solid #444!important;border-radius:50px!important;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,.3)!important;transition:all .3s ease!important;display:inline-flex!important;align-items:center!important;justify-content:center!important;line-height:1!important;text-decoration:none!important;margin:0;z-index:999999;}.gracepress-dark-toggle-btn:hover{background:#444!important;transform:scale(1.05)!important;}.gracepress-pill-switch{background:#cbd5e1;border-radius:50px;position:relative;cursor:pointer;display:inline-block;vertical-align:middle;transition:.3s;box-shadow:inset 0 2px 4px rgba(0,0,0,.1);border:1px solid #94a3b8;box-sizing:content-box;z-index:999999;}.gracepress-pill-knob{background:#fff;border-radius:50%;position:absolute;top:2px;left:2px;display:flex;align-items:center;justify-content:center;transition:.3s;box-shadow:0 2px 5px rgba(0,0,0,.2);}html.gracepress-dark-active .gracepress-pill-switch{background:#0ea5e9;border-color:#0284c7;}.gracepress-text-btn{font-weight:bold;letter-spacing:1px;font-size:12px!important;padding:8px 16px!important;}.gracepress-size-small.gracepress-dark-toggle-btn{padding:6px 10px;font-size:14px;}.gracepress-size-normal.gracepress-dark-toggle-btn{padding:10px 15px;font-size:18px;}.gracepress-size-large.gracepress-dark-toggle-btn{padding:14px 22px;font-size:24px;}.gracepress-size-small.gracepress-pill-switch{width:50px;height:26px;}.gracepress-size-small .gracepress-pill-knob{width:20px;height:20px;font-size:12px;}html.gracepress-dark-active .gracepress-size-small .gracepress-pill-knob{transform:translateX(24px);}.gracepress-size-normal.gracepress-pill-switch{width:64px;height:34px;}.gracepress-size-normal .gracepress-pill-knob{width:28px;height:28px;font-size:16px;}html.gracepress-dark-active .gracepress-size-normal .gracepress-pill-knob{transform:translateX(30px);}.gracepress-size-large.gracepress-pill-switch{width:80px;height:42px;}.gracepress-size-large .gracepress-pill-knob{width:36px;height:36px;font-size:20px;}html.gracepress-dark-active .gracepress-size-large .gracepress-pill-knob{transform:translateX(38px);}.gracepress-floating{position:fixed;z-index:999999;}.gracepress-pos-bottom-right{bottom:25px;right:25px;}.gracepress-pos-bottom-left{bottom:25px;left:25px;}.gracepress-pos-top-right{top:25px;right:25px;}.gracepress-pos-top-left{top:25px;left:25px;}#wpadminbar .gracepress-dark-menu-trigger a{background:transparent;border:none;box-shadow:none;border-radius:0;padding:0 8px;}#wpadminbar .gracepress-dark-menu-trigger a:hover{background:transparent;transform:none;color:#72aee6;}".($pl?"@keyframes gpPulse{0%{box-shadow:0 0 0 0 rgba(100,100,100,.7);}70%{box-shadow:0 0 0 10px rgba(100,100,100,0);}100%{box-shadow:0 0 0 0 rgba(100,100,100,0);}}.gracepress-floating{animation:gpPulse 2.5s infinite;}.gracepress-floating:hover{animation:none;}":"").$cus."</style>";
|
||||||
|
echo "<style>html.gracepress-dark-active :where(.gp-header-logo-link img,.wp-block-image img,.wp-block-gallery img,.wp-block-media-text img,.wp-block-cover img,.wp-block-cover video,.wp-block-post-featured-image img,.wp-block-embed iframe,.wp-block-video video,.wp-block-audio audio,.gp-singular-hero img,.gp-singular-content img,.gp-video-archive-hero img,.gpb-hero-image,.gpb-post-feature-image,.gpb-post-feature-meta img,.gpb-card-avatar,.gpb-thumb-wrapper img,.gpb-thumb-wrapper video,.gpb-short-card img,.gpb-short-card video,.gpb-sidebar-thumb img,.gpb-series-hero-video,.gp-v-player-container video,.gp-v-next-card img,.g-up-t img,.g-up-t video,.widget img,.widget_media_image img,.widget_media_video video,.wp-caption img,.avatar){filter:invert(100%) hue-rotate(180deg) saturate(1.06) contrast(1.04) brightness(1.015)!important;opacity:1!important;mix-blend-mode:normal!important;}html.gracepress-dark-active .gpb-thumb-wrapper img,html.gracepress-dark-active .gpb-thumb-wrapper video,html.gracepress-dark-active .gpb-short-card img,html.gracepress-dark-active .gpb-short-card video,html.gracepress-dark-active .gpb-post-feature-image,html.gracepress-dark-active .gpb-hero-image,html.gracepress-dark-active .gpb-series-hero-video,html.gracepress-dark-active .gp-v-next-card img,html.gracepress-dark-active .g-up-t img,html.gracepress-dark-active .g-up-t video{filter:invert(100%) hue-rotate(180deg) saturate(1.06) contrast(1.04) brightness(1.015)!important;opacity:1!important;mix-blend-mode:normal!important;}html.gracepress-dark-active .gpb-grid-card.gpb-has-preview.gpb-preview-playing .gpb-thumb-wrapper img,html.gracepress-dark-active .gpb-short-card.gpb-has-preview.gpb-preview-playing img{opacity:0!important;visibility:hidden!important;}html.gracepress-dark-active .gpb-grid-card.gpb-preview-playing .gpb-thumb-wrapper video,html.gracepress-dark-active .gpb-short-card.gpb-preview-playing video{opacity:1!important;visibility:visible!important;}html.gracepress-dark-active .gpc video,html.gracepress-dark-active .plyr video,html.gracepress-dark-active video[controls],html.gracepress-dark-active #gp-ad-video{opacity:1!important;}html.gracepress-dark-active .gpc .plyr__poster,html.gracepress-dark-active .plyr__video-wrapper .plyr__poster{filter:invert(100%) hue-rotate(180deg)!important;}html.gracepress-dark-active .plyr--playing .plyr__poster,html.gracepress-dark-active .plyr--paused:not(.plyr--stopped) .plyr__poster{opacity:0!important;visibility:hidden!important;}html.gracepress-dark-active .gp-ad-play-fallback{filter:invert(100%) hue-rotate(180deg)!important;opacity:1!important;}</style>";
|
||||||
|
echo "<style>html.gracepress-dark-active :fullscreen video,html.gracepress-dark-active video:fullscreen,html.gracepress-dark-active .gp-live-player:fullscreen video,html.gracepress-dark-active .plyr:fullscreen video,html.gracepress-dark-active :-webkit-full-screen video,html.gracepress-dark-active video:-webkit-full-screen,html.gracepress-dark-active .gp-live-player:-webkit-full-screen video,html.gracepress-dark-active .plyr:-webkit-full-screen video{filter:none!important;opacity:1!important;mix-blend-mode:normal!important;}</style>";
|
||||||
|
echo "<style>html.gracepress-dark-active .gp-header-logo-link{position:relative!important;z-index:100002!important;isolation:isolate!important;}html.gracepress-dark-active .gp-header-logo-link,html.gracepress-dark-active .gp-header-logo-link *{opacity:1!important;mix-blend-mode:normal!important;}html.gracepress-dark-active .gp-header-logo-link img{filter:invert(100%) hue-rotate(180deg) saturate(1.08) contrast(1.02) brightness(1.02)!important;}</style>";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder hero images are CSS backgrounds rather than img elements. Counter the
|
||||||
|
* root dark-mode inversion on their composed surface so they retain the same
|
||||||
|
* color and contrast as the original artwork.
|
||||||
|
*/
|
||||||
|
function gp_darkmode_builder_hero_correction(){
|
||||||
|
if(!is_admin()&&gp_darkmode_is_excluded())return;
|
||||||
|
echo '<style>html.gracepress-dark-active .gpb-hero{filter:invert(100%) hue-rotate(180deg)!important;opacity:1!important;mix-blend-mode:normal!important;}</style>';
|
||||||
|
}
|
||||||
|
add_action('wp_head','gp_darkmode_builder_hero_correction',2);
|
||||||
|
|
||||||
|
add_action('wp_footer','gp_darkmode_ftr',99); add_action('admin_footer','gp_darkmode_ftr',99);
|
||||||
|
function gp_darkmode_ftr(){
|
||||||
|
if(!is_admin()&&gp_darkmode_is_excluded())return;
|
||||||
|
|
||||||
|
if(!is_admin()&&get_option('gracepress_dark_frontend_enable','1')==='1')echo wp_kses_post(gp_darkmode_btn(true));
|
||||||
|
$kbd=get_option('gp_dm_kbd',1)?'true':'false';
|
||||||
|
$effect_json=wp_json_encode(sanitize_key(get_option('gp_dm_veil_effect','fade')));
|
||||||
|
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- The effect is allowlisted and the keyboard flag is a PHP-generated boolean literal.
|
||||||
|
echo "<script>document.addEventListener('DOMContentLoaded',function(){const h=document.documentElement;const fx=$effect_json;let v=null,busy=false;function ms(){return Math.max(0,parseInt(getComputedStyle(h).getPropertyValue('--gp-dm-veil-ms'))||160);}function after(t,fn){window.setTimeout(fn,Math.max(60,t)+35);}function veil(n,done){const total=ms();if(total<=0){done();return;}const phase=Math.max(70,Math.round(total/2));if(!v){v=document.createElement('div');v.className='gracepress-darkmode-veil';document.body.appendChild(v);}v.dataset.effect=fx||'fade';v.style.transitionDuration=phase+'ms';v.style.background=n==='enabled'?'#000':'#fff';v.classList.remove('is-active');v.offsetHeight;requestAnimationFrame(function(){v.classList.add('is-active');after(phase,function(){done();requestAnimationFrame(function(){v.classList.remove('is-active');after(phase,function(){busy=false;});});});});}function a(s){if(s==='enabled'){h.classList.add('gracepress-dark-active');}else{h.classList.remove('gracepress-dark-active');}document.querySelectorAll('.gracepress-dark-toggle-trigger, .gracepress-dark-menu-trigger a').forEach(t=>{const l=t.getAttribute('data-light')||'🌙',d=t.getAttribute('data-dark')||'☀️',c=(s==='enabled')?d:l,i=t.querySelector('.gracepress-icon-wrapper');if(i)i.innerHTML=c;const k=t.querySelector('.gracepress-pill-knob');if(k)k.innerHTML=c;});}a(h.classList.contains('gracepress-dark-active')?'enabled':'disabled');function tgl(){if(busy)return;busy=true;const n=h.classList.contains('gracepress-dark-active')?'disabled':'enabled';localStorage.setItem('gracepressDarkMode',n);veil(n,function(){a(n);});}document.body.addEventListener('click',function(e){const t=e.target.closest('.gracepress-dark-toggle-trigger, .gracepress-dark-menu-trigger a');if(t){e.preventDefault();tgl();}});if($kbd){document.addEventListener('keydown',function(e){if(e.ctrlKey&&e.altKey&&(e.key==='d'||e.key==='D')){e.preventDefault();tgl();}});}});</script>";
|
||||||
|
}
|
||||||
|
|
||||||
|
add_shortcode('gracepress_dark_toggle',function($a){$a=shortcode_atts(['style'=>''],is_array($a)?$a:[]);return gp_darkmode_btn(false,$a['style']);});
|
||||||
|
|
||||||
|
add_action('admin_bar_menu', function($wp_admin_bar) {
|
||||||
|
if (!is_user_logged_in() || !is_admin_bar_showing()) return;
|
||||||
|
$wp_admin_bar->add_node([
|
||||||
|
'id' => 'gracepress-dark-admin-toggle',
|
||||||
|
'title' => '<span class="ab-icon dashicons dashicons-lightbulb"></span><span class="ab-label">Dark Mode</span>',
|
||||||
|
'href' => '#',
|
||||||
|
'meta' => [
|
||||||
|
'class' => 'gracepress-dark-menu-trigger',
|
||||||
|
'title' => 'Toggle GracePress dark mode',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}, 95);
|
||||||
|
|
||||||
|
add_filter('wp_nav_menu_items',function($i,$a){static $d=false;if(get_option('gracepress_dark_menu_integration','0')==='1'&&!$d){$ic=gp_darkmode_icons();$i.="<li class='menu-item'><a href='#' class='gracepress-dark-menu-trigger' data-light='{$ic['l']}' data-dark='{$ic['d']}'>Theme</a></li>";$d=true;}return $i;},10,2);
|
||||||
|
|
||||||
|
// ADMIN UI RESTORATION
|
||||||
|
function gp_darkmode_admin_page(){
|
||||||
|
if(!current_user_can('manage_options'))return;
|
||||||
|
$post_url = esc_url(admin_url('admin.php?page=gracepress-dark-mode'));
|
||||||
|
|
||||||
|
if(isset($_POST['gp_dm_reset_filters']) && isset($_POST['_gp_dm_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_gp_dm_nonce'])), 'gp_dm_save_action')){
|
||||||
|
update_option('gp_dm_brightness', 95);
|
||||||
|
update_option('gp_dm_contrast', 90);
|
||||||
|
update_option('gp_dm_dim_level', 85);
|
||||||
|
gp_darkmode_log('Dark Mode engine reset visual filters to factory defaults.','filters_reset',['brightness'=>95,'contrast'=>90,'media_dim'=>85]);
|
||||||
|
echo '<div class="notice notice-info is-dismissible" style="border-left-color:#64748b;"><p>Visual filters instantly restored to factory default levels (95/90/85).</p></div>';
|
||||||
|
} elseif(isset($_POST['gp_dm_save']) && isset($_POST['_gp_dm_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_gp_dm_nonce'])), 'gp_dm_save_action')){
|
||||||
|
foreach(['gracepress_dark_default_state','gracepress_dark_icon_style','gracepress_dark_button_size','gracepress_dark_button_position','gracepress_dark_transition_speed','gracepress_dark_exclusions','gracepress_dark_exclude_pages'] as $k) if(isset($_POST[$k])) update_option($k,sanitize_text_field(wp_unslash($_POST[$k])));
|
||||||
|
foreach(['gracepress_dark_frontend_enable','gracepress_dark_pulse_effect','gracepress_dark_menu_integration','gracepress_dark_admin_enable'] as $k) update_option($k,isset($_POST[$k])?'1':'0');
|
||||||
|
update_option('gp_dm_kbd',isset($_POST['gp_dm_kbd'])?1:0);
|
||||||
|
|
||||||
|
update_option('gp_dm_brightness',isset($_POST['gp_dm_brightness'])?absint(wp_unslash($_POST['gp_dm_brightness'])):95);
|
||||||
|
update_option('gp_dm_contrast',isset($_POST['gp_dm_contrast'])?absint(wp_unslash($_POST['gp_dm_contrast'])):90);
|
||||||
|
update_option('gp_dm_dim_level',isset($_POST['gp_dm_dim_level'])?absint(wp_unslash($_POST['gp_dm_dim_level'])):85);
|
||||||
|
update_option('gp_dm_veil_ms',max(0,min(800,absint($_POST['gp_dm_veil_ms'] ?? 160))));
|
||||||
|
$posted_veil_effect=sanitize_key($_POST['gp_dm_veil_effect'] ?? 'fade');
|
||||||
|
if(!in_array($posted_veil_effect,['fade','wipe-right','wipe-down','iris','zoom'],true))$posted_veil_effect='fade';
|
||||||
|
update_option('gp_dm_veil_effect',$posted_veil_effect);
|
||||||
|
|
||||||
|
if(isset($_POST['gp_dm_css'])) update_option('gp_dm_css',sanitize_textarea_field(wp_unslash($_POST['gp_dm_css'])));
|
||||||
|
gp_darkmode_log('Dark Mode configuration updated successfully.','settings_saved',[
|
||||||
|
'default_state'=>get_option('gracepress_dark_default_state','light'),
|
||||||
|
'frontend_enabled'=>get_option('gracepress_dark_frontend_enable','1')==='1',
|
||||||
|
'admin_enabled'=>get_option('gracepress_dark_admin_enable','0')==='1',
|
||||||
|
'menu_integration'=>get_option('gracepress_dark_menu_integration','0')==='1',
|
||||||
|
'keyboard_shortcut'=>get_option('gp_dm_kbd',1)==1,
|
||||||
|
'brightness'=>absint(get_option('gp_dm_brightness',95)),
|
||||||
|
'contrast'=>absint(get_option('gp_dm_contrast',90)),
|
||||||
|
'media_dim'=>absint(get_option('gp_dm_dim_level',85)),
|
||||||
|
'veil_ms'=>absint(get_option('gp_dm_veil_ms',160)),
|
||||||
|
'veil_effect'=>sanitize_key(get_option('gp_dm_veil_effect','fade')),
|
||||||
|
]);
|
||||||
|
echo '<div class="notice notice-success is-dismissible"><p>Dark Mode configurations securely locked in.</p></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$ds=get_option('gracepress_dark_default_state','light');$ic=get_option('gracepress_dark_icon_style','classic');$sz=get_option('gracepress_dark_button_size','normal');$ps=get_option('gracepress_dark_button_position','bottom-right');$sp=get_option('gracepress_dark_transition_speed','0.3s');$ex=get_option('gracepress_dark_exclusions','');$exp=get_option('gracepress_dark_exclude_pages','');
|
||||||
|
$fe=get_option('gracepress_dark_frontend_enable','1');$pl=get_option('gracepress_dark_pulse_effect','0');$mi=get_option('gracepress_dark_menu_integration','0');$ad=get_option('gracepress_dark_admin_enable','0');$kbd=get_option('gp_dm_kbd',1);$cus=get_option('gp_dm_css','');
|
||||||
|
|
||||||
|
$br=get_option('gp_dm_brightness',95);
|
||||||
|
$ct=get_option('gp_dm_contrast',90);
|
||||||
|
$dim=get_option('gp_dm_dim_level',85);
|
||||||
|
$veil_ms=max(0,min(800,absint(get_option('gp_dm_veil_ms',160))));
|
||||||
|
$veil_effect=sanitize_key(get_option('gp_dm_veil_effect','fade'));
|
||||||
|
if(!in_array($veil_effect,['fade','wipe-right','wipe-down','iris','zoom'],true))$veil_effect='fade';
|
||||||
|
$admin_sunrise_url=plugin_dir_url(__FILE__).'assets/images/admin-sunrise.png';
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
.gp-admin-wrap{max-width:1180px;margin:18px 20px 20px 0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;color:#334155}.gp-admin-wrap *{box-sizing:border-box}
|
||||||
|
.gp-header{background:linear-gradient(100deg,rgba(8,18,35,.9) 0%,rgba(15,36,58,.72) 58%,rgba(76,47,24,.5) 100%),url('<?php echo esc_url($admin_sunrise_url)?>') center 58%/cover;color:#fff;padding:26px 30px;border-radius:10px;margin-bottom:18px;border-left:6px solid #0ea5e9;box-shadow:0 7px 18px rgba(15,23,42,.16)}
|
||||||
|
.gp-header h1{color:#fff;margin:0 0 6px;font-size:27px;font-weight:800;letter-spacing:-.35px;text-shadow:0 2px 4px rgba(0,0,0,.55)}.gp-header p{margin:0;font-size:14px;opacity:.96;line-height:1.55;max-width:860px;text-shadow:0 1px 3px rgba(0,0,0,.55)}
|
||||||
|
.gp-layout-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px;align-items:start}.gp-card{background:#fff;padding:20px;border-radius:9px;border:1px solid #dbe3ec;box-shadow:0 2px 8px rgba(15,23,42,.035);margin-bottom:18px}.gp-card h3{margin:0 0 16px;padding:0 0 11px;border-bottom:1px solid #edf1f5;font-size:17px;color:#0f172a;display:flex;align-items:center;gap:8px;font-weight:700}
|
||||||
|
.gp-f{display:flex;flex-direction:column;gap:5px;margin-bottom:14px}.gp-f label{font-weight:700;font-size:13px;color:#0f172a}.gp-f input[type="text"],.gp-f input[type="number"],.gp-f select{width:min(100%,430px);min-height:34px;padding:4px 8px;border:1px solid #b8c4d1;border-radius:5px;font-size:13px;box-shadow:inset 0 1px 2px rgba(15,23,42,.025)}.gp-f textarea{width:100%;padding:8px;border:1px solid #b8c4d1;border-radius:5px;font-size:13px;box-shadow:inset 0 1px 2px rgba(15,23,42,.025)}.gp-f input[type="range"]{width:100%;margin:2px 0}.gp-f small{line-height:1.4}
|
||||||
|
.gp-cg{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px;margin-bottom:14px}.gp-cl{display:flex;align-items:center;gap:7px;background:#f8fafc;padding:9px 10px;border-radius:5px;border:1px solid #dbe3ec;cursor:pointer;font-weight:600;font-size:13px}.gp-btn-primary{background:#0ea5e9;color:#fff;border:1px solid #0284c7;padding:9px 18px;font-size:14px;font-weight:700;border-radius:5px;cursor:pointer;display:inline-block;text-decoration:none;transition:.2s}.gp-btn-primary:hover{background:#0284c7;color:#fff;transform:translateY(-1px);box-shadow:0 3px 5px rgba(14,165,233,.2)}.gp-btn-reset{background:#64748b;color:#fff;border:1px solid #526173;padding:9px 16px;font-size:14px;font-weight:700;border-radius:5px;cursor:pointer;display:inline-block;text-decoration:none;transition:.2s;margin-left:8px}.gp-btn-reset:hover{background:#475569;color:#fff;transform:translateY(-1px);box-shadow:0 3px 5px rgba(100,116,139,.2)}.gp-footer{text-align:center;margin-top:24px;padding:24px 20px 8px;border-top:1px solid #e2e8f0}code{background:#f1f5f9;padding:3px 6px;border-radius:4px;font-family:monospace;color:#b4236c;font-size:12px;font-weight:700;border:1px solid #e2e8f0}
|
||||||
|
.gp-live-preview { width:100%; padding:18px; background:#fff; color:#334155; border:1px dashed #cbd5e1; border-radius:7px; margin-top:12px; transition: 0.3s; }
|
||||||
|
.gp-live-preview.active { filter: invert(100%) hue-rotate(180deg) brightness(var(--prev-br, 95%)) contrast(var(--prev-ct, 90%)) !important; background:#fff !important; color:#334155 !important; }
|
||||||
|
@media(max-width:980px){.gp-layout-grid{grid-template-columns:1fr}.gp-admin-wrap{margin-right:12px}.gp-cg{grid-template-columns:1fr 1fr}}
|
||||||
|
@media(max-width:600px){.gp-header{padding:22px 20px}.gp-card{padding:17px}.gp-cg{grid-template-columns:1fr}.gp-btn-reset{margin:8px 0 0}}
|
||||||
|
</style>
|
||||||
|
<div class="gp-admin-wrap"><header class="gp-header"><h1>🌓 Dark Mode Engine</h1><p>Version <?php echo esc_html(GP_DARKMODE_VERSION)?>. Features absolute Anti-FOUC synchronization, hardware-accelerated CSS inversion, precise filter tuning, variable media correction, configurable two-phase transitions, and optional local OmniLog integration.</p></header>
|
||||||
|
|
||||||
|
<form method="post" action="<?php echo esc_url($post_url); ?>"><?php wp_nonce_field('gp_dm_save_action', '_gp_dm_nonce'); ?>
|
||||||
|
<div class="gp-layout-grid">
|
||||||
|
<div>
|
||||||
|
<div class="gp-card"><h3><span class="dashicons dashicons-visibility" style="color:#0ea5e9;"></span> 1. Core Behavior</h3>
|
||||||
|
<div class="gp-f"><label>Default State (First Visit)</label><select name="gracepress_dark_default_state"><option value="light" <?php echo selected('light',$ds)?>>Light Mode (Standard)</option><option value="dark" <?php echo selected('dark',$ds)?>>Dark Mode (Always)</option><option value="os" <?php echo selected('os',$ds)?>>Follow OS Preference (Recommended)</option><option value="auto" <?php echo selected('auto',$ds)?>>Time Based (Sunset to Sunrise)</option></select></div>
|
||||||
|
<div class="gp-f"><label>Filter Brightness (Current: <span id="br_val"><?php echo esc_html($br)?></span>%)</label><input type="range" id="inp_br" name="gp_dm_brightness" min="0" max="150" value="<?php echo esc_attr($br)?>" oninput="upd_prev()"></div>
|
||||||
|
<div class="gp-f"><label>Filter Contrast (Current: <span id="ct_val"><?php echo esc_html($ct)?></span>%)</label><input type="range" id="inp_ct" name="gp_dm_contrast" min="0" max="150" value="<?php echo esc_attr($ct)?>" oninput="upd_prev()"></div>
|
||||||
|
<div class="gp-f"><label>Media Dimming Level (Images/Videos opacity: <?php echo esc_html($dim)?>%)</label><input type="range" name="gp_dm_dim_level" min="0" max="100" value="<?php echo esc_attr($dim)?>"></div>
|
||||||
|
<div class="gp-cg"><label class="gp-cl"><input type="checkbox" name="gp_dm_kbd" value="1" <?php echo checked($kbd,1)?>> Enable Ctrl+Alt+D Shortcut</label></div>
|
||||||
|
</div>
|
||||||
|
<div class="gp-card"><h3><span class="dashicons dashicons-art" style="color:#0ea5e9;"></span> 2. Visual Aesthetics</h3>
|
||||||
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;">
|
||||||
|
<div class="gp-f"><label>Toggle Style</label><select name="gracepress_dark_icon_style"><option value="classic" <?php echo selected('classic',$ic)?>>Classic Emoji (🌙/☀️)</option><option value="minimal" <?php echo selected('minimal',$ic)?>>Minimal Dots (⚫/⚪)</option><option value="bulbs" <?php echo selected('bulbs',$ic)?>>Bulbs (🌑/💡)</option><option value="text" <?php echo selected('text',$ic)?>>Text (DARK/LIGHT)</option><option value="switch" <?php echo selected('switch',$ic)?>>Pill Switch</option></select></div>
|
||||||
|
<div class="gp-f"><label>Button Size</label><select name="gracepress_dark_button_size"><option value="small" <?php echo selected('small',$sz)?>>Small</option><option value="normal" <?php echo selected('normal',$sz)?>>Normal</option><option value="large" <?php echo selected('large',$sz)?>>Large</option></select></div>
|
||||||
|
</div>
|
||||||
|
<div class="gp-f"><label>Floating Position</label><select name="gracepress_dark_button_position"><option value="bottom-right" <?php echo selected('bottom-right',$ps)?>>Bottom Right</option><option value="bottom-left" <?php echo selected('bottom-left',$ps)?>>Bottom Left</option><option value="top-right" <?php echo selected('top-right',$ps)?>>Top Right</option><option value="top-left" <?php echo selected('top-left',$ps)?>>Top Left</option></select></div>
|
||||||
|
<div class="gp-f"><label>Transition Speed (CSS Time)</label><input type="text" name="gracepress_dark_transition_speed" value="<?php echo esc_attr($sp)?>" placeholder="e.g. 0.3s or 500ms"></div>
|
||||||
|
<div class="gp-f"><label>Mode Switch Transition</label><select name="gp_dm_veil_effect"><option value="fade" <?php echo selected('fade',$veil_effect)?>>Fade</option><option value="wipe-right" <?php echo selected('wipe-right',$veil_effect)?>>Side Wipe</option><option value="wipe-down" <?php echo selected('wipe-down',$veil_effect)?>>Vertical Wipe</option><option value="iris" <?php echo selected('iris',$veil_effect)?>>Iris</option><option value="zoom" <?php echo selected('zoom',$veil_effect)?>>Zoom Fade</option></select></div>
|
||||||
|
<div class="gp-f"><label>Mode Switch Duration (ms)</label><input type="number" name="gp_dm_veil_ms" min="0" max="800" step="10" value="<?php echo esc_attr($veil_ms)?>"><small style="color:#64748b;">0 disables the transition veil. The duration is split into a cover phase and a reveal phase, so 220ms feels like a crisp two-way transition.</small></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="gp-card"><h3><span class="dashicons dashicons-admin-settings" style="color:#0ea5e9;"></span> 3. Integration & Rendering</h3>
|
||||||
|
<div class="gp-cg">
|
||||||
|
<label class="gp-cl"><input type="checkbox" name="gracepress_dark_frontend_enable" value="1" <?php echo checked('1',$fe)?>> Auto-Float on Frontend</label>
|
||||||
|
<label class="gp-cl"><input type="checkbox" name="gracepress_dark_admin_enable" value="1" <?php echo checked('1',$ad)?>> Enable in WP Admin</label>
|
||||||
|
<label class="gp-cl"><input type="checkbox" name="gracepress_dark_pulse_effect" value="1" <?php echo checked('1',$pl)?>> Floating Pulse Effect</label>
|
||||||
|
<label class="gp-cl"><input type="checkbox" name="gracepress_dark_menu_integration" value="1" <?php echo checked('1',$mi)?>> Auto-Inject into Menus</label>
|
||||||
|
</div>
|
||||||
|
<div style="background:#f0fdf4;border:1px solid #bbf7d0;padding:15px;border-radius:6px;font-size:13px;color:#166534;line-height:1.5;">
|
||||||
|
<strong>Manual Shortcode:</strong> Use <code>[gracepress_dark_toggle]</code> to place the button exactly where you want it. You can also override the style locally: <code>[gracepress_dark_toggle style="switch"]</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gp-live-preview active" id="dm_preview">
|
||||||
|
<h4 style="margin-top:0; color:#0f172a;">Matrix Filter Preview Hub</h4>
|
||||||
|
<p style="margin-bottom:0; font-size:13px;">Drag the sliders on the left to real-time sync and balance text opacity variables before saving configurations.</p>
|
||||||
|
</div>
|
||||||
|
<div style="text-align:right; margin-top:10px;"><button type="button" class="gp-btn-sm" style="background:#475569; color:#fff; border:none; padding:6px 12px; border-radius:4px; cursor:pointer; font-size:11px;" onclick="localStorage.removeItem('gracepressDarkMode');alert('Cookie memory cleared. Default parameters will trigger on next visit.');">Wipe Browser LocalStorage Cache</button></div>
|
||||||
|
</div>
|
||||||
|
<div class="gp-card" style="border-top-color:#f59e0b;"><h3><span class="dashicons dashicons-editor-code" style="color:#0ea5e9;"></span> 4. Advanced Customization</h3>
|
||||||
|
<div class="gp-f"><label>Exclude Specific Pages (Comma-separated Page IDs)</label><input type="text" name="gracepress_dark_exclude_pages" value="<?php echo esc_attr($exp)?>" placeholder="e.g. 12, 45, 102">
|
||||||
|
<small style="color:#64748b;">Completely disables the Dark Mode engine on these specific Post/Page IDs.</small></div>
|
||||||
|
|
||||||
|
<div class="gp-f"><label>Exclude Specific Elements (Comma-separated CSS Classes/IDs)</label><input type="text" name="gracepress_dark_exclusions" value="<?php echo esc_attr($ex)?>" placeholder=".no-dark, #header-logo, .elementor-image">
|
||||||
|
<small style="color:#64748b;">Protects specific HTML elements from the inversion filter.</small></div>
|
||||||
|
|
||||||
|
<div class="gp-f"><label>Custom Dark CSS Overrides</label><textarea name="gp_dm_css" rows="4" style="font-family:monospace;" placeholder="html.gracepress-dark-active .my-custom-box { background: #333; }"><?php echo esc_textarea($cus)?></textarea></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="text-align:center; padding-top:10px;">
|
||||||
|
<button type="submit" name="gp_dm_save" class="gp-btn-primary"><span class="dashicons dashicons-lock" style="margin-top:3px;"></span> Lock In Settings</button>
|
||||||
|
<button type="submit" name="gp_dm_reset_filters" class="gp-btn-reset" onclick="return confirm('Are you sure you want to restore the visual filters to their default factory levels?');"><span class="dashicons dashicons-image-rotate" style="margin-top:3px;"></span> Factory Reset Filters</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
function upd_prev() {
|
||||||
|
let br = document.getElementById('inp_br').value;
|
||||||
|
let ct = document.getElementById('inp_ct').value;
|
||||||
|
document.getElementById('br_val').innerText = br;
|
||||||
|
document.getElementById('ct_val').innerText = ct;
|
||||||
|
let box = document.getElementById('dm_preview');
|
||||||
|
box.style.setProperty('--prev-br', br + '%');
|
||||||
|
box.style.setProperty('--prev-ct', ct + '%');
|
||||||
|
}
|
||||||
|
setTimeout(upd_prev, 150);
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
$gp_footer_support = 'jason [at] christp2p [dot] com';
|
||||||
|
$gp_footer_donate_url = apply_filters('gracepress_footer_donate_url', 'https://christp2p.com/gracepress/donate/');
|
||||||
|
$gp_footer_jesus_url = apply_filters('gracepress_footer_about_jesus_url', 'https://christp2p.com/about-jesus-christ/');
|
||||||
|
$gp_footer_support_url = apply_filters('gracepress_footer_support_url', 'https://christp2p.com/gracepress/support/');
|
||||||
|
?>
|
||||||
|
<footer class="gp-footer"><p style="font-style:italic;font-size:16px;color:#1e293b;margin-bottom:15px;">"The light shines in the darkness, and the darkness has not overcome it." — John 1:5</p><div style="display:flex;justify-content:center;gap:14px;flex-wrap:wrap;margin-top:15px;font-weight:bold;color:#0ea5e9;"><a href="<?php echo esc_url($gp_footer_donate_url); ?>" style="color:inherit;text-decoration:none;">Donate</a><a href="<?php echo esc_url($gp_footer_jesus_url); ?>" style="color:inherit;text-decoration:none;">About Jesus Christ</a><a href="<?php echo esc_url($gp_footer_support_url); ?>" style="color:inherit;text-decoration:none;">Support</a></div><div style="margin-top:12px;font-weight:bold;color:#0ea5e9;">Support contact: <?php echo esc_html($gp_footer_support); ?></div><div style="margin-top:30px;font-size:13px;opacity:0.7;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:#0f172a;">All Glory to Jesus Christ<br><span style="font-weight:500;text-transform:none;letter-spacing:0;display:block;margin-top:5px;">Engineered by Christ IT • © <?php echo esc_html(wp_date('Y')); ?></span></div></footer></div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
add_filter('plugin_action_links_'.plugin_basename(__FILE__),function($l){array_unshift($l,'<a href="'.(class_exists('GracePress_Central_Hub')?admin_url('admin.php?page=gracepress-dark-mode'):admin_url('options-general.php?page=gracepress-dark-mode')).'" style="font-weight:bold;color:#0ea5e9;">Configure</a>');return $l;});
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
=== Dark Mode by GracePress ===
|
||||||
|
Contributors: gracepress
|
||||||
|
Tags: dark mode, accessibility, night mode, admin dark mode, color scheme
|
||||||
|
Requires at least: 6.0
|
||||||
|
Tested up to: 7.1
|
||||||
|
Requires PHP: 7.4
|
||||||
|
Stable tag: 4.8.18
|
||||||
|
License: GPLv2 or later
|
||||||
|
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||||
|
|
||||||
|
A complete dark mode engine with anti-flash loading, visual tuning, transitions, flexible controls, and administrator support.
|
||||||
|
|
||||||
|
== Description ==
|
||||||
|
|
||||||
|
Dark Mode by GracePress provides a complete control panel for configuring dark mode on the public site and in WordPress administration.
|
||||||
|
|
||||||
|
Features include:
|
||||||
|
|
||||||
|
* Device-theme, scheduled, light, or dark first-visit behavior.
|
||||||
|
* Five transition treatments: fade, horizontal wipe, vertical wipe, iris, and zoom.
|
||||||
|
* Brightness, contrast, and media controls.
|
||||||
|
* Five toggle styles with three sizes and four floating positions.
|
||||||
|
* Floating, menu, shortcode, administrator-bar, and keyboard controls.
|
||||||
|
* Independent public-site and WordPress administration enablement.
|
||||||
|
* Page and selector exclusions for compatibility.
|
||||||
|
* Custom dark-mode CSS overrides.
|
||||||
|
* Compatibility corrections for images, videos, fullscreen playback, and common WordPress media surfaces.
|
||||||
|
|
||||||
|
The plugin does not collect telemetry and does not depend on a remote service. If GracePress Central Hub or OmniLog is installed, Dark Mode can integrate with those local plugins; neither is required.
|
||||||
|
|
||||||
|
== Installation ==
|
||||||
|
|
||||||
|
1. Upload the plugin directory to `/wp-content/plugins/`, or install the ZIP through Plugins > Add New.
|
||||||
|
2. Activate Dark Mode by GracePress.
|
||||||
|
3. Open Settings > Dark Mode.
|
||||||
|
4. Configure the public-site and administrator behavior, then save.
|
||||||
|
|
||||||
|
Use `[gracepress_dark_toggle]` to place a toggle in content. The optional `style` attribute accepts `classic`, `minimal`, `bulbs`, `text`, or `switch`.
|
||||||
|
|
||||||
|
== Frequently Asked Questions ==
|
||||||
|
|
||||||
|
= Does it follow the visitor's device theme? =
|
||||||
|
|
||||||
|
Yes. Choose Follow device theme as the first-visit behavior.
|
||||||
|
|
||||||
|
= Does it work in WordPress administration? =
|
||||||
|
|
||||||
|
Yes. Administrator dark mode can be enabled independently, with a toolbar toggle available to signed-in users.
|
||||||
|
|
||||||
|
= Does it send data to GracePress? =
|
||||||
|
|
||||||
|
No. This public plugin contains no telemetry or private update connection.
|
||||||
|
|
||||||
|
= Does it require another GracePress plugin? =
|
||||||
|
|
||||||
|
No. Dark Mode works independently. It can use local GracePress Central Hub routing and OmniLog events when those plugins are already installed and active.
|
||||||
|
|
||||||
|
== Changelog ==
|
||||||
|
|
||||||
|
* Continued preparing the mature GracePress Dark Mode engine for public WordPress.org distribution while retaining version 4.8.18 during development.
|
||||||
|
* Preserved all established runtime behavior, settings, option keys, controls, transitions, media corrections, and local integrations.
|
||||||
|
* Removed anonymous ecosystem pings and the private external update engine.
|
||||||
|
* Reorganized the complete settings interface into a denser responsive layout without removing controls.
|
||||||
|
* Replaced the remote administration background with an original bundled sunrise image.
|
||||||
|
* Corrected CSS-backed GracePress Builder hero artwork in dark mode while preserving original color, contrast, and opacity.
|
||||||