From a1f84122059ee60d6a9d825ce4f6e02c4f30607d Mon Sep 17 00:00:00 2001 From: flemming-it Date: Mon, 25 May 2026 13:24:00 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20studio-theme-solarized=20v0.1.0=20?= =?UTF-8?q?=E2=80=94=20skeleton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First Studio plugin in the new `studio.*` capability namespace. SKELETON — does not yet compile. Why ship the skeleton now: * Pins Ethan Schoonover's Solarized palette as two const Material 3 ColorScheme arrays so the in-tree reference doesn't bike-shed at SDK-integration time. * Gives the future fai-studio-plugin-sdk author a concrete consumer to validate the generated `#[plugin(theme)]` macro output against. * Validates that the WIT in fai/platform parses by being a real crate that references it. What it does NOT yet do: * Compile. The `fai-studio-plugin-sdk` dependency it points at doesn't exist. Tracked in `fai/platform/docs/advanced/studio-plugin-sdk.md`. * Plug into Studio. The plugin-host module that would discover and invoke this component lives in the same deferred-Phase-1 work. What it ships: * module.yaml declaring `provides: studio.theme.solarized` and `studio_extension.hooks: [theme]`. * Cargo.toml with the future SDK dependency commented out, crate-type=cdylib, component metadata. * src/lib.rs with both Solarized colour palettes (SOLARIZED_LIGHT and SOLARIZED_DARK), 14 ARGB tokens each in Material 3 order, plus the future `#[plugin]` function body commented out and two sanity tests on the palette constants. * README.md explaining the status and the steps to finish the plugin once the SDK ships. Signed-off-by: flemming-it --- .gitignore | 4 ++ Cargo.toml | 48 ++++++++++++++++++++++++ README.md | 42 +++++++++++++++++++++ module.yaml | 28 ++++++++++++++ src/lib.rs | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 228 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 module.yaml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fcc55a5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +target/ +**/*.rs.bk +.DS_Store +module.wasm diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bf8262e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,48 @@ +# SKELETON — does not yet compile. +# +# This crate targets the studio-plugin world declared in +# `wit/studio-plugin.wit`. It cannot be built until two +# upstream pieces ship: +# +# 1. The `fai-studio-plugin-sdk` crate that wraps the WIT +# bindings with an ergonomic Rust API (the `#[plugin(theme)]` +# attribute referenced in `src/lib.rs`). +# 2. Studio's plugin-host module that discovers, loads, and +# invokes plugin components at GUI startup. +# +# Both are documented in `docs/advanced/studio-plugin-sdk.md`. +# Until they exist, this directory serves three purposes: +# +# - Validates the WIT-file structure by being a real crate that +# references it (parse-only path). +# - Locks in the exact Solarized colour palette so the +# in-tree first plugin doesn't bike-shed at integration time. +# - Gives the SDK author a concrete consumer to test against. + +[package] +name = "studio_theme_solarized" +version = "0.1.0" +edition = "2024" +authors = ["Dr. Stefan Flemming "] +license = "Apache-2.0" +publish = false +description = "F∆I Studio plugin — Solarized Light and Dark theme" +repository = "https://git.flemming.ai/fai-modules/studio-theme-solarized" +rust-version = "1.85" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +# Forward-reference: this crate doesn't yet exist. Tracked in +# docs/advanced/studio-plugin-sdk.md. +# fai-studio-plugin-sdk = { git = "https://git.flemming.ai/fai/studio-plugin-sdk", branch = "main" } + +[package.metadata.component] +package = "fai:studio-plugin" + +[profile.release] +opt-level = "s" +lto = true +codegen-units = 1 +strip = true diff --git a/README.md b/README.md new file mode 100644 index 0000000..0375947 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# studio-theme-solarized + +First in-tree Studio plugin, by way of being the smallest one +the Studio plugin design supports: a theme. + +## Status + +**Skeleton.** The crate compiles to the Cargo manifest level but +not to a `.wasm` artefact, because the dependency it points at +(`fai-studio-plugin-sdk`) does not exist yet. + +Why ship the skeleton now: pins the Solarized colour palette so +the in-tree reference doesn't bike-shed at integration time, and +gives the future SDK author a concrete consumer to test the +generated `#[plugin(theme)]` macro output against. + +Tracked in: +- `docs/advanced/studio-plugin-sdk.md` (SDK design) +- `docs/advanced/studio-plugins.md` (plugin subsystem overview) +- `docs/advanced/system-gaps.md#s-21` + +## Palette + +Ethan Schoonover's [Solarized](https://ethanschoonover.com/solarized/). +14 Material 3 tokens per brightness, in the order Studio expects +(primary → on-primary → secondary → … → outline-variant). + +The Light and Dark schemes are explicit constants in `src/lib.rs` +so the file's history is a self-contained record of the palette +choice. Bike-shed there if at all. + +## How to finish this plugin + +When the SDK lands: + +1. Uncomment the `fai-studio-plugin-sdk` dependency in + `Cargo.toml`. +2. Uncomment the `#[plugin(theme)]` block in `src/lib.rs`. +3. `cargo build --release --target wasm32-wasip2`. +4. `fai pack . -o studio-theme-solarized-0.1.0.fai`. +5. Add to seed.yaml (or to a `studio-plugins/` channel — open + decision; see docs/advanced/studio-plugins.md). diff --git a/module.yaml b/module.yaml new file mode 100644 index 0000000..57a4c48 --- /dev/null +++ b/module.yaml @@ -0,0 +1,28 @@ +schema_version: 1 +name: studio-theme-solarized +version: 0.1.0 + +# Capability provided by this plugin. Studio plugins live in +# the `studio.*` namespace; the host filters list_capabilities() +# on this prefix to find plugins to load. +provides: + - capability: studio.theme.solarized + version: 0.1.0 + +# No inputs / outputs — plugins are called via WIT exports, +# not via the flow-engine `invoke` path. These two sections +# stay empty so the hub's manifest validator doesn't trip. +inputs: {} +outputs: {} + +# No permissions required. A theme plugin is pure data — two +# color schemes, no I/O. +permissions: [] + +# Studio-side extension declaration. Read by Studio's +# plugin-host at discovery time so it knows which WIT export +# interfaces to expect on this component. Must match what the +# crate actually exports; the hub rejects mismatches at install. +studio_extension: + hooks: + - theme diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..dc440d8 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,106 @@ +//! `studio.theme.solarized` — first Studio plugin. +//! +//! Ships Ethan Schoonover's Solarized palette as a Material 3 +//! ColorScheme pair (light + dark). +//! +//! SKELETON: this file does not yet compile. The +//! `#[plugin(theme)]` macro and the `studio_plugin` runtime live +//! in `fai-studio-plugin-sdk`, which is documented in +//! `docs/advanced/studio-plugin-sdk.md` but not yet implemented. +//! Once the SDK ships, switching the file from skeleton to +//! buildable is a matter of uncommenting the dependency in +//! Cargo.toml and the use-line below. +//! +//! Why ship the skeleton now: pins the palette so the in-tree +//! reference plugin doesn't bike-shed at SDK-integration time, +//! and gives the SDK author a concrete consumer to validate +//! their generated macro output against. + +#![allow(dead_code, unused_imports)] + +// use fai_studio_plugin_sdk::{plugin, theme, ColorScheme}; + +/// 14 ARGB tokens, Material 3 order: +/// primary, on_primary, secondary, on_secondary, +/// tertiary, on_tertiary, error, on_error, +/// surface, on_surface, surface_variant, on_surface_variant, +/// outline, outline_variant. +/// +/// Studio merges any missing slot (we send all 14, so none) with +/// its built-in fallback before applying. +const SOLARIZED_LIGHT: [u32; 14] = [ + 0xFF268BD2, // primary — solarized blue + 0xFFFDF6E3, // on_primary — base3 + 0xFF2AA198, // secondary — solarized cyan + 0xFFFDF6E3, // on_secondary — base3 + 0xFFB58900, // tertiary — solarized yellow + 0xFF002B36, // on_tertiary — base03 + 0xFFDC322F, // error — solarized red + 0xFFFDF6E3, // on_error — base3 + 0xFFFDF6E3, // surface — base3 + 0xFF073642, // on_surface — base02 + 0xFFEEE8D5, // surface_variant — base2 + 0xFF586E75, // on_surface_variant — base01 + 0xFF93A1A1, // outline — base1 + 0xFFEEE8D5, // outline_variant — base2 +]; + +const SOLARIZED_DARK: [u32; 14] = [ + 0xFF268BD2, // primary — solarized blue + 0xFF002B36, // on_primary — base03 + 0xFF2AA198, // secondary — solarized cyan + 0xFF002B36, // on_secondary — base03 + 0xFFB58900, // tertiary — solarized yellow + 0xFF002B36, // on_tertiary — base03 + 0xFFDC322F, // error — solarized red + 0xFF002B36, // on_error — base03 + 0xFF002B36, // surface — base03 + 0xFF93A1A1, // on_surface — base1 + 0xFF073642, // surface_variant — base02 + 0xFF839496, // on_surface_variant — base0 + 0xFF586E75, // outline — base01 + 0xFF073642, // outline_variant — base02 +]; + +// ───────────────────────────────────────────────────────────── +// Forward-reference: this is the shape the SDK will generate +// from `#[plugin(theme)]`. Once the SDK exists, the body collapses +// to a return of `Ok(ColorScheme { brightness, tokens: … })`. +// ───────────────────────────────────────────────────────────── +// +// #[plugin(theme)] +// fn theme_for(brightness: &str) -> theme::Result { +// let tokens = match brightness { +// "light" => SOLARIZED_LIGHT.to_vec(), +// "dark" => SOLARIZED_DARK.to_vec(), +// other => { +// return Err(theme::Error::declined(format!( +// "solarized has no scheme for brightness '{other}'", +// ))) +// } +// }; +// Ok(ColorScheme { +// brightness: brightness.to_string(), +// tokens, +// }) +// } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn palette_has_14_tokens() { + assert_eq!(SOLARIZED_LIGHT.len(), 14); + assert_eq!(SOLARIZED_DARK.len(), 14); + } + + #[test] + fn solarized_blue_is_the_primary_in_both_modes() { + // Ethan Schoonover's blue is the brand colour of + // Solarized. Sanity-check we kept it as the primary in + // both schemes. + assert_eq!(SOLARIZED_LIGHT[0], 0xFF268BD2); + assert_eq!(SOLARIZED_DARK[0], 0xFF268BD2); + } +}