AkurAI Build
Menu

AkurAI-IDPCommit c759797b

Commit c759797b

c759797bd0868d3d4edafef701bd09699f30b37d by Ólafur Búi Ólafsson

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28a14f2..57b93e9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,11 @@ All notable changes to this project are documented here. Format: Keep a Changelo
 
 ## [Unreleased]
 
+## [0.1.2] - 2026-06-29
+
+- Add suite theme picker to agent console: serve /themes.json registry + mount the Framework theme selector (17 themes, all families) in the sidebar Account section, matching every other AkurAI-Framework app.
+
+
 ## [0.1.1] - 2026-06-29
 
 - Remove AI-slop 'Agent surfaces' from agent console: delete Tasks, Projects, AGY context, Notes, Passvault, Cron, Curator pages + the dead Tools launcher (all hardcoded placeholder content, no real data). Console nav is now Chat / Kanban / Run details. Also: repo-wide rustfmt + clippy cleanup.
diff --git a/Cargo.toml b/Cargo.toml
index 2fda114..93cd32a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "akurai-idp"
-version = "0.1.1"
+version = "0.1.2"
 edition = "2024"
 description = "Lightweight Rust OIDC/OAuth2 identity provider for AkurAI — replaces 96MB Node process"
 license = "MIT"
diff --git a/VERSION b/VERSION
index 17e51c3..d917d3e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.1
+0.1.2
diff --git a/assets/themes.json b/assets/themes.json
new file mode 100644
index 0000000..bd4f1fa
--- /dev/null
+++ b/assets/themes.json
@@ -0,0 +1 @@
+[{"slug":"akurai","family":"AkurAI","label":"AkurAI","variant":"dark"},{"slug":"akurai-light","family":"AkurAI","label":"AkurAI Light","variant":"light"},{"slug":"claude-code","family":"Claude Code","label":"Claude Code","variant":"dark"},{"slug":"claude-code-light","family":"Claude Code","label":"Claude Code Light","variant":"light"},{"slug":"nord","family":"Nord","label":"Nord","variant":"dark"},{"slug":"nord-light","family":"Nord","label":"Nord Light","variant":"light"},{"slug":"catppuccin-mocha","family":"Catppuccin","label":"Catppuccin Mocha","variant":"dark"},{"slug":"catppuccin-latte","family":"Catppuccin","label":"Catppuccin Latte","variant":"light"},{"slug":"solarized-dark","family":"Solarized","label":"Solarized Dark","variant":"dark"},{"slug":"solarized-light","family":"Solarized","label":"Solarized Light","variant":"light"},{"slug":"gruvbox-dark","family":"Gruvbox","label":"Gruvbox dark, medium","variant":"dark"},{"slug":"gruvbox-light","family":"Gruvbox","label":"Gruvbox light, medium","variant":"light"},{"slug":"tokyo-night","family":"Tokyo Night","label":"Tokyo Night Dark","variant":"dark"},{"slug":"tokyo-night-light","family":"Tokyo Night","label":"Tokyo Night Light","variant":"light"},{"slug":"rose-pine","family":"Rosé Pine","label":"Rosé Pine","variant":"dark"},{"slug":"rose-pine-dawn","family":"Rosé Pine","label":"Rosé Pine Dawn","variant":"light"},{"slug":"dracula","family":"Dracula","label":"Dracula","variant":"dark"}]
diff --git a/src/main.rs b/src/main.rs
index 8b42325..28f858f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -29,6 +29,7 @@ const FAVICON_SVG: &[u8] = include_bytes!("../assets/favicon.svg");
 const APPLE_TOUCH_ICON_PNG: &[u8] = include_bytes!("../assets/apple-touch-icon.png");
 const THEME_JS: &[u8] = include_bytes!("../assets/theme.js");
 const THEMES_CSS: &[u8] = include_bytes!("../assets/themes.css");
+const THEMES_JSON: &[u8] = include_bytes!("../assets/themes.json");
 const LANG_JS: &[u8] = include_bytes!("../assets/lang.js");
 const AGENT_JS: &[u8] = include_bytes!("../assets/agent.js");
 
@@ -99,6 +100,7 @@ async fn main() {
         .route("/apple-touch-icon.png", get(serve_apple_touch_icon))
         .route("/theme.js", get(serve_theme_js))
         .route("/themes.css", get(serve_themes_css))
+        .route("/themes.json", get(serve_themes_json))
         .route("/lang.js", get(serve_lang_js))
         .route("/agent.js", get(serve_agent_js))
         // Health check
@@ -229,6 +231,15 @@ async fn serve_themes_css() -> Response<Body> {
         .unwrap()
 }
 
+async fn serve_themes_json() -> Response<Body> {
+    Response::builder()
+        .status(StatusCode::OK)
+        .header(header::CONTENT_TYPE, "application/json; charset=utf-8")
+        .header(header::CACHE_CONTROL, "public, max-age=3600")
+        .body(Body::from(THEMES_JSON.to_vec()))
+        .unwrap()
+}
+
 async fn serve_lang_js() -> Response<Body> {
     Response::builder()
         .status(StatusCode::OK)
diff --git a/src/routes/agent_os.css b/src/routes/agent_os.css
index f6299ba..7915b9f 100644
--- a/src/routes/agent_os.css
+++ b/src/routes/agent_os.css
@@ -148,6 +148,72 @@ body {
   font-weight: 650;
 }
 
+/* Suite theme picker — theme.js mounts a <select.theme-select> into the slot.
+   Token-driven so it re-themes with everything else. */
+.agent-theme-row {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 8px;
+  padding: 7px 10px;
+}
+
+.agent-theme-label {
+  flex: 0 0 auto;
+  color: var(--muted);
+  font-size: 13px;
+}
+
+.agent-theme-row .theme-picker {
+  display: inline-flex;
+  align-items: center;
+  min-width: 0;
+}
+
+.theme-select {
+  appearance: none;
+  -webkit-appearance: none;
+  min-width: 0;
+  max-width: 150px;
+  font: inherit;
+  font-size: 12px;
+  font-weight: 500;
+  color: var(--fg);
+  background: var(--panel);
+  border: 1px solid var(--soft-border);
+  border-radius: 8px;
+  padding: 6px 26px 6px 10px;
+  cursor: pointer;
+  line-height: 1.2;
+  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
+    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
+  background-position: calc(100% - 14px) center, calc(100% - 9px) center;
+  background-size: 5px 5px, 5px 5px;
+  background-repeat: no-repeat;
+  transition: border-color 0.15s ease, background-color 0.15s ease;
+}
+
+.theme-select:hover {
+  border-color: var(--border-2);
+}
+
+.theme-select:focus-visible {
+  outline: 2px solid var(--accent);
+  outline-offset: 1px;
+}
+
+.theme-select optgroup {
+  color: var(--fg);
+  background: var(--panel);
+  font-weight: 700;
+}
+
+.theme-select option {
+  color: var(--fg);
+  background: var(--panel);
+  font-weight: 500;
+}
+
 .agent-modebar,
 .agent-nav {
   display: grid;
diff --git a/src/routes/agent_view.rs b/src/routes/agent_view.rs
index bec5762..aa627f9 100644
--- a/src/routes/agent_view.rs
+++ b/src/routes/agent_view.rs
@@ -69,6 +69,10 @@ fn agent_page_body(
     <div class="agent-section agent-account">
       <h2>Account</h2>
       <a href="/account">AkurAI ID</a>
+      <div class="agent-theme-row">
+        <span class="agent-theme-label">Theme</span>
+        <span class="theme-picker" data-theme-picker></span>
+      </div>
     </div>
   </aside>
 
@@ -398,6 +402,8 @@ mod tests {
         assert!(html.contains("data-agent-prompt"));
         assert!(html.contains("approval.request"));
         assert!(html.contains("tool_call"));
+        // Suite theme picker slot present so theme.js can mount the selector.
+        assert!(html.contains("data-theme-picker"));
     }
 
     #[test]