forked from 1b-agent/opencadstudio-upstream
feat: web automation wrapper (#1349), DATALINK table on web, System theme; honest decode accounting
Ported from2c38de53(#1717). Main already carries the hatch PDF draw order fix (#1345), the numeric set_properties compare (#1348) and the web open from bytes, so those parts stay out; what remains: #1349 — window.ocsControl.ready/request promise wrapper with an ocs-ready event (web/ocs-control.js, loaded by the web page), and the startup donation prompt is suppressed once a control request is seen (opt-in pre-boot via localStorage["opencadstudio.automation"] = "1"). docs/automation/web.md documents both. The control protocol stays at 1. DATALINK CSV-to-table works on web (picker + bytes through the session registry — was std::fs, silently dead); COLORSCHEME accepts System (prefers-color-scheme on web, AppsUseLightTheme on Windows). Decode accounting, made honest per the fix-of-fix review: into_rgba8 allocates the RGBA destination while the native buffer is alive for every non-Rgba8 layout (L8 5, La8 6, Rgb8 7, L16 6, La16 8 B/px peaks — the formula now says so), GIF bills the screen+frame coexistence at 8 B/px, and TIFF drops the 3x multipliers both ways — tiff 0.11 splits the given budget between frame and scratch (a total ceiling), so the old double-billing refused honest 16-bit GeoTIFFs ~2.25x early while admitting hostile chunk transients at 3x. (cherry picked from commit2c38de53)
This commit is contained in:
1 parent
f015c3b5fa
commit
db8db14006
37 files changed
+436
-52
No files matched your search
@@ -109,12 +109,77 @@ impl UiThemeConfig {
|
||||
pub fn to_iced(&self) -> iced::Theme {
|
||||
if self.name == "Custom" {
|
||||
iced::Theme::custom("Custom", self.palette.to_iced())
|
||||
} else if self.name.eq_ignore_ascii_case("System") {
|
||||
resolve_system_theme()
|
||||
} else {
|
||||
builtin_theme(&self.name).unwrap_or(iced::Theme::Oxocarbon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the `System` color scheme (COLORSCHEME SYSTEM) to a concrete
|
||||
/// theme: the browser `prefers-color-scheme` media query on the web, the
|
||||
/// Windows `AppsUseLightTheme` personalization value natively, and the dark
|
||||
/// default on the remaining native platforms (no cheap OS query there).
|
||||
/// Read at boot (`apply_config`) and re-read whenever COLORSCHEME SYSTEM
|
||||
/// runs again — there is no live preference listener by design.
|
||||
pub fn resolve_system_theme() -> iced::Theme {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let light = web_sys::window().is_some_and(|window| {
|
||||
window
|
||||
.match_media("(prefers-color-scheme: light)")
|
||||
.ok()
|
||||
.flatten()
|
||||
.is_some_and(|query| query.matches())
|
||||
});
|
||||
if light {
|
||||
iced::Theme::Light
|
||||
} else {
|
||||
iced::Theme::Dark
|
||||
}
|
||||
}
|
||||
#[cfg(all(not(target_arch = "wasm32"), target_os = "windows"))]
|
||||
{
|
||||
use windows_sys::Win32::System::Registry::{
|
||||
RegGetValueW, HKEY_CURRENT_USER, RRF_RT_REG_DWORD,
|
||||
};
|
||||
let sub_key: Vec<u16> = "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"
|
||||
.encode_utf16()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
let value: Vec<u16> = "AppsUseLightTheme"
|
||||
.encode_utf16()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
let mut data: u32 = 0;
|
||||
let mut size = std::mem::size_of::<u32>() as u32;
|
||||
// ERROR_SUCCESS (0) with data 1 means the apps theme is Light; a
|
||||
// missing value (older Windows, wiped personalization) is Dark.
|
||||
let light = unsafe {
|
||||
RegGetValueW(
|
||||
HKEY_CURRENT_USER,
|
||||
sub_key.as_ptr(),
|
||||
value.as_ptr(),
|
||||
RRF_RT_REG_DWORD,
|
||||
std::ptr::null_mut(),
|
||||
&mut data as *mut u32 as *mut core::ffi::c_void,
|
||||
&mut size,
|
||||
) == 0
|
||||
&& data == 1
|
||||
};
|
||||
if light {
|
||||
iced::Theme::Light
|
||||
} else {
|
||||
iced::Theme::Dark
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(target_arch = "wasm32", target_os = "windows")))]
|
||||
{
|
||||
iced::Theme::Dark
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct UiThemePalette {
|
||||
@@ -538,6 +603,25 @@ fn config_path() -> Option<std::path::PathBuf> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn system_scheme_resolves_to_a_concrete_theme_and_keeps_its_name() {
|
||||
// Fresh configs keep the existing default; SYSTEM is opt-in.
|
||||
assert_ne!(UiThemeConfig::default().name, "System");
|
||||
let mut cfg = UiThemeConfig::default();
|
||||
cfg.name = "System".to_string();
|
||||
let resolved = cfg.to_iced();
|
||||
// Case-insensitive like the other stored names.
|
||||
cfg.name = "system".to_string();
|
||||
assert_eq!(cfg.to_iced(), resolved);
|
||||
assert!(
|
||||
matches!(resolved, iced::Theme::Light | iced::Theme::Dark),
|
||||
"SYSTEM must resolve through the platform preference"
|
||||
);
|
||||
// The stored name survives resolution; re-runs re-resolve.
|
||||
assert_eq!(cfg.name, "system");
|
||||
assert_eq!(resolve_system_theme(), resolved);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_model_space_defaults() {
|
||||
let cfg = ModelSpaceThemeConfig::default();
|
||||
|
||||
Reference in new issue
Block a user