Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/desktop-tauri/src-tauri/src/floatbar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ impl SettingsPatch {
&& self.provider_ids.is_none()
&& self.dark_text.is_none()
&& self.show_reset_inline.is_none()
&& self.show_cost.is_none()
}

/// Apply this patch to a mutable `Settings`. Values are clamped and
Expand Down
30 changes: 30 additions & 0 deletions apps/desktop-tauri/src/floatbar/SettingsSection.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import type { SettingsSnapshot } from "../types/bridge";
import FloatBarSettingsSection from "./SettingsSection";

vi.mock("../hooks/useLocale", () => ({
useLocale: () => ({ t: (key: string) => key }),
}));

const settings = {
floatBarEnabled: true,
floatBarOpacity: 90,
floatBarScale: 100,
floatBarOrientation: "horizontal",
floatBarStyle: "floating",
floatBarShowCost: false,
floatBarShowResetInline: false,
floatBarDarkText: false,
floatBarClickThrough: false,
} as unknown as SettingsSnapshot;

describe("FloatBar settings", () => {
it("renders one cost toggle", () => {
render(
<FloatBarSettingsSection settings={settings} saving={false} set={vi.fn()} />,
);

expect(screen.getAllByText("FloatBarShowCost")).toHaveLength(1);
});
});
1 change: 1 addition & 0 deletions apps/desktop-tauri/src/i18n/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

export const ALL_LOCALE_KEYS = [
"TabGeneral",
"TabProviders",
"TabNotifications",
"TabMenuBar",
"TabMenu",
Expand Down
11 changes: 11 additions & 0 deletions apps/desktop-tauri/src/surfaces/Settings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { describe, expect, it } from "vitest";
import { TAB_META } from "./Settings";

describe("Settings navigation", () => {
it("lists providers separately after general", () => {
expect(TAB_META.slice(0, 2)).toEqual([
{ id: "general", labelKey: "TabGeneral" },
{ id: "providers", labelKey: "TabProviders" },
]);
});
});
35 changes: 22 additions & 13 deletions apps/desktop-tauri/src/surfaces/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ const TabIcons: Record<SettingsTab, ReactElement> = {
<path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.4 3.4l1.4 1.4M11.2 11.2l1.4 1.4M3.4 12.6l1.4-1.4M11.2 4.8l1.4-1.4" />
</Svg>
),
providers: (
<Svg>
<circle cx="4" cy="4" r="1.5" />
<circle cx="12" cy="4" r="1.5" />
<circle cx="8" cy="12" r="1.5" />
<path d="M5.3 4.8 7.2 10M10.7 4.8 8.8 10M5.5 4h5" />
</Svg>
),
notifications: (
<Svg>
<path d="M3.5 11.5h9l-1.2-1.8V7a3.3 3.3 0 0 0-6.6 0v2.7Z" />
Expand Down Expand Up @@ -89,8 +97,9 @@ const TabIcons: Record<SettingsTab, ReactElement> = {
),
};

const TAB_META: { id: SettingsTab; labelKey: LocaleKey }[] = [
export const TAB_META: { id: SettingsTab; labelKey: LocaleKey }[] = [
{ id: "general", labelKey: "TabGeneral" },
{ id: "providers", labelKey: "TabProviders" },
{ id: "notifications", labelKey: "TabNotifications" },
{ id: "menuBar", labelKey: "TabMenuBar" },
{ id: "menu", labelKey: "TabMenu" },
Expand All @@ -108,7 +117,7 @@ const SETTINGS_WINDOW_PROVIDERS_WIDTH = 600;

async function applySettingsWindowSize(tab: SettingsTab) {
const requestedWidth =
tab === "general"
tab === "providers"
? SETTINGS_WINDOW_PROVIDERS_WIDTH
: SETTINGS_WINDOW_DEFAULT_WIDTH;
const workArea = await getWorkAreaRect().catch(() => null);
Expand Down Expand Up @@ -197,7 +206,7 @@ export default function Settings({ state, initialTab: propTab }: { state: Bootst

return (
<div
className={`settings${activeTab === "general" ? " settings--providers-active" : ""}`}
className={`settings${activeTab === "providers" ? " settings--providers-active" : ""}`}
>
{/* custom title bar (decorations disabled for guaranteed dark theme) */}
<div className="settings-titlebar" data-tauri-drag-region>
Expand Down Expand Up @@ -248,17 +257,17 @@ export default function Settings({ state, initialTab: propTab }: { state: Bootst
)}

{/* tab panels */}
<div className={`settings-body${activeTab === "general" ? " settings-body--providers" : ""}`}>
<div className={`settings-body${activeTab === "providers" ? " settings-body--providers" : ""}`}>
{activeTab === "general" && (
<>
<GeneralTab mode="general" settings={settings} set={set} saving={saving} />
<ProvidersTab
settings={settings}
providers={state.providers}
set={set}
saving={saving}
/>
</>
<GeneralTab mode="general" settings={settings} set={set} saving={saving} />
)}
{activeTab === "providers" && (
<ProvidersTab
settings={settings}
providers={state.providers}
set={set}
saving={saving}
/>
)}
{activeTab === "notifications" && (
<GeneralTab mode="notifications" settings={settings} set={set} saving={saving} />
Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src/types/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export type SurfaceMode = "hidden" | "trayPanel" | "popOut" | "settings";
export type VisibleSurfaceMode = Exclude<SurfaceMode, "hidden">;
export type SettingsTabId =
| "general"
| "providers"
| "notifications"
| "menuBar"
| "menu"
Expand Down
19 changes: 19 additions & 0 deletions docs/superpowers/specs/2026-07-12-restore-providers-tab-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Restore Providers Tab

## Goal

Restore provider management as a top-level Settings tab instead of embedding it below General settings.

## Design

- Add **Providers** immediately after **General** in the Settings tab bar.
- General contains only language, system, and automation settings and uses the normal settings-page scroll behavior.
- Providers retains the existing 600px split-pane layout, searchable provider sidebar, provider detail pane, and independent pane scrolling.
- Opening Settings with the `providers` route selects Providers directly.
- Other tabs, persisted settings, and provider behavior remain unchanged.

## Verification

- Add a focused Settings test proving General and Providers render as separate tab panels.
- Run frontend tests, type-check, locale parity, and the desktop build.
- Verify General, Providers, and provider-pane scrolling in the rebuilt app with CUA Driver.
1 change: 0 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ async-trait = "0.1"
futures = "0.3"

image = { version = "0.25", default-features = false, features = ["png"] }
global-hotkey = "0.7.0"

# SQLite for reading browser cookies
rusqlite = { version = "0.32", features = ["bundled"] }
Expand Down
2 changes: 0 additions & 2 deletions rust/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

mod cost_pricing;
mod credential_migration;
mod credentials;
mod http;
mod jsonl_scanner;
mod models_dev_pricing;
Expand All @@ -19,7 +18,6 @@ mod widget_snapshot;

pub use cost_pricing::*;
pub use credential_migration::*;
pub use credentials::*;
pub use http::*;
pub use jsonl_scanner::*;
pub use models_dev_pricing::*;
Expand Down
1 change: 0 additions & 1 deletion rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub mod notifications;
pub mod providers;
pub mod secure_file;
pub mod settings;
pub mod shortcuts;
pub mod sound;

pub mod status;
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ locale_keys! {

// Tab names (Preferences)
TabGeneral,
TabProviders,
TabNotifications,
TabMenuBar,
TabMenu,
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/en-US.ftl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
TabGeneral = General
TabProviders = Providers
TabNotifications = Notifications
TabMenuBar = Menu Bar
TabMenu = Menu
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/es-MX.ftl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
TabGeneral = General
TabProviders = Proveedores
TabNotifications = Notifications
TabMenuBar = Menu Bar
TabMenu = Menu
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/ja-JP.ftl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
TabGeneral = 一般
TabProviders = プロバイダー
TabNotifications = Notifications
TabMenuBar = Menu Bar
TabMenu = Menu
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/ko-KR.ftl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
TabGeneral = 일반
TabProviders = 제공업체
TabNotifications = Notifications
TabMenuBar = Menu Bar
TabMenu = Menu
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/zh-CN.ftl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
TabGeneral = 通用
TabProviders = 服务商
TabNotifications = Notifications
TabMenuBar = Menu Bar
TabMenu = Menu
Expand Down
1 change: 1 addition & 0 deletions rust/src/locale/zh-TW.ftl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
TabGeneral = 一般
TabProviders = 提供者
TabNotifications = Notifications
TabMenuBar = Menu Bar
TabMenu = Menu
Expand Down
3 changes: 1 addition & 2 deletions rust/src/providers/windsurf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,7 @@ fn valid_json_text(text: &str) -> Option<String> {

fn window_from_remaining_percent(remaining: f64, reset_unix: Option<i64>) -> RateWindow {
let resets_at = reset_unix.and_then(|ts| DateTime::<Utc>::from_timestamp(ts, 0));
// ponytail: reset countdown is localized at render time from `resets_at`;
// do not bake a language-specific description into the cached snapshot.
// Reset countdowns are localized at render time; keep cached snapshots language-neutral.
RateWindow::with_details((100.0 - remaining).clamp(0.0, 100.0), None, resets_at, None)
}

Expand Down
3 changes: 3 additions & 0 deletions rust/src/settings/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ fn float_bar_defaults_are_safe() {
assert!(settings.float_bar_provider_ids.is_empty());
assert!(!settings.float_bar_dark_text);
assert!(!settings.float_bar_show_reset_inline);
assert!(!settings.float_bar_show_cost);
}

#[test]
Expand Down Expand Up @@ -210,6 +211,7 @@ fn float_bar_settings_round_trip_through_raw() {
float_bar_provider_ids: vec!["claude".into(), "codex".into()],
float_bar_dark_text: true,
float_bar_show_reset_inline: true,
float_bar_show_cost: true,
..Settings::default()
};

Expand All @@ -224,6 +226,7 @@ fn float_bar_settings_round_trip_through_raw() {
assert_eq!(back.float_bar_provider_ids, vec!["claude", "codex"]);
assert!(back.float_bar_dark_text);
assert!(back.float_bar_show_reset_inline);
assert!(back.float_bar_show_cost);
}

#[test]
Expand Down
11 changes: 0 additions & 11 deletions rust/src/status/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,9 @@
//! Fetches operational status from provider status pages

#![allow(dead_code)]
#![allow(unused_imports)]

pub mod indicators;

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

// Re-export indicator types for convenience
pub use indicators::{
OverlayPosition, ProviderStatus as IndicatorProviderStatus,
StatusLevel as IndicatorStatusLevel, StatusOverlayConfig, StatuspageIncident,
StatuspageResponse, StatuspageStatus,
};

/// Status level for a provider
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
Expand Down