feat(palette): add Shoreline, the product-client palette, and open on it

The terminal and the GPUI desktop window now cite one palette. Shoreline is
the warm charcoal ground (`#211F23`), one raised plate (`#2B282E`), one
restrained blue for action and selection (`#90B9FF`), Signal Gold for the
human lane, and the whale's ivory ink — the same slots
`codehwhale-gpui/src/workspace/mod.rs` installs into `Theme`.

It ships as two presets (`shoreline`, `shoreline-light`), and it is the
fresh-install default in `Settings::default`, the settings schema, the
theme picker's reset value, and the malformed-selector fallback. The
`underwater` theme keeps the painted water column, the ombre and the
ambient life; it is a choice now, not the ground the product opens on.

Shoreline is listed in `adapt::theme_remap_active`, so every direct
terminal constant lands on the preset's slots instead of a navy-tuned
value. Its mode badges keep the shipped mode ramp: a mode colour must
differ from `accent_primary`, because `theme_semantic_foreground_role`
resolves modes first and would otherwise read the action lane as
`ModeAgent`.

Evidence:
- `cargo test -p codewhale-palette --lib --locked` — 81 passed, 0 failed.
  That includes `every_selectable_theme_clears_the_text_floor`,
  `every_selectable_theme_keeps_action_and_working_roles_distinct_on_ansi16`,
  `every_selectable_theme_keeps_mode_badges_distinct` and
  `every_selectable_theme_keeps_permission_lanes_distinct` over the two new
  presets.
- `cargo build -p codewhale-cli --locked` — ok.
- Real PTY capture of `target/debug/codewhale` on a fresh `CODEWHALE_HOME`:
  field `#211F23`, chrome `#1A181C`, plates `#2B282E`, accent `#90B9FF`,
  muted `#B0A7B2`, gold `#F6C453`. The braille whalemark is unchanged.
- Not yet run: `codewhale-tui` tests. Expected fallout is the
  `theme_picker_*` goldens (two new rows) and tests asserting the old
  default string; reconciled in the next commit.
This commit is contained in:
CodeWhale Bot committed 2026-09-15 02:12:28 -07:00
1 parent 4a85cb7877
commit c1e89cba3a
6 files changed
+557 -16

No files matched your search

+1 -1
View File
@@ -476,7 +476,7 @@ pub const SETTINGS_SCHEMA: &[SettingDef] = &[
def(
"theme",
SettingKind::String,
"underwater",
"shoreline",
ui(
TAB_APPEARANCE,
"display",
+6 -1
View File
@@ -199,13 +199,18 @@ const fn theme_diff_deleted_bg(ui: &UiTheme) -> Color {
/// Returns `true` if the preset participates in the cell-level remap. The
/// default Whale and System themes pass through unchanged so this whole
/// stage compiles down to a single load+compare on the hot path.
/// stage compiles down to a single load+compare on the hot path. Shoreline is
/// listed because it is a full re-ink — warm charcoal instead of the navy the
/// direct terminal constants were tuned for — so every one of those call
/// sites has to land on the preset's slots.
#[inline]
#[must_use]
pub const fn theme_remap_active(theme: ThemeId) -> bool {
matches!(
theme,
ThemeId::Terminal
| ThemeId::Shoreline
| ThemeId::ShorelineLight
| ThemeId::CatppuccinMocha
| ThemeId::TokyoNight
| ThemeId::Dracula
+457
View File
@@ -336,6 +336,443 @@ pub const LIGHT_UI_THEME: UiTheme = UiTheme {
}
.with_terminal_native_shell();
/// Shoreline — the default shell from 0.9.14, and the terminal's half of the
/// product-client palette.
///
/// The GPUI desktop window and this terminal now cite the same slots, so the
/// two clients read as one product: a warm charcoal ground, one raised plate
/// for cards and the composer, one restrained blue for action and selection,
/// and the whale's ivory ink. It does **not** call
/// [`UiTheme::with_terminal_native_shell`] — the field is painted, the way the
/// desktop window paints it — and `adapt::theme_remap_active` remaps every
/// direct terminal constant onto these slots, so nothing navy-tuned leaks
/// through.
///
/// The ocean is not gone, it is a choice: `underwater` keeps the painted
/// water column, the ambient life and the ombre.
///
/// Every pair `contrast::theme_contrast_violations` audits clears its floor.
pub const SHORELINE_UI_THEME: UiTheme = UiTheme {
name: "shoreline",
mode: PaletteMode::Dark,
surface_bg: Color::Rgb(
SHORELINE_SURFACE_RGB.0,
SHORELINE_SURFACE_RGB.1,
SHORELINE_SURFACE_RGB.2,
),
panel_bg: Color::Rgb(
SHORELINE_PANEL_RGB.0,
SHORELINE_PANEL_RGB.1,
SHORELINE_PANEL_RGB.2,
),
elevated_bg: Color::Rgb(
SHORELINE_ELEVATED_RGB.0,
SHORELINE_ELEVATED_RGB.1,
SHORELINE_ELEVATED_RGB.2,
),
composer_bg: Color::Rgb(
SHORELINE_COMPOSER_RGB.0,
SHORELINE_COMPOSER_RGB.1,
SHORELINE_COMPOSER_RGB.2,
),
selection_bg: Color::Rgb(
SHORELINE_SELECTION_RGB.0,
SHORELINE_SELECTION_RGB.1,
SHORELINE_SELECTION_RGB.2,
),
header_bg: Color::Rgb(
SHORELINE_CHROME_RGB.0,
SHORELINE_CHROME_RGB.1,
SHORELINE_CHROME_RGB.2,
),
footer_bg: Color::Rgb(
SHORELINE_CHROME_RGB.0,
SHORELINE_CHROME_RGB.1,
SHORELINE_CHROME_RGB.2,
),
text_dim: Color::Rgb(
SHORELINE_TEXT_DIM_RGB.0,
SHORELINE_TEXT_DIM_RGB.1,
SHORELINE_TEXT_DIM_RGB.2,
),
text_hint: Color::Rgb(
SHORELINE_TEXT_HINT_RGB.0,
SHORELINE_TEXT_HINT_RGB.1,
SHORELINE_TEXT_HINT_RGB.2,
),
text_muted: Color::Rgb(
SHORELINE_TEXT_MUTED_RGB.0,
SHORELINE_TEXT_MUTED_RGB.1,
SHORELINE_TEXT_MUTED_RGB.2,
),
text_body: Color::Rgb(
SHORELINE_TEXT_BODY_RGB.0,
SHORELINE_TEXT_BODY_RGB.1,
SHORELINE_TEXT_BODY_RGB.2,
),
text_soft: Color::Rgb(
SHORELINE_TEXT_SOFT_RGB.0,
SHORELINE_TEXT_SOFT_RGB.1,
SHORELINE_TEXT_SOFT_RGB.2,
),
border: Color::Rgb(
SHORELINE_BORDER_RGB.0,
SHORELINE_BORDER_RGB.1,
SHORELINE_BORDER_RGB.2,
),
accent_primary: Color::Rgb(
SHORELINE_ACTION_RGB.0,
SHORELINE_ACTION_RGB.1,
SHORELINE_ACTION_RGB.2,
),
accent_secondary: Color::Rgb(
SHORELINE_LIVE_RGB.0,
SHORELINE_LIVE_RGB.1,
SHORELINE_LIVE_RGB.2,
),
accent_action: Color::Rgb(
SHORELINE_HUMAN_RGB.0,
SHORELINE_HUMAN_RGB.1,
SHORELINE_HUMAN_RGB.2,
),
error_fg: Color::Rgb(
SHORELINE_ERROR_RGB.0,
SHORELINE_ERROR_RGB.1,
SHORELINE_ERROR_RGB.2,
),
error_hover: Color::Rgb(
SHORELINE_ERROR_HOVER_RGB.0,
SHORELINE_ERROR_HOVER_RGB.1,
SHORELINE_ERROR_HOVER_RGB.2,
),
error_surface: Color::Rgb(
SHORELINE_ERROR_SURFACE_RGB.0,
SHORELINE_ERROR_SURFACE_RGB.1,
SHORELINE_ERROR_SURFACE_RGB.2,
),
error_border: Color::Rgb(
SHORELINE_ERROR_RGB.0,
SHORELINE_ERROR_RGB.1,
SHORELINE_ERROR_RGB.2,
),
error_text: Color::Rgb(
SHORELINE_ERROR_TEXT_RGB.0,
SHORELINE_ERROR_TEXT_RGB.1,
SHORELINE_ERROR_TEXT_RGB.2,
),
warning: Color::Rgb(
SHORELINE_WARNING_RGB.0,
SHORELINE_WARNING_RGB.1,
SHORELINE_WARNING_RGB.2,
),
success: Color::Rgb(
SHORELINE_SUCCESS_RGB.0,
SHORELINE_SUCCESS_RGB.1,
SHORELINE_SUCCESS_RGB.2,
),
info: Color::Rgb(
SHORELINE_ACTION_RGB.0,
SHORELINE_ACTION_RGB.1,
SHORELINE_ACTION_RGB.2,
),
mode_agent: Color::Rgb(
SHORELINE_MODE_AGENT_RGB.0,
SHORELINE_MODE_AGENT_RGB.1,
SHORELINE_MODE_AGENT_RGB.2,
),
mode_yolo: Color::Rgb(
SHORELINE_MODE_YOLO_RGB.0,
SHORELINE_MODE_YOLO_RGB.1,
SHORELINE_MODE_YOLO_RGB.2,
),
mode_plan: Color::Rgb(
SHORELINE_MODE_PLAN_RGB.0,
SHORELINE_MODE_PLAN_RGB.1,
SHORELINE_MODE_PLAN_RGB.2,
),
mode_operate: Color::Rgb(
SHORELINE_MODE_OPERATE_RGB.0,
SHORELINE_MODE_OPERATE_RGB.1,
SHORELINE_MODE_OPERATE_RGB.2,
),
permission_ask: Color::Rgb(
SHORELINE_TEXT_MUTED_RGB.0,
SHORELINE_TEXT_MUTED_RGB.1,
SHORELINE_TEXT_MUTED_RGB.2,
),
permission_auto_review: Color::Rgb(
SHORELINE_HUMAN_RGB.0,
SHORELINE_HUMAN_RGB.1,
SHORELINE_HUMAN_RGB.2,
),
permission_full_access: Color::Rgb(
SHORELINE_WARNING_RGB.0,
SHORELINE_WARNING_RGB.1,
SHORELINE_WARNING_RGB.2,
),
status_ready: Color::Rgb(
SHORELINE_SUCCESS_RGB.0,
SHORELINE_SUCCESS_RGB.1,
SHORELINE_SUCCESS_RGB.2,
),
status_working: Color::Rgb(
SHORELINE_STATUS_WORKING_RGB.0,
SHORELINE_STATUS_WORKING_RGB.1,
SHORELINE_STATUS_WORKING_RGB.2,
),
status_warning: Color::Rgb(
SHORELINE_WARNING_RGB.0,
SHORELINE_WARNING_RGB.1,
SHORELINE_WARNING_RGB.2,
),
diff_added_fg: Color::Rgb(
SHORELINE_DIFF_ADDED_FG_RGB.0,
SHORELINE_DIFF_ADDED_FG_RGB.1,
SHORELINE_DIFF_ADDED_FG_RGB.2,
),
diff_deleted_fg: Color::Rgb(
SHORELINE_DIFF_DELETED_FG_RGB.0,
SHORELINE_DIFF_DELETED_FG_RGB.1,
SHORELINE_DIFF_DELETED_FG_RGB.2,
),
diff_added_bg: Color::Rgb(
SHORELINE_DIFF_ADDED_BG_RGB.0,
SHORELINE_DIFF_ADDED_BG_RGB.1,
SHORELINE_DIFF_ADDED_BG_RGB.2,
),
diff_deleted_bg: Color::Rgb(
SHORELINE_DIFF_DELETED_BG_RGB.0,
SHORELINE_DIFF_DELETED_BG_RGB.1,
SHORELINE_DIFF_DELETED_BG_RGB.2,
),
tool_running: Color::Rgb(
SHORELINE_LIVE_RGB.0,
SHORELINE_LIVE_RGB.1,
SHORELINE_LIVE_RGB.2,
),
tool_success: Color::Rgb(
SHORELINE_SUCCESS_RGB.0,
SHORELINE_SUCCESS_RGB.1,
SHORELINE_SUCCESS_RGB.2,
),
tool_failed: Color::Rgb(
SHORELINE_ERROR_RGB.0,
SHORELINE_ERROR_RGB.1,
SHORELINE_ERROR_RGB.2,
),
};
/// Shoreline Light — Shoreline on warm paper, for light terminals.
pub const SHORELINE_LIGHT_UI_THEME: UiTheme = UiTheme {
name: "shoreline-light",
mode: PaletteMode::Light,
surface_bg: Color::Rgb(
SHORELINE_LIGHT_SURFACE_RGB.0,
SHORELINE_LIGHT_SURFACE_RGB.1,
SHORELINE_LIGHT_SURFACE_RGB.2,
),
panel_bg: Color::Rgb(
SHORELINE_LIGHT_PANEL_RGB.0,
SHORELINE_LIGHT_PANEL_RGB.1,
SHORELINE_LIGHT_PANEL_RGB.2,
),
elevated_bg: Color::Rgb(
SHORELINE_LIGHT_ELEVATED_RGB.0,
SHORELINE_LIGHT_ELEVATED_RGB.1,
SHORELINE_LIGHT_ELEVATED_RGB.2,
),
composer_bg: Color::Rgb(
SHORELINE_LIGHT_COMPOSER_RGB.0,
SHORELINE_LIGHT_COMPOSER_RGB.1,
SHORELINE_LIGHT_COMPOSER_RGB.2,
),
selection_bg: Color::Rgb(
SHORELINE_LIGHT_SELECTION_RGB.0,
SHORELINE_LIGHT_SELECTION_RGB.1,
SHORELINE_LIGHT_SELECTION_RGB.2,
),
header_bg: Color::Rgb(
SHORELINE_LIGHT_CHROME_RGB.0,
SHORELINE_LIGHT_CHROME_RGB.1,
SHORELINE_LIGHT_CHROME_RGB.2,
),
footer_bg: Color::Rgb(
SHORELINE_LIGHT_CHROME_RGB.0,
SHORELINE_LIGHT_CHROME_RGB.1,
SHORELINE_LIGHT_CHROME_RGB.2,
),
text_dim: Color::Rgb(
SHORELINE_LIGHT_TEXT_DIM_RGB.0,
SHORELINE_LIGHT_TEXT_DIM_RGB.1,
SHORELINE_LIGHT_TEXT_DIM_RGB.2,
),
text_hint: Color::Rgb(
SHORELINE_LIGHT_TEXT_HINT_RGB.0,
SHORELINE_LIGHT_TEXT_HINT_RGB.1,
SHORELINE_LIGHT_TEXT_HINT_RGB.2,
),
text_muted: Color::Rgb(
SHORELINE_LIGHT_TEXT_MUTED_RGB.0,
SHORELINE_LIGHT_TEXT_MUTED_RGB.1,
SHORELINE_LIGHT_TEXT_MUTED_RGB.2,
),
text_body: Color::Rgb(
SHORELINE_LIGHT_TEXT_BODY_RGB.0,
SHORELINE_LIGHT_TEXT_BODY_RGB.1,
SHORELINE_LIGHT_TEXT_BODY_RGB.2,
),
text_soft: Color::Rgb(
SHORELINE_LIGHT_TEXT_SOFT_RGB.0,
SHORELINE_LIGHT_TEXT_SOFT_RGB.1,
SHORELINE_LIGHT_TEXT_SOFT_RGB.2,
),
border: Color::Rgb(
SHORELINE_LIGHT_BORDER_RGB.0,
SHORELINE_LIGHT_BORDER_RGB.1,
SHORELINE_LIGHT_BORDER_RGB.2,
),
accent_primary: Color::Rgb(
SHORELINE_LIGHT_ACTION_RGB.0,
SHORELINE_LIGHT_ACTION_RGB.1,
SHORELINE_LIGHT_ACTION_RGB.2,
),
accent_secondary: Color::Rgb(
SHORELINE_LIGHT_LIVE_RGB.0,
SHORELINE_LIGHT_LIVE_RGB.1,
SHORELINE_LIGHT_LIVE_RGB.2,
),
accent_action: Color::Rgb(
SHORELINE_LIGHT_HUMAN_RGB.0,
SHORELINE_LIGHT_HUMAN_RGB.1,
SHORELINE_LIGHT_HUMAN_RGB.2,
),
error_fg: Color::Rgb(
SHORELINE_LIGHT_ERROR_RGB.0,
SHORELINE_LIGHT_ERROR_RGB.1,
SHORELINE_LIGHT_ERROR_RGB.2,
),
error_hover: Color::Rgb(
SHORELINE_LIGHT_ERROR_RGB.0,
SHORELINE_LIGHT_ERROR_RGB.1,
SHORELINE_LIGHT_ERROR_RGB.2,
),
error_surface: Color::Rgb(
SHORELINE_LIGHT_ERROR_SURFACE_RGB.0,
SHORELINE_LIGHT_ERROR_SURFACE_RGB.1,
SHORELINE_LIGHT_ERROR_SURFACE_RGB.2,
),
error_border: Color::Rgb(
SHORELINE_LIGHT_ERROR_RGB.0,
SHORELINE_LIGHT_ERROR_RGB.1,
SHORELINE_LIGHT_ERROR_RGB.2,
),
error_text: Color::Rgb(
SHORELINE_LIGHT_ERROR_TEXT_RGB.0,
SHORELINE_LIGHT_ERROR_TEXT_RGB.1,
SHORELINE_LIGHT_ERROR_TEXT_RGB.2,
),
warning: Color::Rgb(
SHORELINE_LIGHT_WARNING_RGB.0,
SHORELINE_LIGHT_WARNING_RGB.1,
SHORELINE_LIGHT_WARNING_RGB.2,
),
success: Color::Rgb(
SHORELINE_LIGHT_SUCCESS_RGB.0,
SHORELINE_LIGHT_SUCCESS_RGB.1,
SHORELINE_LIGHT_SUCCESS_RGB.2,
),
info: Color::Rgb(
SHORELINE_LIGHT_ACTION_RGB.0,
SHORELINE_LIGHT_ACTION_RGB.1,
SHORELINE_LIGHT_ACTION_RGB.2,
),
mode_agent: Color::Rgb(
SHORELINE_LIGHT_MODE_AGENT_RGB.0,
SHORELINE_LIGHT_MODE_AGENT_RGB.1,
SHORELINE_LIGHT_MODE_AGENT_RGB.2,
),
mode_yolo: Color::Rgb(
SHORELINE_LIGHT_MODE_YOLO_RGB.0,
SHORELINE_LIGHT_MODE_YOLO_RGB.1,
SHORELINE_LIGHT_MODE_YOLO_RGB.2,
),
mode_plan: Color::Rgb(
SHORELINE_LIGHT_MODE_PLAN_RGB.0,
SHORELINE_LIGHT_MODE_PLAN_RGB.1,
SHORELINE_LIGHT_MODE_PLAN_RGB.2,
),
mode_operate: Color::Rgb(
SHORELINE_LIGHT_MODE_OPERATE_RGB.0,
SHORELINE_LIGHT_MODE_OPERATE_RGB.1,
SHORELINE_LIGHT_MODE_OPERATE_RGB.2,
),
permission_ask: Color::Rgb(
SHORELINE_LIGHT_TEXT_MUTED_RGB.0,
SHORELINE_LIGHT_TEXT_MUTED_RGB.1,
SHORELINE_LIGHT_TEXT_MUTED_RGB.2,
),
permission_auto_review: Color::Rgb(
SHORELINE_LIGHT_HUMAN_RGB.0,
SHORELINE_LIGHT_HUMAN_RGB.1,
SHORELINE_LIGHT_HUMAN_RGB.2,
),
permission_full_access: Color::Rgb(
SHORELINE_LIGHT_WARNING_RGB.0,
SHORELINE_LIGHT_WARNING_RGB.1,
SHORELINE_LIGHT_WARNING_RGB.2,
),
status_ready: Color::Rgb(
SHORELINE_LIGHT_SUCCESS_RGB.0,
SHORELINE_LIGHT_SUCCESS_RGB.1,
SHORELINE_LIGHT_SUCCESS_RGB.2,
),
status_working: Color::Rgb(
SHORELINE_LIGHT_LIVE_RGB.0,
SHORELINE_LIGHT_LIVE_RGB.1,
SHORELINE_LIGHT_LIVE_RGB.2,
),
status_warning: Color::Rgb(
SHORELINE_LIGHT_WARNING_RGB.0,
SHORELINE_LIGHT_WARNING_RGB.1,
SHORELINE_LIGHT_WARNING_RGB.2,
),
diff_added_fg: Color::Rgb(
SHORELINE_LIGHT_DIFF_ADDED_FG_RGB.0,
SHORELINE_LIGHT_DIFF_ADDED_FG_RGB.1,
SHORELINE_LIGHT_DIFF_ADDED_FG_RGB.2,
),
diff_deleted_fg: Color::Rgb(
SHORELINE_LIGHT_DIFF_DELETED_FG_RGB.0,
SHORELINE_LIGHT_DIFF_DELETED_FG_RGB.1,
SHORELINE_LIGHT_DIFF_DELETED_FG_RGB.2,
),
diff_added_bg: Color::Rgb(
SHORELINE_LIGHT_DIFF_ADDED_BG_RGB.0,
SHORELINE_LIGHT_DIFF_ADDED_BG_RGB.1,
SHORELINE_LIGHT_DIFF_ADDED_BG_RGB.2,
),
diff_deleted_bg: Color::Rgb(
SHORELINE_LIGHT_DIFF_DELETED_BG_RGB.0,
SHORELINE_LIGHT_DIFF_DELETED_BG_RGB.1,
SHORELINE_LIGHT_DIFF_DELETED_BG_RGB.2,
),
tool_running: Color::Rgb(
SHORELINE_LIGHT_LIVE_RGB.0,
SHORELINE_LIGHT_LIVE_RGB.1,
SHORELINE_LIGHT_LIVE_RGB.2,
),
tool_success: Color::Rgb(
SHORELINE_LIGHT_SUCCESS_RGB.0,
SHORELINE_LIGHT_SUCCESS_RGB.1,
SHORELINE_LIGHT_SUCCESS_RGB.2,
),
tool_failed: Color::Rgb(
SHORELINE_LIGHT_ERROR_RGB.0,
SHORELINE_LIGHT_ERROR_RGB.1,
SHORELINE_LIGHT_ERROR_RGB.2,
),
};
pub const SOLARIZED_LIGHT_UI_THEME: UiTheme = UiTheme {
name: "solarized-light",
mode: PaletteMode::SolarizedLight,
@@ -888,6 +1325,8 @@ pub const UWU_UI_THEME: UiTheme = UiTheme {
pub enum ThemeId {
System,
Terminal,
Shoreline,
ShorelineLight,
Underwater,
UnderwaterRetro,
Whale,
@@ -914,6 +1353,8 @@ impl ThemeId {
"terminal" => Some(Self::Terminal),
"underwater" | "deepsea" => Some(Self::Underwater),
"underwater-retro" | "retro" => Some(Self::UnderwaterRetro),
"shoreline" => Some(Self::Shoreline),
"shoreline-light" => Some(Self::ShorelineLight),
"dark" => Some(Self::Whale),
"light" => Some(Self::WhaleLight),
"grayscale" => Some(Self::Grayscale),
@@ -936,6 +1377,8 @@ impl ThemeId {
match self {
Self::System => "system",
Self::Terminal => "terminal",
Self::Shoreline => "shoreline",
Self::ShorelineLight => "shoreline-light",
Self::Underwater => "underwater",
Self::UnderwaterRetro => "underwater-retro",
Self::Whale => "dark",
@@ -958,6 +1401,8 @@ impl ThemeId {
match self {
Self::System => "System",
Self::Terminal => "Terminal",
Self::Shoreline => "Shoreline",
Self::ShorelineLight => "Shoreline Light",
Self::Underwater => "Underwater",
Self::UnderwaterRetro => "Underwater Retro",
Self::Whale => "Blue Stage",
@@ -980,6 +1425,10 @@ impl ThemeId {
match self {
Self::System => "Follow terminal background (COLORFGBG / macOS appearance)",
Self::Terminal => "Inherit terminal colors fully (transparent surfaces, ANSI accents)",
Self::Shoreline => {
"Warm charcoal, one restrained blue — the desktop client's palette"
}
Self::ShorelineLight => "Shoreline on warm paper — the desktop client's light mode",
Self::Underwater => "The painted ocean field: ombre water, ambient life, the whale",
Self::UnderwaterRetro => "Flat phosphor-teal ocean: the legacy deepsea look, no ombre",
Self::Whale => "Stage black, action blue, and one Signal Gold human beacon",
@@ -1007,6 +1456,8 @@ impl ThemeId {
match self {
Self::System => UiTheme::detect(),
Self::Terminal => TERMINAL_UI_THEME,
Self::Shoreline => SHORELINE_UI_THEME,
Self::ShorelineLight => SHORELINE_LIGHT_UI_THEME,
Self::Underwater => UNDERWATER_UI_THEME,
Self::UnderwaterRetro => UNDERWATER_RETRO_UI_THEME,
Self::Whale => UI_THEME,
@@ -1028,6 +1479,8 @@ impl ThemeId {
pub const SELECTABLE_THEMES: &[ThemeId] = &[
ThemeId::System,
ThemeId::Terminal,
ThemeId::Shoreline,
ThemeId::ShorelineLight,
ThemeId::Underwater,
ThemeId::UnderwaterRetro,
ThemeId::Whale,
@@ -1090,6 +1543,8 @@ pub fn normalize_theme_name(value: &str) -> Option<&'static str> {
"terminal" | "term" | "transparent" | "follow-terminal" | "inherit" => Some("terminal"),
"underwater" | "deepsea" | "deep-sea" | "ocean" | "ombre" => Some("underwater"),
"underwater-retro" | "retro" | "uw-retro" => Some("underwater-retro"),
"shoreline" | "warm" | "charcoal" => Some("shoreline"),
"shoreline-light" | "paper" => Some("shoreline-light"),
"dark" | "whale" | "whale-dark" => Some("dark"),
"light" | "whale-light" => Some("light"),
"grayscale" | "greyscale" | "gray" | "grey" | "mono" | "monochrome" | "black-white"
@@ -1165,6 +1620,8 @@ mod tests {
[
"system",
"terminal",
"shoreline",
"shoreline-light",
"underwater",
"underwater-retro",
"dark",
+78
View File
@@ -222,6 +222,84 @@ pub const MATRIX_TEXT_SOFT_RGB: (u8, u8, u8) = (221, 255, 221); // #DDFFDD
pub const MATRIX_TEXT_DIM_RGB: (u8, u8, u8) = (0, 108, 0); // #006C00, lifted for 3:1
pub const MATRIX_BORDER_RGB: (u8, u8, u8) = (0, 204, 0); // #00CC00
// Shoreline — the product-client palette, shared with the GPUI desktop.
//
// Warm charcoal ground and warm paper sheet, one restrained blue, and the
// whale's ivory ink on both sides. This is the default shell from 0.9.14:
// the navy "Underwater" water column remains a selectable theme, it is no
// longer the ground the product opens on. Values are the GPUI client's
// `Theme` slots (`codehwhale-gpui/src/workspace/mod.rs`), so the terminal
// and the desktop window are one product rather than two visual systems.
//
// Every pair audited by `contrast::theme_contrast_violations` clears its
// floor: body roles clear 4.5:1 on all four surfaces, hint/dim and the
// status roles clear 3:1.
pub const SHORELINE_SURFACE_RGB: (u8, u8, u8) = (33, 31, 35); // #211F23 warm charcoal field
pub const SHORELINE_PANEL_RGB: (u8, u8, u8) = (43, 40, 46); // #2B282E raised plate
pub const SHORELINE_ELEVATED_RGB: (u8, u8, u8) = (53, 49, 58); // #35313A
pub const SHORELINE_COMPOSER_RGB: (u8, u8, u8) = (43, 40, 46); // #2B282E
pub const SHORELINE_CHROME_RGB: (u8, u8, u8) = (26, 24, 28); // #1A181C recessed chrome
pub const SHORELINE_SELECTION_RGB: (u8, u8, u8) = (53, 73, 103); // #354967
pub const SHORELINE_TEXT_BODY_RGB: (u8, u8, u8) = (242, 236, 229); // #F2ECE5
pub const SHORELINE_TEXT_SOFT_RGB: (u8, u8, u8) = (217, 210, 220); // #D9D2DC
pub const SHORELINE_TEXT_MUTED_RGB: (u8, u8, u8) = (176, 167, 178); // #B0A7B2
pub const SHORELINE_TEXT_HINT_RGB: (u8, u8, u8) = (154, 145, 159); // #9A919F
pub const SHORELINE_TEXT_DIM_RGB: (u8, u8, u8) = (126, 117, 131); // #7E7583
pub const SHORELINE_BORDER_RGB: (u8, u8, u8) = (73, 66, 77); // #49424D
pub const SHORELINE_ACTION_RGB: (u8, u8, u8) = (144, 185, 255); // #90B9FF the one blue
pub const SHORELINE_LIVE_RGB: (u8, u8, u8) = (127, 214, 198); // #7FD6C6 the live lane
pub const SHORELINE_HUMAN_RGB: (u8, u8, u8) = (246, 196, 83); // #F6C453 Signal Gold, the human lane
pub const SHORELINE_ERROR_RGB: (u8, u8, u8) = (255, 143, 168); // #FF8FA8
pub const SHORELINE_ERROR_HOVER_RGB: (u8, u8, u8) = (255, 166, 187); // #FFA6BB
pub const SHORELINE_ERROR_SURFACE_RGB: (u8, u8, u8) = (58, 32, 41); // #3A2029
pub const SHORELINE_ERROR_TEXT_RGB: (u8, u8, u8) = (255, 220, 230); // #FFDCE6
pub const SHORELINE_WARNING_RGB: (u8, u8, u8) = (240, 168, 104); // #F0A868
pub const SHORELINE_SUCCESS_RGB: (u8, u8, u8) = (163, 217, 119); // #A3D977
pub const SHORELINE_STATUS_WORKING_RGB: (u8, u8, u8) = SHORELINE_LIVE_RGB;
// Mode badges keep the shipped mode ramp: these four are proof-tested distinct
// from one another and from every semantic lane, and a mode badge is identity,
// not decoration. `mode_agent` must differ from `accent_primary` or
// `adapt::theme_semantic_foreground_role` resolves the action lane to a mode
// role (`ModeAgent`) before it ever reaches `Action`.
pub const SHORELINE_MODE_AGENT_RGB: (u8, u8, u8) = (126, 180, 232); // #7EB4E8
pub const SHORELINE_MODE_YOLO_RGB: (u8, u8, u8) = (255, 112, 160); // #FF70A0
pub const SHORELINE_MODE_PLAN_RGB: (u8, u8, u8) = (185, 220, 236); // #B9DCEC
pub const SHORELINE_MODE_OPERATE_RGB: (u8, u8, u8) = (173, 136, 255); // #AD88FF
pub const SHORELINE_DIFF_ADDED_FG_RGB: (u8, u8, u8) = (127, 214, 160); // #7FD6A0
pub const SHORELINE_DIFF_ADDED_BG_RGB: (u8, u8, u8) = (30, 44, 37); // #1E2C25
pub const SHORELINE_DIFF_DELETED_FG_RGB: (u8, u8, u8) = (255, 154, 171); // #FF9AAB
pub const SHORELINE_DIFF_DELETED_BG_RGB: (u8, u8, u8) = (51, 32, 42); // #33202A
// Shoreline Light — the same system on warm paper.
pub const SHORELINE_LIGHT_SURFACE_RGB: (u8, u8, u8) = (245, 240, 233); // #F5F0E9
pub const SHORELINE_LIGHT_PANEL_RGB: (u8, u8, u8) = (236, 229, 224); // #ECE5E0
pub const SHORELINE_LIGHT_ELEVATED_RGB: (u8, u8, u8) = (255, 252, 247); // #FFFCF7
pub const SHORELINE_LIGHT_COMPOSER_RGB: (u8, u8, u8) = (251, 247, 241); // #FBF7F1
pub const SHORELINE_LIGHT_CHROME_RGB: (u8, u8, u8) = (237, 231, 224); // #EDE7E0
pub const SHORELINE_LIGHT_SELECTION_RGB: (u8, u8, u8) = (200, 217, 249); // #C8D9F9
pub const SHORELINE_LIGHT_TEXT_BODY_RGB: (u8, u8, u8) = (48, 40, 50); // #302832
pub const SHORELINE_LIGHT_TEXT_SOFT_RGB: (u8, u8, u8) = (74, 65, 76); // #4A414C
pub const SHORELINE_LIGHT_TEXT_MUTED_RGB: (u8, u8, u8) = (107, 96, 110); // #6B606E
pub const SHORELINE_LIGHT_TEXT_HINT_RGB: (u8, u8, u8) = (117, 112, 128); // #757080
pub const SHORELINE_LIGHT_TEXT_DIM_RGB: (u8, u8, u8) = (138, 130, 144); // #8A8290
pub const SHORELINE_LIGHT_BORDER_RGB: (u8, u8, u8) = (215, 206, 213); // #D7CED5
pub const SHORELINE_LIGHT_ACTION_RGB: (u8, u8, u8) = (36, 91, 199); // #245BC7
pub const SHORELINE_LIGHT_LIVE_RGB: (u8, u8, u8) = (31, 122, 107); // #1F7A6B
pub const SHORELINE_LIGHT_HUMAN_RGB: (u8, u8, u8) = (122, 85, 0); // #7A5500 Signal Gold at AA on paper
pub const SHORELINE_LIGHT_ERROR_RGB: (u8, u8, u8) = (180, 35, 90); // #B4235A
pub const SHORELINE_LIGHT_ERROR_SURFACE_RGB: (u8, u8, u8) = (251, 228, 236); // #FBE4EC
pub const SHORELINE_LIGHT_ERROR_TEXT_RGB: (u8, u8, u8) = (122, 18, 53); // #7A1235
pub const SHORELINE_LIGHT_WARNING_RGB: (u8, u8, u8) = (143, 85, 20); // #8F5514
pub const SHORELINE_LIGHT_SUCCESS_RGB: (u8, u8, u8) = (47, 107, 58); // #2F6B3A
pub const SHORELINE_LIGHT_MODE_AGENT_RGB: (u8, u8, u8) = (22, 54, 178); // #1636B2 distinct from LIGHT action
pub const SHORELINE_LIGHT_MODE_YOLO_RGB: (u8, u8, u8) = (181, 35, 90); // #B5235A
pub const SHORELINE_LIGHT_MODE_PLAN_RGB: (u8, u8, u8) = (52, 92, 128); // #345C80
pub const SHORELINE_LIGHT_MODE_OPERATE_RGB: (u8, u8, u8) = (112, 71, 184); // #7047B8
pub const SHORELINE_LIGHT_DIFF_ADDED_FG_RGB: (u8, u8, u8) = (31, 107, 69); // #1F6B45
pub const SHORELINE_LIGHT_DIFF_ADDED_BG_RGB: (u8, u8, u8) = (226, 242, 230); // #E2F2E6
pub const SHORELINE_LIGHT_DIFF_DELETED_FG_RGB: (u8, u8, u8) = (168, 40, 80); // #A82850
pub const SHORELINE_LIGHT_DIFF_DELETED_BG_RGB: (u8, u8, u8) = (251, 230, 236); // #FBE6EC
// Semantic colors
pub const BORDER_COLOR_RGB: (u8, u8, u8) = WHALE_BORDER_RGB;
+12 -11
View File
@@ -345,13 +345,14 @@ pub struct Settings {
/// ca, de, fr, id, hi, ru, uk.
/// Every shipped pack holds full `en.json` parity; nothing falls back.
pub locale: String,
/// Named UI theme. `"underwater"` is the fresh-install default and paints
/// the ocean field. `"terminal"` fully inherits the host terminal's
/// foreground/background. `"system"`, `"dark"`, `"light"`,
/// `"grayscale"`, and the community presets: `"catppuccin-mocha"`,
/// `"tokyo-night"`, `"dracula"`, `"gruvbox-dark"`. The
/// `background_color` setting still overrides the surface color on top
/// of the resolved theme.
/// Named UI theme. `"shoreline"` is the fresh-install default: the warm
/// charcoal and restrained blue the desktop client paints too.
/// `"underwater"` paints the ocean field. `"terminal"` fully inherits the
/// host terminal's foreground/background. `"system"`, `"dark"`,
/// `"light"`, `"grayscale"`, and the community presets:
/// `"catppuccin-mocha"`, `"tokyo-night"`, `"dracula"`,
/// `"gruvbox-dark"`. The `background_color` setting still overrides the
/// surface color on top of the resolved theme.
pub theme: String,
/// Optional main TUI background color as a 6-digit hex RGB value.
pub background_color: Option<String>,
@@ -571,7 +572,7 @@ impl Default for Settings {
show_tool_details: false,
inline_diffs: "full".to_string(),
locale: "auto".to_string(),
theme: "underwater".to_string(),
theme: "shoreline".to_string(),
background_color: None,
composer_density: "comfortable".to_string(),
composer_border: true,
@@ -2923,9 +2924,9 @@ fn normalize_synchronized_output(value: &str) -> &str {
fn normalize_settings_theme(value: &str) -> String {
// A malformed persisted selector must not turn into a painted application
// background. Falling back to the underwater default keeps a single
// compiled first-party theme until the user picks an explicit palette.
normalize_theme_setting(value).unwrap_or_else(|_| "underwater".to_string())
// background. Falling back to the compiled default keeps a single
// first-party theme until the user picks an explicit palette.
normalize_theme_setting(value).unwrap_or_else(|_| "shoreline".to_string())
}
/// Returns `true` when the active terminal is Ptyxis (the new default
+3 -3
View File
@@ -226,9 +226,9 @@ fn theme_options_with_custom(
.help("Pick a theme with live preview")
.values(SettingValues::new(
Cow::Owned(current.clone()),
// A reset returns to the underwater default, not a
// A reset returns to the Shoreline default, not a
// detected palette that can repaint it.
Cow::Borrowed("underwater"),
Cow::Borrowed("shoreline"),
Cow::Borrowed(name),
))
.availability(SettingAvailability::Available)
@@ -255,7 +255,7 @@ fn theme_options_with_custom(
.help("Pick a user-authored theme overlay")
.values(SettingValues::new(
Cow::Owned(current.clone()),
Cow::Borrowed("underwater"),
Cow::Borrowed("shoreline"),
Cow::Owned(custom.selector.clone()),
))
.availability(SettingAvailability::Available)