| author | |
| committer | |
| log | 0f1b572b9865256dd61e245b4dd09f6a7b15f0e1 |
| tree | a4eef2cee02beff66f05aadaa3eb90fd9037a9f3 |
| parent | 5378f87f99175bded2b9241876319567875aca03 |
| signature | Signed by PGP key B5690EEEBB952194 |
84 files changed, 1913 insertions(+), 1281 deletions(-)
apps/mobile/clerk-theme.json+27-27| ... | ... | @@ -1,36 +1,36 @@ |
| 1 | 1 | { |
| 2 | 2 | "colors": { |
| 3 | "primary": "#262626", | |
| 4 | "background": "#F2F2F7", | |
| 5 | "input": "#FFFFFF", | |
| 6 | "danger": "#DC2626", | |
| 3 | "primary": "#1b4ed8", | |
| 4 | "background": "#fcfcfc", | |
| 5 | "input": "#ffffff", | |
| 6 | "danger": "#c10007", | |
| 7 | 7 | "success": "#059669", |
| 8 | "warning": "#D97706", | |
| 9 | "foreground": "#262626", | |
| 10 | "mutedForeground": "#737373", | |
| 11 | "primaryForeground": "#FFFFFF", | |
| 12 | "inputForeground": "#262626", | |
| 13 | "neutral": "#F5F5F5", | |
| 14 | "border": "#E5E5EA", | |
| 15 | "ring": "#A3A3A3", | |
| 16 | "muted": "#F2F2F7", | |
| 8 | "warning": "#bb4d00", | |
| 9 | "foreground": "#27272a", | |
| 10 | "mutedForeground": "#71717b", | |
| 11 | "primaryForeground": "#ffffff", | |
| 12 | "inputForeground": "#27272a", | |
| 13 | "neutral": "#fafafa", | |
| 14 | "border": "#e4e4e7", | |
| 15 | "ring": "#1b4ed8", | |
| 16 | "muted": "#fafafa", | |
| 17 | 17 | "shadow": "#000000" |
| 18 | 18 | }, |
| 19 | 19 | "darkColors": { |
| 20 | "primary": "#F5F5F5", | |
| 21 | "background": "#0E0E0E", | |
| 22 | "input": "#171717", | |
| 23 | "danger": "#FCA5A5", | |
| 24 | "success": "#34D399", | |
| 25 | "warning": "#FBBF24", | |
| 26 | "foreground": "#F5F5F5", | |
| 27 | "mutedForeground": "#A3A3A3", | |
| 28 | "primaryForeground": "#0A0A0A", | |
| 29 | "inputForeground": "#F5F5F5", | |
| 30 | "neutral": "#1C1C1C", | |
| 31 | "border": "#2A2A2A", | |
| 32 | "ring": "#525252", | |
| 33 | "muted": "#0E0E0E", | |
| 20 | "primary": "#346bf1", | |
| 21 | "background": "#0a0a0a", | |
| 22 | "input": "#111111", | |
| 23 | "danger": "#ff6467", | |
| 24 | "success": "#34d399", | |
| 25 | "warning": "#ffb900", | |
| 26 | "foreground": "#f5f5f5", | |
| 27 | "mutedForeground": "#818181", | |
| 28 | "primaryForeground": "#ffffff", | |
| 29 | "inputForeground": "#f5f5f5", | |
| 30 | "neutral": "#111111", | |
| 31 | "border": "#191919", | |
| 32 | "ring": "#346bf1", | |
| 33 | "muted": "#111111", | |
| 34 | 34 | "shadow": "#000000" |
| 35 | 35 | }, |
| 36 | 36 | "design": { |
apps/mobile/generated-uniwind-default-theme-variables.json+150-126| ... | ... | @@ -1,156 +1,180 @@ |
| 1 | 1 | { |
| 2 | 2 | "light": { |
| 3 | "--color-screen": "#f2f2f7", | |
| 4 | "--color-sheet": "rgba(242, 242, 247, 0.98)", | |
| 5 | "--color-sheet-solid": "#f2f2f7", | |
| 3 | "--color-screen": "#fcfcfc", | |
| 4 | "--color-sheet": "rgba(252, 252, 252, 0.98)", | |
| 5 | "--color-sheet-solid": "#fcfcfc", | |
| 6 | 6 | "--color-card": "#ffffff", |
| 7 | "--color-card-alt": "#f5f5f5", | |
| 7 | "--color-grouped-card": "#f4f4f5", | |
| 8 | "--color-card-alt": "#fcfcfc", | |
| 8 | 9 | "--color-card-translucent": "rgba(255, 255, 255, 0.8)", |
| 9 | "--color-thread-canvas": "#f5f5f5", | |
| 10 | "--color-thread-canvas": "#fcfcfc", | |
| 10 | 11 | "--color-thread-selected": "#ffffff", |
| 11 | "--color-thread-selected-foreground": "#262626", | |
| 12 | "--color-thread-selected-foreground-muted": "#737373", | |
| 13 | "--color-composer-panel": "rgba(245, 245, 245, 0.88)", | |
| 14 | "--color-composer-surface": "rgba(255, 255, 255, 0.94)", | |
| 15 | "--color-composer-border": "rgba(0, 0, 0, 0.1)", | |
| 16 | "--color-foreground": "#262626", | |
| 17 | "--color-foreground-secondary": "#525252", | |
| 18 | "--color-foreground-muted": "#737373", | |
| 19 | "--color-foreground-tertiary": "#8e8e93", | |
| 20 | "--color-border": "rgba(0, 0, 0, 0.08)", | |
| 21 | "--color-border-subtle": "rgba(0, 0, 0, 0.06)", | |
| 22 | "--color-separator": "rgba(0, 0, 0, 0.04)", | |
| 23 | "--color-subtle": "rgba(0, 0, 0, 0.04)", | |
| 24 | "--color-subtle-strong": "rgba(0, 0, 0, 0.08)", | |
| 25 | "--color-inline-skill-background": "rgba(217, 70, 239, 0.12)", | |
| 26 | "--color-inline-skill-border": "rgba(217, 70, 239, 0.25)", | |
| 27 | "--color-inline-skill-foreground": "#a21caf", | |
| 28 | "--color-primary": "#262626", | |
| 12 | "--color-thread-selected-foreground": "#27272a", | |
| 13 | "--color-thread-selected-foreground-muted": "#71717b", | |
| 14 | "--color-thread-hover": "#fcfcfc", | |
| 15 | "--color-row-hover": "#f4f4f5", | |
| 16 | "--color-composer-panel": "rgba(252, 252, 252, 0.88)", | |
| 17 | "--color-composer-surface": "rgba(244, 244, 245, 0.94)", | |
| 18 | "--color-composer-border": "rgba(228, 228, 231, 0.8)", | |
| 19 | "--color-foreground": "#27272a", | |
| 20 | "--color-foreground-secondary": "#6f6f79", | |
| 21 | "--color-foreground-muted": "#6f6f79", | |
| 22 | "--color-foreground-tertiary": "#71717b", | |
| 23 | "--color-border": "#e4e4e7", | |
| 24 | "--color-focus": "#1b4ed8", | |
| 25 | "--color-border-subtle": "rgba(228, 228, 231, 0.7)", | |
| 26 | "--color-separator": "rgba(228, 228, 231, 0.55)", | |
| 27 | "--color-subtle": "#fafafa", | |
| 28 | "--color-subtle-strong": "#fafafa", | |
| 29 | "--color-inline-skill-background": "#f4f4f5", | |
| 30 | "--color-inline-skill-border": "rgba(27, 78, 216, 0.42)", | |
| 31 | "--color-inline-skill-foreground": "#18181b", | |
| 32 | "--color-primary": "#1b4ed8", | |
| 29 | 33 | "--color-primary-foreground": "#ffffff", |
| 34 | "--color-primary-text": "#1b4ed8", | |
| 30 | 35 | "--color-primary-shadow": "#000000", |
| 31 | "--color-secondary": "#ffffff", | |
| 32 | "--color-secondary-foreground": "#262626", | |
| 33 | "--color-secondary-border": "rgba(0, 0, 0, 0.08)", | |
| 34 | "--color-switch-active-track": "#34c759", | |
| 36 | "--color-secondary": "#fafafa", | |
| 37 | "--color-secondary-foreground": "#27272a", | |
| 38 | "--color-secondary-border": "#e4e4e7", | |
| 39 | "--color-switch-active-track": "#1b4ed8", | |
| 35 | 40 | "--color-switch-active-thumb": "#ffffff", |
| 36 | "--color-switch-inactive-track": "rgba(0, 0, 0, 0.08)", | |
| 37 | "--color-switch-inactive-thumb": "#8e8e93", | |
| 38 | "--color-warning": "#fffbeb", | |
| 39 | "--color-warning-border": "#fde68a", | |
| 40 | "--color-warning-foreground": "#b45309", | |
| 41 | "--color-danger": "#fef2f2", | |
| 42 | "--color-danger-border": "rgba(239, 68, 68, 0.12)", | |
| 43 | "--color-danger-foreground": "#dc2626", | |
| 41 | "--color-switch-inactive-track": "#fafafa", | |
| 42 | "--color-switch-inactive-thumb": "#71717b", | |
| 43 | "--color-warning": "#fcf4e8", | |
| 44 | "--color-warning-border": "rgba(254, 154, 0, 0.32)", | |
| 45 | "--color-warning-foreground": "#bb4d00", | |
| 46 | "--color-danger": "#fcebec", | |
| 47 | "--color-danger-border": "rgba(251, 44, 54, 0.32)", | |
| 48 | "--color-danger-foreground": "#c10007", | |
| 49 | "--color-update": "#e0e6f7", | |
| 50 | "--color-update-foreground": "#1b4ed8", | |
| 44 | 51 | "--color-input": "#ffffff", |
| 45 | "--color-input-border": "rgba(0, 0, 0, 0.1)", | |
| 46 | "--color-sidebar-search": "rgba(118, 118, 128, 0.12)", | |
| 47 | "--color-placeholder": "#737373", | |
| 48 | "--color-icon": "#262626", | |
| 49 | "--color-icon-muted": "#525252", | |
| 50 | "--color-icon-subtle": "#a3a3a3", | |
| 51 | "--color-header": "rgba(255, 255, 255, 0.97)", | |
| 52 | "--color-header-border": "rgba(0, 0, 0, 0.06)", | |
| 53 | "--color-glass-surface": "rgba(255, 255, 255, 0.72)", | |
| 54 | "--color-glass-tint": "rgba(255, 255, 255, 0.18)", | |
| 55 | "--color-status-bar": "#f2f2f7", | |
| 56 | "--color-md-body": "#111111", | |
| 57 | "--color-md-strong": "#000000", | |
| 58 | "--color-md-link": "#2563eb", | |
| 59 | "--color-md-blockquote-border": "rgba(0, 0, 0, 0.08)", | |
| 60 | "--color-md-blockquote-bg": "rgba(0, 0, 0, 0.02)", | |
| 61 | "--color-md-code-bg": "rgba(0, 0, 0, 0.04)", | |
| 62 | "--color-md-code-text": "#262626", | |
| 63 | "--color-md-user-code-bg": "rgba(0, 0, 0, 0.04)", | |
| 64 | "--color-md-user-code-text": "#262626", | |
| 65 | "--color-md-user-fence-bg": "rgba(0, 0, 0, 0.06)", | |
| 66 | "--color-md-user-fence-text": "#262626", | |
| 67 | "--color-md-hr": "rgba(0, 0, 0, 0.08)", | |
| 68 | "--color-user-bubble": "#ffffff", | |
| 69 | "--color-user-bubble-foreground": "#262626", | |
| 70 | "--color-user-bubble-foreground-muted": "rgba(38, 38, 38, 0.78)", | |
| 71 | "--color-user-bubble-skill-foreground": "#2563eb", | |
| 52 | "--color-input-border": "#d4d4d8", | |
| 53 | "--color-sidebar-search": "#f4f4f5", | |
| 54 | "--color-placeholder": "#6f6f79", | |
| 55 | "--color-icon": "#27272a", | |
| 56 | "--color-icon-muted": "#71717b", | |
| 57 | "--color-icon-subtle": "#71717b", | |
| 58 | "--color-header": "rgba(252, 252, 252, 0.97)", | |
| 59 | "--color-header-foreground": "#27272a", | |
| 60 | "--color-header-border": "#e4e4e7", | |
| 61 | "--color-glass-surface": "rgba(255, 255, 255, 0.74)", | |
| 62 | "--color-glass-fallback": "rgba(244, 244, 245, 0.94)", | |
| 63 | "--color-glass-tint": "rgba(255, 255, 255, 0.22)", | |
| 64 | "--color-status-bar": "#fcfcfc", | |
| 65 | "--color-md-body": "#27272a", | |
| 66 | "--color-md-strong": "#27272a", | |
| 67 | "--color-md-link": "#1b4ed8", | |
| 68 | "--color-md-blockquote-border": "#e4e4e7", | |
| 69 | "--color-md-blockquote-bg": "#fafafa", | |
| 70 | "--color-md-code-bg": "#ffffff", | |
| 71 | "--color-md-code-text": "#27272a", | |
| 72 | "--color-md-user-code-bg": "rgba(39, 39, 42, 0.18)", | |
| 73 | "--color-md-user-code-text": "#27272a", | |
| 74 | "--color-md-user-fence-bg": "rgba(0, 0, 0, 0.16)", | |
| 75 | "--color-md-user-fence-text": "#27272a", | |
| 76 | "--color-md-hr": "#e4e4e7", | |
| 77 | "--color-user-bubble": "#efeff1", | |
| 78 | "--color-user-bubble-foreground": "#27272a", | |
| 79 | "--color-user-bubble-foreground-muted": "rgba(39, 39, 42, 0.78)", | |
| 80 | "--color-user-bubble-skill-foreground": "#1b4ed8", | |
| 72 | 81 | "--color-backdrop": "rgba(0, 0, 0, 0.22)", |
| 73 | "--color-drawer": "rgba(255, 255, 255, 0.99)", | |
| 82 | "--color-drawer": "#fafafa", | |
| 83 | "--color-drawer-foreground": "#27272a", | |
| 84 | "--color-drawer-foreground-muted": "#71717b", | |
| 85 | "--color-drawer-border": "#e4e4e7", | |
| 74 | 86 | "--color-drawer-shadow": "rgba(0, 0, 0, 0.12)", |
| 75 | "--color-dot-separator": "rgba(0, 0, 0, 0.2)", | |
| 76 | "--color-wordmark": "#262626", | |
| 77 | "--color-chevron": "rgba(0, 0, 0, 0.2)" | |
| 87 | "--color-dot-separator": "rgba(113, 113, 123, 0.35)", | |
| 88 | "--color-wordmark": "#27272a", | |
| 89 | "--color-chevron": "rgba(113, 113, 123, 0.42)" | |
| 78 | 90 | }, |
| 79 | 91 | "dark": { |
| 80 | 92 | "--color-screen": "#0a0a0a", |
| 81 | "--color-sheet": "rgba(14, 14, 14, 0.98)", | |
| 82 | "--color-sheet-solid": "#0e0e0e", | |
| 83 | "--color-card": "#171717", | |
| 84 | "--color-card-alt": "#1c1c1c", | |
| 93 | "--color-sheet": "rgba(10, 10, 10, 0.98)", | |
| 94 | "--color-sheet-solid": "#0a0a0a", | |
| 95 | "--color-card": "#111111", | |
| 96 | "--color-grouped-card": "#1a1b1b", | |
| 97 | "--color-card-alt": "#111111", | |
| 85 | 98 | "--color-card-translucent": "rgba(17, 17, 17, 0.8)", |
| 86 | "--color-thread-canvas": "#1c1c1c", | |
| 87 | "--color-thread-selected": "#171717", | |
| 88 | "--color-thread-selected-foreground": "#f5f5f5", | |
| 89 | "--color-thread-selected-foreground-muted": "#8e8e93", | |
| 90 | "--color-composer-panel": "rgba(28, 28, 28, 0.92)", | |
| 91 | "--color-composer-surface": "rgba(23, 23, 23, 0.9)", | |
| 92 | "--color-composer-border": "rgba(255, 255, 255, 0.08)", | |
| 99 | "--color-thread-canvas": "#0a0a0a", | |
| 100 | "--color-thread-selected": "#1a1b1b", | |
| 101 | "--color-thread-selected-foreground": "#f1f3f7", | |
| 102 | "--color-thread-selected-foreground-muted": "#a3a3a3", | |
| 103 | "--color-thread-hover": "#131313", | |
| 104 | "--color-row-hover": "#141414", | |
| 105 | "--color-composer-panel": "rgba(10, 10, 10, 0.92)", | |
| 106 | "--color-composer-surface": "rgba(26, 27, 27, 0.9)", | |
| 107 | "--color-composer-border": "rgba(25, 25, 25, 0.8)", | |
| 93 | 108 | "--color-foreground": "#f5f5f5", |
| 94 | "--color-foreground-secondary": "#a3a3a3", | |
| 95 | "--color-foreground-muted": "#8e8e93", | |
| 96 | "--color-foreground-tertiary": "#636366", | |
| 97 | "--color-border": "rgba(255, 255, 255, 0.06)", | |
| 98 | "--color-border-subtle": "rgba(255, 255, 255, 0.04)", | |
| 99 | "--color-separator": "rgba(255, 255, 255, 0.03)", | |
| 100 | "--color-subtle": "rgba(255, 255, 255, 0.04)", | |
| 101 | "--color-subtle-strong": "rgba(255, 255, 255, 0.08)", | |
| 102 | "--color-inline-skill-background": "rgba(217, 70, 239, 0.12)", | |
| 103 | "--color-inline-skill-border": "rgba(217, 70, 239, 0.25)", | |
| 104 | "--color-inline-skill-foreground": "#f0abfc", | |
| 105 | "--color-primary": "#f5f5f5", | |
| 106 | "--color-primary-foreground": "#0a0a0a", | |
| 109 | "--color-foreground-secondary": "#838383", | |
| 110 | "--color-foreground-muted": "#838383", | |
| 111 | "--color-foreground-tertiary": "#818181", | |
| 112 | "--color-border": "#191919", | |
| 113 | "--color-focus": "#346bf1", | |
| 114 | "--color-border-subtle": "rgba(25, 25, 25, 0.7)", | |
| 115 | "--color-separator": "rgba(25, 25, 25, 0.55)", | |
| 116 | "--color-subtle": "#111111", | |
| 117 | "--color-subtle-strong": "#111111", | |
| 118 | "--color-inline-skill-background": "#141414", | |
| 119 | "--color-inline-skill-border": "rgba(52, 107, 241, 0.42)", | |
| 120 | "--color-inline-skill-foreground": "#f5f5f5", | |
| 121 | "--color-primary": "#346bf1", | |
| 122 | "--color-primary-foreground": "#ffffff", | |
| 123 | "--color-primary-text": "#4b7cf3", | |
| 107 | 124 | "--color-primary-shadow": "#000000", |
| 108 | "--color-secondary": "rgba(255, 255, 255, 0.04)", | |
| 125 | "--color-secondary": "#111111", | |
| 109 | 126 | "--color-secondary-foreground": "#f5f5f5", |
| 110 | "--color-secondary-border": "rgba(255, 255, 255, 0.06)", | |
| 111 | "--color-switch-active-track": "#30d158", | |
| 127 | "--color-secondary-border": "#191919", | |
| 128 | "--color-switch-active-track": "#346bf1", | |
| 112 | 129 | "--color-switch-active-thumb": "#ffffff", |
| 113 | "--color-switch-inactive-track": "rgba(255, 255, 255, 0.06)", | |
| 114 | "--color-switch-inactive-thumb": "#8e8e93", | |
| 115 | "--color-warning": "rgba(69, 26, 3, 0.4)", | |
| 116 | "--color-warning-border": "rgba(120, 53, 15, 0.6)", | |
| 117 | "--color-warning-foreground": "#fcd34d", | |
| 118 | "--color-danger": "rgba(239, 68, 68, 0.14)", | |
| 119 | "--color-danger-border": "rgba(248, 113, 113, 0.18)", | |
| 120 | "--color-danger-foreground": "#fca5a5", | |
| 121 | "--color-input": "#141414", | |
| 122 | "--color-input-border": "rgba(255, 255, 255, 0.08)", | |
| 123 | "--color-sidebar-search": "rgba(118, 118, 128, 0.24)", | |
| 124 | "--color-placeholder": "#8e8e93", | |
| 130 | "--color-switch-inactive-track": "#111111", | |
| 131 | "--color-switch-inactive-thumb": "#818181", | |
| 132 | "--color-warning": "#312108", | |
| 133 | "--color-warning-border": "rgba(254, 154, 0, 0.32)", | |
| 134 | "--color-warning-foreground": "#ffb900", | |
| 135 | "--color-danger": "#301214", | |
| 136 | "--color-danger-border": "rgba(251, 65, 74, 0.32)", | |
| 137 | "--color-danger-foreground": "#ff6467", | |
| 138 | "--color-update": "#121b34", | |
| 139 | "--color-update-foreground": "#51a2ff", | |
| 140 | "--color-input": "#111111", | |
| 141 | "--color-input-border": "#1e1e1e", | |
| 142 | "--color-sidebar-search": "#0a0a0a", | |
| 143 | "--color-placeholder": "#838383", | |
| 125 | 144 | "--color-icon": "#f5f5f5", |
| 126 | "--color-icon-muted": "#a3a3a3", | |
| 127 | "--color-icon-subtle": "#8e8e93", | |
| 145 | "--color-icon-muted": "#818181", | |
| 146 | "--color-icon-subtle": "#818181", | |
| 128 | 147 | "--color-header": "rgba(10, 10, 10, 0.97)", |
| 129 | "--color-header-border": "rgba(255, 255, 255, 0.06)", | |
| 130 | "--color-glass-surface": "rgba(23, 23, 23, 0.78)", | |
| 131 | "--color-glass-tint": "rgba(23, 23, 23, 0.24)", | |
| 148 | "--color-header-foreground": "#f5f5f5", | |
| 149 | "--color-header-border": "#191919", | |
| 150 | "--color-glass-surface": "rgba(17, 17, 17, 0.74)", | |
| 151 | "--color-glass-fallback": "rgba(26, 27, 27, 0.9)", | |
| 152 | "--color-glass-tint": "rgba(17, 17, 17, 0.22)", | |
| 132 | 153 | "--color-status-bar": "#0a0a0a", |
| 133 | "--color-md-body": "#e5e5e5", | |
| 154 | "--color-md-body": "#f5f5f5", | |
| 134 | 155 | "--color-md-strong": "#f5f5f5", |
| 135 | "--color-md-link": "#60a5fa", | |
| 136 | "--color-md-blockquote-border": "rgba(255, 255, 255, 0.1)", | |
| 137 | "--color-md-blockquote-bg": "rgba(255, 255, 255, 0.03)", | |
| 138 | "--color-md-code-bg": "rgba(255, 255, 255, 0.06)", | |
| 139 | "--color-md-code-text": "#e5e5e5", | |
| 140 | "--color-md-user-code-bg": "rgba(255, 255, 255, 0.06)", | |
| 141 | "--color-md-user-code-text": "#e5e5e5", | |
| 142 | "--color-md-user-fence-bg": "rgba(255, 255, 255, 0.09)", | |
| 143 | "--color-md-user-fence-text": "#e5e5e5", | |
| 144 | "--color-md-hr": "rgba(255, 255, 255, 0.08)", | |
| 145 | "--color-user-bubble": "#171717", | |
| 156 | "--color-md-link": "#3b70f1", | |
| 157 | "--color-md-blockquote-border": "#191919", | |
| 158 | "--color-md-blockquote-bg": "#111111", | |
| 159 | "--color-md-code-bg": "#111111", | |
| 160 | "--color-md-code-text": "#f5f5f5", | |
| 161 | "--color-md-user-code-bg": "rgba(245, 245, 245, 0.18)", | |
| 162 | "--color-md-user-code-text": "#f5f5f5", | |
| 163 | "--color-md-user-fence-bg": "rgba(0, 0, 0, 0.28)", | |
| 164 | "--color-md-user-fence-text": "#f5f5f5", | |
| 165 | "--color-md-hr": "#191919", | |
| 166 | "--color-user-bubble": "#161616", | |
| 146 | 167 | "--color-user-bubble-foreground": "#f5f5f5", |
| 147 | 168 | "--color-user-bubble-foreground-muted": "rgba(245, 245, 245, 0.78)", |
| 148 | "--color-user-bubble-skill-foreground": "#60a5fa", | |
| 169 | "--color-user-bubble-skill-foreground": "#4678f2", | |
| 149 | 170 | "--color-backdrop": "rgba(0, 0, 0, 0.48)", |
| 150 | "--color-drawer": "rgba(14, 14, 14, 0.99)", | |
| 171 | "--color-drawer": "#000000", | |
| 172 | "--color-drawer-foreground": "#f1f3f7", | |
| 173 | "--color-drawer-foreground-muted": "#a3a3a3", | |
| 174 | "--color-drawer-border": "#141414", | |
| 151 | 175 | "--color-drawer-shadow": "rgba(0, 0, 0, 0.32)", |
| 152 | "--color-dot-separator": "rgba(255, 255, 255, 0.2)", | |
| 176 | "--color-dot-separator": "rgba(129, 129, 129, 0.35)", | |
| 153 | 177 | "--color-wordmark": "#f5f5f5", |
| 154 | "--color-chevron": "rgba(255, 255, 255, 0.2)" | |
| 178 | "--color-chevron": "rgba(129, 129, 129, 0.42)" | |
| 155 | 179 | } |
| 156 | 180 | } |
apps/mobile/generated-uniwind-themes.css+470-176| ... | ... | @@ -2,6 +2,93 @@ |
| 2 | 2 | @layer theme { |
| 3 | 3 | :root { |
| 4 | 4 | @variant light { |
| 5 | --color-screen: #fcfcfc; | |
| 6 | --color-sheet: rgba(252, 252, 252, 0.98); | |
| 7 | --color-sheet-solid: #fcfcfc; | |
| 8 | --color-card: #ffffff; | |
| 9 | --color-grouped-card: #f4f4f5; | |
| 10 | --color-card-alt: #fcfcfc; | |
| 11 | --color-card-translucent: rgba(255, 255, 255, 0.8); | |
| 12 | --color-thread-canvas: #fcfcfc; | |
| 13 | --color-thread-selected: #ffffff; | |
| 14 | --color-thread-selected-foreground: #27272a; | |
| 15 | --color-thread-selected-foreground-muted: #71717b; | |
| 16 | --color-thread-hover: #fcfcfc; | |
| 17 | --color-row-hover: #f4f4f5; | |
| 18 | --color-composer-panel: rgba(252, 252, 252, 0.88); | |
| 19 | --color-composer-surface: rgba(244, 244, 245, 0.94); | |
| 20 | --color-composer-border: rgba(228, 228, 231, 0.8); | |
| 21 | --color-foreground: #27272a; | |
| 22 | --color-foreground-secondary: #6f6f79; | |
| 23 | --color-foreground-muted: #6f6f79; | |
| 24 | --color-foreground-tertiary: #71717b; | |
| 25 | --color-border: #e4e4e7; | |
| 26 | --color-focus: #1b4ed8; | |
| 27 | --color-border-subtle: rgba(228, 228, 231, 0.7); | |
| 28 | --color-separator: rgba(228, 228, 231, 0.55); | |
| 29 | --color-subtle: #fafafa; | |
| 30 | --color-subtle-strong: #fafafa; | |
| 31 | --color-inline-skill-background: #f4f4f5; | |
| 32 | --color-inline-skill-border: rgba(27, 78, 216, 0.42); | |
| 33 | --color-inline-skill-foreground: #18181b; | |
| 34 | --color-primary: #1b4ed8; | |
| 35 | --color-primary-foreground: #ffffff; | |
| 36 | --color-primary-text: #1b4ed8; | |
| 37 | --color-primary-shadow: #000000; | |
| 38 | --color-secondary: #fafafa; | |
| 39 | --color-secondary-foreground: #27272a; | |
| 40 | --color-secondary-border: #e4e4e7; | |
| 41 | --color-switch-active-track: #1b4ed8; | |
| 42 | --color-switch-active-thumb: #ffffff; | |
| 43 | --color-switch-inactive-track: #fafafa; | |
| 44 | --color-switch-inactive-thumb: #71717b; | |
| 45 | --color-warning: #fcf4e8; | |
| 46 | --color-warning-border: rgba(254, 154, 0, 0.32); | |
| 47 | --color-warning-foreground: #bb4d00; | |
| 48 | --color-danger: #fcebec; | |
| 49 | --color-danger-border: rgba(251, 44, 54, 0.32); | |
| 50 | --color-danger-foreground: #c10007; | |
| 51 | --color-update: #e0e6f7; | |
| 52 | --color-update-foreground: #1b4ed8; | |
| 53 | --color-input: #ffffff; | |
| 54 | --color-input-border: #d4d4d8; | |
| 55 | --color-sidebar-search: #f4f4f5; | |
| 56 | --color-placeholder: #6f6f79; | |
| 57 | --color-icon: #27272a; | |
| 58 | --color-icon-muted: #71717b; | |
| 59 | --color-icon-subtle: #71717b; | |
| 60 | --color-header: rgba(252, 252, 252, 0.97); | |
| 61 | --color-header-foreground: #27272a; | |
| 62 | --color-header-border: #e4e4e7; | |
| 63 | --color-glass-surface: rgba(255, 255, 255, 0.74); | |
| 64 | --color-glass-fallback: rgba(244, 244, 245, 0.94); | |
| 65 | --color-glass-tint: rgba(255, 255, 255, 0.22); | |
| 66 | --color-status-bar: #fcfcfc; | |
| 67 | --color-md-body: #27272a; | |
| 68 | --color-md-strong: #27272a; | |
| 69 | --color-md-link: #1b4ed8; | |
| 70 | --color-md-blockquote-border: #e4e4e7; | |
| 71 | --color-md-blockquote-bg: #fafafa; | |
| 72 | --color-md-code-bg: #ffffff; | |
| 73 | --color-md-code-text: #27272a; | |
| 74 | --color-md-user-code-bg: rgba(39, 39, 42, 0.18); | |
| 75 | --color-md-user-code-text: #27272a; | |
| 76 | --color-md-user-fence-bg: rgba(0, 0, 0, 0.16); | |
| 77 | --color-md-user-fence-text: #27272a; | |
| 78 | --color-md-hr: #e4e4e7; | |
| 79 | --color-user-bubble: #efeff1; | |
| 80 | --color-user-bubble-foreground: #27272a; | |
| 81 | --color-user-bubble-foreground-muted: rgba(39, 39, 42, 0.78); | |
| 82 | --color-user-bubble-skill-foreground: #1b4ed8; | |
| 83 | --color-backdrop: rgba(0, 0, 0, 0.22); | |
| 84 | --color-drawer: #fafafa; | |
| 85 | --color-drawer-foreground: #27272a; | |
| 86 | --color-drawer-foreground-muted: #71717b; | |
| 87 | --color-drawer-border: #e4e4e7; | |
| 88 | --color-drawer-shadow: rgba(0, 0, 0, 0.12); | |
| 89 | --color-dot-separator: rgba(113, 113, 123, 0.35); | |
| 90 | --color-wordmark: #27272a; | |
| 91 | --color-chevron: rgba(113, 113, 123, 0.42); | |
| 5 | 92 | --color-adaptive-amber-50-950-a40: oklch(98.7% 0.022 95.277); |
| 6 | 93 | --color-adaptive-amber-200-900-a60: oklch(92.4% 0.12 95.746); |
| 7 | 94 | --color-adaptive-amber-500-a12-a16: oklch(76.9% 0.188 70.08 / 12%); |
| ... | ... | @@ -59,14 +146,101 @@ |
| 59 | 146 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%); |
| 60 | 147 | --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938); |
| 61 | 148 | --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786); |
| 62 | --color-clerk-page: #f2f2f7; | |
| 63 | --color-clerk-foreground: #262626; | |
| 64 | --color-clerk-foreground-muted: #737373; | |
| 65 | --color-clerk-border: rgba(229, 229, 234, 0.06); | |
| 66 | --color-clerk-danger: #dc2626; | |
| 149 | --color-clerk-page: #fcfcfc; | |
| 150 | --color-clerk-foreground: #27272a; | |
| 151 | --color-clerk-foreground-muted: #71717b; | |
| 152 | --color-clerk-border: #e4e4e7; | |
| 153 | --color-clerk-danger: #c10007; | |
| 67 | 154 | } |
| 68 | 155 | |
| 69 | 156 | @variant dark { |
| 157 | --color-screen: #0a0a0a; | |
| 158 | --color-sheet: rgba(10, 10, 10, 0.98); | |
| 159 | --color-sheet-solid: #0a0a0a; | |
| 160 | --color-card: #111111; | |
| 161 | --color-grouped-card: #1a1b1b; | |
| 162 | --color-card-alt: #111111; | |
| 163 | --color-card-translucent: rgba(17, 17, 17, 0.8); | |
| 164 | --color-thread-canvas: #0a0a0a; | |
| 165 | --color-thread-selected: #1a1b1b; | |
| 166 | --color-thread-selected-foreground: #f1f3f7; | |
| 167 | --color-thread-selected-foreground-muted: #a3a3a3; | |
| 168 | --color-thread-hover: #131313; | |
| 169 | --color-row-hover: #141414; | |
| 170 | --color-composer-panel: rgba(10, 10, 10, 0.92); | |
| 171 | --color-composer-surface: rgba(26, 27, 27, 0.9); | |
| 172 | --color-composer-border: rgba(25, 25, 25, 0.8); | |
| 173 | --color-foreground: #f5f5f5; | |
| 174 | --color-foreground-secondary: #838383; | |
| 175 | --color-foreground-muted: #838383; | |
| 176 | --color-foreground-tertiary: #818181; | |
| 177 | --color-border: #191919; | |
| 178 | --color-focus: #346bf1; | |
| 179 | --color-border-subtle: rgba(25, 25, 25, 0.7); | |
| 180 | --color-separator: rgba(25, 25, 25, 0.55); | |
| 181 | --color-subtle: #111111; | |
| 182 | --color-subtle-strong: #111111; | |
| 183 | --color-inline-skill-background: #141414; | |
| 184 | --color-inline-skill-border: rgba(52, 107, 241, 0.42); | |
| 185 | --color-inline-skill-foreground: #f5f5f5; | |
| 186 | --color-primary: #346bf1; | |
| 187 | --color-primary-foreground: #ffffff; | |
| 188 | --color-primary-text: #4b7cf3; | |
| 189 | --color-primary-shadow: #000000; | |
| 190 | --color-secondary: #111111; | |
| 191 | --color-secondary-foreground: #f5f5f5; | |
| 192 | --color-secondary-border: #191919; | |
| 193 | --color-switch-active-track: #346bf1; | |
| 194 | --color-switch-active-thumb: #ffffff; | |
| 195 | --color-switch-inactive-track: #111111; | |
| 196 | --color-switch-inactive-thumb: #818181; | |
| 197 | --color-warning: #312108; | |
| 198 | --color-warning-border: rgba(254, 154, 0, 0.32); | |
| 199 | --color-warning-foreground: #ffb900; | |
| 200 | --color-danger: #301214; | |
| 201 | --color-danger-border: rgba(251, 65, 74, 0.32); | |
| 202 | --color-danger-foreground: #ff6467; | |
| 203 | --color-update: #121b34; | |
| 204 | --color-update-foreground: #51a2ff; | |
| 205 | --color-input: #111111; | |
| 206 | --color-input-border: #1e1e1e; | |
| 207 | --color-sidebar-search: #0a0a0a; | |
| 208 | --color-placeholder: #838383; | |
| 209 | --color-icon: #f5f5f5; | |
| 210 | --color-icon-muted: #818181; | |
| 211 | --color-icon-subtle: #818181; | |
| 212 | --color-header: rgba(10, 10, 10, 0.97); | |
| 213 | --color-header-foreground: #f5f5f5; | |
| 214 | --color-header-border: #191919; | |
| 215 | --color-glass-surface: rgba(17, 17, 17, 0.74); | |
| 216 | --color-glass-fallback: rgba(26, 27, 27, 0.9); | |
| 217 | --color-glass-tint: rgba(17, 17, 17, 0.22); | |
| 218 | --color-status-bar: #0a0a0a; | |
| 219 | --color-md-body: #f5f5f5; | |
| 220 | --color-md-strong: #f5f5f5; | |
| 221 | --color-md-link: #3b70f1; | |
| 222 | --color-md-blockquote-border: #191919; | |
| 223 | --color-md-blockquote-bg: #111111; | |
| 224 | --color-md-code-bg: #111111; | |
| 225 | --color-md-code-text: #f5f5f5; | |
| 226 | --color-md-user-code-bg: rgba(245, 245, 245, 0.18); | |
| 227 | --color-md-user-code-text: #f5f5f5; | |
| 228 | --color-md-user-fence-bg: rgba(0, 0, 0, 0.28); | |
| 229 | --color-md-user-fence-text: #f5f5f5; | |
| 230 | --color-md-hr: #191919; | |
| 231 | --color-user-bubble: #161616; | |
| 232 | --color-user-bubble-foreground: #f5f5f5; | |
| 233 | --color-user-bubble-foreground-muted: rgba(245, 245, 245, 0.78); | |
| 234 | --color-user-bubble-skill-foreground: #4678f2; | |
| 235 | --color-backdrop: rgba(0, 0, 0, 0.48); | |
| 236 | --color-drawer: #000000; | |
| 237 | --color-drawer-foreground: #f1f3f7; | |
| 238 | --color-drawer-foreground-muted: #a3a3a3; | |
| 239 | --color-drawer-border: #141414; | |
| 240 | --color-drawer-shadow: rgba(0, 0, 0, 0.32); | |
| 241 | --color-dot-separator: rgba(129, 129, 129, 0.35); | |
| 242 | --color-wordmark: #f5f5f5; | |
| 243 | --color-chevron: rgba(129, 129, 129, 0.42); | |
| 70 | 244 | --color-adaptive-amber-50-950-a40: oklch(27.9% 0.077 45.635 / 40%); |
| 71 | 245 | --color-adaptive-amber-200-900-a60: oklch(41.4% 0.112 45.904 / 60%); |
| 72 | 246 | --color-adaptive-amber-500-a12-a16: oklch(76.9% 0.188 70.08 / 16%); |
| ... | ... | @@ -124,32 +298,36 @@ |
| 124 | 298 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%); |
| 125 | 299 | --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067); |
| 126 | 300 | --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286); |
| 127 | --color-clerk-page: #0e0e0e; | |
| 301 | --color-clerk-page: #0a0a0a; | |
| 128 | 302 | --color-clerk-foreground: #f5f5f5; |
| 129 | --color-clerk-foreground-muted: #a3a3a3; | |
| 130 | --color-clerk-border: rgba(42, 42, 42, 0.06); | |
| 131 | --color-clerk-danger: #fca5a5; | |
| 303 | --color-clerk-foreground-muted: #818181; | |
| 304 | --color-clerk-border: #191919; | |
| 305 | --color-clerk-danger: #ff6467; | |
| 132 | 306 | } |
| 133 | 307 | |
| 134 | 308 | @variant t3-chat-light { |
| 135 | 309 | --color-screen: #fdf7fd; |
| 136 | 310 | --color-sheet: rgba(253, 247, 253, 0.98); |
| 137 | 311 | --color-sheet-solid: #fdf7fd; |
| 138 | --color-card: #fdfafd; | |
| 139 | --color-card-alt: #faf3fb; | |
| 140 | --color-card-translucent: rgba(253, 250, 253, 0.8); | |
| 141 | --color-thread-canvas: #faf3fb; | |
| 142 | --color-thread-selected: #fdfafd; | |
| 143 | --color-thread-selected-foreground: #501854; | |
| 312 | --color-card: #faf3fb; | |
| 313 | --color-grouped-card: #faf3fb; | |
| 314 | --color-card-alt: #fdfafd; | |
| 315 | --color-card-translucent: rgba(250, 243, 251, 0.8); | |
| 316 | --color-thread-canvas: #fdf7fd; | |
| 317 | --color-thread-selected: #f8f8f7; | |
| 318 | --color-thread-selected-foreground: #454554; | |
| 144 | 319 | --color-thread-selected-foreground-muted: #ac1668; |
| 145 | --color-composer-panel: rgba(250, 243, 251, 0.88); | |
| 146 | --color-composer-surface: rgba(253, 250, 253, 0.94); | |
| 320 | --color-thread-hover: #f8f8f7; | |
| 321 | --color-row-hover: #eccfe3; | |
| 322 | --color-composer-panel: rgba(253, 247, 253, 0.88); | |
| 323 | --color-composer-surface: rgba(250, 243, 251, 0.94); | |
| 147 | 324 | --color-composer-border: rgba(238, 225, 237, 0.54); |
| 148 | 325 | --color-foreground: #501854; |
| 149 | 326 | --color-foreground-secondary: #ac1668; |
| 150 | 327 | --color-foreground-muted: #8d1255; |
| 151 | 328 | --color-foreground-tertiary: #ac1668; |
| 152 | 329 | --color-border: #eee1ed; |
| 330 | --color-focus: #db2777; | |
| 153 | 331 | --color-border-subtle: rgba(238, 225, 237, 0.7); |
| 154 | 332 | --color-separator: rgba(238, 225, 237, 0.55); |
| 155 | 333 | --color-subtle: #eaa7cb; |
| ... | ... | @@ -159,6 +337,7 @@ |
| 159 | 337 | --color-inline-skill-foreground: #454554; |
| 160 | 338 | --color-primary: #db2777; |
| 161 | 339 | --color-primary-foreground: #ffffff; |
| 340 | --color-primary-text: #d32572; | |
| 162 | 341 | --color-primary-shadow: #000000; |
| 163 | 342 | --color-secondary: #f1c4e6; |
| 164 | 343 | --color-secondary-foreground: #77347c; |
| ... | ... | @@ -173,7 +352,9 @@ |
| 173 | 352 | --color-danger: #fde4f1; |
| 174 | 353 | --color-danger-border: rgba(247, 8, 108, 0.32); |
| 175 | 354 | --color-danger-foreground: #9d174d; |
| 176 | --color-input: #fdfafd; | |
| 355 | --color-update: #fadfef; | |
| 356 | --color-update-foreground: #ac1668; | |
| 357 | --color-input: #faf3fb; | |
| 177 | 358 | --color-input-border: #e7c1dc; |
| 178 | 359 | --color-sidebar-search: #f8f8f7; |
| 179 | 360 | --color-placeholder: #8b5f90; |
| ... | ... | @@ -181,13 +362,15 @@ |
| 181 | 362 | --color-icon-muted: #ac1668; |
| 182 | 363 | --color-icon-subtle: #ac1668; |
| 183 | 364 | --color-header: rgba(253, 247, 253, 0.97); |
| 365 | --color-header-foreground: #501854; | |
| 184 | 366 | --color-header-border: #efbdeb; |
| 185 | 367 | --color-glass-surface: rgba(255, 255, 255, 0.74); |
| 368 | --color-glass-fallback: rgba(250, 243, 251, 0.94); | |
| 186 | 369 | --color-glass-tint: rgba(255, 255, 255, 0.22); |
| 187 | 370 | --color-status-bar: #fdf7fd; |
| 188 | 371 | --color-md-body: #501854; |
| 189 | 372 | --color-md-strong: #501854; |
| 190 | --color-md-link: #db2777; | |
| 373 | --color-md-link: #d62675; | |
| 191 | 374 | --color-md-blockquote-border: #eee1ed; |
| 192 | 375 | --color-md-blockquote-bg: #eaa7cb; |
| 193 | 376 | --color-md-code-bg: #f5ecf9; |
| ... | ... | @@ -202,7 +385,10 @@ |
| 202 | 385 | --color-user-bubble-foreground-muted: rgba(73, 44, 97, 0.78); |
| 203 | 386 | --color-user-bubble-skill-foreground: #c12269; |
| 204 | 387 | --color-backdrop: rgba(0, 0, 0, 0.22); |
| 205 | --color-drawer: rgba(242, 225, 244, 0.99); | |
| 388 | --color-drawer: #f2e1f4; | |
| 389 | --color-drawer-foreground: #454554; | |
| 390 | --color-drawer-foreground-muted: #ac1668; | |
| 391 | --color-drawer-border: #eceae9; | |
| 206 | 392 | --color-drawer-shadow: rgba(0, 0, 0, 0.12); |
| 207 | 393 | --color-dot-separator: rgba(172, 22, 104, 0.35); |
| 208 | 394 | --color-wordmark: #501854; |
| ... | ... | @@ -264,32 +450,36 @@ |
| 264 | 450 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%); |
| 265 | 451 | --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938); |
| 266 | 452 | --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786); |
| 267 | --color-clerk-page: #f2f2f7; | |
| 268 | --color-clerk-foreground: #262626; | |
| 269 | --color-clerk-foreground-muted: #737373; | |
| 270 | --color-clerk-border: rgba(229, 229, 234, 0.06); | |
| 271 | --color-clerk-danger: #dc2626; | |
| 453 | --color-clerk-page: #fcfcfc; | |
| 454 | --color-clerk-foreground: #27272a; | |
| 455 | --color-clerk-foreground-muted: #71717b; | |
| 456 | --color-clerk-border: #e4e4e7; | |
| 457 | --color-clerk-danger: #c10007; | |
| 272 | 458 | } |
| 273 | 459 | |
| 274 | 460 | @variant t3-chat-dark { |
| 275 | 461 | --color-screen: #1f1a24; |
| 276 | 462 | --color-sheet: rgba(31, 26, 36, 0.98); |
| 277 | 463 | --color-sheet-solid: #1f1a24; |
| 278 | --color-card: #2c2631; | |
| 279 | --color-card-alt: #29232d; | |
| 280 | --color-card-translucent: rgba(44, 38, 49, 0.8); | |
| 281 | --color-thread-canvas: #29232d; | |
| 282 | --color-thread-selected: #2c2631; | |
| 283 | --color-thread-selected-foreground: #f9f8fb; | |
| 464 | --color-card: #29232d; | |
| 465 | --color-grouped-card: #29232d; | |
| 466 | --color-card-alt: #2c2631; | |
| 467 | --color-card-translucent: rgba(41, 35, 45, 0.8); | |
| 468 | --color-thread-canvas: #1f1a24; | |
| 469 | --color-thread-selected: #261922; | |
| 470 | --color-thread-selected-foreground: #f4f4f5; | |
| 284 | 471 | --color-thread-selected-foreground-muted: #e7d0dd; |
| 285 | --color-composer-panel: rgba(41, 35, 45, 0.92); | |
| 286 | --color-composer-surface: rgba(44, 38, 49, 0.9); | |
| 472 | --color-thread-hover: #261922; | |
| 473 | --color-row-hover: #463753; | |
| 474 | --color-composer-panel: rgba(31, 26, 36, 0.92); | |
| 475 | --color-composer-surface: rgba(41, 35, 45, 0.9); | |
| 287 | 476 | --color-composer-border: rgba(39, 36, 44, 0.46); |
| 288 | 477 | --color-foreground: #f9f8fb; |
| 289 | 478 | --color-foreground-secondary: #e7d0dd; |
| 290 | 479 | --color-foreground-muted: #e7d0dd; |
| 291 | 480 | --color-foreground-tertiary: #e7d0dd; |
| 292 | 481 | --color-border: #27242c; |
| 482 | --color-focus: #db2777; | |
| 293 | 483 | --color-border-subtle: rgba(39, 36, 44, 0.7); |
| 294 | 484 | --color-separator: rgba(39, 36, 44, 0.55); |
| 295 | 485 | --color-subtle: #423a45; |
| ... | ... | @@ -299,6 +489,7 @@ |
| 299 | 489 | --color-inline-skill-foreground: #f8f1f5; |
| 300 | 490 | --color-primary: #a3004c; |
| 301 | 491 | --color-primary-foreground: #fbd0e8; |
| 492 | --color-primary-text: #cc729c; | |
| 302 | 493 | --color-primary-shadow: #000000; |
| 303 | 494 | --color-secondary: #362d3d; |
| 304 | 495 | --color-secondary-foreground: #d4c7e1; |
| ... | ... | @@ -313,7 +504,9 @@ |
| 313 | 504 | --color-danger: #331a2b; |
| 314 | 505 | --color-danger-border: rgba(157, 23, 77, 0.32); |
| 315 | 506 | --color-danger-foreground: #fbd0e8; |
| 316 | --color-input: #2c2631; | |
| 507 | --color-update: #37152b; | |
| 508 | --color-update-foreground: #fbd0e8; | |
| 509 | --color-input: #29232d; | |
| 317 | 510 | --color-input-border: #302029; |
| 318 | 511 | --color-sidebar-search: #261922; |
| 319 | 512 | --color-placeholder: #968d9f; |
| ... | ... | @@ -321,13 +514,15 @@ |
| 321 | 514 | --color-icon-muted: #d4c7e1; |
| 322 | 515 | --color-icon-subtle: #e7d0dd; |
| 323 | 516 | --color-header: rgba(31, 26, 36, 0.97); |
| 517 | --color-header-foreground: #f9f8fb; | |
| 324 | 518 | --color-header-border: #27242c; |
| 325 | 519 | --color-glass-surface: rgba(16, 10, 14, 0.74); |
| 520 | --color-glass-fallback: rgba(41, 35, 45, 0.9); | |
| 326 | 521 | --color-glass-tint: rgba(16, 10, 14, 0.22); |
| 327 | 522 | --color-status-bar: #1f1a24; |
| 328 | 523 | --color-md-body: #f9f8fb; |
| 329 | 524 | --color-md-strong: #f9f8fb; |
| 330 | --color-md-link: #a3004c; | |
| 525 | --color-md-link: #c66290; | |
| 331 | 526 | --color-md-blockquote-border: #27242c; |
| 332 | 527 | --color-md-blockquote-bg: #423a45; |
| 333 | 528 | --color-md-code-bg: #1f1a24; |
| ... | ... | @@ -342,7 +537,10 @@ |
| 342 | 537 | --color-user-bubble-foreground-muted: rgba(242, 235, 250, 0.78); |
| 343 | 538 | --color-user-bubble-skill-foreground: #cb709a; |
| 344 | 539 | --color-backdrop: rgba(0, 0, 0, 0.48); |
| 345 | --color-drawer: rgba(23, 16, 24, 0.99); | |
| 540 | --color-drawer: #171018; | |
| 541 | --color-drawer-foreground: #f4f4f5; | |
| 542 | --color-drawer-foreground-muted: #e7d0dd; | |
| 543 | --color-drawer-border: #322028; | |
| 346 | 544 | --color-drawer-shadow: rgba(0, 0, 0, 0.32); |
| 347 | 545 | --color-dot-separator: rgba(231, 208, 221, 0.35); |
| 348 | 546 | --color-wordmark: #f9f8fb; |
| ... | ... | @@ -404,32 +602,36 @@ |
| 404 | 602 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%); |
| 405 | 603 | --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067); |
| 406 | 604 | --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286); |
| 407 | --color-clerk-page: #0e0e0e; | |
| 605 | --color-clerk-page: #0a0a0a; | |
| 408 | 606 | --color-clerk-foreground: #f5f5f5; |
| 409 | --color-clerk-foreground-muted: #a3a3a3; | |
| 410 | --color-clerk-border: rgba(42, 42, 42, 0.06); | |
| 411 | --color-clerk-danger: #fca5a5; | |
| 607 | --color-clerk-foreground-muted: #818181; | |
| 608 | --color-clerk-border: #191919; | |
| 609 | --color-clerk-danger: #ff6467; | |
| 412 | 610 | } |
| 413 | 611 | |
| 414 | 612 | @variant grove-light { |
| 415 | 613 | --color-screen: #f3f7f4; |
| 416 | 614 | --color-sheet: rgba(243, 247, 244, 0.98); |
| 417 | 615 | --color-sheet-solid: #f3f7f4; |
| 418 | --color-card: #ecefed; | |
| 419 | --color-card-alt: #f3f7f4; | |
| 420 | --color-card-translucent: rgba(236, 239, 237, 0.8); | |
| 616 | --color-card: #f3f7f4; | |
| 617 | --color-grouped-card: #f3f7f4; | |
| 618 | --color-card-alt: #ecefed; | |
| 619 | --color-card-translucent: rgba(243, 247, 244, 0.8); | |
| 421 | 620 | --color-thread-canvas: #f3f7f4; |
| 422 | --color-thread-selected: #ecefed; | |
| 621 | --color-thread-selected: #bad7c9; | |
| 423 | 622 | --color-thread-selected-foreground: #241523; |
| 424 | --color-thread-selected-foreground-muted: #746c73; | |
| 623 | --color-thread-selected-foreground-muted: #5d585e; | |
| 624 | --color-thread-hover: #cae0d5; | |
| 625 | --color-row-hover: #d5e6dd; | |
| 425 | 626 | --color-composer-panel: rgba(243, 247, 244, 0.88); |
| 426 | --color-composer-surface: rgba(236, 239, 237, 0.94); | |
| 627 | --color-composer-surface: rgba(243, 247, 244, 0.94); | |
| 427 | 628 | --color-composer-border: rgba(203, 213, 209, 0.54); |
| 428 | 629 | --color-foreground: #241523; |
| 429 | --color-foreground-secondary: #746c73; | |
| 630 | --color-foreground-secondary: #726a71; | |
| 430 | 631 | --color-foreground-muted: #6e696f; |
| 431 | 632 | --color-foreground-tertiary: #746c73; |
| 432 | 633 | --color-border: #cbd5d1; |
| 634 | --color-focus: #1b7d50; | |
| 433 | 635 | --color-border-subtle: rgba(203, 213, 209, 0.7); |
| 434 | 636 | --color-separator: rgba(203, 213, 209, 0.55); |
| 435 | 637 | --color-subtle: #e6f0ea; |
| ... | ... | @@ -437,13 +639,14 @@ |
| 437 | 639 | --color-inline-skill-background: #d5e6dd; |
| 438 | 640 | --color-inline-skill-border: rgba(27, 125, 80, 0.42); |
| 439 | 641 | --color-inline-skill-foreground: #241523; |
| 440 | --color-primary: #1b7d50; | |
| 642 | --color-primary: #8f6410; | |
| 441 | 643 | --color-primary-foreground: #fffaff; |
| 644 | --color-primary-text: #8f6410; | |
| 442 | 645 | --color-primary-shadow: #000000; |
| 443 | 646 | --color-secondary: #e2ede7; |
| 444 | 647 | --color-secondary-foreground: #241523; |
| 445 | 648 | --color-secondary-border: #cbd5d1; |
| 446 | --color-switch-active-track: #1b7d50; | |
| 649 | --color-switch-active-track: #8f6410; | |
| 447 | 650 | --color-switch-active-thumb: #fffaff; |
| 448 | 651 | --color-switch-inactive-track: #e2ede7; |
| 449 | 652 | --color-switch-inactive-thumb: #6e696f; |
| ... | ... | @@ -453,7 +656,9 @@ |
| 453 | 656 | --color-danger: #f4e7e5; |
| 454 | 657 | --color-danger-border: rgba(251, 44, 54, 0.32); |
| 455 | 658 | --color-danger-foreground: #c10007; |
| 456 | --color-input: #ecefed; | |
| 659 | --color-update: #d0e3da; | |
| 660 | --color-update-foreground: #125134; | |
| 661 | --color-input: #f3f7f4; | |
| 457 | 662 | --color-input-border: #becbc5; |
| 458 | 663 | --color-sidebar-search: #d3dcd8; |
| 459 | 664 | --color-placeholder: #716971; |
| ... | ... | @@ -461,13 +666,15 @@ |
| 461 | 666 | --color-icon-muted: #746c73; |
| 462 | 667 | --color-icon-subtle: #746c73; |
| 463 | 668 | --color-header: rgba(243, 247, 244, 0.97); |
| 669 | --color-header-foreground: #241523; | |
| 464 | 670 | --color-header-border: #d5e6dd; |
| 465 | 671 | --color-glass-surface: rgba(231, 233, 232, 0.74); |
| 672 | --color-glass-fallback: rgba(243, 247, 244, 0.94); | |
| 466 | 673 | --color-glass-tint: rgba(231, 233, 232, 0.22); |
| 467 | 674 | --color-status-bar: #f3f7f4; |
| 468 | 675 | --color-md-body: #241523; |
| 469 | 676 | --color-md-strong: #241523; |
| 470 | --color-md-link: #1b7d50; | |
| 677 | --color-md-link: #8f6410; | |
| 471 | 678 | --color-md-blockquote-border: #cbd5d1; |
| 472 | 679 | --color-md-blockquote-bg: #e6f0ea; |
| 473 | 680 | --color-md-code-bg: #eef1ef; |
| ... | ... | @@ -482,7 +689,10 @@ |
| 482 | 689 | --color-user-bubble-foreground-muted: rgba(36, 21, 35, 0.78); |
| 483 | 690 | --color-user-bubble-skill-foreground: #815a0e; |
| 484 | 691 | --color-backdrop: rgba(0, 0, 0, 0.22); |
| 485 | --color-drawer: rgba(226, 237, 231, 0.99); | |
| 692 | --color-drawer: #e2ede7; | |
| 693 | --color-drawer-foreground: #241523; | |
| 694 | --color-drawer-foreground-muted: #645f64; | |
| 695 | --color-drawer-border: #cbd3d0; | |
| 486 | 696 | --color-drawer-shadow: rgba(0, 0, 0, 0.12); |
| 487 | 697 | --color-dot-separator: rgba(116, 108, 115, 0.35); |
| 488 | 698 | --color-wordmark: #241523; |
| ... | ... | @@ -544,32 +754,36 @@ |
| 544 | 754 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%); |
| 545 | 755 | --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938); |
| 546 | 756 | --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786); |
| 547 | --color-clerk-page: #f2f2f7; | |
| 548 | --color-clerk-foreground: #262626; | |
| 549 | --color-clerk-foreground-muted: #737373; | |
| 550 | --color-clerk-border: rgba(229, 229, 234, 0.06); | |
| 551 | --color-clerk-danger: #dc2626; | |
| 757 | --color-clerk-page: #fcfcfc; | |
| 758 | --color-clerk-foreground: #27272a; | |
| 759 | --color-clerk-foreground-muted: #71717b; | |
| 760 | --color-clerk-border: #e4e4e7; | |
| 761 | --color-clerk-danger: #c10007; | |
| 552 | 762 | } |
| 553 | 763 | |
| 554 | 764 | @variant grove-dark { |
| 555 | 765 | --color-screen: #1b2821; |
| 556 | 766 | --color-sheet: rgba(27, 40, 33, 0.98); |
| 557 | 767 | --color-sheet-solid: #1b2821; |
| 558 | --color-card: #36413c; | |
| 559 | --color-card-alt: #1b2821; | |
| 560 | --color-card-translucent: rgba(54, 65, 60, 0.8); | |
| 768 | --color-card: #1b2821; | |
| 769 | --color-grouped-card: #1b2821; | |
| 770 | --color-card-alt: #36413c; | |
| 771 | --color-card-translucent: rgba(27, 40, 33, 0.8); | |
| 561 | 772 | --color-thread-canvas: #1b2821; |
| 562 | --color-thread-selected: #36413c; | |
| 773 | --color-thread-selected: #2f5641; | |
| 563 | 774 | --color-thread-selected-foreground: #fffaff; |
| 564 | --color-thread-selected-foreground-muted: #919595; | |
| 775 | --color-thread-selected-foreground-muted: #bbc0bf; | |
| 776 | --color-thread-hover: #2a4938; | |
| 777 | --color-row-hover: #325c46; | |
| 565 | 778 | --color-composer-panel: rgba(27, 40, 33, 0.92); |
| 566 | --color-composer-surface: rgba(54, 65, 60, 0.9); | |
| 779 | --color-composer-surface: rgba(27, 40, 33, 0.9); | |
| 567 | 780 | --color-composer-border: rgba(65, 95, 79, 0.46); |
| 568 | 781 | --color-foreground: #fffaff; |
| 569 | --color-foreground-secondary: #919595; | |
| 570 | --color-foreground-muted: #9da5a2; | |
| 782 | --color-foreground-secondary: #a6aaaa; | |
| 783 | --color-foreground-muted: #a3aba8; | |
| 571 | 784 | --color-foreground-tertiary: #919595; |
| 572 | 785 | --color-border: #415f4f; |
| 786 | --color-focus: #69d69a; | |
| 573 | 787 | --color-border-subtle: rgba(65, 95, 79, 0.7); |
| 574 | 788 | --color-separator: rgba(65, 95, 79, 0.55); |
| 575 | 789 | --color-subtle: #253e31; |
| ... | ... | @@ -577,13 +791,14 @@ |
| 577 | 791 | --color-inline-skill-background: #325c46; |
| 578 | 792 | --color-inline-skill-border: rgba(105, 214, 154, 0.42); |
| 579 | 793 | --color-inline-skill-foreground: #fffaff; |
| 580 | --color-primary: #69d69a; | |
| 794 | --color-primary: #e3b34e; | |
| 581 | 795 | --color-primary-foreground: #241523; |
| 796 | --color-primary-text: #e3b34e; | |
| 582 | 797 | --color-primary-shadow: #000000; |
| 583 | 798 | --color-secondary: #2a4b39; |
| 584 | 799 | --color-secondary-foreground: #fffaff; |
| 585 | 800 | --color-secondary-border: #415f4f; |
| 586 | --color-switch-active-track: #69d69a; | |
| 801 | --color-switch-active-track: #e3b34e; | |
| 587 | 802 | --color-switch-active-thumb: #241523; |
| 588 | 803 | --color-switch-inactive-track: #2a4b39; |
| 589 | 804 | --color-switch-inactive-thumb: #9da5a2; |
| ... | ... | @@ -593,7 +808,9 @@ |
| 593 | 808 | --color-danger: #3f2c28; |
| 594 | 809 | --color-danger-border: rgba(251, 65, 74, 0.32); |
| 595 | 810 | --color-danger-foreground: #ff6668; |
| 596 | --color-input: #36413c; | |
| 811 | --color-update: #345f48; | |
| 812 | --color-update-foreground: #9ee4be; | |
| 813 | --color-input: #1b2821; | |
| 597 | 814 | --color-input-border: #4f725f; |
| 598 | 815 | --color-sidebar-search: #45554d; |
| 599 | 816 | --color-placeholder: #a9abab; |
| ... | ... | @@ -601,13 +818,15 @@ |
| 601 | 818 | --color-icon-muted: #919595; |
| 602 | 819 | --color-icon-subtle: #919595; |
| 603 | 820 | --color-header: rgba(27, 40, 33, 0.97); |
| 821 | --color-header-foreground: #fffaff; | |
| 604 | 822 | --color-header-border: #36654c; |
| 605 | 823 | --color-glass-surface: rgba(68, 77, 73, 0.74); |
| 824 | --color-glass-fallback: rgba(27, 40, 33, 0.9); | |
| 606 | 825 | --color-glass-tint: rgba(68, 77, 73, 0.22); |
| 607 | 826 | --color-status-bar: #1b2821; |
| 608 | 827 | --color-md-body: #fffaff; |
| 609 | 828 | --color-md-strong: #fffaff; |
| 610 | --color-md-link: #69d69a; | |
| 829 | --color-md-link: #e3b34e; | |
| 611 | 830 | --color-md-blockquote-border: #415f4f; |
| 612 | 831 | --color-md-blockquote-bg: #253e31; |
| 613 | 832 | --color-md-code-bg: #28342e; |
| ... | ... | @@ -622,7 +841,10 @@ |
| 622 | 841 | --color-user-bubble-foreground-muted: rgba(255, 250, 255, 0.78); |
| 623 | 842 | --color-user-bubble-skill-foreground: #eed295; |
| 624 | 843 | --color-backdrop: rgba(0, 0, 0, 0.48); |
| 625 | --color-drawer: rgba(33, 54, 43, 0.99); | |
| 844 | --color-drawer: #21362b; | |
| 845 | --color-drawer-foreground: #fffaff; | |
| 846 | --color-drawer-foreground-muted: #aab0af; | |
| 847 | --color-drawer-border: #6f7a75; | |
| 626 | 848 | --color-drawer-shadow: rgba(0, 0, 0, 0.32); |
| 627 | 849 | --color-dot-separator: rgba(145, 149, 149, 0.35); |
| 628 | 850 | --color-wordmark: #fffaff; |
| ... | ... | @@ -684,32 +906,36 @@ |
| 684 | 906 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%); |
| 685 | 907 | --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067); |
| 686 | 908 | --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286); |
| 687 | --color-clerk-page: #0e0e0e; | |
| 909 | --color-clerk-page: #0a0a0a; | |
| 688 | 910 | --color-clerk-foreground: #f5f5f5; |
| 689 | --color-clerk-foreground-muted: #a3a3a3; | |
| 690 | --color-clerk-border: rgba(42, 42, 42, 0.06); | |
| 691 | --color-clerk-danger: #fca5a5; | |
| 911 | --color-clerk-foreground-muted: #818181; | |
| 912 | --color-clerk-border: #191919; | |
| 913 | --color-clerk-danger: #ff6467; | |
| 692 | 914 | } |
| 693 | 915 | |
| 694 | 916 | @variant ocean-light { |
| 695 | 917 | --color-screen: #f5f7f8; |
| 696 | 918 | --color-sheet: rgba(245, 247, 248, 0.98); |
| 697 | 919 | --color-sheet-solid: #f5f7f8; |
| 698 | --color-card: #edeff1; | |
| 699 | --color-card-alt: #f5f7f8; | |
| 700 | --color-card-translucent: rgba(237, 239, 241, 0.8); | |
| 920 | --color-card: #f5f7f8; | |
| 921 | --color-grouped-card: #f5f7f8; | |
| 922 | --color-card-alt: #edeff1; | |
| 923 | --color-card-translucent: rgba(245, 247, 248, 0.8); | |
| 701 | 924 | --color-thread-canvas: #f5f7f8; |
| 702 | --color-thread-selected: #edeff1; | |
| 925 | --color-thread-selected: #bed4e5; | |
| 703 | 926 | --color-thread-selected-foreground: #241523; |
| 704 | --color-thread-selected-foreground-muted: #746c75; | |
| 927 | --color-thread-selected-foreground-muted: #5e5862; | |
| 928 | --color-thread-hover: #cdddea; | |
| 929 | --color-row-hover: #d8e4ee; | |
| 705 | 930 | --color-composer-panel: rgba(245, 247, 248, 0.88); |
| 706 | --color-composer-surface: rgba(237, 239, 241, 0.94); | |
| 931 | --color-composer-surface: rgba(245, 247, 248, 0.94); | |
| 707 | 932 | --color-composer-border: rgba(205, 212, 220, 0.54); |
| 708 | 933 | --color-foreground: #241523; |
| 709 | --color-foreground-secondary: #746c75; | |
| 934 | --color-foreground-secondary: #726a73; | |
| 710 | 935 | --color-foreground-muted: #6f6873; |
| 711 | 936 | --color-foreground-tertiary: #746c75; |
| 712 | 937 | --color-border: #cdd4dc; |
| 938 | --color-focus: #2672af; | |
| 713 | 939 | --color-border-subtle: rgba(205, 212, 220, 0.7); |
| 714 | 940 | --color-separator: rgba(205, 212, 220, 0.55); |
| 715 | 941 | --color-subtle: #e8eff4; |
| ... | ... | @@ -717,13 +943,14 @@ |
| 717 | 943 | --color-inline-skill-background: #d8e4ee; |
| 718 | 944 | --color-inline-skill-border: rgba(38, 114, 175, 0.42); |
| 719 | 945 | --color-inline-skill-foreground: #241523; |
| 720 | --color-primary: #2672af; | |
| 946 | --color-primary: #0a6f75; | |
| 721 | 947 | --color-primary-foreground: #fffaff; |
| 948 | --color-primary-text: #0a6f75; | |
| 722 | 949 | --color-primary-shadow: #000000; |
| 723 | 950 | --color-secondary: #e4ecf2; |
| 724 | 951 | --color-secondary-foreground: #241523; |
| 725 | 952 | --color-secondary-border: #cdd4dc; |
| 726 | --color-switch-active-track: #2672af; | |
| 953 | --color-switch-active-track: #0a6f75; | |
| 727 | 954 | --color-switch-active-thumb: #fffaff; |
| 728 | 955 | --color-switch-inactive-track: #e4ecf2; |
| 729 | 956 | --color-switch-inactive-thumb: #6f6873; |
| ... | ... | @@ -733,7 +960,9 @@ |
| 733 | 960 | --color-danger: #f5e6e9; |
| 734 | 961 | --color-danger-border: rgba(251, 44, 54, 0.32); |
| 735 | 962 | --color-danger-foreground: #c10007; |
| 736 | --color-input: #edeff1; | |
| 963 | --color-update: #d4e1ed; | |
| 964 | --color-update-foreground: #194a72; | |
| 965 | --color-input: #f5f7f8; | |
| 737 | 966 | --color-input-border: #c0c9d4; |
| 738 | 967 | --color-sidebar-search: #d5dbe2; |
| 739 | 968 | --color-placeholder: #716972; |
| ... | ... | @@ -741,13 +970,15 @@ |
| 741 | 970 | --color-icon-muted: #746c75; |
| 742 | 971 | --color-icon-subtle: #746c75; |
| 743 | 972 | --color-header: rgba(245, 247, 248, 0.97); |
| 973 | --color-header-foreground: #241523; | |
| 744 | 974 | --color-header-border: #d8e4ee; |
| 745 | 975 | --color-glass-surface: rgba(232, 233, 235, 0.74); |
| 976 | --color-glass-fallback: rgba(245, 247, 248, 0.94); | |
| 746 | 977 | --color-glass-tint: rgba(232, 233, 235, 0.22); |
| 747 | 978 | --color-status-bar: #f5f7f8; |
| 748 | 979 | --color-md-body: #241523; |
| 749 | 980 | --color-md-strong: #241523; |
| 750 | --color-md-link: #2672af; | |
| 981 | --color-md-link: #0a6f75; | |
| 751 | 982 | --color-md-blockquote-border: #cdd4dc; |
| 752 | 983 | --color-md-blockquote-bg: #e8eff4; |
| 753 | 984 | --color-md-code-bg: #f0f1f3; |
| ... | ... | @@ -762,7 +993,10 @@ |
| 762 | 993 | --color-user-bubble-foreground-muted: rgba(36, 21, 35, 0.78); |
| 763 | 994 | --color-user-bubble-skill-foreground: #0a6c72; |
| 764 | 995 | --color-backdrop: rgba(0, 0, 0, 0.22); |
| 765 | --color-drawer: rgba(228, 236, 242, 0.99); | |
| 996 | --color-drawer: #e4ecf2; | |
| 997 | --color-drawer-foreground: #241523; | |
| 998 | --color-drawer-foreground-muted: #655e69; | |
| 999 | --color-drawer-border: #cdd2d9; | |
| 766 | 1000 | --color-drawer-shadow: rgba(0, 0, 0, 0.12); |
| 767 | 1001 | --color-dot-separator: rgba(116, 108, 117, 0.35); |
| 768 | 1002 | --color-wordmark: #241523; |
| ... | ... | @@ -824,32 +1058,36 @@ |
| 824 | 1058 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%); |
| 825 | 1059 | --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938); |
| 826 | 1060 | --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786); |
| 827 | --color-clerk-page: #f2f2f7; | |
| 828 | --color-clerk-foreground: #262626; | |
| 829 | --color-clerk-foreground-muted: #737373; | |
| 830 | --color-clerk-border: rgba(229, 229, 234, 0.06); | |
| 831 | --color-clerk-danger: #dc2626; | |
| 1061 | --color-clerk-page: #fcfcfc; | |
| 1062 | --color-clerk-foreground: #27272a; | |
| 1063 | --color-clerk-foreground-muted: #71717b; | |
| 1064 | --color-clerk-border: #e4e4e7; | |
| 1065 | --color-clerk-danger: #c10007; | |
| 832 | 1066 | } |
| 833 | 1067 | |
| 834 | 1068 | @variant ocean-dark { |
| 835 | 1069 | --color-screen: #17212b; |
| 836 | 1070 | --color-sheet: rgba(23, 33, 43, 0.98); |
| 837 | 1071 | --color-sheet-solid: #17212b; |
| 838 | --color-card: #333b45; | |
| 839 | --color-card-alt: #17212b; | |
| 840 | --color-card-translucent: rgba(51, 59, 69, 0.8); | |
| 1072 | --color-card: #17212b; | |
| 1073 | --color-grouped-card: #17212b; | |
| 1074 | --color-card-alt: #333b45; | |
| 1075 | --color-card-translucent: rgba(23, 33, 43, 0.8); | |
| 841 | 1076 | --color-thread-canvas: #17212b; |
| 842 | --color-thread-selected: #333b45; | |
| 1077 | --color-thread-selected: #2f495f; | |
| 843 | 1078 | --color-thread-selected-foreground: #fffaff; |
| 844 | --color-thread-selected-foreground-muted: #8d8f97; | |
| 1079 | --color-thread-selected-foreground-muted: #b0b4ba; | |
| 1080 | --color-thread-hover: #283e50; | |
| 1081 | --color-row-hover: #324e66; | |
| 845 | 1082 | --color-composer-panel: rgba(23, 33, 43, 0.92); |
| 846 | --color-composer-surface: rgba(51, 59, 69, 0.9); | |
| 1083 | --color-composer-surface: rgba(23, 33, 43, 0.9); | |
| 847 | 1084 | --color-composer-border: rgba(64, 85, 103, 0.46); |
| 848 | 1085 | --color-foreground: #fffaff; |
| 849 | --color-foreground-secondary: #8d8f97; | |
| 850 | --color-foreground-muted: #969ca6; | |
| 1086 | --color-foreground-secondary: #a2a3aa; | |
| 1087 | --color-foreground-muted: #9ea4ad; | |
| 851 | 1088 | --color-foreground-tertiary: #8d8f97; |
| 852 | 1089 | --color-border: #405567; |
| 1090 | --color-focus: #70b9ee; | |
| 853 | 1091 | --color-border-subtle: rgba(64, 85, 103, 0.7); |
| 854 | 1092 | --color-separator: rgba(64, 85, 103, 0.55); |
| 855 | 1093 | --color-subtle: #233544; |
| ... | ... | @@ -857,13 +1095,14 @@ |
| 857 | 1095 | --color-inline-skill-background: #324e66; |
| 858 | 1096 | --color-inline-skill-border: rgba(112, 185, 238, 0.42); |
| 859 | 1097 | --color-inline-skill-foreground: #fffaff; |
| 860 | --color-primary: #70b9ee; | |
| 1098 | --color-primary: #5bd0d6; | |
| 861 | 1099 | --color-primary-foreground: #241523; |
| 1100 | --color-primary-text: #5bd0d6; | |
| 862 | 1101 | --color-primary-shadow: #000000; |
| 863 | 1102 | --color-secondary: #293f52; |
| 864 | 1103 | --color-secondary-foreground: #fffaff; |
| 865 | 1104 | --color-secondary-border: #405567; |
| 866 | --color-switch-active-track: #70b9ee; | |
| 1105 | --color-switch-active-track: #5bd0d6; | |
| 867 | 1106 | --color-switch-active-thumb: #241523; |
| 868 | 1107 | --color-switch-inactive-track: #293f52; |
| 869 | 1108 | --color-switch-inactive-thumb: #969ca6; |
| ... | ... | @@ -873,7 +1112,9 @@ |
| 873 | 1112 | --color-danger: #3c2630; |
| 874 | 1113 | --color-danger-border: rgba(251, 65, 74, 0.32); |
| 875 | 1114 | --color-danger-foreground: #ff6467; |
| 876 | --color-input: #333b45; | |
| 1115 | --color-update: #345269; | |
| 1116 | --color-update-foreground: #a2d2f4; | |
| 1117 | --color-input: #17212b; | |
| 877 | 1118 | --color-input-border: #4f677b; |
| 878 | 1119 | --color-sidebar-search: #424e5a; |
| 879 | 1120 | --color-placeholder: #a4a4ac; |
| ... | ... | @@ -881,13 +1122,15 @@ |
| 881 | 1122 | --color-icon-muted: #8d8f97; |
| 882 | 1123 | --color-icon-subtle: #8d8f97; |
| 883 | 1124 | --color-header: rgba(23, 33, 43, 0.97); |
| 1125 | --color-header-foreground: #fffaff; | |
| 884 | 1126 | --color-header-border: #36566f; |
| 885 | 1127 | --color-glass-surface: rgba(65, 72, 81, 0.74); |
| 1128 | --color-glass-fallback: rgba(23, 33, 43, 0.9); | |
| 886 | 1129 | --color-glass-tint: rgba(65, 72, 81, 0.22); |
| 887 | 1130 | --color-status-bar: #17212b; |
| 888 | 1131 | --color-md-body: #fffaff; |
| 889 | 1132 | --color-md-strong: #fffaff; |
| 890 | --color-md-link: #70b9ee; | |
| 1133 | --color-md-link: #5bd0d6; | |
| 891 | 1134 | --color-md-blockquote-border: #405567; |
| 892 | 1135 | --color-md-blockquote-bg: #233544; |
| 893 | 1136 | --color-md-code-bg: #252e38; |
| ... | ... | @@ -902,7 +1145,10 @@ |
| 902 | 1145 | --color-user-bubble-foreground-muted: rgba(255, 250, 255, 0.78); |
| 903 | 1146 | --color-user-bubble-skill-foreground: #75d8dd; |
| 904 | 1147 | --color-backdrop: rgba(0, 0, 0, 0.48); |
| 905 | --color-drawer: rgba(30, 45, 59, 0.99); | |
| 1148 | --color-drawer: #1e2d3b; | |
| 1149 | --color-drawer-foreground: #fffaff; | |
| 1150 | --color-drawer-foreground-muted: #a2a5ae; | |
| 1151 | --color-drawer-border: #6d757f; | |
| 906 | 1152 | --color-drawer-shadow: rgba(0, 0, 0, 0.32); |
| 907 | 1153 | --color-dot-separator: rgba(141, 143, 151, 0.35); |
| 908 | 1154 | --color-wordmark: #fffaff; |
| ... | ... | @@ -964,32 +1210,36 @@ |
| 964 | 1210 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%); |
| 965 | 1211 | --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067); |
| 966 | 1212 | --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286); |
| 967 | --color-clerk-page: #0e0e0e; | |
| 1213 | --color-clerk-page: #0a0a0a; | |
| 968 | 1214 | --color-clerk-foreground: #f5f5f5; |
| 969 | --color-clerk-foreground-muted: #a3a3a3; | |
| 970 | --color-clerk-border: rgba(42, 42, 42, 0.06); | |
| 971 | --color-clerk-danger: #fca5a5; | |
| 1215 | --color-clerk-foreground-muted: #818181; | |
| 1216 | --color-clerk-border: #191919; | |
| 1217 | --color-clerk-danger: #ff6467; | |
| 972 | 1218 | } |
| 973 | 1219 | |
| 974 | 1220 | @variant ember-light { |
| 975 | 1221 | --color-screen: #f9f7f5; |
| 976 | 1222 | --color-sheet: rgba(249, 247, 245, 0.98); |
| 977 | 1223 | --color-sheet-solid: #f9f7f5; |
| 978 | --color-card: #f1efee; | |
| 979 | --color-card-alt: #f9f7f5; | |
| 980 | --color-card-translucent: rgba(241, 239, 238, 0.8); | |
| 1224 | --color-card: #f9f7f5; | |
| 1225 | --color-grouped-card: #f9f7f5; | |
| 1226 | --color-card-alt: #f1efee; | |
| 1227 | --color-card-translucent: rgba(249, 247, 245, 0.8); | |
| 981 | 1228 | --color-thread-canvas: #f9f7f5; |
| 982 | --color-thread-selected: #f1efee; | |
| 1229 | --color-thread-selected: #e5ccc0; | |
| 983 | 1230 | --color-thread-selected-foreground: #241523; |
| 984 | --color-thread-selected-foreground-muted: #766c74; | |
| 1231 | --color-thread-selected-foreground-muted: #62575d; | |
| 1232 | --color-thread-hover: #ead8cf; | |
| 1233 | --color-row-hover: #eee0d9; | |
| 985 | 1234 | --color-composer-panel: rgba(249, 247, 245, 0.88); |
| 986 | --color-composer-surface: rgba(241, 239, 238, 0.94); | |
| 1235 | --color-composer-surface: rgba(249, 247, 245, 0.94); | |
| 987 | 1236 | --color-composer-border: rgba(221, 210, 206, 0.54); |
| 988 | 1237 | --color-foreground: #241523; |
| 989 | --color-foreground-secondary: #766c74; | |
| 1238 | --color-foreground-secondary: #746a72; | |
| 990 | 1239 | --color-foreground-muted: #74686f; |
| 991 | 1240 | --color-foreground-tertiary: #766c74; |
| 992 | 1241 | --color-border: #ddd2ce; |
| 1242 | --color-focus: #ae552a; | |
| 993 | 1243 | --color-border-subtle: rgba(221, 210, 206, 0.7); |
| 994 | 1244 | --color-separator: rgba(221, 210, 206, 0.55); |
| 995 | 1245 | --color-subtle: #f4ede9; |
| ... | ... | @@ -997,13 +1247,14 @@ |
| 997 | 1247 | --color-inline-skill-background: #eee0d9; |
| 998 | 1248 | --color-inline-skill-border: rgba(174, 85, 42, 0.42); |
| 999 | 1249 | --color-inline-skill-foreground: #241523; |
| 1000 | --color-primary: #ae552a; | |
| 1250 | --color-primary: #b23535; | |
| 1001 | 1251 | --color-primary-foreground: #fffaff; |
| 1252 | --color-primary-text: #b23535; | |
| 1002 | 1253 | --color-primary-shadow: #000000; |
| 1003 | 1254 | --color-secondary: #f3eae5; |
| 1004 | 1255 | --color-secondary-foreground: #241523; |
| 1005 | 1256 | --color-secondary-border: #ddd2ce; |
| 1006 | --color-switch-active-track: #ae552a; | |
| 1257 | --color-switch-active-track: #b23535; | |
| 1007 | 1258 | --color-switch-active-thumb: #fffaff; |
| 1008 | 1259 | --color-switch-inactive-track: #f3eae5; |
| 1009 | 1260 | --color-switch-inactive-thumb: #74686f; |
| ... | ... | @@ -1013,7 +1264,9 @@ |
| 1013 | 1264 | --color-danger: #f9e7e6; |
| 1014 | 1265 | --color-danger-border: rgba(251, 44, 54, 0.32); |
| 1015 | 1266 | --color-danger-foreground: #c10007; |
| 1016 | --color-input: #f1efee; | |
| 1267 | --color-update: #edddd5; | |
| 1268 | --color-update-foreground: #71381b; | |
| 1269 | --color-input: #f9f7f5; | |
| 1017 | 1270 | --color-input-border: #d4c6c1; |
| 1018 | 1271 | --color-sidebar-search: #e2d9d6; |
| 1019 | 1272 | --color-placeholder: #736971; |
| ... | ... | @@ -1021,13 +1274,15 @@ |
| 1021 | 1274 | --color-icon-muted: #766c74; |
| 1022 | 1275 | --color-icon-subtle: #766c74; |
| 1023 | 1276 | --color-header: rgba(249, 247, 245, 0.97); |
| 1277 | --color-header-foreground: #241523; | |
| 1024 | 1278 | --color-header-border: #eee0d9; |
| 1025 | 1279 | --color-glass-surface: rgba(236, 233, 233, 0.74); |
| 1280 | --color-glass-fallback: rgba(249, 247, 245, 0.94); | |
| 1026 | 1281 | --color-glass-tint: rgba(236, 233, 233, 0.22); |
| 1027 | 1282 | --color-status-bar: #f9f7f5; |
| 1028 | 1283 | --color-md-body: #241523; |
| 1029 | 1284 | --color-md-strong: #241523; |
| 1030 | --color-md-link: #ae552a; | |
| 1285 | --color-md-link: #b23535; | |
| 1031 | 1286 | --color-md-blockquote-border: #ddd2ce; |
| 1032 | 1287 | --color-md-blockquote-bg: #f4ede9; |
| 1033 | 1288 | --color-md-code-bg: #f3f1f0; |
| ... | ... | @@ -1042,7 +1297,10 @@ |
| 1042 | 1297 | --color-user-bubble-foreground-muted: rgba(36, 21, 35, 0.78); |
| 1043 | 1298 | --color-user-bubble-skill-foreground: #b13535; |
| 1044 | 1299 | --color-backdrop: rgba(0, 0, 0, 0.22); |
| 1045 | --color-drawer: rgba(243, 234, 229, 0.99); | |
| 1300 | --color-drawer: #f3eae5; | |
| 1301 | --color-drawer-foreground: #241523; | |
| 1302 | --color-drawer-foreground-muted: #6a5d64; | |
| 1303 | --color-drawer-border: #dad0ce; | |
| 1046 | 1304 | --color-drawer-shadow: rgba(0, 0, 0, 0.12); |
| 1047 | 1305 | --color-dot-separator: rgba(118, 108, 116, 0.35); |
| 1048 | 1306 | --color-wordmark: #241523; |
| ... | ... | @@ -1104,32 +1362,36 @@ |
| 1104 | 1362 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%); |
| 1105 | 1363 | --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938); |
| 1106 | 1364 | --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786); |
| 1107 | --color-clerk-page: #f2f2f7; | |
| 1108 | --color-clerk-foreground: #262626; | |
| 1109 | --color-clerk-foreground-muted: #737373; | |
| 1110 | --color-clerk-border: rgba(229, 229, 234, 0.06); | |
| 1111 | --color-clerk-danger: #dc2626; | |
| 1365 | --color-clerk-page: #fcfcfc; | |
| 1366 | --color-clerk-foreground: #27272a; | |
| 1367 | --color-clerk-foreground-muted: #71717b; | |
| 1368 | --color-clerk-border: #e4e4e7; | |
| 1369 | --color-clerk-danger: #c10007; | |
| 1112 | 1370 | } |
| 1113 | 1371 | |
| 1114 | 1372 | @variant ember-dark { |
| 1115 | 1373 | --color-screen: #291e1a; |
| 1116 | 1374 | --color-sheet: rgba(41, 30, 26, 0.98); |
| 1117 | 1375 | --color-sheet-solid: #291e1a; |
| 1118 | --color-card: #433835; | |
| 1119 | --color-card-alt: #291e1a; | |
| 1120 | --color-card-translucent: rgba(67, 56, 53, 0.8); | |
| 1376 | --color-card: #291e1a; | |
| 1377 | --color-grouped-card: #291e1a; | |
| 1378 | --color-card-alt: #433835; | |
| 1379 | --color-card-translucent: rgba(41, 30, 26, 0.8); | |
| 1121 | 1380 | --color-thread-canvas: #291e1a; |
| 1122 | --color-thread-selected: #433835; | |
| 1381 | --color-thread-selected: #5d3f2d; | |
| 1123 | 1382 | --color-thread-selected-foreground: #fffaff; |
| 1124 | --color-thread-selected-foreground-muted: #968e8f; | |
| 1383 | --color-thread-selected-foreground-muted: #b9b1b0; | |
| 1384 | --color-thread-hover: #4f3528; | |
| 1385 | --color-row-hover: #644330; | |
| 1125 | 1386 | --color-composer-panel: rgba(41, 30, 26, 0.92); |
| 1126 | --color-composer-surface: rgba(67, 56, 53, 0.9); | |
| 1387 | --color-composer-surface: rgba(41, 30, 26, 0.9); | |
| 1127 | 1388 | --color-composer-border: rgba(102, 76, 63, 0.46); |
| 1128 | 1389 | --color-foreground: #fffaff; |
| 1129 | --color-foreground-secondary: #968e8f; | |
| 1130 | --color-foreground-muted: #a59996; | |
| 1390 | --color-foreground-secondary: #a8a2a2; | |
| 1391 | --color-foreground-muted: #aca19f; | |
| 1131 | 1392 | --color-foreground-tertiary: #968e8f; |
| 1132 | 1393 | --color-border: #664c3f; |
| 1394 | --color-focus: #f09a64; | |
| 1133 | 1395 | --color-border-subtle: rgba(102, 76, 63, 0.7); |
| 1134 | 1396 | --color-separator: rgba(102, 76, 63, 0.55); |
| 1135 | 1397 | --color-subtle: #432e23; |
| ... | ... | @@ -1137,13 +1399,14 @@ |
| 1137 | 1399 | --color-inline-skill-background: #644330; |
| 1138 | 1400 | --color-inline-skill-border: rgba(240, 154, 100, 0.42); |
| 1139 | 1401 | --color-inline-skill-foreground: #fffaff; |
| 1140 | --color-primary: #f09a64; | |
| 1402 | --color-primary: #f78a7a; | |
| 1141 | 1403 | --color-primary-foreground: #241523; |
| 1404 | --color-primary-text: #f78a7a; | |
| 1142 | 1405 | --color-primary-shadow: #000000; |
| 1143 | 1406 | --color-secondary: #513728; |
| 1144 | 1407 | --color-secondary-foreground: #fffaff; |
| 1145 | 1408 | --color-secondary-border: #664c3f; |
| 1146 | --color-switch-active-track: #f09a64; | |
| 1409 | --color-switch-active-track: #f78a7a; | |
| 1147 | 1410 | --color-switch-active-thumb: #241523; |
| 1148 | 1411 | --color-switch-inactive-track: #513728; |
| 1149 | 1412 | --color-switch-inactive-thumb: #a59996; |
| ... | ... | @@ -1153,7 +1416,9 @@ |
| 1153 | 1416 | --color-danger: #4a2321; |
| 1154 | 1417 | --color-danger-border: rgba(251, 65, 74, 0.32); |
| 1155 | 1418 | --color-danger-foreground: #ff6467; |
| 1156 | --color-input: #433835; | |
| 1419 | --color-update: #684631; | |
| 1420 | --color-update-foreground: #f5bd9a; | |
| 1421 | --color-input: #291e1a; | |
| 1157 | 1422 | --color-input-border: #7a5d4d; |
| 1158 | 1423 | --color-sidebar-search: #584943; |
| 1159 | 1424 | --color-placeholder: #aba3a5; |
| ... | ... | @@ -1161,13 +1426,15 @@ |
| 1161 | 1426 | --color-icon-muted: #968e8f; |
| 1162 | 1427 | --color-icon-subtle: #968e8f; |
| 1163 | 1428 | --color-header: rgba(41, 30, 26, 0.97); |
| 1429 | --color-header-foreground: #fffaff; | |
| 1164 | 1430 | --color-header-border: #6e4934; |
| 1165 | 1431 | --color-glass-surface: rgba(79, 69, 67, 0.74); |
| 1432 | --color-glass-fallback: rgba(41, 30, 26, 0.9); | |
| 1166 | 1433 | --color-glass-tint: rgba(79, 69, 67, 0.22); |
| 1167 | 1434 | --color-status-bar: #291e1a; |
| 1168 | 1435 | --color-md-body: #fffaff; |
| 1169 | 1436 | --color-md-strong: #fffaff; |
| 1170 | --color-md-link: #f09a64; | |
| 1437 | --color-md-link: #f78a7a; | |
| 1171 | 1438 | --color-md-blockquote-border: #664c3f; |
| 1172 | 1439 | --color-md-blockquote-bg: #432e23; |
| 1173 | 1440 | --color-md-code-bg: #362b27; |
| ... | ... | @@ -1182,7 +1449,10 @@ |
| 1182 | 1449 | --color-user-bubble-foreground-muted: rgba(255, 250, 255, 0.78); |
| 1183 | 1450 | --color-user-bubble-skill-foreground: #fab6ad; |
| 1184 | 1451 | --color-backdrop: rgba(0, 0, 0, 0.48); |
| 1185 | --color-drawer: rgba(57, 40, 31, 0.99); | |
| 1452 | --color-drawer: #39281f; | |
| 1453 | --color-drawer-foreground: #fffaff; | |
| 1454 | --color-drawer-foreground-muted: #aca2a1; | |
| 1455 | --color-drawer-border: #7e716e; | |
| 1186 | 1456 | --color-drawer-shadow: rgba(0, 0, 0, 0.32); |
| 1187 | 1457 | --color-dot-separator: rgba(150, 142, 143, 0.35); |
| 1188 | 1458 | --color-wordmark: #fffaff; |
| ... | ... | @@ -1244,32 +1514,36 @@ |
| 1244 | 1514 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%); |
| 1245 | 1515 | --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067); |
| 1246 | 1516 | --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286); |
| 1247 | --color-clerk-page: #0e0e0e; | |
| 1517 | --color-clerk-page: #0a0a0a; | |
| 1248 | 1518 | --color-clerk-foreground: #f5f5f5; |
| 1249 | --color-clerk-foreground-muted: #a3a3a3; | |
| 1250 | --color-clerk-border: rgba(42, 42, 42, 0.06); | |
| 1251 | --color-clerk-danger: #fca5a5; | |
| 1519 | --color-clerk-foreground-muted: #818181; | |
| 1520 | --color-clerk-border: #191919; | |
| 1521 | --color-clerk-danger: #ff6467; | |
| 1252 | 1522 | } |
| 1253 | 1523 | |
| 1254 | 1524 | @variant iris-light { |
| 1255 | 1525 | --color-screen: #f8f7f9; |
| 1256 | 1526 | --color-sheet: rgba(248, 247, 249, 0.98); |
| 1257 | 1527 | --color-sheet-solid: #f8f7f9; |
| 1258 | --color-card: #f0eff2; | |
| 1259 | --color-card-alt: #f8f7f9; | |
| 1260 | --color-card-translucent: rgba(240, 239, 242, 0.8); | |
| 1528 | --color-card: #f8f7f9; | |
| 1529 | --color-grouped-card: #f8f7f9; | |
| 1530 | --color-card-alt: #f0eff2; | |
| 1531 | --color-card-translucent: rgba(248, 247, 249, 0.8); | |
| 1261 | 1532 | --color-thread-canvas: #f8f7f9; |
| 1262 | --color-thread-selected: #f0eff2; | |
| 1533 | --color-thread-selected: #d4cce8; | |
| 1263 | 1534 | --color-thread-selected-foreground: #241523; |
| 1264 | --color-thread-selected-foreground-muted: #766c76; | |
| 1535 | --color-thread-selected-foreground-muted: #605662; | |
| 1536 | --color-thread-hover: #ded8ed; | |
| 1537 | --color-row-hover: #e5e0f0; | |
| 1265 | 1538 | --color-composer-panel: rgba(248, 247, 249, 0.88); |
| 1266 | --color-composer-surface: rgba(240, 239, 242, 0.94); | |
| 1539 | --color-composer-surface: rgba(248, 247, 249, 0.94); | |
| 1267 | 1540 | --color-composer-border: rgba(214, 209, 222, 0.54); |
| 1268 | 1541 | --color-foreground: #241523; |
| 1269 | --color-foreground-secondary: #766c76; | |
| 1542 | --color-foreground-secondary: #746a74; | |
| 1270 | 1543 | --color-foreground-muted: #726874; |
| 1271 | 1544 | --color-foreground-tertiary: #766c76; |
| 1272 | 1545 | --color-border: #d6d1de; |
| 1546 | --color-focus: #7253b9; | |
| 1273 | 1547 | --color-border-subtle: rgba(214, 209, 222, 0.7); |
| 1274 | 1548 | --color-separator: rgba(214, 209, 222, 0.55); |
| 1275 | 1549 | --color-subtle: #f0edf6; |
| ... | ... | @@ -1277,13 +1551,14 @@ |
| 1277 | 1551 | --color-inline-skill-background: #e5e0f0; |
| 1278 | 1552 | --color-inline-skill-border: rgba(114, 83, 185, 0.42); |
| 1279 | 1553 | --color-inline-skill-foreground: #241523; |
| 1280 | --color-primary: #7253b9; | |
| 1554 | --color-primary: #a82c87; | |
| 1281 | 1555 | --color-primary-foreground: #fffaff; |
| 1556 | --color-primary-text: #a82c87; | |
| 1282 | 1557 | --color-primary-shadow: #000000; |
| 1283 | 1558 | --color-secondary: #edeaf4; |
| 1284 | 1559 | --color-secondary-foreground: #241523; |
| 1285 | 1560 | --color-secondary-border: #d6d1de; |
| 1286 | --color-switch-active-track: #7253b9; | |
| 1561 | --color-switch-active-track: #a82c87; | |
| 1287 | 1562 | --color-switch-active-thumb: #fffaff; |
| 1288 | 1563 | --color-switch-inactive-track: #edeaf4; |
| 1289 | 1564 | --color-switch-inactive-thumb: #726874; |
| ... | ... | @@ -1293,7 +1568,9 @@ |
| 1293 | 1568 | --color-danger: #f8e6ea; |
| 1294 | 1569 | --color-danger-border: rgba(251, 44, 54, 0.32); |
| 1295 | 1570 | --color-danger-foreground: #c10007; |
| 1296 | --color-input: #f0eff2; | |
| 1571 | --color-update: #e2ddef; | |
| 1572 | --color-update-foreground: #4a3678; | |
| 1573 | --color-input: #f8f7f9; | |
| 1297 | 1574 | --color-input-border: #ccc5d6; |
| 1298 | 1575 | --color-sidebar-search: #ddd9e3; |
| 1299 | 1576 | --color-placeholder: #736973; |
| ... | ... | @@ -1301,13 +1578,15 @@ |
| 1301 | 1578 | --color-icon-muted: #766c76; |
| 1302 | 1579 | --color-icon-subtle: #766c76; |
| 1303 | 1580 | --color-header: rgba(248, 247, 249, 0.97); |
| 1581 | --color-header-foreground: #241523; | |
| 1304 | 1582 | --color-header-border: #e5e0f0; |
| 1305 | 1583 | --color-glass-surface: rgba(235, 233, 237, 0.74); |
| 1584 | --color-glass-fallback: rgba(248, 247, 249, 0.94); | |
| 1306 | 1585 | --color-glass-tint: rgba(235, 233, 237, 0.22); |
| 1307 | 1586 | --color-status-bar: #f8f7f9; |
| 1308 | 1587 | --color-md-body: #241523; |
| 1309 | 1588 | --color-md-strong: #241523; |
| 1310 | --color-md-link: #7253b9; | |
| 1589 | --color-md-link: #a82c87; | |
| 1311 | 1590 | --color-md-blockquote-border: #d6d1de; |
| 1312 | 1591 | --color-md-blockquote-bg: #f0edf6; |
| 1313 | 1592 | --color-md-code-bg: #f2f1f4; |
| ... | ... | @@ -1322,7 +1601,10 @@ |
| 1322 | 1601 | --color-user-bubble-foreground-muted: rgba(36, 21, 35, 0.78); |
| 1323 | 1602 | --color-user-bubble-skill-foreground: #a82c87; |
| 1324 | 1603 | --color-backdrop: rgba(0, 0, 0, 0.22); |
| 1325 | --color-drawer: rgba(237, 234, 244, 0.99); | |
| 1604 | --color-drawer: #edeaf4; | |
| 1605 | --color-drawer-foreground: #241523; | |
| 1606 | --color-drawer-foreground-muted: #685d6a; | |
| 1607 | --color-drawer-border: #d5d0db; | |
| 1326 | 1608 | --color-drawer-shadow: rgba(0, 0, 0, 0.12); |
| 1327 | 1609 | --color-dot-separator: rgba(118, 108, 118, 0.35); |
| 1328 | 1610 | --color-wordmark: #241523; |
| ... | ... | @@ -1384,32 +1666,36 @@ |
| 1384 | 1666 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%); |
| 1385 | 1667 | --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938); |
| 1386 | 1668 | --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786); |
| 1387 | --color-clerk-page: #f2f2f7; | |
| 1388 | --color-clerk-foreground: #262626; | |
| 1389 | --color-clerk-foreground-muted: #737373; | |
| 1390 | --color-clerk-border: rgba(229, 229, 234, 0.06); | |
| 1391 | --color-clerk-danger: #dc2626; | |
| 1669 | --color-clerk-page: #fcfcfc; | |
| 1670 | --color-clerk-foreground: #27272a; | |
| 1671 | --color-clerk-foreground-muted: #71717b; | |
| 1672 | --color-clerk-border: #e4e4e7; | |
| 1673 | --color-clerk-danger: #c10007; | |
| 1392 | 1674 | } |
| 1393 | 1675 | |
| 1394 | 1676 | @variant iris-dark { |
| 1395 | 1677 | --color-screen: #1d1929; |
| 1396 | 1678 | --color-sheet: rgba(29, 25, 41, 0.98); |
| 1397 | 1679 | --color-sheet-solid: #1d1929; |
| 1398 | --color-card: #383443; | |
| 1399 | --color-card-alt: #1d1929; | |
| 1400 | --color-card-translucent: rgba(56, 52, 67, 0.8); | |
| 1680 | --color-card: #1d1929; | |
| 1681 | --color-grouped-card: #1d1929; | |
| 1682 | --color-card-alt: #383443; | |
| 1683 | --color-card-translucent: rgba(29, 25, 41, 0.8); | |
| 1401 | 1684 | --color-thread-canvas: #1d1929; |
| 1402 | --color-thread-selected: #383443; | |
| 1685 | --color-thread-selected: #3f345e; | |
| 1403 | 1686 | --color-thread-selected-foreground: #fffaff; |
| 1404 | --color-thread-selected-foreground-muted: #8e8a95; | |
| 1687 | --color-thread-selected-foreground-muted: #a6a2ae; | |
| 1688 | --color-thread-hover: #352c4f; | |
| 1689 | --color-row-hover: #433765; | |
| 1405 | 1690 | --color-composer-panel: rgba(29, 25, 41, 0.92); |
| 1406 | --color-composer-surface: rgba(56, 52, 67, 0.9); | |
| 1691 | --color-composer-surface: rgba(29, 25, 41, 0.9); | |
| 1407 | 1692 | --color-composer-border: rgba(77, 67, 102, 0.46); |
| 1408 | 1693 | --color-foreground: #fffaff; |
| 1409 | --color-foreground-secondary: #8e8a95; | |
| 1410 | --color-foreground-muted: #9690a1; | |
| 1694 | --color-foreground-secondary: #a09da6; | |
| 1695 | --color-foreground-muted: #a19cab; | |
| 1411 | 1696 | --color-foreground-tertiary: #8e8a95; |
| 1412 | 1697 | --color-border: #4d4366; |
| 1698 | --color-focus: #9d7df2; | |
| 1413 | 1699 | --color-border-subtle: rgba(77, 67, 102, 0.7); |
| 1414 | 1700 | --color-separator: rgba(77, 67, 102, 0.55); |
| 1415 | 1701 | --color-subtle: #2d2643; |
| ... | ... | @@ -1417,13 +1703,14 @@ |
| 1417 | 1703 | --color-inline-skill-background: #433765; |
| 1418 | 1704 | --color-inline-skill-border: rgba(157, 125, 242, 0.42); |
| 1419 | 1705 | --color-inline-skill-foreground: #fffaff; |
| 1420 | --color-primary: #9d7df2; | |
| 1706 | --color-primary: #f099d8; | |
| 1421 | 1707 | --color-primary-foreground: #241523; |
| 1708 | --color-primary-text: #f099d8; | |
| 1422 | 1709 | --color-primary-shadow: #000000; |
| 1423 | 1710 | --color-secondary: #362d51; |
| 1424 | 1711 | --color-secondary-foreground: #fffaff; |
| 1425 | 1712 | --color-secondary-border: #4d4366; |
| 1426 | --color-switch-active-track: #9d7df2; | |
| 1713 | --color-switch-active-track: #f099d8; | |
| 1427 | 1714 | --color-switch-active-thumb: #241523; |
| 1428 | 1715 | --color-switch-inactive-track: #362d51; |
| 1429 | 1716 | --color-switch-inactive-thumb: #9690a1; |
| ... | ... | @@ -1433,7 +1720,9 @@ |
| 1433 | 1720 | --color-danger: #40202e; |
| 1434 | 1721 | --color-danger-border: rgba(251, 65, 74, 0.32); |
| 1435 | 1722 | --color-danger-foreground: #ff6467; |
| 1436 | --color-input: #383443; | |
| 1723 | --color-update: #463969; | |
| 1724 | --color-update-foreground: #bfabf7; | |
| 1725 | --color-input: #1d1929; | |
| 1437 | 1726 | --color-input-border: #5d527b; |
| 1438 | 1727 | --color-sidebar-search: #494459; |
| 1439 | 1728 | --color-placeholder: #a29ea8; |
| ... | ... | @@ -1441,13 +1730,15 @@ |
| 1441 | 1730 | --color-icon-muted: #8e8a95; |
| 1442 | 1731 | --color-icon-subtle: #8e8a95; |
| 1443 | 1732 | --color-header: rgba(29, 25, 41, 0.97); |
| 1733 | --color-header-foreground: #fffaff; | |
| 1444 | 1734 | --color-header-border: #4a3c70; |
| 1445 | 1735 | --color-glass-surface: rgba(69, 66, 80, 0.74); |
| 1736 | --color-glass-fallback: rgba(29, 25, 41, 0.9); | |
| 1446 | 1737 | --color-glass-tint: rgba(69, 66, 80, 0.22); |
| 1447 | 1738 | --color-status-bar: #1d1929; |
| 1448 | 1739 | --color-md-body: #fffaff; |
| 1449 | 1740 | --color-md-strong: #fffaff; |
| 1450 | --color-md-link: #9d7df2; | |
| 1741 | --color-md-link: #f099d8; | |
| 1451 | 1742 | --color-md-blockquote-border: #4d4366; |
| 1452 | 1743 | --color-md-blockquote-bg: #2d2643; |
| 1453 | 1744 | --color-md-code-bg: #2a2736; |
| ... | ... | @@ -1462,7 +1753,10 @@ |
| 1462 | 1753 | --color-user-bubble-foreground-muted: rgba(255, 250, 255, 0.78); |
| 1463 | 1754 | --color-user-bubble-skill-foreground: #f099d8; |
| 1464 | 1755 | --color-backdrop: rgba(0, 0, 0, 0.48); |
| 1465 | --color-drawer: rgba(39, 33, 57, 0.99); | |
| 1756 | --color-drawer: #272139; | |
| 1757 | --color-drawer-foreground: #fffaff; | |
| 1758 | --color-drawer-foreground-muted: #9b97a4; | |
| 1759 | --color-drawer-border: #736d7e; | |
| 1466 | 1760 | --color-drawer-shadow: rgba(0, 0, 0, 0.32); |
| 1467 | 1761 | --color-dot-separator: rgba(142, 138, 149, 0.35); |
| 1468 | 1762 | --color-wordmark: #fffaff; |
| ... | ... | @@ -1524,11 +1818,11 @@ |
| 1524 | 1818 | --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%); |
| 1525 | 1819 | --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067); |
| 1526 | 1820 | --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286); |
| 1527 | --color-clerk-page: #0e0e0e; | |
| 1821 | --color-clerk-page: #0a0a0a; | |
| 1528 | 1822 | --color-clerk-foreground: #f5f5f5; |
| 1529 | --color-clerk-foreground-muted: #a3a3a3; | |
| 1530 | --color-clerk-border: rgba(42, 42, 42, 0.06); | |
| 1531 | --color-clerk-danger: #fca5a5; | |
| 1823 | --color-clerk-foreground-muted: #818181; | |
| 1824 | --color-clerk-border: #191919; | |
| 1825 | --color-clerk-danger: #ff6467; | |
| 1532 | 1826 | } |
| 1533 | 1827 | } |
| 1534 | 1828 | } |
apps/mobile/global.css-222| ... | ... | @@ -8,228 +8,6 @@ |
| 8 | 8 | @variant android { |
| 9 | 9 | --font-mono: "monospace"; |
| 10 | 10 | } |
| 11 | ||
| 12 | @variant light { | |
| 13 | /* Page backgrounds */ | |
| 14 | --color-screen: #f2f2f7; | |
| 15 | --color-sheet: rgba(242, 242, 247, 0.98); | |
| 16 | --color-sheet-solid: #f2f2f7; | |
| 17 | ||
| 18 | /* Card / surface */ | |
| 19 | --color-card: #ffffff; | |
| 20 | --color-card-alt: #f5f5f5; | |
| 21 | --color-card-translucent: rgba(255, 255, 255, 0.8); | |
| 22 | --color-thread-canvas: #f5f5f5; | |
| 23 | --color-thread-selected: #ffffff; | |
| 24 | --color-thread-selected-foreground: #262626; | |
| 25 | --color-thread-selected-foreground-muted: #737373; | |
| 26 | --color-composer-panel: rgba(245, 245, 245, 0.88); | |
| 27 | --color-composer-surface: rgba(255, 255, 255, 0.94); | |
| 28 | --color-composer-border: rgba(0, 0, 0, 0.1); | |
| 29 | ||
| 30 | /* Text */ | |
| 31 | --color-foreground: #262626; | |
| 32 | --color-foreground-secondary: #525252; | |
| 33 | --color-foreground-muted: #737373; | |
| 34 | --color-foreground-tertiary: #8e8e93; | |
| 35 | ||
| 36 | /* Borders & separators */ | |
| 37 | --color-border: rgba(0, 0, 0, 0.08); | |
| 38 | --color-border-subtle: rgba(0, 0, 0, 0.06); | |
| 39 | --color-separator: rgba(0, 0, 0, 0.04); | |
| 40 | ||
| 41 | /* Subtle backgrounds (badges, pills, overlays) */ | |
| 42 | --color-subtle: rgba(0, 0, 0, 0.04); | |
| 43 | --color-subtle-strong: rgba(0, 0, 0, 0.08); | |
| 44 | --color-inline-skill-background: rgba(217, 70, 239, 0.12); | |
| 45 | --color-inline-skill-border: rgba(217, 70, 239, 0.25); | |
| 46 | --color-inline-skill-foreground: #a21caf; | |
| 47 | ||
| 48 | /* Primary action */ | |
| 49 | --color-primary: #262626; | |
| 50 | --color-primary-foreground: #ffffff; | |
| 51 | --color-primary-shadow: #000000; | |
| 52 | ||
| 53 | /* Secondary action */ | |
| 54 | --color-secondary: #ffffff; | |
| 55 | --color-secondary-foreground: #262626; | |
| 56 | --color-secondary-border: rgba(0, 0, 0, 0.08); | |
| 57 | --color-switch-active-track: #34c759; | |
| 58 | --color-switch-active-thumb: #ffffff; | |
| 59 | --color-switch-inactive-track: rgba(0, 0, 0, 0.08); | |
| 60 | --color-switch-inactive-thumb: #8e8e93; | |
| 61 | ||
| 62 | /* Warning */ | |
| 63 | --color-warning: #fffbeb; | |
| 64 | --color-warning-border: #fde68a; | |
| 65 | --color-warning-foreground: #b45309; | |
| 66 | ||
| 67 | /* Danger */ | |
| 68 | --color-danger: #fef2f2; | |
| 69 | --color-danger-border: rgba(239, 68, 68, 0.12); | |
| 70 | --color-danger-foreground: #dc2626; | |
| 71 | ||
| 72 | /* Inputs */ | |
| 73 | --color-input: #ffffff; | |
| 74 | --color-input-border: rgba(0, 0, 0, 0.1); | |
| 75 | --color-sidebar-search: rgba(118, 118, 128, 0.12); | |
| 76 | --color-placeholder: #737373; | |
| 77 | ||
| 78 | /* Icons */ | |
| 79 | --color-icon: #262626; | |
| 80 | --color-icon-muted: #525252; | |
| 81 | --color-icon-subtle: #a3a3a3; | |
| 82 | ||
| 83 | /* Header / glass chrome */ | |
| 84 | --color-header: rgba(255, 255, 255, 0.97); | |
| 85 | --color-header-border: rgba(0, 0, 0, 0.06); | |
| 86 | --color-glass-surface: rgba(255, 255, 255, 0.72); | |
| 87 | --color-glass-tint: rgba(255, 255, 255, 0.18); | |
| 88 | ||
| 89 | /* StatusBar */ | |
| 90 | --color-status-bar: #f2f2f7; | |
| 91 | ||
| 92 | /* Markdown */ | |
| 93 | --color-md-body: #111111; | |
| 94 | --color-md-strong: #000000; | |
| 95 | --color-md-link: #2563eb; | |
| 96 | --color-md-blockquote-border: rgba(0, 0, 0, 0.08); | |
| 97 | --color-md-blockquote-bg: rgba(0, 0, 0, 0.02); | |
| 98 | --color-md-code-bg: rgba(0, 0, 0, 0.04); | |
| 99 | --color-md-code-text: #262626; | |
| 100 | --color-md-user-code-bg: rgba(0, 0, 0, 0.04); | |
| 101 | --color-md-user-code-text: #262626; | |
| 102 | --color-md-user-fence-bg: rgba(0, 0, 0, 0.06); | |
| 103 | --color-md-user-fence-text: #262626; | |
| 104 | --color-md-hr: rgba(0, 0, 0, 0.08); | |
| 105 | ||
| 106 | /* User bubble: a raised card on the thread canvas, matching web's message surface */ | |
| 107 | --color-user-bubble: #ffffff; | |
| 108 | --color-user-bubble-foreground: #262626; | |
| 109 | --color-user-bubble-foreground-muted: rgba(38, 38, 38, 0.78); | |
| 110 | --color-user-bubble-skill-foreground: #2563eb; | |
| 111 | ||
| 112 | /* Drawer / modal backdrop */ | |
| 113 | --color-backdrop: rgba(0, 0, 0, 0.22); | |
| 114 | --color-drawer: rgba(255, 255, 255, 0.99); | |
| 115 | --color-drawer-shadow: rgba(0, 0, 0, 0.12); | |
| 116 | ||
| 117 | /* Misc */ | |
| 118 | --color-dot-separator: rgba(0, 0, 0, 0.2); | |
| 119 | --color-wordmark: #262626; | |
| 120 | --color-chevron: rgba(0, 0, 0, 0.2); | |
| 121 | } | |
| 122 | ||
| 123 | @variant dark { | |
| 124 | /* Page backgrounds */ | |
| 125 | --color-screen: #0a0a0a; | |
| 126 | --color-sheet: rgba(14, 14, 14, 0.98); | |
| 127 | --color-sheet-solid: #0e0e0e; | |
| 128 | ||
| 129 | /* Card / surface */ | |
| 130 | --color-card: #171717; | |
| 131 | --color-card-alt: #1c1c1c; | |
| 132 | --color-card-translucent: rgba(17, 17, 17, 0.8); | |
| 133 | --color-thread-canvas: #1c1c1c; | |
| 134 | --color-thread-selected: #171717; | |
| 135 | --color-thread-selected-foreground: #f5f5f5; | |
| 136 | --color-thread-selected-foreground-muted: #8e8e93; | |
| 137 | --color-composer-panel: rgba(28, 28, 28, 0.92); | |
| 138 | --color-composer-surface: rgba(23, 23, 23, 0.9); | |
| 139 | --color-composer-border: rgba(255, 255, 255, 0.08); | |
| 140 | ||
| 141 | /* Text */ | |
| 142 | --color-foreground: #f5f5f5; | |
| 143 | --color-foreground-secondary: #a3a3a3; | |
| 144 | --color-foreground-muted: #8e8e93; | |
| 145 | --color-foreground-tertiary: #636366; | |
| 146 | ||
| 147 | /* Borders & separators */ | |
| 148 | --color-border: rgba(255, 255, 255, 0.06); | |
| 149 | --color-border-subtle: rgba(255, 255, 255, 0.04); | |
| 150 | --color-separator: rgba(255, 255, 255, 0.03); | |
| 151 | ||
| 152 | /* Subtle backgrounds (badges, pills, overlays) */ | |
| 153 | --color-subtle: rgba(255, 255, 255, 0.04); | |
| 154 | --color-subtle-strong: rgba(255, 255, 255, 0.08); | |
| 155 | --color-inline-skill-background: rgba(217, 70, 239, 0.12); | |
| 156 | --color-inline-skill-border: rgba(217, 70, 239, 0.25); | |
| 157 | --color-inline-skill-foreground: #f0abfc; | |
| 158 | ||
| 159 | /* Primary action */ | |
| 160 | --color-primary: #f5f5f5; | |
| 161 | --color-primary-foreground: #0a0a0a; | |
| 162 | --color-primary-shadow: #000000; | |
| 163 | ||
| 164 | /* Secondary action */ | |
| 165 | --color-secondary: rgba(255, 255, 255, 0.04); | |
| 166 | --color-secondary-foreground: #f5f5f5; | |
| 167 | --color-secondary-border: rgba(255, 255, 255, 0.06); | |
| 168 | --color-switch-active-track: #30d158; | |
| 169 | --color-switch-active-thumb: #ffffff; | |
| 170 | --color-switch-inactive-track: rgba(255, 255, 255, 0.06); | |
| 171 | --color-switch-inactive-thumb: #8e8e93; | |
| 172 | ||
| 173 | /* Warning */ | |
| 174 | --color-warning: rgba(69, 26, 3, 0.4); | |
| 175 | --color-warning-border: rgba(120, 53, 15, 0.6); | |
| 176 | --color-warning-foreground: #fcd34d; | |
| 177 | ||
| 178 | /* Danger */ | |
| 179 | --color-danger: rgba(239, 68, 68, 0.14); | |
| 180 | --color-danger-border: rgba(248, 113, 113, 0.18); | |
| 181 | --color-danger-foreground: #fca5a5; | |
| 182 | ||
| 183 | /* Inputs */ | |
| 184 | --color-input: #141414; | |
| 185 | --color-input-border: rgba(255, 255, 255, 0.08); | |
| 186 | --color-sidebar-search: rgba(118, 118, 128, 0.24); | |
| 187 | --color-placeholder: #8e8e93; | |
| 188 | ||
| 189 | /* Icons */ | |
| 190 | --color-icon: #f5f5f5; | |
| 191 | --color-icon-muted: #a3a3a3; | |
| 192 | --color-icon-subtle: #8e8e93; | |
| 193 | ||
| 194 | /* Header / glass chrome */ | |
| 195 | --color-header: rgba(10, 10, 10, 0.97); | |
| 196 | --color-header-border: rgba(255, 255, 255, 0.06); | |
| 197 | --color-glass-surface: rgba(23, 23, 23, 0.78); | |
| 198 | --color-glass-tint: rgba(23, 23, 23, 0.24); | |
| 199 | ||
| 200 | /* StatusBar */ | |
| 201 | --color-status-bar: #0a0a0a; | |
| 202 | ||
| 203 | /* Markdown */ | |
| 204 | --color-md-body: #e5e5e5; | |
| 205 | --color-md-strong: #f5f5f5; | |
| 206 | --color-md-link: #60a5fa; | |
| 207 | --color-md-blockquote-border: rgba(255, 255, 255, 0.1); | |
| 208 | --color-md-blockquote-bg: rgba(255, 255, 255, 0.03); | |
| 209 | --color-md-code-bg: rgba(255, 255, 255, 0.06); | |
| 210 | --color-md-code-text: #e5e5e5; | |
| 211 | --color-md-user-code-bg: rgba(255, 255, 255, 0.06); | |
| 212 | --color-md-user-code-text: #e5e5e5; | |
| 213 | --color-md-user-fence-bg: rgba(255, 255, 255, 0.09); | |
| 214 | --color-md-user-fence-text: #e5e5e5; | |
| 215 | --color-md-hr: rgba(255, 255, 255, 0.08); | |
| 216 | ||
| 217 | /* User bubble: a raised card on the thread canvas, matching web's message surface */ | |
| 218 | --color-user-bubble: #171717; | |
| 219 | --color-user-bubble-foreground: #f5f5f5; | |
| 220 | --color-user-bubble-foreground-muted: rgba(245, 245, 245, 0.78); | |
| 221 | --color-user-bubble-skill-foreground: #60a5fa; | |
| 222 | ||
| 223 | /* Drawer / modal backdrop */ | |
| 224 | --color-backdrop: rgba(0, 0, 0, 0.48); | |
| 225 | --color-drawer: rgba(14, 14, 14, 0.99); | |
| 226 | --color-drawer-shadow: rgba(0, 0, 0, 0.32); | |
| 227 | ||
| 228 | /* Misc */ | |
| 229 | --color-dot-separator: rgba(255, 255, 255, 0.2); | |
| 230 | --color-wordmark: #f5f5f5; | |
| 231 | --color-chevron: rgba(255, 255, 255, 0.2); | |
| 232 | } | |
| 233 | 11 | } |
| 234 | 12 | } |
| 235 | 13 |
apps/mobile/modules/t3-markdown-text/android/src/main/java/expo/modules/t3markdowntext/T3ContextChip.kt+9-1| ... | ... | @@ -114,7 +114,15 @@ class T3ContextChip( |
| 114 | 114 | fun color( |
| 115 | 115 | value: String, |
| 116 | 116 | fallback: Int |
| 117 | ): Int = runCatching { Color.parseColor(value) }.getOrDefault(fallback) | |
| 117 | ): Int = runCatching { | |
| 118 | // React Native hex colors put alpha last; Android's parser puts it first. | |
| 119 | val androidColor = if (value.length == 9 && value.startsWith("#")) { | |
| 120 | "#${value.takeLast(2)}${value.substring(1, 7)}" | |
| 121 | } else { | |
| 122 | value | |
| 123 | } | |
| 124 | Color.parseColor(androidColor) | |
| 125 | }.getOrDefault(fallback) | |
| 118 | 126 | |
| 119 | 127 | private fun blend(accent: Int, base: Int, weight: Float): Int = Color.rgb( |
| 120 | 128 | (Color.red(accent) * weight + Color.red(base) * (1 - weight)).toInt(), |
apps/mobile/plugins/withAndroidModernAlertDialog.cjs+15-16| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const fs = require("node:fs"); |
| 2 | 2 | const path = require("node:path"); |
| 3 | const themeVariables = require("../generated-uniwind-default-theme-variables.json"); | |
| 3 | 4 | const { |
| 4 | 5 | AndroidConfig, |
| 5 | 6 | withAndroidColors, |
| ... | ... | @@ -11,9 +12,10 @@ const { |
| 11 | 12 | // React Native's Alert renders an AppCompat AlertDialog on Android, which |
| 12 | 13 | // inherits the dated framework dialog chrome (square gray panel, teal |
| 13 | 14 | // all-caps buttons) from the app theme. These resources restyle it with the |
| 14 | // app's uniwind tokens from global.css: --color-card panel, --color-foreground | |
| 15 | // text, --color-primary buttons, DM Sans type. The @font resources referenced | |
| 16 | // here are embedded by the expo-font plugin config in app.config.ts. | |
| 15 | // generated default palette: card panel, foreground text, readable primary | |
| 16 | // buttons, DM Sans type. Alert exposes no runtime custom-palette API, so these | |
| 17 | // build-time resources use the stock palette for each native appearance. | |
| 18 | // The fonts are embedded by the expo-font plugin config in app.config.ts. | |
| 17 | 19 | |
| 18 | 20 | // AppCompat's default dialog window background is an inset rounded rect, so |
| 19 | 21 | // the replacement keeps the same 16dp inset to preserve the dialog's margins. |
| ... | ... | @@ -30,21 +32,18 @@ const DIALOG_BACKGROUND_DRAWABLE = `<?xml version="1.0" encoding="utf-8"?> |
| 30 | 32 | </inset> |
| 31 | 33 | `; |
| 32 | 34 | |
| 33 | const COLORS = { | |
| 34 | light: { | |
| 35 | background: "#FFFFFF", // --color-card | |
| 36 | text: "#262626", // --color-foreground | |
| 37 | secondaryText: "#525252", // --color-foreground-secondary | |
| 38 | buttonText: "#262626", // --color-primary | |
| 39 | }, | |
| 40 | night: { | |
| 41 | background: "#171717", | |
| 42 | text: "#F5F5F5", | |
| 43 | secondaryText: "#A3A3A3", | |
| 44 | buttonText: "#F5F5F5", | |
| 45 | }, | |
| 35 | const colorsFor = (appearance) => { | |
| 36 | const variables = themeVariables[appearance]; | |
| 37 | return { | |
| 38 | background: variables["--color-card"], | |
| 39 | text: variables["--color-foreground"], | |
| 40 | secondaryText: variables["--color-foreground-secondary"], | |
| 41 | buttonText: variables["--color-primary-text"], | |
| 42 | }; | |
| 46 | 43 | }; |
| 47 | 44 | |
| 45 | const COLORS = { light: colorsFor("light"), night: colorsFor("dark") }; | |
| 46 | ||
| 48 | 47 | function assignStyleItem(style, name, value) { |
| 49 | 48 | style.item = style.item ?? []; |
| 50 | 49 | const existing = style.item.find((item) => item.$?.name === name); |
apps/mobile/scripts/generate-uniwind-themes.mts+46-44| ... | ... | @@ -5,17 +5,15 @@ import * as NodePath from "node:path"; |
| 5 | 5 | import tailwindColors from "tailwindcss/colors"; |
| 6 | 6 | import { BUILT_IN_THEME_IDS, type BuiltInThemeId } from "@t3tools/shared/themePalettes"; |
| 7 | 7 | |
| 8 | import clerkTheme from "../clerk-theme.json" with { type: "json" }; | |
| 9 | 8 | import { |
| 9 | createMobileThemeVariables, | |
| 10 | getMobileThemeColors, | |
| 10 | 11 | getMobileThemeVariables, |
| 11 | MOBILE_THEME_VARIABLE_NAMES, | |
| 12 | themeColorWithAlpha, | |
| 12 | DEFAULT_MOBILE_THEME_ID, | |
| 13 | 13 | type MobileThemeAppearance, |
| 14 | type MobileThemeVariables, | |
| 15 | 14 | } from "../src/lib/mobileTheme.ts"; |
| 16 | 15 | |
| 17 | 16 | const APPEARANCES = ["light", "dark"] as const; |
| 18 | const GLOBAL_CSS_PATH = NodePath.resolve(import.meta.dirname, "../global.css"); | |
| 19 | 17 | const GENERATED_CSS_PATH = NodePath.resolve(import.meta.dirname, "../generated-uniwind-themes.css"); |
| 20 | 18 | const GENERATED_NAMES_PATH = NodePath.resolve( |
| 21 | 19 | import.meta.dirname, |
| ... | ... | @@ -25,6 +23,7 @@ const GENERATED_DEFAULT_VARIABLES_PATH = NodePath.resolve( |
| 25 | 23 | import.meta.dirname, |
| 26 | 24 | "../generated-uniwind-default-theme-variables.json", |
| 27 | 25 | ); |
| 26 | const GENERATED_CLERK_THEME_PATH = NodePath.resolve(import.meta.dirname, "../clerk-theme.json"); | |
| 28 | 27 | |
| 29 | 28 | type TailwindColorFamily = keyof typeof tailwindColors; |
| 30 | 29 | type TailwindColorShade = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950; |
| ... | ... | @@ -159,13 +158,38 @@ const variablesFor = (themeId: BuiltInThemeId, appearance: MobileThemeAppearance |
| 159 | 158 | |
| 160 | 159 | // Clerk's native screens use one build-time palette per appearance. Custom |
| 161 | 160 | // profile pages must match it even when the rest of the app uses a named theme. |
| 161 | const clerkColorsFor = (appearance: MobileThemeAppearance) => { | |
| 162 | // Native authentication uses plain cards, rather than tonal settings groups. | |
| 163 | const variables = createMobileThemeVariables( | |
| 164 | getMobileThemeColors(DEFAULT_MOBILE_THEME_ID, appearance), | |
| 165 | appearance, | |
| 166 | ); | |
| 167 | return { | |
| 168 | primary: variables["--color-primary"], | |
| 169 | background: variables["--color-sheet-solid"], | |
| 170 | input: variables["--color-input"], | |
| 171 | danger: variables["--color-danger-foreground"], | |
| 172 | success: appearance === "dark" ? "#34d399" : "#059669", | |
| 173 | warning: variables["--color-warning-foreground"], | |
| 174 | foreground: variables["--color-foreground"], | |
| 175 | mutedForeground: variables["--color-foreground-muted"], | |
| 176 | primaryForeground: variables["--color-primary-foreground"], | |
| 177 | inputForeground: variables["--color-foreground"], | |
| 178 | neutral: variables["--color-secondary"], | |
| 179 | border: variables["--color-border"], | |
| 180 | ring: variables["--color-focus"], | |
| 181 | muted: variables["--color-subtle"], | |
| 182 | shadow: variables["--color-primary-shadow"], | |
| 183 | }; | |
| 184 | }; | |
| 185 | ||
| 162 | 186 | const clerkVariablesFor = (appearance: MobileThemeAppearance) => { |
| 163 | const colors = appearance === "dark" ? clerkTheme.darkColors : clerkTheme.colors; | |
| 187 | const colors = clerkColorsFor(appearance); | |
| 164 | 188 | return { |
| 165 | 189 | "--color-clerk-page": colors.background.toLowerCase(), |
| 166 | 190 | "--color-clerk-foreground": colors.foreground.toLowerCase(), |
| 167 | 191 | "--color-clerk-foreground-muted": colors.mutedForeground.toLowerCase(), |
| 168 | "--color-clerk-border": themeColorWithAlpha(colors.border, 0.06), | |
| 192 | "--color-clerk-border": colors.border, | |
| 169 | 193 | "--color-clerk-danger": colors.danger.toLowerCase(), |
| 170 | 194 | }; |
| 171 | 195 | }; |
| ... | ... | @@ -181,6 +205,7 @@ export const renderUniwindThemesCSS = () => { |
| 181 | 205 | const variants = [ |
| 182 | 206 | ...APPEARANCES.map((appearance) => |
| 183 | 207 | renderVariant(appearance, { |
| 208 | ...getMobileThemeVariables(DEFAULT_MOBILE_THEME_ID, appearance), | |
| 184 | 209 | ...adaptiveVariablesFor(appearance), |
| 185 | 210 | ...clerkVariablesFor(appearance), |
| 186 | 211 | }), |
| ... | ... | @@ -202,50 +227,27 @@ export const renderUniwindThemesCSS = () => { |
| 202 | 227 | ].join("\n"); |
| 203 | 228 | }; |
| 204 | 229 | |
| 205 | const readVariantBody = (css: string, appearance: MobileThemeAppearance): string => { | |
| 206 | const marker = `@variant ${appearance} {`; | |
| 207 | const markerIndex = css.indexOf(marker); | |
| 208 | if (markerIndex === -1) throw new Error(`Could not find ${marker} in global.css.`); | |
| 209 | ||
| 210 | const openingBraceIndex = css.indexOf("{", markerIndex); | |
| 211 | let depth = 0; | |
| 212 | for (let index = openingBraceIndex; index < css.length; index += 1) { | |
| 213 | if (css[index] === "{") depth += 1; | |
| 214 | if (css[index] !== "}") continue; | |
| 215 | depth -= 1; | |
| 216 | if (depth === 0) return css.slice(openingBraceIndex + 1, index); | |
| 217 | } | |
| 218 | throw new Error(`Could not find the end of ${marker} in global.css.`); | |
| 219 | }; | |
| 220 | ||
| 221 | export const readDefaultThemeVariables = (css: string) => | |
| 222 | Object.fromEntries( | |
| 223 | APPEARANCES.map((appearance) => { | |
| 224 | const body = readVariantBody(css, appearance); | |
| 225 | const variables = Object.fromEntries( | |
| 226 | MOBILE_THEME_VARIABLE_NAMES.map((name) => { | |
| 227 | const match = new RegExp(`^\\s*${name}:\\s*([^;]+);`, "mu").exec(body); | |
| 228 | if (!match?.[1]) { | |
| 229 | throw new Error(`Default ${appearance} theme is missing ${name}.`); | |
| 230 | } | |
| 231 | return [name, match[1].trim()]; | |
| 232 | }), | |
| 233 | ) as MobileThemeVariables; | |
| 234 | return [appearance, variables]; | |
| 235 | }), | |
| 236 | ) as Readonly<Record<MobileThemeAppearance, MobileThemeVariables>>; | |
| 237 | ||
| 238 | export const renderDefaultThemeVariablesJSON = (css: string) => | |
| 239 | `${JSON.stringify(readDefaultThemeVariables(css), null, 2)}\n`; | |
| 230 | export const renderDefaultThemeVariablesJSON = () => | |
| 231 | `${JSON.stringify( | |
| 232 | Object.fromEntries( | |
| 233 | APPEARANCES.map((appearance) => [ | |
| 234 | appearance, | |
| 235 | getMobileThemeVariables(DEFAULT_MOBILE_THEME_ID, appearance), | |
| 236 | ]), | |
| 237 | ), | |
| 238 | null, | |
| 239 | 2, | |
| 240 | )}\n`; | |
| 240 | 241 | |
| 241 | 242 | export const getGeneratedUniwindThemeOutputs = (): ReadonlyArray< |
| 242 | 243 | readonly [filename: string, contents: string] |
| 243 | 244 | > => [ |
| 244 | 245 | [GENERATED_CSS_PATH, renderUniwindThemesCSS()], |
| 245 | 246 | [GENERATED_NAMES_PATH, `${JSON.stringify(customThemeNames, null, 2)}\n`], |
| 247 | [GENERATED_DEFAULT_VARIABLES_PATH, renderDefaultThemeVariablesJSON()], | |
| 246 | 248 | [ |
| 247 | GENERATED_DEFAULT_VARIABLES_PATH, | |
| 248 | renderDefaultThemeVariablesJSON(NodeFS.readFileSync(GLOBAL_CSS_PATH, "utf8")), | |
| 249 | GENERATED_CLERK_THEME_PATH, | |
| 250 | `${JSON.stringify({ colors: clerkColorsFor("light"), darkColors: clerkColorsFor("dark"), design: { borderRadius: 18 } }, null, 2)}\n`, | |
| 249 | 251 | ], |
| 250 | 252 | ]; |
| 251 | 253 |
apps/mobile/scripts/generate-uniwind-themes.test.ts+14-10| ... | ... | @@ -5,9 +5,10 @@ import { describe, expect, it } from "vite-plus/test"; |
| 5 | 5 | import { |
| 6 | 6 | customThemeNames, |
| 7 | 7 | getGeneratedUniwindThemeOutputs, |
| 8 | readDefaultThemeVariables, | |
| 8 | renderDefaultThemeVariablesJSON, | |
| 9 | 9 | renderUniwindThemesCSS, |
| 10 | 10 | } from "./generate-uniwind-themes.mts"; |
| 11 | import { readDefaultMobileThemeVariables } from "../src/lib/mobileTheme.test-support"; | |
| 11 | 12 | |
| 12 | 13 | describe("generate mobile Uniwind themes", () => { |
| 13 | 14 | it("keeps the committed outputs current", () => { |
| ... | ... | @@ -44,12 +45,15 @@ describe("generate mobile Uniwind themes", () => { |
| 44 | 45 | } |
| 45 | 46 | }); |
| 46 | 47 | |
| 47 | it("generates the default runtime bridge from the authored CSS", () => { | |
| 48 | const css = NodeFS.readFileSync(NodePath.resolve(import.meta.dirname, "../global.css"), "utf8"); | |
| 49 | const variables = readDefaultThemeVariables(css); | |
| 48 | it("keeps the default runtime bridge and generated CSS on the same palette", () => { | |
| 49 | const variables = JSON.parse(renderDefaultThemeVariablesJSON()); | |
| 50 | 50 | |
| 51 | expect(variables.light["--color-screen"]).toBe("#f2f2f7"); | |
| 51 | expect(variables.light).toEqual(readDefaultMobileThemeVariables("light")); | |
| 52 | expect(variables.dark).toEqual(readDefaultMobileThemeVariables("dark")); | |
| 53 | expect(variables.light["--color-screen"]).toBe("#fcfcfc"); | |
| 54 | expect(variables.light["--color-drawer"]).toBe("#fafafa"); | |
| 52 | 55 | expect(variables.dark["--color-screen"]).toBe("#0a0a0a"); |
| 56 | expect(variables.dark["--color-drawer"]).toBe("#000000"); | |
| 53 | 57 | expect(Object.keys(variables.light)).toEqual(Object.keys(variables.dark)); |
| 54 | 58 | }); |
| 55 | 59 | |
| ... | ... | @@ -77,11 +81,11 @@ describe("generate mobile Uniwind themes", () => { |
| 77 | 81 | ), |
| 78 | 82 | name, |
| 79 | 83 | ).toEqual({ |
| 80 | "--color-clerk-page": isDark ? "#0e0e0e" : "#f2f2f7", | |
| 81 | "--color-clerk-foreground": isDark ? "#f5f5f5" : "#262626", | |
| 82 | "--color-clerk-foreground-muted": isDark ? "#a3a3a3" : "#737373", | |
| 83 | "--color-clerk-border": isDark ? "rgba(42, 42, 42, 0.06)" : "rgba(229, 229, 234, 0.06)", | |
| 84 | "--color-clerk-danger": isDark ? "#fca5a5" : "#dc2626", | |
| 84 | "--color-clerk-page": isDark ? "#0a0a0a" : "#fcfcfc", | |
| 85 | "--color-clerk-foreground": isDark ? "#f5f5f5" : "#27272a", | |
| 86 | "--color-clerk-foreground-muted": isDark ? "#818181" : "#71717b", | |
| 87 | "--color-clerk-border": isDark ? "#191919" : "#e4e4e7", | |
| 88 | "--color-clerk-danger": isDark ? "#ff6467" : "#c10007", | |
| 85 | 89 | }); |
| 86 | 90 | } |
| 87 | 91 | }); |
apps/mobile/src/components/AndroidScreenHeader.tsx+10-2| ... | ... | @@ -25,7 +25,13 @@ export function AndroidHeaderIconButton(props: { |
| 25 | 25 | readonly disabled?: boolean; |
| 26 | 26 | readonly selected?: boolean; |
| 27 | 27 | }) { |
| 28 | return <MaterialIconButton {...props} variant={props.selected ? "tonal" : "standard"} />; | |
| 28 | return ( | |
| 29 | <MaterialIconButton | |
| 30 | {...props} | |
| 31 | tintColorClassName="accent-header-foreground" | |
| 32 | variant={props.selected ? "tonal" : "standard"} | |
| 33 | /> | |
| 34 | ); | |
| 29 | 35 | } |
| 30 | 36 | |
| 31 | 37 | export function AndroidScreenHeader(props: { |
| ... | ... | @@ -65,6 +71,7 @@ export function AndroidScreenHeader(props: { |
| 65 | 71 | <MaterialIconButton |
| 66 | 72 | accessibilityLabel="Navigate up" |
| 67 | 73 | icon="arrow.left" |
| 74 | tintColorClassName="accent-header-foreground" | |
| 68 | 75 | onPress={props.onBack} |
| 69 | 76 | /> |
| 70 | 77 | ) : null} |
| ... | ... | @@ -72,7 +79,7 @@ export function AndroidScreenHeader(props: { |
| 72 | 79 | {props.leading} |
| 73 | 80 | |
| 74 | 81 | <View className={cn("min-w-0 flex-1", !props.onBack && "pl-1")}> |
| 75 | <Text numberOfLines={1} style={titleTypography} className="text-foreground"> | |
| 82 | <Text numberOfLines={1} style={titleTypography} className="text-header-foreground"> | |
| 76 | 83 | {props.title} |
| 77 | 84 | </Text> |
| 78 | 85 | {props.subtitle ? ( |
| ... | ... | @@ -114,6 +121,7 @@ export function AndroidScreenHeader(props: { |
| 114 | 121 | <MaterialIconButton |
| 115 | 122 | accessibilityLabel="More actions" |
| 116 | 123 | icon="ellipsis" |
| 124 | tintColorClassName="accent-header-foreground" | |
| 117 | 125 | onPress={open} |
| 118 | 126 | /> |
| 119 | 127 | )} |
apps/mobile/src/components/AppText.tsx+4-8| ... | ... | @@ -18,7 +18,7 @@ export function AppText({ className, ...props }: AppTextProps) { |
| 18 | 18 | return ( |
| 19 | 19 | <RNText |
| 20 | 20 | className={cn("font-sans text-foreground", className)} |
| 21 | selectionColorClassName={Platform.OS === "android" ? "accent-primary/32" : undefined} | |
| 21 | selectionColorClassName={Platform.OS === "android" ? "accent-focus/32" : undefined} | |
| 22 | 22 | {...props} |
| 23 | 23 | /> |
| 24 | 24 | ); |
| ... | ... | @@ -42,13 +42,9 @@ export function AppTextInput({ className, ref, ...props }: AppTextInputProps) { |
| 42 | 42 | className, |
| 43 | 43 | )} |
| 44 | 44 | placeholderTextColorClassName="accent-placeholder" |
| 45 | selectionColorClassName={ | |
| 46 | Platform.OS === "android" ? "accent-primary/32" : "accent-foreground-secondary" | |
| 47 | } | |
| 48 | cursorColorClassName={ | |
| 49 | Platform.OS === "android" ? "accent-primary" : "accent-foreground-secondary" | |
| 50 | } | |
| 51 | selectionHandleColorClassName={Platform.OS === "android" ? "accent-primary" : undefined} | |
| 45 | selectionColorClassName={"accent-focus/32"} | |
| 46 | cursorColorClassName={"accent-focus"} | |
| 47 | selectionHandleColorClassName={Platform.OS === "android" ? "accent-focus" : undefined} | |
| 52 | 48 | {...props} |
| 53 | 49 | /> |
| 54 | 50 | ); |
apps/mobile/src/components/ComposerContextSheet.tsx+3-5| ... | ... | @@ -187,11 +187,9 @@ export function ComposerContextSheet(props: { |
| 187 | 187 | onRequestClose={props.onClose} |
| 188 | 188 | > |
| 189 | 189 | <View |
| 190 | style={{ | |
| 191 | flex: 1, | |
| 192 | justifyContent: "flex-end", | |
| 193 | backgroundColor: Platform.OS === "android" ? "#00000066" : undefined, | |
| 194 | }} | |
| 190 | className={ | |
| 191 | Platform.OS === "android" ? "flex-1 justify-end bg-backdrop" : "flex-1 justify-end" | |
| 192 | } | |
| 195 | 193 | > |
| 196 | 194 | {Platform.OS === "android" ? ( |
| 197 | 195 | <Pressable |
apps/mobile/src/components/ControlPill.tsx+3-1| ... | ... | @@ -72,7 +72,9 @@ export function ControlPill(props: { |
| 72 | 72 | ? props.disabled |
| 73 | 73 | ? "text-foreground-muted" |
| 74 | 74 | : "text-primary-foreground" |
| 75 | : "", | |
| 75 | : variant === "danger" | |
| 76 | ? "text-danger-foreground" | |
| 77 | : "text-foreground", | |
| 76 | 78 | ); |
| 77 | 79 | |
| 78 | 80 | if ( |
apps/mobile/src/components/FilePreview.tsx+1-1| ... | ... | @@ -46,7 +46,7 @@ function DocumentPreview(props: { |
| 46 | 46 | }, [uri, name, props.source.mimeType, props.source.kind]); |
| 47 | 47 | return ( |
| 48 | 48 | <Modal transparent animationType="fade" onRequestClose={props.onRequestClose}> |
| 49 | <View className="flex-1 items-center justify-center bg-black/40 p-6"> | |
| 49 | <View className="flex-1 items-center justify-center bg-backdrop p-6"> | |
| 50 | 50 | <View className="w-full max-w-sm gap-4 rounded-2xl bg-sheet-solid p-6"> |
| 51 | 51 | <Text className="font-t3-semibold text-foreground">Opening document…</Text> |
| 52 | 52 | <Text className="text-foreground-muted" numberOfLines={2}> |
apps/mobile/src/components/GlassBackdrop.tsx+8-10| ... | ... | @@ -6,12 +6,13 @@ import { themeColorWithAlpha } from "../lib/mobileTheme"; |
| 6 | 6 | |
| 7 | 7 | /** Frosted backdrop for containers that clip their children to their shape. */ |
| 8 | 8 | export function GlassBackdrop(props: { readonly fallbackColor?: ColorValue }) { |
| 9 | const { themeAppearance } = useAppearancePreferences(); | |
| 9 | const { themeAppearance, themeVariables } = useAppearancePreferences(); | |
| 10 | 10 | const supportsBlur = Platform.OS === "ios"; |
| 11 | const colorStyle = | |
| 12 | props.fallbackColor === undefined | |
| 13 | ? undefined | |
| 14 | : { backgroundColor: themeColorWithAlpha(String(props.fallbackColor), 1) }; | |
| 11 | const color = props.fallbackColor ?? themeVariables["--color-glass-fallback"]; | |
| 12 | const colorStyle = { | |
| 13 | backgroundColor: | |
| 14 | supportsBlur || typeof color !== "string" ? color : themeColorWithAlpha(color, 1), | |
| 15 | }; | |
| 15 | 16 | |
| 16 | 17 | return ( |
| 17 | 18 | <> |
| ... | ... | @@ -25,11 +26,8 @@ export function GlassBackdrop(props: { readonly fallbackColor?: ColorValue }) { |
| 25 | 26 | ) : null} |
| 26 | 27 | <View |
| 27 | 28 | pointerEvents="none" |
| 28 | className="absolute inset-0 bg-card" | |
| 29 | style={[ | |
| 30 | colorStyle, | |
| 31 | { opacity: supportsBlur ? (themeAppearance === "dark" ? 0.25 : 0.55) : 1 }, | |
| 32 | ]} | |
| 29 | className="absolute inset-0 bg-glass-fallback" | |
| 30 | style={colorStyle} | |
| 33 | 31 | /> |
| 34 | 32 | </> |
| 35 | 33 | ); |
apps/mobile/src/components/GlassSurface.tsx+4-9| ... | ... | @@ -1,16 +1,10 @@ |
| 1 | 1 | import { GlassView, isGlassEffectAPIAvailable } from "expo-glass-effect"; |
| 2 | 2 | import type { ReactNode, Ref } from "react"; |
| 3 | import { | |
| 4 | Platform, | |
| 5 | useColorScheme, | |
| 6 | View, | |
| 7 | type ColorValue, | |
| 8 | type ViewProps, | |
| 9 | type ViewStyle, | |
| 10 | } from "react-native"; | |
| 3 | import { Platform, View, type ColorValue, type ViewProps, type ViewStyle } from "react-native"; | |
| 11 | 4 | import { withUniwind } from "uniwind"; |
| 12 | 5 | |
| 13 | 6 | import { cn } from "../lib/cn"; |
| 7 | import { useAppearancePreferences } from "../features/settings/appearance/AppearancePreferencesProvider"; | |
| 14 | 8 | import { GlassBackdrop } from "./GlassBackdrop"; |
| 15 | 9 | |
| 16 | 10 | // Explicit mappings keep the native glassEffectStyle enum out of style-array conversion. |
| ... | ... | @@ -45,7 +39,8 @@ export function GlassSurface({ |
| 45 | 39 | style, |
| 46 | 40 | ...props |
| 47 | 41 | }: GlassSurfaceProps) { |
| 48 | const isDarkMode = useColorScheme() === "dark"; | |
| 42 | const { themeAppearance } = useAppearancePreferences(); | |
| 43 | const isDarkMode = themeAppearance === "dark"; | |
| 49 | 44 | const supportsGlass = Platform.OS === "ios" && isGlassEffectAPIAvailable(); |
| 50 | 45 | const hasShadow = chrome !== "none" && Platform.OS !== "android"; |
| 51 | 46 | const surfaceStyle: ViewStyle = { |
apps/mobile/src/components/MaterialButton.android.tsx+1-1| ... | ... | @@ -35,7 +35,7 @@ export function MaterialButton(props: MaterialButtonProps) { |
| 35 | 35 | : tone === "danger" |
| 36 | 36 | ? colors["--color-danger-foreground"] |
| 37 | 37 | : tone === "text" |
| 38 | ? colors["--color-primary"] | |
| 38 | ? colors["--color-primary-text"] | |
| 39 | 39 | : colors["--color-secondary-foreground"]; |
| 40 | 40 | return ( |
| 41 | 41 | <View |
apps/mobile/src/components/MaterialButton.tsx+25-3| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | import { Pressable } from "react-native"; |
| 2 | 2 | import { AppText } from "./AppText"; |
| 3 | import { cn } from "../lib/cn"; | |
| 3 | 4 | |
| 4 | 5 | export interface MaterialButtonProps { |
| 5 | 6 | readonly label: string; |
| ... | ... | @@ -10,15 +11,36 @@ export interface MaterialButtonProps { |
| 10 | 11 | readonly fullWidth?: boolean; |
| 11 | 12 | } |
| 12 | 13 | |
| 14 | const TONE_CLASS_NAMES = { | |
| 15 | primary: ["bg-primary", "text-primary-foreground"], | |
| 16 | secondary: ["bg-secondary", "text-secondary-foreground"], | |
| 17 | danger: ["bg-danger", "text-danger-foreground"], | |
| 18 | text: ["bg-transparent", "text-primary-text"], | |
| 19 | } as const; | |
| 20 | ||
| 13 | 21 | export function MaterialButton(props: MaterialButtonProps) { |
| 22 | const [containerClassName, labelClassName] = TONE_CLASS_NAMES[props.tone ?? "secondary"]; | |
| 23 | const disabled = Boolean(props.disabled || props.loading); | |
| 14 | 24 | return ( |
| 15 | 25 | <Pressable |
| 16 | 26 | accessibilityRole="button" |
| 17 | disabled={props.disabled} | |
| 27 | accessibilityState={{ disabled }} | |
| 28 | disabled={disabled} | |
| 18 | 29 | onPress={props.onPress} |
| 19 | className="min-h-12 justify-center rounded-full bg-secondary px-6" | |
| 30 | className={cn( | |
| 31 | "min-h-12 justify-center rounded-full px-6 active:opacity-70", | |
| 32 | props.fullWidth ? "w-full" : "self-start", | |
| 33 | disabled ? "bg-subtle-strong" : containerClassName, | |
| 34 | )} | |
| 20 | 35 | > |
| 21 | <AppText>{props.label}</AppText> | |
| 36 | <AppText | |
| 37 | className={cn( | |
| 38 | "text-center font-t3-medium", | |
| 39 | disabled ? "text-foreground-muted" : labelClassName, | |
| 40 | )} | |
| 41 | > | |
| 42 | {props.label} | |
| 43 | </AppText> | |
| 22 | 44 | </Pressable> |
| 23 | 45 | ); |
| 24 | 46 | } |
apps/mobile/src/components/MaterialConfirmDialog.android.tsx+9-4| ... | ... | @@ -58,9 +58,9 @@ export function MaterialConfirmDialog(props: MaterialConfirmDialogProps) { |
| 58 | 58 | colors={{ |
| 59 | 59 | focusedTextColor: colors["--color-foreground"], |
| 60 | 60 | unfocusedTextColor: colors["--color-foreground"], |
| 61 | focusedIndicatorColor: colors["--color-primary"], | |
| 61 | focusedIndicatorColor: colors["--color-focus"], | |
| 62 | 62 | unfocusedIndicatorColor: colors["--color-border"], |
| 63 | cursorColor: colors["--color-primary"], | |
| 63 | cursorColor: colors["--color-focus"], | |
| 64 | 64 | }} |
| 65 | 65 | > |
| 66 | 66 | <OutlinedTextField.Label> |
| ... | ... | @@ -74,7 +74,10 @@ export function MaterialConfirmDialog(props: MaterialConfirmDialogProps) { |
| 74 | 74 | </AlertDialog.Text> |
| 75 | 75 | ) : null} |
| 76 | 76 | <AlertDialog.DismissButton> |
| 77 | <TextButton onClick={props.onCancel} colors={{ contentColor: colors["--color-primary"] }}> | |
| 77 | <TextButton | |
| 78 | onClick={props.onCancel} | |
| 79 | colors={{ contentColor: colors["--color-primary-text"] }} | |
| 80 | > | |
| 78 | 81 | <Text style={bodyTypography}>{props.request.cancelText ?? "Cancel"}</Text> |
| 79 | 82 | </TextButton> |
| 80 | 83 | </AlertDialog.DismissButton> |
| ... | ... | @@ -84,7 +87,9 @@ export function MaterialConfirmDialog(props: MaterialConfirmDialogProps) { |
| 84 | 87 | enabled={!props.confirmDisabled} |
| 85 | 88 | colors={{ |
| 86 | 89 | contentColor: |
| 87 | colors[props.request.destructive ? "--color-danger" : "--color-primary"], | |
| 90 | colors[ | |
| 91 | props.request.destructive ? "--color-danger-foreground" : "--color-primary-text" | |
| 92 | ], | |
| 88 | 93 | }} |
| 89 | 94 | > |
| 90 | 95 | <Text style={bodyTypography}>{props.request.confirmText}</Text> |
apps/mobile/src/components/MaterialFloatingActionButton.android.tsx+3-5| ... | ... | @@ -25,9 +25,9 @@ export function MaterialFloatingActionButton(props: { |
| 25 | 25 | const { themeAppearance, themeVariables: colors } = useAppearancePreferences(); |
| 26 | 26 | const typography = useScaledTextRole("footnote"); |
| 27 | 27 | const primary = props.tone === "primary"; |
| 28 | const containerColor = colors[primary ? "--color-primary" : "--color-thread-selected"]; | |
| 28 | const containerColor = colors[primary ? "--color-primary" : "--color-secondary"]; | |
| 29 | 29 | const contentColor = |
| 30 | colors[primary ? "--color-primary-foreground" : "--color-thread-selected-foreground"]; | |
| 30 | colors[primary ? "--color-primary-foreground" : "--color-secondary-foreground"]; | |
| 31 | 31 | const Component = |
| 32 | 32 | props.variant === "extended" |
| 33 | 33 | ? ExtendedFloatingActionButton |
| ... | ... | @@ -76,9 +76,7 @@ export function MaterialFloatingActionButton(props: { |
| 76 | 76 | <SymbolView |
| 77 | 77 | name={props.icon} |
| 78 | 78 | size={iconSize} |
| 79 | tintColorClassName={ | |
| 80 | primary ? "accent-primary-foreground" : "accent-thread-selected-foreground" | |
| 81 | } | |
| 79 | tintColorClassName={primary ? "accent-primary-foreground" : "accent-secondary-foreground"} | |
| 82 | 80 | /> |
| 83 | 81 | </View> |
| 84 | 82 | </View> |
apps/mobile/src/components/MaterialFloatingActionButton.tsx+3-7| ... | ... | @@ -15,7 +15,7 @@ export function MaterialFloatingActionButton( |
| 15 | 15 | onPress={props.onPress} |
| 16 | 16 | className={cn( |
| 17 | 17 | "min-h-14 min-w-14 flex-row items-center justify-center gap-2 rounded-2xl px-4", |
| 18 | props.tone === "primary" ? "bg-primary" : "bg-thread-selected", | |
| 18 | props.tone === "primary" ? "bg-primary" : "bg-secondary", | |
| 19 | 19 | props.className, |
| 20 | 20 | )} |
| 21 | 21 | style={props.style} |
| ... | ... | @@ -24,18 +24,14 @@ export function MaterialFloatingActionButton( |
| 24 | 24 | name={props.icon} |
| 25 | 25 | size={24} |
| 26 | 26 | tintColorClassName={ |
| 27 | props.tone === "primary" | |
| 28 | ? "accent-primary-foreground" | |
| 29 | : "accent-thread-selected-foreground" | |
| 27 | props.tone === "primary" ? "accent-primary-foreground" : "accent-secondary-foreground" | |
| 30 | 28 | } |
| 31 | 29 | /> |
| 32 | 30 | {props.variant === "extended" && props.expanded !== false ? ( |
| 33 | 31 | <AppText |
| 34 | 32 | className={cn( |
| 35 | 33 | "text-sm", |
| 36 | props.tone === "primary" | |
| 37 | ? "text-primary-foreground" | |
| 38 | : "text-thread-selected-foreground", | |
| 34 | props.tone === "primary" ? "text-primary-foreground" : "text-secondary-foreground", | |
| 39 | 35 | )} |
| 40 | 36 | > |
| 41 | 37 | {props.label} |
apps/mobile/src/components/MaterialIconButton.android.tsx+2-1| ... | ... | @@ -17,6 +17,7 @@ export function MaterialIconButton(props: { |
| 17 | 17 | readonly disabled?: boolean; |
| 18 | 18 | readonly selected?: boolean; |
| 19 | 19 | readonly variant?: "standard" | "primary" | "tonal" | "danger"; |
| 20 | readonly tintColorClassName?: string; | |
| 20 | 21 | }) { |
| 21 | 22 | const { themeAppearance, themeVariables: colors } = useAppearancePreferences(); |
| 22 | 23 | const variant = props.variant ?? "standard"; |
| ... | ... | @@ -40,7 +41,7 @@ export function MaterialIconButton(props: { |
| 40 | 41 | ? "accent-danger-foreground" |
| 41 | 42 | : variant === "tonal" |
| 42 | 43 | ? "accent-secondary-foreground" |
| 43 | : "accent-foreground"; | |
| 44 | : (props.tintColorClassName ?? "accent-foreground"); | |
| 44 | 45 | return ( |
| 45 | 46 | <View |
| 46 | 47 | accessible |
apps/mobile/src/components/MaterialIconButton.tsx+31-3| ... | ... | @@ -1,7 +1,15 @@ |
| 1 | import { cn } from "../lib/cn"; | |
| 1 | 2 | import { Pressable } from "react-native"; |
| 2 | 3 | |
| 3 | 4 | import { SymbolView, type AppSymbolName } from "./AppSymbol"; |
| 4 | 5 | |
| 6 | const VARIANT_CLASS_NAMES = { | |
| 7 | standard: ["", "accent-foreground"], | |
| 8 | primary: ["rounded-full bg-primary", "accent-primary-foreground"], | |
| 9 | danger: ["rounded-full bg-danger", "accent-danger-foreground"], | |
| 10 | tonal: ["rounded-full bg-secondary", "accent-secondary-foreground"], | |
| 11 | } as const; | |
| 12 | ||
| 5 | 13 | export function MaterialIconButton(props: { |
| 6 | 14 | readonly accessibilityLabel: string; |
| 7 | 15 | readonly icon: AppSymbolName; |
| ... | ... | @@ -9,15 +17,35 @@ export function MaterialIconButton(props: { |
| 9 | 17 | readonly disabled?: boolean; |
| 10 | 18 | readonly selected?: boolean; |
| 11 | 19 | readonly variant?: "standard" | "primary" | "tonal" | "danger"; |
| 20 | readonly tintColorClassName?: string; | |
| 12 | 21 | }) { |
| 22 | const variant = props.variant ?? "standard"; | |
| 23 | const [containerClassName, iconTintClassName] = VARIANT_CLASS_NAMES[variant]; | |
| 13 | 24 | return ( |
| 14 | 25 | <Pressable |
| 15 | {...props} | |
| 26 | onPress={props.onPress} | |
| 27 | disabled={props.disabled} | |
| 28 | accessibilityLabel={props.accessibilityLabel} | |
| 16 | 29 | accessibilityRole="button" |
| 17 | 30 | accessibilityState={{ disabled: Boolean(props.disabled), selected: props.selected }} |
| 18 | className="size-12 items-center justify-center" | |
| 31 | className={cn( | |
| 32 | "size-12 items-center justify-center", | |
| 33 | props.disabled && variant !== "standard" | |
| 34 | ? "rounded-full bg-subtle-strong" | |
| 35 | : containerClassName, | |
| 36 | )} | |
| 19 | 37 | > |
| 20 | <SymbolView name={props.icon} size={24} tintColorClassName="accent-foreground" /> | |
| 38 | <SymbolView | |
| 39 | name={props.icon} | |
| 40 | size={24} | |
| 41 | tintColorClassName={ | |
| 42 | props.disabled | |
| 43 | ? "accent-icon-subtle" | |
| 44 | : variant === "standard" | |
| 45 | ? (props.tintColorClassName ?? iconTintClassName) | |
| 46 | : iconTintClassName | |
| 47 | } | |
| 48 | /> | |
| 21 | 49 | </Pressable> |
| 22 | 50 | ); |
| 23 | 51 | } |
apps/mobile/src/components/MaterialSearchField.tsx+3-3| ... | ... | @@ -30,9 +30,9 @@ export function MaterialSearchField({ |
| 30 | 30 | returnKeyType="search" |
| 31 | 31 | placeholder={placeholder} |
| 32 | 32 | placeholderTextColorClassName="accent-placeholder" |
| 33 | selectionColorClassName="accent-primary/32" | |
| 34 | cursorColorClassName="accent-primary" | |
| 35 | selectionHandleColorClassName="accent-primary" | |
| 33 | selectionColorClassName="accent-focus/32" | |
| 34 | cursorColorClassName="accent-focus" | |
| 35 | selectionHandleColorClassName="accent-focus" | |
| 36 | 36 | className="min-w-0 flex-1 py-2 font-sans text-base text-foreground" |
| 37 | 37 | value={value} |
| 38 | 38 | onChangeText={onChangeText} |
apps/mobile/src/components/RowPressable.tsx+5-3| ... | ... | @@ -9,22 +9,24 @@ import { useHoverGesture } from "../lib/useHoverGesture"; |
| 9 | 9 | export function RowPressable({ |
| 10 | 10 | children, |
| 11 | 11 | className, |
| 12 | interactionClassName = "bg-primary", | |
| 12 | interactionClassName = "bg-row-hover", | |
| 13 | interactionOpacity = 1, | |
| 13 | 14 | ...props |
| 14 | 15 | }: Omit<ComponentProps<typeof Pressable>, "children"> & { |
| 15 | 16 | readonly children: ReactNode; |
| 16 | 17 | readonly interactionClassName?: string; |
| 18 | readonly interactionOpacity?: number; | |
| 17 | 19 | }) { |
| 18 | 20 | const { hovered, hoverGesture } = useHoverGesture(props.disabled ?? false); |
| 19 | 21 | return ( |
| 20 | 22 | <GestureDetector gesture={hoverGesture}> |
| 21 | 23 | <Pressable {...props} className={cn("relative overflow-hidden", className)}> |
| 22 | {({ pressed }) => ( | |
| 24 | {() => ( | |
| 23 | 25 | <> |
| 24 | 26 | <View |
| 25 | 27 | pointerEvents="none" |
| 26 | 28 | className={cn("absolute inset-0", interactionClassName)} |
| 27 | style={{ opacity: props.disabled || !hovered ? 0 : pressed ? 0.2 : 0.1 }} | |
| 29 | style={{ opacity: props.disabled || !hovered ? 0 : interactionOpacity }} | |
| 28 | 30 | /> |
| 29 | 31 | {children} |
| 30 | 32 | </> |
apps/mobile/src/components/ScreenHeader.android.tsx+4-4| ... | ... | @@ -53,7 +53,7 @@ export function ScreenHeader(props: ScreenHeaderProps) { |
| 53 | 53 | <SymbolView |
| 54 | 54 | name={menu.icon} |
| 55 | 55 | size={16} |
| 56 | tintColorClassName="accent-icon" | |
| 56 | tintColorClassName="accent-header-foreground" | |
| 57 | 57 | type="monochrome" |
| 58 | 58 | /> |
| 59 | 59 | </Pressable> |
| ... | ... | @@ -95,7 +95,7 @@ export function ScreenHeader(props: ScreenHeaderProps) { |
| 95 | 95 | <SymbolView |
| 96 | 96 | name="chevron.left" |
| 97 | 97 | size={24} |
| 98 | tintColorClassName="accent-foreground" | |
| 98 | tintColorClassName="accent-header-foreground" | |
| 99 | 99 | type="monochrome" |
| 100 | 100 | /> |
| 101 | 101 | </Pressable> |
| ... | ... | @@ -104,7 +104,7 @@ export function ScreenHeader(props: ScreenHeaderProps) { |
| 104 | 104 | <SymbolView |
| 105 | 105 | name="magnifyingglass" |
| 106 | 106 | size={17} |
| 107 | tintColorClassName="accent-icon" | |
| 107 | tintColorClassName="accent-header-foreground" | |
| 108 | 108 | type="monochrome" |
| 109 | 109 | /> |
| 110 | 110 | <TextInput |
| ... | ... | @@ -114,7 +114,7 @@ export function ScreenHeader(props: ScreenHeaderProps) { |
| 114 | 114 | value={search.value} |
| 115 | 115 | placeholder={search.placeholder} |
| 116 | 116 | placeholderTextColorClassName="accent-placeholder" |
| 117 | className="flex-1 py-2 text-base font-sans text-foreground" | |
| 117 | className="flex-1 py-2 text-base font-sans text-header-foreground" | |
| 118 | 118 | /> |
| 119 | 119 | </View> |
| 120 | 120 | {menuView} |
apps/mobile/src/components/SegmentedControl.tsx+2-2| ... | ... | @@ -40,7 +40,7 @@ export function SegmentedControl<Value extends number | string>( |
| 40 | 40 | layout={LinearTransition.duration(200) |
| 41 | 41 | .easing(Easing.out(Easing.cubic)) |
| 42 | 42 | .reduceMotion(ReduceMotion.System)} |
| 43 | className="absolute inset-y-0 rounded-full bg-subtle-strong" | |
| 43 | className="absolute inset-y-0 rounded-full bg-secondary" | |
| 44 | 44 | style={{ |
| 45 | 45 | width: `${100 / props.options.length}%`, |
| 46 | 46 | start: `${ |
| ... | ... | @@ -70,7 +70,7 @@ export function SegmentedControl<Value extends number | string>( |
| 70 | 70 | <Text |
| 71 | 71 | className={cn( |
| 72 | 72 | compact ? "text-xs" : "text-sm", |
| 73 | active ? "font-t3-medium text-foreground" : "text-foreground-muted", | |
| 73 | active ? "font-t3-medium text-secondary-foreground" : "text-foreground-muted", | |
| 74 | 74 | )} |
| 75 | 75 | > |
| 76 | 76 | {option.label} |
apps/mobile/src/features/connection/ConnectionStatusDot.tsx+17-10| ... | ... | @@ -10,10 +10,16 @@ import Animated, { |
| 10 | 10 | } from "react-native-reanimated"; |
| 11 | 11 | |
| 12 | 12 | import type { RemoteClientConnectionState } from "../../lib/connection"; |
| 13 | import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider"; | |
| 14 | import { themeColorWithAlpha, type MobileThemeVariables } from "../../lib/mobileTheme"; | |
| 13 | 15 | |
| 14 | 16 | export type ConnectionStatusDotState = RemoteClientConnectionState; |
| 15 | 17 | |
| 16 | function statusDotTone(state: ConnectionStatusDotState): { | |
| 18 | function statusDotTone( | |
| 19 | state: ConnectionStatusDotState, | |
| 20 | theme: MobileThemeVariables, | |
| 21 | dark: boolean, | |
| 22 | ): { | |
| 17 | 23 | readonly dotColor: string; |
| 18 | 24 | readonly haloColor: string; |
| 19 | 25 | } { |
| ... | ... | @@ -23,25 +29,25 @@ function statusDotTone(state: ConnectionStatusDotState): { |
| 23 | 29 | case "available": |
| 24 | 30 | case "unsupported": |
| 25 | 31 | return { |
| 26 | dotColor: "#9ca3af", | |
| 27 | haloColor: "rgba(156,163,175,0.42)", | |
| 32 | dotColor: theme["--color-icon-muted"], | |
| 33 | haloColor: themeColorWithAlpha(theme["--color-icon-muted"], 0.42), | |
| 28 | 34 | }; |
| 29 | 35 | case "connected": |
| 30 | 36 | return { |
| 31 | dotColor: "#34d399", | |
| 32 | haloColor: "rgba(52,211,153,0.48)", | |
| 37 | dotColor: dark ? "#34d399" : "#059669", | |
| 38 | haloColor: themeColorWithAlpha(dark ? "#34d399" : "#059669", 0.48), | |
| 33 | 39 | }; |
| 34 | 40 | case "connecting": |
| 35 | 41 | case "reconnecting": |
| 36 | 42 | return { |
| 37 | dotColor: "#f59e0b", | |
| 38 | haloColor: "rgba(245,158,11,0.5)", | |
| 43 | dotColor: theme["--color-warning-foreground"], | |
| 44 | haloColor: themeColorWithAlpha(theme["--color-warning-foreground"], 0.5), | |
| 39 | 45 | }; |
| 40 | 46 | case "offline": |
| 41 | 47 | case "error": |
| 42 | 48 | return { |
| 43 | dotColor: "#ef4444", | |
| 44 | haloColor: "rgba(239,68,68,0.48)", | |
| 49 | dotColor: theme["--color-danger-foreground"], | |
| 50 | haloColor: themeColorWithAlpha(theme["--color-danger-foreground"], 0.48), | |
| 45 | 51 | }; |
| 46 | 52 | } |
| 47 | 53 | } |
| ... | ... | @@ -78,7 +84,8 @@ export function ConnectionStatusDot(props: { |
| 78 | 84 | readonly size?: number; |
| 79 | 85 | }) { |
| 80 | 86 | const pulseProgress = usePulseAnimation(props.pulse); |
| 81 | const tone = statusDotTone(props.state); | |
| 87 | const { themeAppearance, themeVariables } = useAppearancePreferences(); | |
| 88 | const tone = statusDotTone(props.state, themeVariables, themeAppearance === "dark"); | |
| 82 | 89 | const dotSize = props.size ?? 10; |
| 83 | 90 | const haloSize = dotSize + 4; |
| 84 | 91 | const containerSize = haloSize + 4; |
apps/mobile/src/features/connection/connectionTone.ts+2-2| ... | ... | @@ -18,8 +18,8 @@ export function connectionTone(state: RemoteClientConnectionState): StatusTone { |
| 18 | 18 | case "connecting": |
| 19 | 19 | return { |
| 20 | 20 | label: "Connecting", |
| 21 | pillClassName: "bg-primary/10", | |
| 22 | textClassName: "text-foreground-secondary", | |
| 21 | pillClassName: "bg-update", | |
| 22 | textClassName: "text-update-foreground", | |
| 23 | 23 | }; |
| 24 | 24 | case "unsupported": |
| 25 | 25 | return { |
apps/mobile/src/features/devices/DevicePreviewRouteScreen.tsx+7-5| ... | ... | @@ -231,10 +231,10 @@ function DevicePreviewScreen({ |
| 231 | 231 | </AppText> |
| 232 | 232 | <Pressable |
| 233 | 233 | accessibilityRole="button" |
| 234 | className="rounded-full bg-subtle px-6 py-3" | |
| 234 | className="rounded-full border border-secondary-border bg-secondary px-6 py-3" | |
| 235 | 235 | onPress={state.refresh} |
| 236 | 236 | > |
| 237 | <AppText>Retry</AppText> | |
| 237 | <AppText className="text-secondary-foreground">Retry</AppText> | |
| 238 | 238 | </Pressable> |
| 239 | 239 | </> |
| 240 | 240 | ) : focused && foreground ? ( |
| ... | ... | @@ -278,7 +278,9 @@ function OpenDevicePreview({ |
| 278 | 278 | background: themeVariables["--color-sheet-solid"], |
| 279 | 279 | foreground: themeVariables["--color-foreground"], |
| 280 | 280 | muted: themeVariables["--color-foreground-muted"], |
| 281 | buttonBackground: themeVariables["--color-subtle"], | |
| 281 | buttonBackground: themeVariables["--color-secondary"], | |
| 282 | buttonForeground: themeVariables["--color-secondary-foreground"], | |
| 283 | buttonBorder: themeVariables["--color-secondary-border"], | |
| 282 | 284 | }} |
| 283 | 285 | onUnauthorized={onUnauthorized} |
| 284 | 286 | onInputConnected={onInputConnected} |
| ... | ... | @@ -292,10 +294,10 @@ function OpenDevicePreview({ |
| 292 | 294 | </AppText> |
| 293 | 295 | <Pressable |
| 294 | 296 | accessibilityRole="button" |
| 295 | className="rounded-full bg-subtle px-6 py-3" | |
| 297 | className="rounded-full border border-secondary-border bg-secondary px-6 py-3" | |
| 296 | 298 | onPress={refresh} |
| 297 | 299 | > |
| 298 | <AppText>Retry</AppText> | |
| 300 | <AppText className="text-secondary-foreground">Retry</AppText> | |
| 299 | 301 | </Pressable> |
| 300 | 302 | </> |
| 301 | 303 | ) : ( |
apps/mobile/src/features/devices/device-stream-document.ts+2| ... | ... | @@ -10,6 +10,8 @@ export interface DeviceStreamConfiguration { |
| 10 | 10 | readonly foreground: string; |
| 11 | 11 | readonly muted: string; |
| 12 | 12 | readonly buttonBackground: string; |
| 13 | readonly buttonForeground: string; | |
| 14 | readonly buttonBorder: string; | |
| 13 | 15 | }; |
| 14 | 16 | } |
| 15 | 17 |
apps/mobile/src/features/devices/device-stream.browser.ts+2-2| ... | ... | @@ -90,9 +90,9 @@ export function start(configuration: DeviceStreamConfiguration) { |
| 90 | 90 | Object.assign(retry.style, { |
| 91 | 91 | padding: "12px 24px", |
| 92 | 92 | borderRadius: "20px", |
| 93 | border: "0", | |
| 93 | border: `1px solid ${colors.buttonBorder}`, | |
| 94 | 94 | background: colors.buttonBackground, |
| 95 | color: colors.foreground, | |
| 95 | color: colors.buttonForeground, | |
| 96 | 96 | font: "inherit", |
| 97 | 97 | display: "none", |
| 98 | 98 | }); |
apps/mobile/src/features/files/SourceFileSurface.tsx+3-3| ... | ... | @@ -74,7 +74,7 @@ const HighlightedSourceLine = memo(function HighlightedSourceLine(props: { |
| 74 | 74 | </NativeText> |
| 75 | 75 | <NativeText |
| 76 | 76 | selectable |
| 77 | selectionColorClassName={Platform.OS === "android" ? "accent-primary/32" : undefined} | |
| 77 | selectionColorClassName={Platform.OS === "android" ? "accent-focus/32" : undefined} | |
| 78 | 78 | numberOfLines={props.wordBreak ? undefined : 1} |
| 79 | 79 | className="flex-1 font-normal text-foreground" |
| 80 | 80 | style={{ |
| ... | ... | @@ -105,7 +105,7 @@ const HighlightedSourceLine = memo(function HighlightedSourceLine(props: { |
| 105 | 105 | key={`${start}:${token.content.length}:${token.color ?? ""}`} |
| 106 | 106 | selectable |
| 107 | 107 | selectionColorClassName={ |
| 108 | Platform.OS === "android" ? "accent-primary/32" : undefined | |
| 108 | Platform.OS === "android" ? "accent-focus/32" : undefined | |
| 109 | 109 | } |
| 110 | 110 | style={{ |
| 111 | 111 | color: token.color ?? undefined, |
| ... | ... | @@ -279,7 +279,7 @@ function JavaScriptSourceFileSurface(props: SourceFileSurfaceProps) { |
| 279 | 279 | <MarkdownTextPrimitive |
| 280 | 280 | uiTextView |
| 281 | 281 | selectable |
| 282 | selectionColorClassName={Platform.OS === "android" ? "accent-primary/32" : undefined} | |
| 282 | selectionColorClassName={Platform.OS === "android" ? "accent-focus/32" : undefined} | |
| 283 | 283 | style={{ |
| 284 | 284 | color: foreground, |
| 285 | 285 | fontFamily: REVIEW_MONO_FONT_FAMILY, |
apps/mobile/src/features/keyboard/CommandPalette.tsx+30-8| ... | ... | @@ -20,6 +20,7 @@ import { GlassSurface } from "../../components/GlassSurface"; |
| 20 | 20 | import { RowPressable } from "../../components/RowPressable"; |
| 21 | 21 | import { AppText as Text } from "../../components/AppText"; |
| 22 | 22 | import { SymbolView, type AppSymbolName } from "../../components/AppSymbol"; |
| 23 | import { cn } from "../../lib/cn"; | |
| 23 | 24 | import { scopedProjectKey, scopedThreadKey } from "../../lib/scopedEntities"; |
| 24 | 25 | import { T3KeyboardCommands } from "../../native/T3KeyboardCommands"; |
| 25 | 26 | import { useProjects, useThreadShell, useThreadShells } from "../../state/entities"; |
| ... | ... | @@ -27,6 +28,7 @@ import { useThreadSearch } from "../../state/queries"; |
| 27 | 28 | import { useWorkspaceState } from "../../state/workspace"; |
| 28 | 29 | import { useSavedRemoteConnections } from "../../state/use-remote-environment-registry"; |
| 29 | 30 | import { useAdaptiveWorkspaceLayout } from "../layout/AdaptiveWorkspaceLayout"; |
| 31 | import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider"; | |
| 30 | 32 | import { ThreadSearchMatchExcerpt } from "../threads/thread-search-match"; |
| 31 | 33 | import { |
| 32 | 34 | filterCommandPaletteItems, |
| ... | ... | @@ -74,35 +76,53 @@ function PaletteRow(props: { |
| 74 | 76 | readonly searchQuery: string; |
| 75 | 77 | readonly onSelect: () => void; |
| 76 | 78 | }) { |
| 79 | const foregroundClassName = props.selected | |
| 80 | ? "text-thread-selected-foreground" | |
| 81 | : "text-foreground"; | |
| 82 | const mutedForegroundClassName = props.selected | |
| 83 | ? "text-thread-selected-foreground-muted" | |
| 84 | : "text-foreground-muted"; | |
| 77 | 85 | return ( |
| 78 | 86 | <RowPressable |
| 79 | 87 | accessibilityRole="button" |
| 88 | interactionOpacity={props.selected ? 0 : 1} | |
| 80 | 89 | accessibilityState={{ selected: props.selected }} |
| 81 | 90 | onPress={props.onSelect} |
| 82 | 91 | className={ |
| 83 | 92 | props.selected |
| 84 | ? "mx-2 flex-row items-center gap-3 rounded-xl bg-primary/10 px-3" | |
| 93 | ? "mx-2 flex-row items-center gap-3 rounded-xl bg-thread-selected px-3" | |
| 85 | 94 | : "mx-2 flex-row items-center gap-3 rounded-xl px-3" |
| 86 | 95 | } |
| 87 | 96 | style={{ height: ROW_HEIGHT }} |
| 88 | 97 | > |
| 89 | 98 | <View className="w-7 items-center"> |
| 90 | <SymbolView name={itemIcon(props.item)} size={20} tintColorClassName="accent-icon" /> | |
| 99 | <SymbolView | |
| 100 | name={itemIcon(props.item)} | |
| 101 | size={20} | |
| 102 | tintColorClassName={props.selected ? "accent-thread-selected-foreground" : "accent-icon"} | |
| 103 | /> | |
| 91 | 104 | </View> |
| 92 | 105 | <View className="flex-1"> |
| 93 | <Text numberOfLines={1} className="text-base"> | |
| 106 | <Text numberOfLines={1} className={cn("text-base", foregroundClassName)}> | |
| 94 | 107 | {props.item.title} |
| 95 | 108 | </Text> |
| 96 | 109 | {props.searchMatch ? ( |
| 97 | <ThreadSearchMatchExcerpt match={props.searchMatch} query={props.searchQuery} compact /> | |
| 110 | <ThreadSearchMatchExcerpt | |
| 111 | match={props.searchMatch} | |
| 112 | query={props.searchQuery} | |
| 113 | selected={props.selected} | |
| 114 | compact | |
| 115 | /> | |
| 98 | 116 | ) : props.item.detail ? ( |
| 99 | <Text numberOfLines={1} className="text-sm text-foreground-muted"> | |
| 117 | <Text numberOfLines={1} className={cn("text-sm", mutedForegroundClassName)}> | |
| 100 | 118 | {props.item.detail} |
| 101 | 119 | </Text> |
| 102 | 120 | ) : null} |
| 103 | 121 | </View> |
| 104 | 122 | {props.index < 9 ? ( |
| 105 | <NativeText className="w-8 shrink-0 text-right text-sm tabular-nums text-foreground-muted"> | |
| 123 | <NativeText | |
| 124 | className={cn("w-8 shrink-0 text-right text-sm tabular-nums", mutedForegroundClassName)} | |
| 125 | > | |
| 106 | 126 | ⌘{props.index + 1} |
| 107 | 127 | </NativeText> |
| 108 | 128 | ) : null} |
| ... | ... | @@ -117,6 +137,7 @@ export function CommandPalette(props: { |
| 117 | 137 | readonly onCommand: (command: HardwareKeyboardCommand) => void; |
| 118 | 138 | }) { |
| 119 | 139 | const navigation = useNavigation(); |
| 140 | const { themeVariables } = useAppearancePreferences(); | |
| 120 | 141 | const { selectThread } = useAdaptiveWorkspaceLayout(); |
| 121 | 142 | const runCommand = props.onCommand; |
| 122 | 143 | const projects = useProjects(); |
| ... | ... | @@ -392,14 +413,15 @@ export function CommandPalette(props: { |
| 392 | 413 | className="flex-1 items-center justify-center p-4" |
| 393 | 414 | > |
| 394 | 415 | <Pressable |
| 395 | className="absolute inset-0 bg-black/15" | |
| 416 | className="absolute inset-0 bg-backdrop" | |
| 396 | 417 | accessibilityLabel="Close command palette" |
| 397 | 418 | onPress={() => close()} |
| 398 | 419 | /> |
| 399 | 420 | <GlassSurface |
| 400 | 421 | accessibilityViewIsModal |
| 401 | className="bg-sheet/70" | |
| 422 | className="bg-sheet" | |
| 402 | 423 | tintColorClassName="accent-sheet/20" |
| 424 | fallbackColor={themeVariables["--color-sheet"]} | |
| 403 | 425 | style={{ |
| 404 | 426 | width: Math.min(600, width - 32), |
| 405 | 427 | height: Math.min(520, height - 80), |
apps/mobile/src/features/projects/AddProjectScreen.tsx+1-1| ... | ... | @@ -129,7 +129,7 @@ function SectionTitle(props: { readonly children: string }) { |
| 129 | 129 | <Text |
| 130 | 130 | className={ |
| 131 | 131 | Platform.OS === "android" |
| 132 | ? "px-4 text-sm font-t3-medium text-primary" | |
| 132 | ? "px-4 text-sm font-t3-medium text-primary-text" | |
| 133 | 133 | : "px-1 text-2xs font-t3-bold tracking-[0.7px] uppercase text-foreground-muted" |
| 134 | 134 | } |
| 135 | 135 | > |
apps/mobile/src/features/review/ReviewSheet.tsx+5-3| ... | ... | @@ -262,7 +262,7 @@ const ReviewFileNavigatorRow = memo(function ReviewFileNavigatorRow(props: { |
| 262 | 262 | Platform.OS === "android" |
| 263 | 263 | ? cn( |
| 264 | 264 | "mt-1 min-h-12 justify-center rounded-[20px] px-3 py-2 active:bg-subtle", |
| 265 | selected && "bg-thread-selected", | |
| 265 | selected && "bg-subtle-strong", | |
| 266 | 266 | ) |
| 267 | 267 | : selected |
| 268 | 268 | ? "mt-1 min-h-12 justify-center rounded-xl bg-subtle-strong px-3 py-2" |
| ... | ... | @@ -281,8 +281,10 @@ const ReviewFileNavigatorRow = memo(function ReviewFileNavigatorRow(props: { |
| 281 | 281 | {file.path} |
| 282 | 282 | </Text> |
| 283 | 283 | <View className="mt-1 flex-row gap-2"> |
| 284 | <Text className="text-2xs font-t3-bold text-emerald-600">+{file.additions}</Text> | |
| 285 | <Text className="text-2xs font-t3-bold text-rose-600">-{file.deletions}</Text> | |
| 284 | <Text className="text-2xs font-t3-bold text-adaptive-emerald-700-300"> | |
| 285 | +{file.additions} | |
| 286 | </Text> | |
| 287 | <Text className="text-2xs font-t3-bold text-adaptive-rose-700-300">-{file.deletions}</Text> | |
| 286 | 288 | </View> |
| 287 | 289 | </Pressable> |
| 288 | 290 | ); |
apps/mobile/src/features/review/nativeReviewDiffAdapter.test.ts+19-2| ... | ... | @@ -97,6 +97,19 @@ function appTheme(themeId: MobileThemeId, appearance: MobileThemeAppearance) { |
| 97 | 97 | : getMobileThemeVariables(themeId, appearance); |
| 98 | 98 | } |
| 99 | 99 | |
| 100 | function contrastRatio(first: string, second: string): number { | |
| 101 | const luminance = (hex: string) => { | |
| 102 | const [red, green, blue] = [1, 3, 5].map((offset) => { | |
| 103 | const channel = Number.parseInt(hex.slice(offset, offset + 2), 16) / 255; | |
| 104 | return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4; | |
| 105 | }); | |
| 106 | return 0.2126 * red! + 0.7152 * green! + 0.0722 * blue!; | |
| 107 | }; | |
| 108 | const a = luminance(first); | |
| 109 | const b = luminance(second); | |
| 110 | return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05); | |
| 111 | } | |
| 112 | ||
| 100 | 113 | describe("getCachedNativeReviewDiffData", () => { |
| 101 | 114 | it.each([true, false])( |
| 102 | 115 | "preserves available diff rows before a notice (has excerpt: %s)", |
| ... | ... | @@ -251,6 +264,10 @@ describe("createNativeReviewDiffTheme", () => { |
| 251 | 264 | for (const color of Object.values(theme)) { |
| 252 | 265 | expect(color, `${themeId}/${appearance}`).toMatch(/^#[\da-f]{6}$/i); |
| 253 | 266 | } |
| 267 | expect( | |
| 268 | contrastRatio(theme.hunkText, theme.hunkBackground), | |
| 269 | `${themeId}/${appearance} hunk text`, | |
| 270 | ).toBeGreaterThanOrEqual(4.5); | |
| 254 | 271 | } |
| 255 | 272 | } |
| 256 | 273 | }); |
| ... | ... | @@ -261,7 +278,7 @@ describe("createNativeReviewDiffTheme", () => { |
| 261 | 278 | const variables = { |
| 262 | 279 | ...appTheme("material-you", appearance), |
| 263 | 280 | "--color-screen": "#101214FF", |
| 264 | "--color-sheet": "#20222480", | |
| 281 | "--color-md-code-bg": "#20222480", | |
| 265 | 282 | "--color-md-code-text": "#E3E2E6FF", |
| 266 | 283 | "--color-foreground-muted": "#C7C5D080", |
| 267 | 284 | "--color-border": "#44464F80", |
| ... | ... | @@ -273,7 +290,7 @@ describe("createNativeReviewDiffTheme", () => { |
| 273 | 290 | expect(theme.text).toBe("#e3e2e6"); |
| 274 | 291 | expect(theme.mutedText).toBe("#707076"); |
| 275 | 292 | expect(theme.border).toBe("#2e3036"); |
| 276 | expect(theme.hunkText).toBe("#a8c7fa"); | |
| 293 | expect(contrastRatio(theme.hunkText, theme.hunkBackground)).toBeGreaterThanOrEqual(4.5); | |
| 277 | 294 | for (const color of Object.values(theme)) { |
| 278 | 295 | expect(color).toMatch(/^#[\da-f]{6}$/i); |
| 279 | 296 | } |
apps/mobile/src/features/review/nativeReviewDiffAdapter.ts+4-7| ... | ... | @@ -177,20 +177,19 @@ export function createNativeReviewDiffTheme( |
| 177 | 177 | appTheme["--color-screen"], |
| 178 | 178 | scheme === "dark" ? "#000000" : "#ffffff", |
| 179 | 179 | ); |
| 180 | const background = opaqueNativeHexColor(appTheme["--color-sheet"], screen); | |
| 180 | const background = opaqueNativeHexColor(appTheme["--color-md-code-bg"], screen); | |
| 181 | 181 | const nativeColor = (color: string) => opaqueNativeHexColor(color, background); |
| 182 | 182 | |
| 183 | 183 | if (scheme === "dark") { |
| 184 | 184 | return { |
| 185 | // Match the app surface (--color-sheet) so code views blend with the rest of | |
| 186 | // the app instead of using a distinct code-editor background. | |
| 185 | // Code surfaces share the desktop palette rather than the sheet behind them. | |
| 187 | 186 | background, |
| 188 | 187 | text: nativeColor(appTheme["--color-md-code-text"]), |
| 189 | 188 | mutedText: nativeColor(appTheme["--color-foreground-muted"]), |
| 190 | 189 | headerBackground: background, |
| 191 | 190 | border: nativeColor(appTheme["--color-border"]), |
| 192 | 191 | hunkBackground: nativeColor(appTheme["--color-subtle-strong"]), |
| 193 | hunkText: nativeColor(appTheme["--color-primary"]), | |
| 192 | hunkText: nativeColor(appTheme["--color-foreground"]), | |
| 194 | 193 | addBackground: "#0d2f28", |
| 195 | 194 | deleteBackground: "#391415", |
| 196 | 195 | addBar: "#00cab1", |
| ... | ... | @@ -201,15 +200,13 @@ export function createNativeReviewDiffTheme( |
| 201 | 200 | } |
| 202 | 201 | |
| 203 | 202 | return { |
| 204 | // Match the app surface (--color-sheet) so code views blend with the rest of the | |
| 205 | // app instead of using a distinct code-editor background. | |
| 206 | 203 | background, |
| 207 | 204 | text: nativeColor(appTheme["--color-md-code-text"]), |
| 208 | 205 | mutedText: nativeColor(appTheme["--color-foreground-muted"]), |
| 209 | 206 | headerBackground: background, |
| 210 | 207 | border: nativeColor(appTheme["--color-border"]), |
| 211 | 208 | hunkBackground: nativeColor(appTheme["--color-subtle-strong"]), |
| 212 | hunkText: nativeColor(appTheme["--color-primary"]), | |
| 209 | hunkText: nativeColor(appTheme["--color-foreground"]), | |
| 213 | 210 | addBackground: "#e5f8f5", |
| 214 | 211 | deleteBackground: "#ffe6e7", |
| 215 | 212 | addBar: "#00cab1", |
apps/mobile/src/features/settings/SettingsOpenSourceLicensesRouteScreen.tsx+4-4| ... | ... | @@ -44,7 +44,7 @@ function LicenseRow(props: { |
| 44 | 44 | accessibilityLabel={`${props.entry.name}, ${props.entry.license}`} |
| 45 | 45 | accessibilityRole="button" |
| 46 | 46 | onPress={props.onPress} |
| 47 | className="border-b border-border bg-card px-5 py-4 active:bg-card-alt" | |
| 47 | className="border-b border-border bg-grouped-card px-5 py-4 active:bg-card-alt" | |
| 48 | 48 | > |
| 49 | 49 | <View className="flex-row items-start gap-3"> |
| 50 | 50 | <View className="min-w-0 flex-1 gap-1"> |
| ... | ... | @@ -235,11 +235,11 @@ export function SettingsOpenSourceLicenseRouteScreen({ route }: LicenseDetailPro |
| 235 | 235 | onPress={() => void Linking.openURL(sourceUrl)} |
| 236 | 236 | className="min-h-12 flex-row items-center gap-2 self-start py-2 active:opacity-60" |
| 237 | 237 | > |
| 238 | <Text className="font-t3-medium text-primary">Project source</Text> | |
| 238 | <Text className="font-t3-medium text-primary-text">Project source</Text> | |
| 239 | 239 | <SymbolView |
| 240 | 240 | name="arrow.up.right" |
| 241 | 241 | size={16} |
| 242 | tintColorClassName={"accent-primary"} | |
| 242 | tintColorClassName={"accent-primary-text"} | |
| 243 | 243 | type="monochrome" |
| 244 | 244 | weight="semibold" |
| 245 | 245 | /> |
| ... | ... | @@ -247,7 +247,7 @@ export function SettingsOpenSourceLicenseRouteScreen({ route }: LicenseDetailPro |
| 247 | 247 | ) : null} |
| 248 | 248 | </View> |
| 249 | 249 | |
| 250 | <View className="overflow-hidden rounded-[24px] border-continuous bg-card p-4"> | |
| 250 | <View className="overflow-hidden rounded-[24px] border-continuous bg-grouped-card p-4"> | |
| 251 | 251 | <Text selectable className="font-mono text-base leading-normal text-foreground"> |
| 252 | 252 | {entry.noticeText} |
| 253 | 253 | </Text> |
apps/mobile/src/features/settings/appearance/sections/ThemeAppearanceSection.tsx+4-2| ... | ... | @@ -151,7 +151,7 @@ function ThemeCard(props: { |
| 151 | 151 | ); |
| 152 | 152 | |
| 153 | 153 | return ( |
| 154 | <View className="min-w-36 flex-1 basis-[47%] gap-3 rounded-[24px] border border-border bg-card px-2 py-4"> | |
| 154 | <View className="min-w-36 flex-1 basis-[47%] gap-3 rounded-[24px] border border-border bg-grouped-card px-2 py-4"> | |
| 155 | 155 | <Pressable |
| 156 | 156 | accessibilityHint="Sets both light and dark appearances" |
| 157 | 157 | accessibilityLabel={`${props.label} theme`} |
| ... | ... | @@ -259,7 +259,9 @@ function ModeCard(props: { |
| 259 | 259 | accessibilityState={{ checked: props.selected, disabled: props.disabled }} |
| 260 | 260 | className={cn( |
| 261 | 261 | "min-w-0 flex-1 gap-2 rounded-[24px] p-2 active:scale-[0.97]", |
| 262 | props.selected ? "border-2 border-primary bg-subtle" : "border border-border bg-card", | |
| 262 | props.selected | |
| 263 | ? "border-2 border-primary bg-subtle" | |
| 264 | : "border border-border bg-grouped-card", | |
| 263 | 265 | )} |
| 264 | 266 | disabled={props.disabled} |
| 265 | 267 | onPress={props.onPress} |
apps/mobile/src/features/settings/components/AutoSettleDaysField.ios.tsx+3-3| ... | ... | @@ -44,7 +44,7 @@ export function AutoSettleDaysField(props: AutoSettleDaysFieldProps) { |
| 44 | 44 | disabled(props.disabled), |
| 45 | 45 | accessibilityLabel(`Days before auto-settle: ${props.value}`), |
| 46 | 46 | frame({ minWidth: 64, minHeight: 44 }), |
| 47 | foregroundStyle(colors["--color-primary"]), | |
| 47 | foregroundStyle(colors["--color-primary-text"]), | |
| 48 | 48 | font({ size: appearance.baseFontSize }), |
| 49 | 49 | ]} |
| 50 | 50 | > |
| ... | ... | @@ -78,7 +78,7 @@ export function AutoSettleDaysField(props: AutoSettleDaysFieldProps) { |
| 78 | 78 | <Button |
| 79 | 79 | label="Cancel" |
| 80 | 80 | onPress={() => setOpen(false)} |
| 81 | modifiers={[foregroundStyle(colors["--color-primary"])]} | |
| 81 | modifiers={[foregroundStyle(colors["--color-primary-text"])]} | |
| 82 | 82 | /> |
| 83 | 83 | <Button |
| 84 | 84 | label="Done" |
| ... | ... | @@ -86,7 +86,7 @@ export function AutoSettleDaysField(props: AutoSettleDaysFieldProps) { |
| 86 | 86 | setOpen(false); |
| 87 | 87 | if (!props.disabled && draft !== props.value) props.onValueChange(draft); |
| 88 | 88 | }} |
| 89 | modifiers={[foregroundStyle(colors["--color-primary"])]} | |
| 89 | modifiers={[foregroundStyle(colors["--color-primary-text"])]} | |
| 90 | 90 | /> |
| 91 | 91 | </HStack> |
| 92 | 92 | </VStack> |
apps/mobile/src/features/settings/components/SettingsActionRow.tsx+1| ... | ... | @@ -31,6 +31,7 @@ export function SettingsActionRow(props: { |
| 31 | 31 | if (Platform.OS === "android") { |
| 32 | 32 | return ( |
| 33 | 33 | <MaterialListRow |
| 34 | className="bg-grouped-card" | |
| 34 | 35 | title={props.label} |
| 35 | 36 | titleClassName={textClassName} |
| 36 | 37 | leading={icon} |
apps/mobile/src/features/settings/components/SettingsProjectOverridesSection.tsx+1-1| ... | ... | @@ -23,7 +23,7 @@ export function SettingsProjectOverridesSection(props: { |
| 23 | 23 | onPress={props.onClear} |
| 24 | 24 | className="px-2 py-2 active:opacity-70" |
| 25 | 25 | > |
| 26 | <Text className="text-sm font-t3-medium text-primary">Use defaults</Text> | |
| 26 | <Text className="text-sm font-t3-medium text-primary-text">Use defaults</Text> | |
| 27 | 27 | </Pressable> |
| 28 | 28 | ) : null} |
| 29 | 29 | </View> |
apps/mobile/src/features/settings/components/SettingsRow.tsx+1| ... | ... | @@ -24,6 +24,7 @@ export function SettingsRow(props: { |
| 24 | 24 | if (Platform.OS === "android") { |
| 25 | 25 | return ( |
| 26 | 26 | <MaterialListRow |
| 27 | className="bg-grouped-card" | |
| 27 | 28 | title={props.label} |
| 28 | 29 | subtitle={props.valuePosition === "trailing" ? undefined : props.value} |
| 29 | 30 | accessibilityLabel={[props.label, props.value].filter(Boolean).join(", ")} |
apps/mobile/src/features/settings/components/SettingsSection.tsx+3-3| ... | ... | @@ -15,7 +15,7 @@ export function SettingsSection(props: { |
| 15 | 15 | <Text |
| 16 | 16 | className={ |
| 17 | 17 | Platform.OS === "android" |
| 18 | ? "px-4 text-sm font-t3-medium text-primary" | |
| 18 | ? "px-4 text-sm font-t3-medium text-primary-text" | |
| 19 | 19 | : "px-2 text-sm font-t3-medium text-foreground-muted" |
| 20 | 20 | } |
| 21 | 21 | > |
| ... | ... | @@ -27,8 +27,8 @@ export function SettingsSection(props: { |
| 27 | 27 | <View |
| 28 | 28 | className={ |
| 29 | 29 | Platform.OS === "android" |
| 30 | ? "overflow-hidden rounded-[28px] bg-card" | |
| 31 | : "overflow-hidden rounded-[24px] border-continuous bg-card" | |
| 30 | ? "overflow-hidden rounded-[28px] bg-grouped-card" | |
| 31 | : "overflow-hidden rounded-[24px] border-continuous bg-grouped-card" | |
| 32 | 32 | } |
| 33 | 33 | > |
| 34 | 34 | {props.children} |
apps/mobile/src/features/terminal/terminalTheme.test.ts+10-10| ... | ... | @@ -6,20 +6,20 @@ import { themeColorToNativeColor } from "../../lib/mobileTheme"; |
| 6 | 6 | import { buildGhosttyThemeConfig, getMobileTerminalTheme } from "./terminalTheme"; |
| 7 | 7 | |
| 8 | 8 | describe("getMobileTerminalTheme", () => { |
| 9 | it("preserves the default light terminal palette", () => { | |
| 9 | it("uses the shared default light terminal colors", () => { | |
| 10 | 10 | expect(getMobileTerminalTheme("t3-code", "light")).toMatchObject({ |
| 11 | background: "#f2f2f7", | |
| 12 | foreground: "#6C6C71", | |
| 13 | cursorForeground: "#009fff", | |
| 14 | cursorBackground: "#f2f2f7", | |
| 11 | background: "#fcfcfc", | |
| 12 | foreground: "#27272a", | |
| 13 | cursorForeground: "#26384e", | |
| 14 | cursorBackground: "#fcfcfc", | |
| 15 | 15 | }); |
| 16 | 16 | }); |
| 17 | 17 | |
| 18 | it("preserves the default dark terminal palette", () => { | |
| 18 | it("uses the shared default dark terminal colors", () => { | |
| 19 | 19 | expect(getMobileTerminalTheme("t3-code", "dark")).toMatchObject({ |
| 20 | 20 | background: "#0a0a0a", |
| 21 | foreground: "#adadb1", | |
| 22 | cursorForeground: "#009fff", | |
| 21 | foreground: "#f5f5f5", | |
| 22 | cursorForeground: "#b4cbff", | |
| 23 | 23 | cursorBackground: "#0a0a0a", |
| 24 | 24 | }); |
| 25 | 25 | }); |
| ... | ... | @@ -48,8 +48,8 @@ describe("buildGhosttyThemeConfig", () => { |
| 48 | 48 | const config = buildGhosttyThemeConfig(getMobileTerminalTheme("t3-code", "dark")); |
| 49 | 49 | |
| 50 | 50 | expect(config).toContain("background = #0a0a0a"); |
| 51 | expect(config).toContain("foreground = #adadb1"); | |
| 52 | expect(config).toContain("cursor-color = #009fff"); | |
| 51 | expect(config).toContain("foreground = #f5f5f5"); | |
| 52 | expect(config).toContain("cursor-color = #b4cbff"); | |
| 53 | 53 | expect(config).toContain("palette = 0=#141415"); |
| 54 | 54 | expect(config).toContain("palette = 15=#c6c6c8"); |
| 55 | 55 | expect(config.endsWith("\n")).toBe(true); |
apps/mobile/src/features/terminal/terminalTheme.ts+6-13| ... | ... | @@ -1,10 +1,5 @@ |
| 1 | 1 | import { |
| 2 | BUILT_IN_THEMES, | |
| 3 | T3_CHAT_THEME, | |
| 4 | getThemeColorsForAppearance, | |
| 5 | } from "@t3tools/shared/themePalettes"; | |
| 6 | ||
| 7 | import { | |
| 2 | getMobileThemeColors, | |
| 8 | 3 | getMobileThemeVariables, |
| 9 | 4 | themeColorToNativeColor, |
| 10 | 5 | type MobileThemeId, |
| ... | ... | @@ -44,12 +39,12 @@ type TerminalPalette = readonly [ |
| 44 | 39 | |
| 45 | 40 | const PIERRE_LIGHT_THEME: TerminalTheme = { |
| 46 | 41 | // Pierre terminal palette with the app's shared screen background. |
| 47 | background: "#f2f2f7", | |
| 42 | background: "#fcfcfc", | |
| 48 | 43 | foreground: "#6C6C71", |
| 49 | 44 | mutedForeground: "#8E8E95", |
| 50 | 45 | border: "#eeeeef", |
| 51 | 46 | cursorForeground: "#009fff", |
| 52 | cursorBackground: "#f2f2f7", | |
| 47 | cursorBackground: "#fcfcfc", | |
| 53 | 48 | palette: [ |
| 54 | 49 | "#1F1F21", |
| 55 | 50 | "#ff2e3f", |
| ... | ... | @@ -107,11 +102,9 @@ export function getMobileTerminalTheme( |
| 107 | 102 | scheme: TerminalAppearanceScheme, |
| 108 | 103 | ): TerminalTheme { |
| 109 | 104 | const base = getPierreTerminalTheme(scheme); |
| 110 | if (themeId === "t3-code" || themeId === "material-you") return base; | |
| 111 | ||
| 112 | const theme = BUILT_IN_THEMES.find((candidate) => candidate.id === themeId) ?? T3_CHAT_THEME; | |
| 113 | const palette = getThemeColorsForAppearance(theme, scheme) ?? theme.colors; | |
| 114 | const colors = getMobileThemeVariables(themeId, scheme); | |
| 105 | const paletteId = themeId === "material-you" ? "t3-code" : themeId; | |
| 106 | const palette = getMobileThemeColors(paletteId, scheme); | |
| 107 | const colors = getMobileThemeVariables(paletteId, scheme); | |
| 115 | 108 | const background = themeColorToNativeColor(palette.terminalBackground); |
| 116 | 109 | return { |
| 117 | 110 | ...base, |
apps/mobile/src/features/threads/CustomSnoozeSheet.shared.tsx+1-1| ... | ... | @@ -31,7 +31,7 @@ export function CustomSnoozeSheet(props: { |
| 31 | 31 | return ( |
| 32 | 32 | <Modal visible transparent animationType="fade" onRequestClose={props.onClose}> |
| 33 | 33 | <KeyboardAvoidingView |
| 34 | className="flex-1 items-center justify-center bg-black/40 px-6" | |
| 34 | className="flex-1 items-center justify-center bg-backdrop px-6" | |
| 35 | 35 | behavior={Platform.OS === "ios" ? "padding" : undefined} |
| 36 | 36 | > |
| 37 | 37 | <ScrollView |
apps/mobile/src/features/threads/NewTaskContextPickerScreens.tsx+4-4| ... | ... | @@ -66,7 +66,7 @@ function SelectionRow(props: { |
| 66 | 66 | } |
| 67 | 67 | trailing={ |
| 68 | 68 | props.selected ? ( |
| 69 | <SymbolView name="checkmark" size={20} tintColorClassName="accent-primary" /> | |
| 69 | <SymbolView name="checkmark" size={20} tintColorClassName="accent-focus" /> | |
| 70 | 70 | ) : null |
| 71 | 71 | } |
| 72 | 72 | accessibilityRole="radio" |
| ... | ... | @@ -482,9 +482,9 @@ export function NewTaskBranchPickerRouteScreen() { |
| 482 | 482 | autoCorrect={false} |
| 483 | 483 | accessibilityLabel="Find a branch" |
| 484 | 484 | className="h-12 rounded-full border border-input-border bg-input px-4 font-sans text-base text-foreground" |
| 485 | selectionColorClassName="accent-primary/32" | |
| 486 | cursorColorClassName="accent-primary" | |
| 487 | selectionHandleColorClassName="accent-primary" | |
| 485 | selectionColorClassName="accent-focus/32" | |
| 486 | cursorColorClassName="accent-focus" | |
| 487 | selectionHandleColorClassName="accent-focus" | |
| 488 | 488 | onChangeText={flow.setBranchQuery} |
| 489 | 489 | placeholder="Find a branch" |
| 490 | 490 | placeholderTextColorClassName="accent-placeholder" |
apps/mobile/src/features/threads/ThreadArrangementSheet.tsx+1-1| ... | ... | @@ -379,7 +379,7 @@ export function ThreadArrangementSheet(props: { onClose: () => void }) { |
| 379 | 379 | onPress={props.onClose} |
| 380 | 380 | className="min-h-11 justify-center px-3" |
| 381 | 381 | > |
| 382 | <Text className="text-base text-primary">Done</Text> | |
| 382 | <Text className="text-base text-primary-text">Done</Text> | |
| 383 | 383 | </Pressable> |
| 384 | 384 | </View> |
| 385 | 385 | <Text className="px-5 pb-3 text-sm text-foreground-muted"> |
apps/mobile/src/features/threads/ThreadComposer.tsx+2-6| ... | ... | @@ -244,12 +244,8 @@ export function ComposerSurface(props: { |
| 244 | 244 | > |
| 245 | 245 | <AnimatedGlassSurface |
| 246 | 246 | chrome="none" |
| 247 | fallbackColor={ | |
| 248 | Platform.OS === "android" ? colors["--color-composer-surface"] : colors["--color-card"] | |
| 249 | } | |
| 250 | fallbackClassName={ | |
| 251 | Platform.OS === "android" ? "border border-composer-border" : "border border-border" | |
| 252 | } | |
| 247 | fallbackColor={colors["--color-composer-surface"]} | |
| 248 | fallbackClassName="border border-composer-border" | |
| 253 | 249 | glassEffectStyle="regular" |
| 254 | 250 | // The composer is a passive material containing interactive controls. |
| 255 | 251 | // Keep native glass out of the interactive content's layout path. |
apps/mobile/src/features/threads/ThreadCreationFailedCard.tsx+4-4| ... | ... | @@ -13,14 +13,14 @@ export function ThreadCreationFailedCard(props: { |
| 13 | 13 | readonly onEditTask: () => void; |
| 14 | 14 | }) { |
| 15 | 15 | return ( |
| 16 | <View className="gap-2.5 rounded-[20px] border border-adaptive-neutral-200-white-a6 bg-adaptive-neutral-100-900 p-4"> | |
| 17 | <Text className="font-t3-bold text-2xs uppercase tracking-[1.1px] text-adaptive-rose-700-300"> | |
| 16 | <View className="gap-2.5 rounded-[20px] border border-border-subtle bg-card-alt p-4"> | |
| 17 | <Text className="font-t3-bold text-2xs uppercase tracking-[1.1px] text-danger-foreground"> | |
| 18 | 18 | Could not start task |
| 19 | 19 | </Text> |
| 20 | <Text className="font-sans text-sm leading-normal text-adaptive-neutral-600-400"> | |
| 20 | <Text className="font-sans text-sm leading-normal text-foreground-secondary"> | |
| 21 | 21 | {props.reason} |
| 22 | 22 | </Text> |
| 23 | <Text className="font-sans text-xs leading-normal text-adaptive-neutral-600-400"> | |
| 23 | <Text className="font-sans text-xs leading-normal text-foreground-secondary"> | |
| 24 | 24 | Your prompt was kept in the project draft. |
| 25 | 25 | </Text> |
| 26 | 26 | <View className="flex-row"> |
apps/mobile/src/features/threads/ThreadFeed.tsx+9-9| ... | ... | @@ -888,7 +888,7 @@ function MarkdownCodeBlock(props: { |
| 888 | 888 | > |
| 889 | 889 | <NativeText |
| 890 | 890 | selectable |
| 891 | selectionColorClassName={Platform.OS === "android" ? "accent-primary/32" : undefined} | |
| 891 | selectionColorClassName={Platform.OS === "android" ? "accent-focus/32" : undefined} | |
| 892 | 892 | className="font-mono" |
| 893 | 893 | style={{ |
| 894 | 894 | color: props.textColor, |
| ... | ... | @@ -1400,7 +1400,7 @@ function renderFeedEntry( |
| 1400 | 1400 | accessibilityState={{ expanded: entry.expanded }} |
| 1401 | 1401 | onPress={() => props.onToggleTurnFold(entry.turnId)} |
| 1402 | 1402 | hitSlop={4} |
| 1403 | className="mb-1 min-h-11 flex-row items-center gap-2 border-b border-adaptive-neutral-200-a80-white-a8 px-2" | |
| 1403 | className="mb-1 min-h-11 flex-row items-center gap-2 border-b border-border-subtle px-2" | |
| 1404 | 1404 | style={{ |
| 1405 | 1405 | minHeight: Math.max(TURN_FOLD_HEIGHT - 3.5, props.workRowSizing.estimatedRowHeight), |
| 1406 | 1406 | }} |
| ... | ... | @@ -1467,7 +1467,7 @@ function renderFeedEntry( |
| 1467 | 1467 | accessibilityLabel={label} |
| 1468 | 1468 | className="mb-3 flex-row items-center gap-3 px-1 py-1" |
| 1469 | 1469 | > |
| 1470 | <View className="h-px flex-1 bg-adaptive-neutral-200-a80-white-a8" /> | |
| 1470 | <View className="h-px flex-1 bg-subtle" /> | |
| 1471 | 1471 | <View className="shrink-0 flex-row items-center gap-1.5"> |
| 1472 | 1472 | <SymbolView |
| 1473 | 1473 | name="arrow.down.right.and.arrow.up.left" |
| ... | ... | @@ -1477,7 +1477,7 @@ function renderFeedEntry( |
| 1477 | 1477 | /> |
| 1478 | 1478 | <Text className="font-t3-medium text-xs text-foreground-muted">{label}</Text> |
| 1479 | 1479 | </View> |
| 1480 | <View className="h-px flex-1 bg-adaptive-neutral-200-a80-white-a8" /> | |
| 1480 | <View className="h-px flex-1 bg-subtle" /> | |
| 1481 | 1481 | </View> |
| 1482 | 1482 | ); |
| 1483 | 1483 | } |
| ... | ... | @@ -1599,8 +1599,8 @@ function renderFeedEntry( |
| 1599 | 1599 | mimeType={attachment.mimeType} |
| 1600 | 1600 | className={ |
| 1601 | 1601 | inlineAttachmentIds.size |
| 1602 | ? "h-24 w-24 rounded-[14px] bg-white/15" | |
| 1603 | : "aspect-[1.3] w-full rounded-[14px] bg-white/15" | |
| 1602 | ? "h-24 w-24 rounded-[14px] bg-user-bubble-foreground/15" | |
| 1603 | : "aspect-[1.3] w-full rounded-[14px] bg-user-bubble-foreground/15" | |
| 1604 | 1604 | } |
| 1605 | 1605 | onPressPreview={props.onPressPreview} |
| 1606 | 1606 | /> |
| ... | ... | @@ -1636,7 +1636,7 @@ function renderFeedEntry( |
| 1636 | 1636 | ) : null} |
| 1637 | 1637 | </View> |
| 1638 | 1638 | <View className="mt-1 flex-row items-center justify-end gap-1 pr-0.5"> |
| 1639 | <Text className="font-t3-medium text-xs tabular-nums text-adaptive-neutral-600-400"> | |
| 1639 | <Text className="font-t3-medium text-xs tabular-nums text-foreground-secondary"> | |
| 1640 | 1640 | {entry.pendingMessage && !entry.acknowledged ? "Pending" : timestampLabel} |
| 1641 | 1641 | </Text> |
| 1642 | 1642 | {entry.pendingMessage && |
| ... | ... | @@ -1715,7 +1715,7 @@ function renderFeedEntry( |
| 1715 | 1715 | attachmentId={attachment.id} |
| 1716 | 1716 | name={attachment.name} |
| 1717 | 1717 | mimeType={attachment.mimeType} |
| 1718 | className="mt-1.5 aspect-[1.3] w-full rounded-[18px] bg-adaptive-neutral-200-800" | |
| 1718 | className="mt-1.5 aspect-[1.3] w-full rounded-[18px] bg-subtle-strong" | |
| 1719 | 1719 | onPressPreview={props.onPressPreview} |
| 1720 | 1720 | /> |
| 1721 | 1721 | ) : isFileAttachment(attachment) ? ( |
| ... | ... | @@ -1739,7 +1739,7 @@ function renderFeedEntry( |
| 1739 | 1739 | buttonSize={28} |
| 1740 | 1740 | iconSize={13} |
| 1741 | 1741 | /> |
| 1742 | <Text className="font-t3-medium text-xs tabular-nums text-adaptive-neutral-600-400"> | |
| 1742 | <Text className="font-t3-medium text-xs tabular-nums text-foreground-secondary"> | |
| 1743 | 1743 | {timestampLabel} |
| 1744 | 1744 | </Text> |
| 1745 | 1745 | </View> |
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx+17-13| ... | ... | @@ -152,7 +152,7 @@ function ThreadNavigationSidebarPane( |
| 152 | 152 | props: ThreadNavigationSidebarProps & { readonly nativeChrome: boolean }, |
| 153 | 153 | ) { |
| 154 | 154 | const { themeVariables: materialTheme } = useAppearancePreferences(); |
| 155 | const screenColor = materialTheme["--color-screen"]; | |
| 155 | const drawerColor = materialTheme["--color-drawer"]; | |
| 156 | 156 | |
| 157 | 157 | const insets = useSafeAreaInsets(); |
| 158 | 158 | const projects = useProjects(); |
| ... | ... | @@ -935,7 +935,7 @@ function ThreadNavigationSidebarPane( |
| 935 | 935 | }), |
| 936 | 936 | )} |
| 937 | 937 | searchQuery={props.searchQuery} |
| 938 | pane={Platform.OS === "android" ? "screen" : "sidebar"} | |
| 938 | pane="sidebar" | |
| 939 | 939 | selected={ |
| 940 | 940 | scopedThreadKey(thread.environmentId, thread.id) === props.selectedThreadKey |
| 941 | 941 | } |
| ... | ... | @@ -976,7 +976,7 @@ function ThreadNavigationSidebarPane( |
| 976 | 976 | disabled={!shelfPreferencesLoaded} |
| 977 | 977 | expanded={item.expanded} |
| 978 | 978 | onToggle={toggleSnoozedShelf} |
| 979 | pane={Platform.OS === "android" ? "screen" : "sidebar"} | |
| 979 | pane="sidebar" | |
| 980 | 980 | /> |
| 981 | 981 | ); |
| 982 | 982 | case "v2-settled-shelf": |
| ... | ... | @@ -986,17 +986,21 @@ function ThreadNavigationSidebarPane( |
| 986 | 986 | disabled={!shelfPreferencesLoaded} |
| 987 | 987 | expanded={item.expanded} |
| 988 | 988 | onToggle={toggleSettledShelf} |
| 989 | pane={Platform.OS === "android" ? "screen" : "sidebar"} | |
| 989 | pane="sidebar" | |
| 990 | 990 | /> |
| 991 | 991 | ); |
| 992 | 992 | case "v2-show-more": |
| 993 | 993 | return ( |
| 994 | <ThreadListV2ShowMoreRow hiddenCount={item.hiddenCount} onPress={showMoreSettled} /> | |
| 994 | <ThreadListV2ShowMoreRow | |
| 995 | pane="sidebar" | |
| 996 | hiddenCount={item.hiddenCount} | |
| 997 | onPress={showMoreSettled} | |
| 998 | /> | |
| 995 | 999 | ); |
| 996 | 1000 | case "header": |
| 997 | 1001 | return ( |
| 998 | 1002 | <ThreadListGroupHeader |
| 999 | variant={Platform.OS === "android" ? "compact" : "sidebar"} | |
| 1003 | variant="sidebar" | |
| 1000 | 1004 | collapsed={item.collapsed} |
| 1001 | 1005 | isFirst={item.isFirst} |
| 1002 | 1006 | groupKey={item.group.key} |
| ... | ... | @@ -1014,7 +1018,7 @@ function ThreadNavigationSidebarPane( |
| 1014 | 1018 | case "pending-task": |
| 1015 | 1019 | return ( |
| 1016 | 1020 | <PendingTaskListRow |
| 1017 | variant={Platform.OS === "android" ? "compact" : "sidebar"} | |
| 1021 | variant="sidebar" | |
| 1018 | 1022 | pendingTask={item.pendingTask} |
| 1019 | 1023 | environmentLabel={ |
| 1020 | 1024 | savedConnectionsById[item.pendingTask.environmentId]?.environmentLabel ?? null |
| ... | ... | @@ -1064,7 +1068,7 @@ function ThreadNavigationSidebarPane( |
| 1064 | 1068 | case "show-more": |
| 1065 | 1069 | return ( |
| 1066 | 1070 | <ThreadListShowMoreRow |
| 1067 | variant={Platform.OS === "android" ? "compact" : "sidebar"} | |
| 1071 | variant="sidebar" | |
| 1068 | 1072 | hiddenCount={item.hiddenCount} |
| 1069 | 1073 | canShowLess={item.canShowLess} |
| 1070 | 1074 | groupKey={item.groupKey} |
| ... | ... | @@ -1168,8 +1172,8 @@ function ThreadNavigationSidebarPane( |
| 1168 | 1172 | <Text |
| 1169 | 1173 | className={ |
| 1170 | 1174 | Platform.OS === "android" |
| 1171 | ? "px-4 py-4 text-center text-sm text-foreground-muted" | |
| 1172 | : "px-2 py-4 text-sm text-foreground-muted" | |
| 1175 | ? "px-4 py-4 text-center text-sm text-drawer-foreground-muted" | |
| 1176 | : "px-2 py-4 text-sm text-drawer-foreground-muted" | |
| 1173 | 1177 | } |
| 1174 | 1178 | > |
| 1175 | 1179 | {catalogState.isLoadingConnections |
| ... | ... | @@ -1277,7 +1281,7 @@ function ThreadNavigationSidebarPane( |
| 1277 | 1281 | marginTop: stickyHeaderHeight, |
| 1278 | 1282 | marginHorizontal: 4, |
| 1279 | 1283 | paddingBottom: insets.bottom, |
| 1280 | backgroundColor: screenColor, | |
| 1284 | backgroundColor: drawerColor, | |
| 1281 | 1285 | borderTopLeftRadius: 28, |
| 1282 | 1286 | borderTopRightRadius: 28, |
| 1283 | 1287 | overflow: "hidden", |
| ... | ... | @@ -1374,7 +1378,7 @@ function ThreadNavigationSidebarPane( |
| 1374 | 1378 | <SymbolView |
| 1375 | 1379 | name="magnifyingglass" |
| 1376 | 1380 | size={15} |
| 1377 | tintColorClassName="accent-foreground-muted" | |
| 1381 | tintColorClassName="accent-drawer-foreground-muted" | |
| 1378 | 1382 | type="monochrome" |
| 1379 | 1383 | /> |
| 1380 | 1384 | <TextInput |
| ... | ... | @@ -1390,7 +1394,7 @@ function ThreadNavigationSidebarPane( |
| 1390 | 1394 | cursorColorClassName={undefined} |
| 1391 | 1395 | selectionHandleColorClassName={undefined} |
| 1392 | 1396 | returnKeyType="search" |
| 1393 | className="h-[34px] flex-1 px-0 py-0 font-sans text-base text-foreground" | |
| 1397 | className="h-[34px] flex-1 px-0 py-0 font-sans text-base text-drawer-foreground" | |
| 1394 | 1398 | value={props.searchQuery} |
| 1395 | 1399 | /> |
| 1396 | 1400 | </View> |
apps/mobile/src/features/threads/ThreadSettingsRows.android.tsx+1-1| ... | ... | @@ -12,7 +12,7 @@ export function ModelRow(props: ModelRowProps) { |
| 12 | 12 | {...props} |
| 13 | 13 | labelNumberOfLines={2} |
| 14 | 14 | minimumHeight={56} |
| 15 | selectedClassName={props.selected ? "bg-thread-selected" : undefined} | |
| 15 | selectedClassName={props.selected ? "bg-secondary" : undefined} | |
| 16 | 16 | leadingSelection={<MaterialRadioIndicator selected={props.selected} />} |
| 17 | 17 | /> |
| 18 | 18 | ); |
apps/mobile/src/features/threads/ThreadSettingsSheet.tsx+3-3| ... | ... | @@ -896,9 +896,9 @@ function ThreadSettingsMainContent(props: { |
| 896 | 896 | onChangeText={session.setSearchQuery} |
| 897 | 897 | placeholder="Find a model" |
| 898 | 898 | placeholderTextColorClassName="accent-placeholder" |
| 899 | selectionColorClassName="accent-primary/32" | |
| 900 | cursorColorClassName="accent-primary" | |
| 901 | selectionHandleColorClassName="accent-primary" | |
| 899 | selectionColorClassName="accent-focus/32" | |
| 900 | cursorColorClassName="accent-focus" | |
| 901 | selectionHandleColorClassName="accent-focus" | |
| 902 | 902 | value={session.searchQuery} |
| 903 | 903 | /> |
| 904 | 904 | {session.searchQuery.length > 0 ? ( |
apps/mobile/src/features/threads/floating-working-control.tsx+3-3| ... | ... | @@ -213,7 +213,7 @@ export function FloatingWorkingControl(props: { |
| 213 | 213 | <View pointerEvents="box-none" className="flex-row items-center gap-4"> |
| 214 | 214 | <Animated.View |
| 215 | 215 | pointerEvents={capsuleInteractive ? "box-none" : "none"} |
| 216 | className="h-11 items-center justify-center overflow-hidden rounded-full border border-border bg-card shadow-md shadow-black/10" | |
| 216 | className="h-11 items-center justify-center overflow-hidden rounded-full border border-border bg-glass-fallback shadow-md shadow-black/10" | |
| 217 | 217 | style={capsuleStyle} |
| 218 | 218 | > |
| 219 | 219 | {capsuleContent} |
| ... | ... | @@ -228,7 +228,7 @@ export function FloatingWorkingControl(props: { |
| 228 | 228 | <ControlPill |
| 229 | 229 | accessibilityLabel="Scroll to end" |
| 230 | 230 | activateOnPressIn |
| 231 | className="h-11 w-11 border border-border bg-card shadow-md shadow-black/10" | |
| 231 | className="h-11 w-11 border border-border bg-glass-fallback shadow-md shadow-black/10" | |
| 232 | 232 | disabled={!props.showScrollToEnd} |
| 233 | 233 | icon={{ ios: "chevron.down", android: "keyboard_arrow_down" }} |
| 234 | 234 | onPress={props.onScrollToEnd} |
| ... | ... | @@ -248,7 +248,7 @@ export function FloatingWorkingControl(props: { |
| 248 | 248 | <ControlPill |
| 249 | 249 | accessibilityLabel="Scroll to end" |
| 250 | 250 | activateOnPressIn |
| 251 | className="h-11 w-11 border border-border bg-card shadow-md shadow-black/10" | |
| 251 | className="h-11 w-11 border border-border bg-glass-fallback shadow-md shadow-black/10" | |
| 252 | 252 | icon={{ ios: "chevron.down", android: "keyboard_arrow_down" }} |
| 253 | 253 | onPress={props.onScrollToEnd} |
| 254 | 254 | /> |
apps/mobile/src/features/threads/git/GitCommitSheet.tsx+8-6| ... | ... | @@ -164,7 +164,7 @@ export function GitCommitSheet(_props: GitCommitSheetProps) { |
| 164 | 164 | <Text |
| 165 | 165 | className={ |
| 166 | 166 | Platform.OS === "android" |
| 167 | ? "text-primary text-sm font-t3-medium" | |
| 167 | ? "text-primary-text text-sm font-t3-medium" | |
| 168 | 168 | : "text-foreground text-2xs font-t3-bold uppercase" |
| 169 | 169 | } |
| 170 | 170 | > |
| ... | ... | @@ -183,7 +183,7 @@ export function GitCommitSheet(_props: GitCommitSheetProps) { |
| 183 | 183 | <Text |
| 184 | 184 | className={ |
| 185 | 185 | Platform.OS === "android" |
| 186 | ? "text-primary text-sm font-t3-medium" | |
| 186 | ? "text-primary-text text-sm font-t3-medium" | |
| 187 | 187 | : "text-foreground text-2xs font-t3-bold uppercase" |
| 188 | 188 | } |
| 189 | 189 | > |
| ... | ... | @@ -204,10 +204,12 @@ export function GitCommitSheet(_props: GitCommitSheetProps) { |
| 204 | 204 | <Text className="text-foreground flex-1 text-sm font-medium" numberOfLines={1}> |
| 205 | 205 | {file.path} |
| 206 | 206 | </Text> |
| 207 | <Text className="text-xs font-t3-bold text-emerald-500"> | |
| 207 | <Text className="text-xs font-t3-bold text-adaptive-emerald-700-300"> | |
| 208 | 208 | +{file.insertions} |
| 209 | 209 | </Text> |
| 210 | <Text className="text-xs font-t3-bold text-rose-500">-{file.deletions}</Text> | |
| 210 | <Text className="text-xs font-t3-bold text-adaptive-rose-700-300"> | |
| 211 | -{file.deletions} | |
| 212 | </Text> | |
| 211 | 213 | </View> |
| 212 | 214 | ))} |
| 213 | 215 | {selectedFiles.length > selectedFilePreview.length ? ( |
| ... | ... | @@ -286,10 +288,10 @@ export function GitCommitSheet(_props: GitCommitSheetProps) { |
| 286 | 288 | ) : null} |
| 287 | 289 | </View> |
| 288 | 290 | <View className="items-end gap-1"> |
| 289 | <Text className="text-xs font-t3-bold text-emerald-500"> | |
| 291 | <Text className="text-xs font-t3-bold text-adaptive-emerald-700-300"> | |
| 290 | 292 | +{file.insertions} |
| 291 | 293 | </Text> |
| 292 | <Text className="text-xs font-t3-bold text-rose-500"> | |
| 294 | <Text className="text-xs font-t3-bold text-adaptive-rose-700-300"> | |
| 293 | 295 | -{file.deletions} |
| 294 | 296 | </Text> |
| 295 | 297 | </View> |
apps/mobile/src/features/threads/sidebar-navigation-shell.tsx+1-1| ... | ... | @@ -52,7 +52,7 @@ const SidebarStack = createNativeStackNavigator(); |
| 52 | 52 | * navigation hooks used for header configuration inside the pane. |
| 53 | 53 | */ |
| 54 | 54 | export function SidebarNavigationShell(props: { readonly children: ReactNode }) { |
| 55 | const navigationTheme = useMobileNavigationTheme(); | |
| 55 | const navigationTheme = useMobileNavigationTheme("sidebar"); | |
| 56 | 56 | |
| 57 | 57 | return ( |
| 58 | 58 | <NavigationIndependentTree> |
apps/mobile/src/features/threads/thread-list-items.tsx+76-95| ... | ... | @@ -157,7 +157,7 @@ export const ThreadListGroupHeader = memo(function ThreadListGroupHeader(props: |
| 157 | 157 | className={ |
| 158 | 158 | compact |
| 159 | 159 | ? "flex-shrink text-base font-t3-bold tracking-[0.2px] text-foreground-muted" |
| 160 | : "flex-shrink text-sm font-t3-bold tracking-[0.2px] text-foreground-muted" | |
| 160 | : "flex-shrink text-sm font-t3-bold tracking-[0.2px] text-drawer-foreground-muted" | |
| 161 | 161 | } |
| 162 | 162 | numberOfLines={1} |
| 163 | 163 | > |
| ... | ... | @@ -167,7 +167,7 @@ export const ThreadListGroupHeader = memo(function ThreadListGroupHeader(props: |
| 167 | 167 | className={ |
| 168 | 168 | compact |
| 169 | 169 | ? "flex-1 text-sm font-t3-medium text-foreground-tertiary" |
| 170 | : "flex-1 text-xs font-t3-medium text-foreground-tertiary" | |
| 170 | : "flex-1 text-xs font-t3-medium text-drawer-foreground-muted" | |
| 171 | 171 | } |
| 172 | 172 | > |
| 173 | 173 | {props.threadCount} |
| ... | ... | @@ -241,7 +241,7 @@ export const ThreadListShowMoreRow = memo(function ThreadListShowMoreRow(props: |
| 241 | 241 | className={ |
| 242 | 242 | compact |
| 243 | 243 | ? "text-sm font-t3-medium text-foreground-muted" |
| 244 | : "text-xs font-t3-medium text-foreground-muted" | |
| 244 | : "text-xs font-t3-medium text-drawer-foreground-muted" | |
| 245 | 245 | } |
| 246 | 246 | > |
| 247 | 247 | {label} |
| ... | ... | @@ -320,7 +320,14 @@ export const PendingTaskListRow = memo(function PendingTaskListRow(props: { |
| 320 | 320 | </View> |
| 321 | 321 | ) : ( |
| 322 | 322 | <View className="rounded-full bg-subtle px-1.5 py-0.5"> |
| 323 | <Text className="text-3xs font-t3-bold text-foreground-muted">Pending</Text> | |
| 323 | <Text | |
| 324 | className={cn( | |
| 325 | "text-3xs font-t3-bold text-foreground-muted", | |
| 326 | !compact && "text-drawer-foreground-muted", | |
| 327 | )} | |
| 328 | > | |
| 329 | Pending | |
| 330 | </Text> | |
| 324 | 331 | </View> |
| 325 | 332 | ); |
| 326 | 333 | |
| ... | ... | @@ -330,21 +337,21 @@ export const PendingTaskListRow = memo(function PendingTaskListRow(props: { |
| 330 | 337 | <SymbolView |
| 331 | 338 | name={isDraft ? "square.and.pencil" : "tray.and.arrow.up"} |
| 332 | 339 | size={10} |
| 333 | tintColorClassName={compact ? "accent-icon-subtle" : "accent-foreground-muted"} | |
| 340 | tintColorClassName={compact ? "accent-icon-subtle" : "accent-drawer-foreground-muted"} | |
| 334 | 341 | type="monochrome" |
| 335 | 342 | /> |
| 336 | 343 | {props.environmentLabel && props.environmentMachine ? ( |
| 337 | 344 | <EnvironmentMachineSymbol |
| 338 | 345 | kind={props.environmentMachine} |
| 339 | 346 | size={compact ? 12 : 10} |
| 340 | tintColorClassName={compact ? "accent-icon-subtle" : "accent-foreground-muted"} | |
| 347 | tintColorClassName={compact ? "accent-icon-subtle" : "accent-drawer-foreground-muted"} | |
| 341 | 348 | /> |
| 342 | 349 | ) : null} |
| 343 | 350 | <Text |
| 344 | 351 | className={ |
| 345 | 352 | compact |
| 346 | 353 | ? "shrink text-sm text-foreground-muted" |
| 347 | : "shrink text-xs text-foreground-muted" | |
| 354 | : "shrink text-xs text-drawer-foreground-muted" | |
| 348 | 355 | } |
| 349 | 356 | numberOfLines={1} |
| 350 | 357 | > |
| ... | ... | @@ -369,13 +376,26 @@ export const PendingTaskListRow = memo(function PendingTaskListRow(props: { |
| 369 | 376 | <View className="pr-[18px] pt-[10px]" style={{ paddingLeft: THREAD_LIST_COMPACT_INSET }}> |
| 370 | 377 | <View className={cn("gap-[3px] pb-[10px]", !props.isLast && "border-b border-separator")}> |
| 371 | 378 | <View className="flex-row items-center justify-between gap-2"> |
| 372 | <Text className="flex-1 text-lg font-t3-bold text-foreground" numberOfLines={1}> | |
| 379 | <Text | |
| 380 | className={cn( | |
| 381 | "flex-1 text-lg font-t3-bold text-foreground", | |
| 382 | !compact && "text-drawer-foreground", | |
| 383 | )} | |
| 384 | numberOfLines={1} | |
| 385 | > | |
| 373 | 386 | {pendingTask.title} |
| 374 | 387 | </Text> |
| 375 | 388 | <View className="flex-row items-center gap-2"> |
| 376 | 389 | {statusPill} |
| 377 | 390 | {timestamp !== null ? ( |
| 378 | <Text className="text-base tabular-nums text-foreground-tertiary">{timestamp}</Text> | |
| 391 | <Text | |
| 392 | className={cn( | |
| 393 | "text-base tabular-nums text-foreground-tertiary", | |
| 394 | !compact && "text-drawer-foreground-muted", | |
| 395 | )} | |
| 396 | > | |
| 397 | {timestamp} | |
| 398 | </Text> | |
| 379 | 399 | ) : null} |
| 380 | 400 | <SymbolView |
| 381 | 401 | name="chevron.right" |
| ... | ... | @@ -395,6 +415,7 @@ export const PendingTaskListRow = memo(function PendingTaskListRow(props: { |
| 395 | 415 | accessibilityHint={accessibilityHint} |
| 396 | 416 | accessibilityLabel={pendingTask.title} |
| 397 | 417 | accessibilityRole="button" |
| 418 | interactionClassName="bg-thread-hover" | |
| 398 | 419 | onPress={() => onSelectPendingTask(pendingTask)} |
| 399 | 420 | style={{ |
| 400 | 421 | borderRadius: SIDEBAR_ROW_RADIUS, |
| ... | ... | @@ -406,13 +427,22 @@ export const PendingTaskListRow = memo(function PendingTaskListRow(props: { |
| 406 | 427 | > |
| 407 | 428 | <View className="gap-[3px]"> |
| 408 | 429 | <View className="flex-row items-center justify-between gap-2"> |
| 409 | <Text className="flex-1 text-base font-t3-medium text-foreground" numberOfLines={1}> | |
| 430 | <Text | |
| 431 | className="flex-1 text-base font-t3-medium text-drawer-foreground" | |
| 432 | numberOfLines={1} | |
| 433 | > | |
| 410 | 434 | {pendingTask.title} |
| 411 | 435 | </Text> |
| 412 | 436 | <View className="flex-row items-center gap-2"> |
| 413 | 437 | {statusPill} |
| 414 | 438 | {timestamp !== null ? ( |
| 415 | <Text className="text-xs tabular-nums text-foreground-muted" numberOfLines={1}> | |
| 439 | <Text | |
| 440 | className={cn( | |
| 441 | "text-xs tabular-nums text-foreground-muted", | |
| 442 | !compact && "text-drawer-foreground-muted", | |
| 443 | )} | |
| 444 | numberOfLines={1} | |
| 445 | > | |
| 416 | 446 | {timestamp} |
| 417 | 447 | </Text> |
| 418 | 448 | ) : null} |
| ... | ... | @@ -477,10 +507,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 477 | 507 | const theme = useUniwindTheme(); |
| 478 | 508 | const screenColor = theme["--color-screen"]; |
| 479 | 509 | const drawerColor = theme["--color-drawer"]; |
| 480 | const selectedBackgroundColor = theme["--color-user-bubble"]; | |
| 481 | const materialSelectedBackgroundColor = theme["--color-thread-selected"]; | |
| 482 | const materialSelectedForegroundColor = theme["--color-thread-selected-foreground"]; | |
| 483 | const selectedForegroundColor = theme["--color-user-bubble-foreground"]; | |
| 510 | const selectedBackgroundColor = theme["--color-thread-selected"]; | |
| 484 | 511 | |
| 485 | 512 | const { |
| 486 | 513 | thread, |
| ... | ... | @@ -508,24 +535,6 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 508 | 535 | ); |
| 509 | 536 | |
| 510 | 537 | const backgroundColor = compact ? screenColor : drawerColor; |
| 511 | const effectiveSelectedBackground = | |
| 512 | Platform.OS === "android" ? materialSelectedBackgroundColor : selectedBackgroundColor; | |
| 513 | const effectiveSelectedForeground = | |
| 514 | Platform.OS === "android" ? materialSelectedForegroundColor : selectedForegroundColor; | |
| 515 | const effectiveStatus = | |
| 516 | visuallySelected && status | |
| 517 | ? { | |
| 518 | ...status, | |
| 519 | pillClassName: | |
| 520 | Platform.OS === "android" | |
| 521 | ? "bg-thread-selected-foreground/20" | |
| 522 | : "bg-user-bubble-foreground/20", | |
| 523 | textClassName: | |
| 524 | Platform.OS === "android" | |
| 525 | ? "text-thread-selected-foreground" | |
| 526 | : "text-user-bubble-foreground", | |
| 527 | } | |
| 528 | : status; | |
| 529 | 538 | |
| 530 | 539 | const handleDelete = useCallback(() => onDeleteThread(thread), [onDeleteThread, thread]); |
| 531 | 540 | const handleArchive = useCallback(() => onArchiveThread(thread), [onArchiveThread, thread]); |
| ... | ... | @@ -580,11 +589,9 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 580 | 589 | [handleArchive, handleDelete, handleRegenerateTitle, handleRename, onNewThreadOnBranch, thread], |
| 581 | 590 | ); |
| 582 | 591 | |
| 583 | const statusPill = effectiveStatus ? ( | |
| 584 | <View className={`${effectiveStatus.pillClassName} rounded-full px-1.5 py-0.5`}> | |
| 585 | <Text className={`text-3xs font-t3-bold ${effectiveStatus.textClassName}`}> | |
| 586 | {effectiveStatus.label} | |
| 587 | </Text> | |
| 592 | const statusPill = status ? ( | |
| 593 | <View className={`${status.pillClassName} rounded-full px-1.5 py-0.5`}> | |
| 594 | <Text className={`text-3xs font-t3-bold ${status.textClassName}`}>{status.label}</Text> | |
| 588 | 595 | </View> |
| 589 | 596 | ) : null; |
| 590 | 597 | |
| ... | ... | @@ -601,10 +608,8 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 601 | 608 | compact |
| 602 | 609 | ? "accent-icon-subtle" |
| 603 | 610 | : selected |
| 604 | ? Platform.OS === "android" | |
| 605 | ? "accent-thread-selected-foreground-muted" | |
| 606 | : "accent-user-bubble-foreground-muted" | |
| 607 | : "accent-foreground-muted" | |
| 611 | ? "accent-thread-selected-foreground-muted" | |
| 612 | : "accent-drawer-foreground-muted" | |
| 608 | 613 | } |
| 609 | 614 | /> |
| 610 | 615 | ) : null} |
| ... | ... | @@ -613,10 +618,10 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 613 | 618 | "shrink", |
| 614 | 619 | compact ? "text-sm" : "text-xs", |
| 615 | 620 | visuallySelected |
| 616 | ? Platform.OS === "android" | |
| 617 | ? "text-thread-selected-foreground-muted" | |
| 618 | : "text-user-bubble-foreground-muted" | |
| 619 | : "text-foreground-muted", | |
| 621 | ? "text-thread-selected-foreground-muted" | |
| 622 | : compact | |
| 623 | ? "text-foreground-muted" | |
| 624 | : "text-drawer-foreground-muted", | |
| 620 | 625 | )} |
| 621 | 626 | numberOfLines={1} |
| 622 | 627 | > |
| ... | ... | @@ -633,30 +638,16 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 633 | 638 | <SymbolView |
| 634 | 639 | name="square.3.layers.3d" |
| 635 | 640 | size={compact ? 13 : 11} |
| 636 | tintColor={ | |
| 637 | visuallySelected | |
| 638 | ? String(effectiveSelectedForeground) | |
| 639 | : pullRequestTintColor(pr, colorScheme) | |
| 640 | } | |
| 641 | tintColor={pullRequestTintColor(pr, colorScheme)} | |
| 641 | 642 | /> |
| 642 | 643 | ) : ( |
| 643 | 644 | <PullRequestIcon |
| 644 | 645 | size={compact ? 13 : 11} |
| 645 | color={ | |
| 646 | visuallySelected | |
| 647 | ? String(effectiveSelectedForeground) | |
| 648 | : pullRequestTintColor(pr, colorScheme) | |
| 649 | } | |
| 646 | color={pullRequestTintColor(pr, colorScheme)} | |
| 650 | 647 | /> |
| 651 | 648 | )} |
| 652 | 649 | <Text |
| 653 | className={`${compact ? "text-sm" : "text-xs"} font-t3-medium ${ | |
| 654 | visuallySelected | |
| 655 | ? Platform.OS === "android" | |
| 656 | ? "text-thread-selected-foreground" | |
| 657 | : "text-user-bubble-foreground" | |
| 658 | : pr.textClassName | |
| 659 | }`} | |
| 650 | className={`${compact ? "text-sm" : "text-xs"} font-t3-medium ${pr.textClassName}`} | |
| 660 | 651 | > |
| 661 | 652 | {pr.label} |
| 662 | 653 | </Text> |
| ... | ... | @@ -669,13 +660,8 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 669 | 660 | compact ? ( |
| 670 | 661 | <RowPressable |
| 671 | 662 | key={`${thread.environmentId}:${thread.id}`} |
| 672 | interactionClassName={ | |
| 673 | visuallySelected | |
| 674 | ? Platform.OS === "android" | |
| 675 | ? "bg-thread-selected-foreground" | |
| 676 | : "bg-user-bubble-foreground" | |
| 677 | : "bg-primary" | |
| 678 | } | |
| 663 | interactionClassName="bg-row-hover" | |
| 664 | interactionOpacity={visuallySelected ? 0 : 1} | |
| 679 | 665 | accessibilityHint="Swipe left for archive and delete actions" |
| 680 | 666 | accessibilityLabel={threadAccessibilityLabel} |
| 681 | 667 | accessibilityRole="button" |
| ... | ... | @@ -683,7 +669,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 683 | 669 | style={ |
| 684 | 670 | Platform.OS === "android" |
| 685 | 671 | ? { |
| 686 | backgroundColor: visuallySelected ? effectiveSelectedBackground : backgroundColor, | |
| 672 | backgroundColor: visuallySelected ? selectedBackgroundColor : backgroundColor, | |
| 687 | 673 | borderRadius: 20, |
| 688 | 674 | } |
| 689 | 675 | : undefined |
| ... | ... | @@ -705,10 +691,10 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 705 | 691 | className={cn( |
| 706 | 692 | "flex-1 text-lg font-t3-bold", |
| 707 | 693 | visuallySelected |
| 708 | ? Platform.OS === "android" | |
| 709 | ? "text-thread-selected-foreground" | |
| 710 | : "text-user-bubble-foreground" | |
| 711 | : "text-foreground", | |
| 694 | ? "text-thread-selected-foreground" | |
| 695 | : compact | |
| 696 | ? "text-foreground" | |
| 697 | : "text-drawer-foreground", | |
| 712 | 698 | )} |
| 713 | 699 | numberOfLines={1} |
| 714 | 700 | > |
| ... | ... | @@ -723,10 +709,8 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 723 | 709 | className={cn( |
| 724 | 710 | "text-base tabular-nums", |
| 725 | 711 | visuallySelected |
| 726 | ? Platform.OS === "android" | |
| 727 | ? "text-thread-selected-foreground-muted" | |
| 728 | : "text-user-bubble-foreground-muted" | |
| 729 | : "text-foreground-tertiary", | |
| 712 | ? "text-thread-selected-foreground-muted" | |
| 713 | : "text-drawer-foreground-muted", | |
| 730 | 714 | )} |
| 731 | 715 | > |
| 732 | 716 | {timestamp} |
| ... | ... | @@ -742,6 +726,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 742 | 726 | {props.searchMatch ? ( |
| 743 | 727 | <ThreadSearchMatchExcerpt |
| 744 | 728 | compact |
| 729 | sidebar={!compact} | |
| 745 | 730 | match={props.searchMatch} |
| 746 | 731 | query={props.searchQuery ?? ""} |
| 747 | 732 | selected={visuallySelected} |
| ... | ... | @@ -754,13 +739,8 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 754 | 739 | ) : ( |
| 755 | 740 | <RowPressable |
| 756 | 741 | key={`${thread.environmentId}:${thread.id}`} |
| 757 | interactionClassName={ | |
| 758 | visuallySelected | |
| 759 | ? Platform.OS === "android" | |
| 760 | ? "bg-thread-selected-foreground" | |
| 761 | : "bg-user-bubble-foreground" | |
| 762 | : "bg-primary" | |
| 763 | } | |
| 742 | interactionClassName="bg-thread-hover" | |
| 743 | interactionOpacity={visuallySelected ? 0 : 1} | |
| 764 | 744 | accessibilityHint="Opens the thread" |
| 765 | 745 | accessibilityLabel={threadAccessibilityLabel} |
| 766 | 746 | accessibilityRole="button" |
| ... | ... | @@ -770,7 +750,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 770 | 750 | onSelectThread(thread); |
| 771 | 751 | }} |
| 772 | 752 | style={{ |
| 773 | backgroundColor: visuallySelected ? effectiveSelectedBackground : backgroundColor, | |
| 753 | backgroundColor: visuallySelected ? selectedBackgroundColor : backgroundColor, | |
| 774 | 754 | borderRadius: Platform.OS === "android" ? 20 : SIDEBAR_ROW_RADIUS, |
| 775 | 755 | minHeight: 64, |
| 776 | 756 | justifyContent: "center", |
| ... | ... | @@ -784,10 +764,10 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 784 | 764 | className={cn( |
| 785 | 765 | "flex-1 text-base font-t3-medium", |
| 786 | 766 | visuallySelected |
| 787 | ? Platform.OS === "android" | |
| 788 | ? "text-thread-selected-foreground" | |
| 789 | : "text-user-bubble-foreground" | |
| 790 | : "text-foreground", | |
| 767 | ? "text-thread-selected-foreground" | |
| 768 | : compact | |
| 769 | ? "text-foreground" | |
| 770 | : "text-drawer-foreground", | |
| 791 | 771 | )} |
| 792 | 772 | numberOfLines={1} |
| 793 | 773 | > |
| ... | ... | @@ -802,10 +782,10 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 802 | 782 | className={cn( |
| 803 | 783 | "text-xs tabular-nums", |
| 804 | 784 | visuallySelected |
| 805 | ? Platform.OS === "android" | |
| 806 | ? "text-thread-selected-foreground-muted" | |
| 807 | : "text-user-bubble-foreground-muted" | |
| 808 | : "text-foreground-muted", | |
| 785 | ? "text-thread-selected-foreground-muted" | |
| 786 | : compact | |
| 787 | ? "text-foreground-muted" | |
| 788 | : "text-drawer-foreground-muted", | |
| 809 | 789 | )} |
| 810 | 790 | numberOfLines={1} |
| 811 | 791 | > |
| ... | ... | @@ -815,6 +795,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: { |
| 815 | 795 | </View> |
| 816 | 796 | {props.searchMatch ? ( |
| 817 | 797 | <ThreadSearchMatchExcerpt |
| 798 | sidebar={!compact} | |
| 818 | 799 | match={props.searchMatch} |
| 819 | 800 | query={props.searchQuery ?? ""} |
| 820 | 801 | selected={visuallySelected} |
apps/mobile/src/features/threads/thread-list-v2-items.tsx+109-40| ... | ... | @@ -64,7 +64,7 @@ const STATUS_LABEL_BY_STATUS: Partial< |
| 64 | 64 | Record<ThreadListV2Status, { label: string; className: string }> |
| 65 | 65 | > = { |
| 66 | 66 | approval: { label: "Approval", className: "text-warning-foreground" }, |
| 67 | input: { label: "Input", className: "text-foreground-secondary" }, | |
| 67 | input: { label: "Input", className: "text-adaptive-indigo-600-300" }, | |
| 68 | 68 | working: { label: "Working", className: "text-adaptive-sky-600-400" }, |
| 69 | 69 | failed: { label: "Failed", className: "text-danger-foreground" }, |
| 70 | 70 | }; |
| ... | ... | @@ -112,6 +112,7 @@ function ThreadListV2Section(props: { |
| 112 | 112 | }; |
| 113 | 113 | }) { |
| 114 | 114 | const snoozed = props.tone === "snoozed"; |
| 115 | const sidebarPane = props.pane === "sidebar"; | |
| 115 | 116 | const className = cn( |
| 116 | 117 | "mb-1.5 mt-4 flex-row items-center gap-2.5", |
| 117 | 118 | props.pane === "sidebar" ? "px-3" : "px-5", |
| ... | ... | @@ -121,17 +122,32 @@ function ThreadListV2Section(props: { |
| 121 | 122 | <Text |
| 122 | 123 | className={cn( |
| 123 | 124 | "text-xs font-t3-medium", |
| 124 | snoozed ? "text-foreground-secondary" : "text-foreground-tertiary", | |
| 125 | sidebarPane | |
| 126 | ? "text-drawer-foreground-muted" | |
| 127 | : snoozed | |
| 128 | ? "text-foreground-secondary" | |
| 129 | : "text-foreground-tertiary", | |
| 125 | 130 | )} |
| 126 | 131 | > |
| 127 | 132 | {props.label} |
| 128 | 133 | </Text> |
| 129 | <View className={cn("h-px flex-1", snoozed ? "bg-primary/20" : "bg-border")} /> | |
| 134 | <View | |
| 135 | className={cn( | |
| 136 | "h-px flex-1", | |
| 137 | snoozed ? "bg-primary/20" : sidebarPane ? "bg-drawer-border" : "bg-border", | |
| 138 | )} | |
| 139 | /> | |
| 130 | 140 | {props.disclosure ? ( |
| 131 | 141 | <SymbolView |
| 132 | 142 | name="chevron.down" |
| 133 | 143 | size={10} |
| 134 | tintColorClassName={snoozed ? "accent-icon-muted" : "accent-foreground-muted"} | |
| 144 | tintColorClassName={ | |
| 145 | sidebarPane | |
| 146 | ? "accent-drawer-foreground-muted" | |
| 147 | : snoozed | |
| 148 | ? "accent-icon-muted" | |
| 149 | : "accent-foreground-muted" | |
| 150 | } | |
| 135 | 151 | type="monochrome" |
| 136 | 152 | style={{ transform: [{ rotate: props.disclosure.expanded ? "180deg" : "0deg" }] }} |
| 137 | 153 | /> |
| ... | ... | @@ -209,6 +225,7 @@ export const ThreadListV2SettledShelfHeader = memo(function ThreadListV2SettledS |
| 209 | 225 | }); |
| 210 | 226 | |
| 211 | 227 | export const ThreadListV2ShowMoreRow = memo(function ThreadListV2ShowMoreRow(props: { |
| 228 | readonly pane?: "screen" | "sidebar"; | |
| 212 | 229 | readonly hiddenCount: number; |
| 213 | 230 | readonly onPress: () => void; |
| 214 | 231 | }) { |
| ... | ... | @@ -220,7 +237,13 @@ export const ThreadListV2ShowMoreRow = memo(function ThreadListV2ShowMoreRow(pro |
| 220 | 237 | className="mx-4 mt-2 items-center rounded-lg border border-dashed border-border py-2.5" |
| 221 | 238 | style={({ pressed }) => ({ opacity: pressed ? 0.6 : 1 })} |
| 222 | 239 | > |
| 223 | <Text className="text-xs font-t3-medium text-foreground-muted"> | |
| 240 | <Text | |
| 241 | className={ | |
| 242 | props.pane === "sidebar" | |
| 243 | ? "text-xs font-t3-medium text-drawer-foreground-muted" | |
| 244 | : "text-xs font-t3-medium text-foreground-muted" | |
| 245 | } | |
| 246 | > | |
| 224 | 247 | Show more ({props.hiddenCount} settled hidden) |
| 225 | 248 | </Text> |
| 226 | 249 | </Pressable> |
| ... | ... | @@ -282,7 +305,13 @@ export const ThreadListV2PendingRow = memo(function ThreadListV2PendingRow(props |
| 282 | 305 | workspaceRoot={props.project.workspaceRoot} |
| 283 | 306 | /> |
| 284 | 307 | ) : null} |
| 285 | <Text className="flex-1 text-sm font-t3-medium text-foreground-muted" numberOfLines={1}> | |
| 308 | <Text | |
| 309 | className={cn( | |
| 310 | "flex-1 text-sm font-t3-medium text-foreground-muted", | |
| 311 | sidebarPane && "text-drawer-foreground-muted", | |
| 312 | )} | |
| 313 | numberOfLines={1} | |
| 314 | > | |
| 286 | 315 | {projectTitle} |
| 287 | 316 | </Text> |
| 288 | 317 | {isDraft ? ( |
| ... | ... | @@ -296,33 +325,67 @@ export const ThreadListV2PendingRow = memo(function ThreadListV2PendingRow(props |
| 296 | 325 | <Text className="text-xs text-adaptive-amber-700-300">Draft</Text> |
| 297 | 326 | </View> |
| 298 | 327 | ) : ( |
| 299 | <Text className="text-xs text-foreground-tertiary">Sends on reconnect</Text> | |
| 328 | <Text | |
| 329 | className={cn( | |
| 330 | "text-xs text-foreground-tertiary", | |
| 331 | sidebarPane && "text-drawer-foreground-muted", | |
| 332 | )} | |
| 333 | > | |
| 334 | Sends on reconnect | |
| 335 | </Text> | |
| 300 | 336 | )} |
| 301 | 337 | </View> |
| 302 | 338 | {/* One line, unlike the two an active row allows: a queued title is |
| 303 | 339 | derived from the whole prompt rather than written as a title, so the |
| 304 | 340 | second line is usually a stray word or emoji rather than meaning. */} |
| 305 | <Text className="mt-1 text-base font-t3-medium text-foreground" numberOfLines={1}> | |
| 341 | <Text | |
| 342 | className={cn( | |
| 343 | "mt-1 text-base font-t3-medium text-foreground", | |
| 344 | sidebarPane && "text-drawer-foreground", | |
| 345 | )} | |
| 346 | numberOfLines={1} | |
| 347 | > | |
| 306 | 348 | {pendingTask.title} |
| 307 | 349 | </Text> |
| 308 | 350 | {branch || props.environmentLabel ? ( |
| 309 | 351 | <View className="mt-1 flex-row items-center gap-1"> |
| 310 | <Text className="shrink text-xs text-foreground-muted" numberOfLines={1}> | |
| 352 | <Text | |
| 353 | className={cn( | |
| 354 | "shrink text-xs text-foreground-muted", | |
| 355 | sidebarPane && "text-drawer-foreground-muted", | |
| 356 | )} | |
| 357 | numberOfLines={1} | |
| 358 | > | |
| 311 | 359 | {branch ? ( |
| 312 | <Text className="text-xs text-foreground-muted" style={{ fontFamily: MONO_FONT }}> | |
| 360 | <Text | |
| 361 | className={cn( | |
| 362 | "text-xs text-foreground-muted", | |
| 363 | sidebarPane && "text-drawer-foreground-muted", | |
| 364 | )} | |
| 365 | style={{ fontFamily: MONO_FONT }} | |
| 366 | > | |
| 313 | 367 | {branch} |
| 314 | 368 | </Text> |
| 315 | 369 | ) : null} |
| 316 | 370 | {branch && props.environmentLabel ? " · " : null} |
| 317 | 371 | {props.environmentLabel ? ( |
| 318 | <Text className="text-xs text-foreground-tertiary">{props.environmentLabel}</Text> | |
| 372 | <Text | |
| 373 | className={cn( | |
| 374 | "text-xs text-foreground-tertiary", | |
| 375 | sidebarPane && "text-drawer-foreground-muted", | |
| 376 | )} | |
| 377 | > | |
| 378 | {props.environmentLabel} | |
| 379 | </Text> | |
| 319 | 380 | ) : null} |
| 320 | 381 | </Text> |
| 321 | 382 | {props.environmentLabel && props.environmentMachine ? ( |
| 322 | 383 | <EnvironmentMachineSymbol |
| 323 | 384 | kind={props.environmentMachine} |
| 324 | 385 | size={11} |
| 325 | tintColorClassName="accent-foreground-tertiary" | |
| 386 | tintColorClassName={ | |
| 387 | sidebarPane ? "accent-drawer-foreground-muted" : "accent-foreground-tertiary" | |
| 388 | } | |
| 326 | 389 | /> |
| 327 | 390 | ) : null} |
| 328 | 391 | </View> |
| ... | ... | @@ -350,6 +413,7 @@ export const ThreadListV2PendingRow = memo(function ThreadListV2PendingRow(props |
| 350 | 413 | accessibilityRole="button" |
| 351 | 414 | key={pendingTask.key} |
| 352 | 415 | className={sidebarPane ? "bg-drawer" : "bg-screen"} |
| 416 | interactionClassName={sidebarPane ? "bg-thread-hover" : "bg-row-hover"} | |
| 353 | 417 | onPress={() => onSelectPendingTask(pendingTask)} |
| 354 | 418 | style={ |
| 355 | 419 | sidebarPane |
| ... | ... | @@ -759,8 +823,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 759 | 823 | ? `Opens the thread. Swipe left to ${primaryAction.label.toLowerCase()}.` |
| 760 | 824 | : `Opens the thread. Swipe left for ${primaryAction.label.toLowerCase()} and snooze actions.`; |
| 761 | 825 | |
| 762 | // The sidebar pane fills selected rows with the theme's message surface, so | |
| 763 | // every piece of row text must use that surface's paired foreground. | |
| 826 | // Sidebar rows use navigation foregrounds on their active and idle surfaces. | |
| 764 | 827 | const cardContent = ( |
| 765 | 828 | <> |
| 766 | 829 | <View className="flex-row items-center gap-1.5"> |
| ... | ... | @@ -776,7 +839,9 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 776 | 839 | <Text |
| 777 | 840 | className={cn( |
| 778 | 841 | "flex-1 text-sm font-t3-medium", |
| 779 | selected ? selectedThreadRowColors.mutedForegroundClassName : "text-foreground-muted", | |
| 842 | selected | |
| 843 | ? selectedThreadRowColors.mutedForegroundClassName | |
| 844 | : rowAppearance.mutedForegroundClassName, | |
| 780 | 845 | )} |
| 781 | 846 | numberOfLines={1} |
| 782 | 847 | > |
| ... | ... | @@ -787,16 +852,17 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 787 | 852 | <SymbolView |
| 788 | 853 | name="pin" |
| 789 | 854 | size={11} |
| 790 | tintColorClassName="accent-foreground-muted" | |
| 855 | tintColorClassName={rowAppearance.mutedIconTintClassName} | |
| 791 | 856 | type="monochrome" |
| 792 | 857 | /> |
| 793 | 858 | ) : null} |
| 794 | 859 | <Text |
| 795 | 860 | className={cn( |
| 796 | 861 | "text-xs tabular-nums", |
| 797 | selected | |
| 798 | ? selectedThreadRowColors.foregroundClassName | |
| 799 | : (statusLabel?.className ?? "text-foreground-tertiary"), | |
| 862 | statusLabel?.className ?? | |
| 863 | (selected | |
| 864 | ? selectedThreadRowColors.foregroundClassName | |
| 865 | : rowAppearance.tertiaryForegroundClassName), | |
| 800 | 866 | )} |
| 801 | 867 | > |
| 802 | 868 | {statusLabel?.label ?? timeLabel} |
| ... | ... | @@ -805,7 +871,9 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 805 | 871 | <Text |
| 806 | 872 | className={cn( |
| 807 | 873 | "mt-1 text-base font-t3-medium", |
| 808 | selected ? selectedThreadRowColors.foregroundClassName : "text-foreground", | |
| 874 | selected | |
| 875 | ? selectedThreadRowColors.foregroundClassName | |
| 876 | : rowAppearance.foregroundClassName, | |
| 809 | 877 | )} |
| 810 | 878 | numberOfLines={2} |
| 811 | 879 | > |
| ... | ... | @@ -814,6 +882,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 814 | 882 | {props.searchMatch ? ( |
| 815 | 883 | <View className="mt-1"> |
| 816 | 884 | <ThreadSearchMatchExcerpt |
| 885 | sidebar={sidebarPane} | |
| 817 | 886 | match={props.searchMatch} |
| 818 | 887 | query={props.searchQuery ?? ""} |
| 819 | 888 | selected={selected} |
| ... | ... | @@ -846,7 +915,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 846 | 915 | "shrink text-xs", |
| 847 | 916 | selected |
| 848 | 917 | ? selectedThreadRowColors.mutedForegroundClassName |
| 849 | : "text-foreground-muted", | |
| 918 | : rowAppearance.mutedForegroundClassName, | |
| 850 | 919 | )} |
| 851 | 920 | numberOfLines={1} |
| 852 | 921 | > |
| ... | ... | @@ -856,7 +925,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 856 | 925 | "text-xs", |
| 857 | 926 | selected |
| 858 | 927 | ? selectedThreadRowColors.mutedForegroundClassName |
| 859 | : "text-foreground-muted", | |
| 928 | : rowAppearance.mutedForegroundClassName, | |
| 860 | 929 | )} |
| 861 | 930 | style={{ fontFamily: MONO_FONT }} |
| 862 | 931 | > |
| ... | ... | @@ -870,7 +939,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 870 | 939 | "text-xs", |
| 871 | 940 | selected |
| 872 | 941 | ? selectedThreadRowColors.mutedForegroundClassName |
| 873 | : "text-foreground-tertiary", | |
| 942 | : rowAppearance.tertiaryForegroundClassName, | |
| 874 | 943 | )} |
| 875 | 944 | > |
| 876 | 945 | {props.environmentLabel} |
| ... | ... | @@ -884,7 +953,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 884 | 953 | tintColorClassName={ |
| 885 | 954 | selected |
| 886 | 955 | ? selectedThreadRowColors.mutedIconTintClassName |
| 887 | : "accent-foreground-tertiary" | |
| 956 | : rowAppearance.tertiaryIconTintClassName | |
| 888 | 957 | } |
| 889 | 958 | /> |
| 890 | 959 | ) : null} |
| ... | ... | @@ -899,24 +968,19 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 899 | 968 | name={pr.kind === "stack" ? "square.3.layers.3d" : "arrow.triangle.pull"} |
| 900 | 969 | size={12} |
| 901 | 970 | tintColorClassName={ |
| 902 | selected | |
| 903 | ? selectedThreadRowColors.iconTintClassName | |
| 904 | : pr.state === null || pr.isDraft | |
| 905 | ? "accent-foreground-muted" | |
| 906 | : pr.state === "open" | |
| 907 | ? "accent-adaptive-emerald-600-400" | |
| 908 | : pr.state === "closed" | |
| 909 | ? "accent-adaptive-rose-600-400" | |
| 910 | : "accent-adaptive-violet-600-400" | |
| 971 | pr.state === null || pr.isDraft | |
| 972 | ? rowAppearance.mutedIconTintClassName | |
| 973 | : pr.state === "open" | |
| 974 | ? "accent-adaptive-emerald-600-400" | |
| 975 | : pr.state === "closed" | |
| 976 | ? "accent-adaptive-rose-600-400" | |
| 977 | : "accent-adaptive-violet-600-400" | |
| 911 | 978 | } |
| 912 | 979 | /> |
| 913 | 980 | ) : null} |
| 914 | 981 | <Text |
| 915 | 982 | accessibilityLabel={pr.accessibilityLabel} |
| 916 | className={cn( | |
| 917 | "text-xs", | |
| 918 | selected ? selectedThreadRowColors.foregroundClassName : pr.textClassName, | |
| 919 | )} | |
| 983 | className={cn("text-xs", pr.textClassName)} | |
| 920 | 984 | style={{ fontFamily: MONO_FONT }} |
| 921 | 985 | > |
| 922 | 986 | {pr.kind === "stack" || pr.others > 0 ? pr.label : `#${pr.label}`} |
| ... | ... | @@ -942,6 +1006,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 942 | 1006 | <RowPressable |
| 943 | 1007 | key={`${thread.environmentId}:${thread.id}`} |
| 944 | 1008 | interactionClassName={rowAppearance.interactionClassName} |
| 1009 | interactionOpacity={rowAppearance.interactionOpacity} | |
| 945 | 1010 | className={rowAppearance.className} |
| 946 | 1011 | accessibilityHint={swipeAccessibilityHint} |
| 947 | 1012 | accessibilityLabel={ |
| ... | ... | @@ -974,6 +1039,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 974 | 1039 | <RowPressable |
| 975 | 1040 | key={`${thread.environmentId}:${thread.id}`} |
| 976 | 1041 | interactionClassName={rowAppearance.interactionClassName} |
| 1042 | interactionOpacity={rowAppearance.interactionOpacity} | |
| 977 | 1043 | accessibilityHint={swipeAccessibilityHint} |
| 978 | 1044 | accessibilityLabel={ |
| 979 | 1045 | props.hasQueuedMessages ? `${thread.title}, messages queued to send` : thread.title |
| ... | ... | @@ -1009,7 +1075,9 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 1009 | 1075 | <Text |
| 1010 | 1076 | className={cn( |
| 1011 | 1077 | "text-base", |
| 1012 | selected ? selectedThreadRowColors.foregroundClassName : "text-foreground-muted", | |
| 1078 | selected | |
| 1079 | ? selectedThreadRowColors.foregroundClassName | |
| 1080 | : rowAppearance.mutedForegroundClassName, | |
| 1013 | 1081 | )} |
| 1014 | 1082 | numberOfLines={1} |
| 1015 | 1083 | > |
| ... | ... | @@ -1017,6 +1085,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 1017 | 1085 | </Text> |
| 1018 | 1086 | {props.searchMatch ? ( |
| 1019 | 1087 | <ThreadSearchMatchExcerpt |
| 1088 | sidebar={sidebarPane} | |
| 1020 | 1089 | match={props.searchMatch} |
| 1021 | 1090 | query={props.searchQuery ?? ""} |
| 1022 | 1091 | selected={selected} |
| ... | ... | @@ -1030,8 +1099,8 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 1030 | 1099 | selected |
| 1031 | 1100 | ? selectedThreadRowColors.mutedForegroundClassName |
| 1032 | 1101 | : snoozedRow |
| 1033 | ? "text-foreground-secondary" | |
| 1034 | : "text-foreground-tertiary", | |
| 1102 | ? rowAppearance.mutedForegroundClassName | |
| 1103 | : rowAppearance.tertiaryForegroundClassName, | |
| 1035 | 1104 | )} |
| 1036 | 1105 | style={{ fontFamily: MONO_FONT }} |
| 1037 | 1106 | > |
apps/mobile/src/features/threads/thread-list-v2-row-appearance.android.ts+19-5| ... | ... | @@ -22,8 +22,9 @@ export function getThreadListV2RowAppearance( |
| 22 | 22 | selected: boolean, |
| 23 | 23 | ) { |
| 24 | 24 | const selectedBackgroundColor = theme["--color-thread-selected"]; |
| 25 | const backgroundColor = theme[sidebarPane ? "--color-drawer" : "--color-screen"]; | |
| 25 | 26 | const style: ViewStyle = { |
| 26 | backgroundColor: selected ? selectedBackgroundColor : theme["--color-screen"], | |
| 27 | backgroundColor: selected ? selectedBackgroundColor : backgroundColor, | |
| 27 | 28 | borderRadius: 20, |
| 28 | 29 | }; |
| 29 | 30 | const swipeContainerStyle: ViewStyle = { |
| ... | ... | @@ -35,12 +36,25 @@ export function getThreadListV2RowAppearance( |
| 35 | 36 | |
| 36 | 37 | return { |
| 37 | 38 | className: undefined, |
| 38 | interactionClassName: selected ? "bg-thread-selected-foreground" : "bg-primary", | |
| 39 | interactionClassName: sidebarPane ? "bg-thread-hover" : "bg-row-hover", | |
| 40 | interactionOpacity: selected ? 0 : 1, | |
| 41 | foregroundClassName: sidebarPane ? "text-drawer-foreground" : "text-foreground", | |
| 42 | mutedForegroundClassName: sidebarPane | |
| 43 | ? "text-drawer-foreground-muted" | |
| 44 | : "text-foreground-muted", | |
| 45 | tertiaryForegroundClassName: sidebarPane | |
| 46 | ? "text-drawer-foreground-muted" | |
| 47 | : "text-foreground-tertiary", | |
| 48 | mutedIconTintClassName: sidebarPane | |
| 49 | ? "accent-drawer-foreground-muted" | |
| 50 | : "accent-foreground-muted", | |
| 51 | tertiaryIconTintClassName: sidebarPane | |
| 52 | ? "accent-drawer-foreground-muted" | |
| 53 | : "accent-foreground-tertiary", | |
| 39 | 54 | style, |
| 40 | 55 | cardStyle: sidebarPane ? { ...style, paddingHorizontal: 12, paddingVertical: 10 } : style, |
| 41 | 56 | swipeContainerStyle, |
| 42 | swipeBackgroundColor: theme["--color-screen"], | |
| 43 | providerIconSurfaceColor: | |
| 44 | sidebarPane && selected ? selectedBackgroundColor : theme["--color-screen"], | |
| 57 | swipeBackgroundColor: backgroundColor, | |
| 58 | providerIconSurfaceColor: selected ? selectedBackgroundColor : backgroundColor, | |
| 45 | 59 | }; |
| 46 | 60 | } |
apps/mobile/src/features/threads/thread-list-v2-row-appearance.test.ts created+37| ... | ... | @@ -0,0 +1,37 @@ |
| 1 | import { describe, expect, it } from "vite-plus/test"; | |
| 2 | import { MOBILE_THEME_IDS } from "@t3tools/shared/themePalettes"; | |
| 3 | ||
| 4 | import { getMobileThemeVariables } from "../../lib/mobileTheme"; | |
| 5 | import { getThreadListV2RowAppearance as iosAppearance } from "./thread-list-v2-row-appearance"; | |
| 6 | import { getThreadListV2RowAppearance as androidAppearance } from "./thread-list-v2-row-appearance.android"; | |
| 7 | ||
| 8 | describe.each([ | |
| 9 | ["ios", iosAppearance], | |
| 10 | ["android", androidAppearance], | |
| 11 | ] as const)("%s thread row colors", (_platform, appearanceFor) => { | |
| 12 | it.each(MOBILE_THEME_IDS)( | |
| 13 | "preserves active selection and uses neutral hover for %s", | |
| 14 | (themeId) => { | |
| 15 | for (const appearance of ["light", "dark"] as const) { | |
| 16 | const theme = getMobileThemeVariables(themeId, appearance); | |
| 17 | const idle = appearanceFor(theme, true, false); | |
| 18 | const active = appearanceFor(theme, true, true); | |
| 19 | ||
| 20 | expect(idle.style?.backgroundColor).toBe(theme["--color-drawer"]); | |
| 21 | expect(idle.interactionClassName).toBe("bg-thread-hover"); | |
| 22 | expect(idle.interactionOpacity).toBe(1); | |
| 23 | expect(active.style?.backgroundColor).toBe(theme["--color-thread-selected"]); | |
| 24 | // Pointer feedback must not mix a second color into the active background. | |
| 25 | expect(active.interactionOpacity).toBe(0); | |
| 26 | expect(active.providerIconSurfaceColor).toBe(active.style?.backgroundColor); | |
| 27 | expect(idle.foregroundClassName).toBe("text-drawer-foreground"); | |
| 28 | expect(idle.mutedForegroundClassName).toBe("text-drawer-foreground-muted"); | |
| 29 | ||
| 30 | const phone = appearanceFor(theme, false, false); | |
| 31 | expect(phone.swipeBackgroundColor).toBe(theme["--color-screen"]); | |
| 32 | expect(phone.interactionClassName).toBe("bg-row-hover"); | |
| 33 | expect(phone.foregroundClassName).toBe("text-foreground"); | |
| 34 | } | |
| 35 | }, | |
| 36 | ); | |
| 37 | }); |
apps/mobile/src/features/threads/thread-list-v2-row-appearance.ts+20-6| ... | ... | @@ -6,10 +6,10 @@ export const THREAD_LIST_V2_ROW_CONTENT_CLASS_NAME = "px-5 py-2.5"; |
| 6 | 6 | export const THREAD_LIST_V2_ROW_DIVIDERS = true; |
| 7 | 7 | |
| 8 | 8 | export const selectedThreadRowColors = { |
| 9 | foregroundClassName: "text-user-bubble-foreground", | |
| 10 | mutedForegroundClassName: "text-user-bubble-foreground-muted", | |
| 11 | iconTintClassName: "accent-user-bubble-foreground", | |
| 12 | mutedIconTintClassName: "accent-user-bubble-foreground-muted", | |
| 9 | foregroundClassName: "text-thread-selected-foreground", | |
| 10 | mutedForegroundClassName: "text-thread-selected-foreground-muted", | |
| 11 | iconTintClassName: "accent-thread-selected-foreground", | |
| 12 | mutedIconTintClassName: "accent-thread-selected-foreground-muted", | |
| 13 | 13 | }; |
| 14 | 14 | |
| 15 | 15 | export function getThreadListV2NewBranchMenuTitle(_branch: string) { |
| ... | ... | @@ -21,7 +21,7 @@ export function getThreadListV2RowAppearance( |
| 21 | 21 | sidebarPane: boolean, |
| 22 | 22 | selected: boolean, |
| 23 | 23 | ) { |
| 24 | const selectedBackgroundColor = theme["--color-user-bubble"]; | |
| 24 | const selectedBackgroundColor = theme["--color-thread-selected"]; | |
| 25 | 25 | const style: ViewStyle | undefined = sidebarPane |
| 26 | 26 | ? { |
| 27 | 27 | backgroundColor: selected ? selectedBackgroundColor : theme["--color-drawer"], |
| ... | ... | @@ -34,7 +34,21 @@ export function getThreadListV2RowAppearance( |
| 34 | 34 | |
| 35 | 35 | return { |
| 36 | 36 | className: sidebarPane ? undefined : "bg-screen", |
| 37 | interactionClassName: selected && sidebarPane ? "bg-user-bubble-foreground" : "bg-primary", | |
| 37 | interactionClassName: sidebarPane ? "bg-thread-hover" : "bg-row-hover", | |
| 38 | interactionOpacity: selected ? 0 : 1, | |
| 39 | foregroundClassName: sidebarPane ? "text-drawer-foreground" : "text-foreground", | |
| 40 | mutedForegroundClassName: sidebarPane | |
| 41 | ? "text-drawer-foreground-muted" | |
| 42 | : "text-foreground-muted", | |
| 43 | tertiaryForegroundClassName: sidebarPane | |
| 44 | ? "text-drawer-foreground-muted" | |
| 45 | : "text-foreground-tertiary", | |
| 46 | mutedIconTintClassName: sidebarPane | |
| 47 | ? "accent-drawer-foreground-muted" | |
| 48 | : "accent-foreground-muted", | |
| 49 | tertiaryIconTintClassName: sidebarPane | |
| 50 | ? "accent-drawer-foreground-muted" | |
| 51 | : "accent-foreground-tertiary", | |
| 38 | 52 | style, |
| 39 | 53 | cardStyle: sidebarPane ? { ...style, paddingHorizontal: 12, paddingVertical: 10 } : undefined, |
| 40 | 54 | swipeContainerStyle, |
apps/mobile/src/features/threads/thread-search-match.tsx+16-14| ... | ... | @@ -1,4 +1,3 @@ |
| 1 | import { Platform } from "react-native"; | |
| 2 | 1 | import type { EnvironmentThreadSearchMatch } from "@t3tools/client-runtime/state/thread-search"; |
| 3 | 2 | |
| 4 | 3 | import { AppText as Text } from "../../components/AppText"; |
| ... | ... | @@ -49,6 +48,7 @@ export function ThreadSearchMatchExcerpt(props: { |
| 49 | 48 | readonly query: string; |
| 50 | 49 | readonly selected?: boolean; |
| 51 | 50 | readonly compact?: boolean; |
| 51 | readonly sidebar?: boolean; | |
| 52 | 52 | }) { |
| 53 | 53 | const isUser = props.match.source === "user"; |
| 54 | 54 | const parts = splitHighlightParts(props.match.snippet, props.query); |
| ... | ... | @@ -57,10 +57,10 @@ export function ThreadSearchMatchExcerpt(props: { |
| 57 | 57 | className={cn( |
| 58 | 58 | props.compact ? "text-sm" : "text-xs", |
| 59 | 59 | props.selected |
| 60 | ? Platform.OS === "android" | |
| 61 | ? "text-thread-selected-foreground-muted" | |
| 62 | : "text-user-bubble-foreground-muted" | |
| 63 | : "text-foreground-muted", | |
| 60 | ? "text-thread-selected-foreground-muted" | |
| 61 | : props.sidebar | |
| 62 | ? "text-drawer-foreground-muted" | |
| 63 | : "text-foreground-muted", | |
| 64 | 64 | )} |
| 65 | 65 | numberOfLines={1} |
| 66 | 66 | > |
| ... | ... | @@ -68,11 +68,11 @@ export function ThreadSearchMatchExcerpt(props: { |
| 68 | 68 | className={cn( |
| 69 | 69 | props.compact ? "text-sm font-t3-medium" : "text-xs font-t3-medium", |
| 70 | 70 | props.selected |
| 71 | ? Platform.OS === "android" | |
| 72 | ? "text-thread-selected-foreground" | |
| 73 | : "text-user-bubble-foreground" | |
| 71 | ? "text-thread-selected-foreground" | |
| 74 | 72 | : isUser |
| 75 | ? "text-foreground-secondary" | |
| 73 | ? props.sidebar | |
| 74 | ? "text-drawer-foreground-muted" | |
| 75 | : "text-foreground-secondary" | |
| 76 | 76 | : "text-adaptive-emerald-600-400", |
| 77 | 77 | )} |
| 78 | 78 | > |
| ... | ... | @@ -84,12 +84,14 @@ export function ThreadSearchMatchExcerpt(props: { |
| 84 | 84 | props.compact ? "text-sm" : "text-xs", |
| 85 | 85 | part.highlighted && "font-t3-bold", |
| 86 | 86 | props.selected |
| 87 | ? Platform.OS === "android" | |
| 88 | ? "text-thread-selected-foreground" | |
| 89 | : "text-user-bubble-foreground" | |
| 87 | ? "text-thread-selected-foreground" | |
| 90 | 88 | : part.highlighted |
| 91 | ? "text-foreground" | |
| 92 | : "text-foreground-muted", | |
| 89 | ? props.sidebar | |
| 90 | ? "text-drawer-foreground" | |
| 91 | : "text-foreground" | |
| 92 | : props.sidebar | |
| 93 | ? "text-drawer-foreground-muted" | |
| 94 | : "text-foreground-muted", | |
| 93 | 95 | )} |
| 94 | 96 | key={part.start} |
| 95 | 97 | > |
apps/mobile/src/features/threads/thread-work-log.tsx+3-3| ... | ... | @@ -882,7 +882,7 @@ const ThreadWorkLogRow = memo(function ThreadWorkLogRow( |
| 882 | 882 | entering={WORK_LOG_DETAIL_ENTER_TRANSITION} |
| 883 | 883 | exiting={WORK_LOG_DETAIL_EXIT_TRANSITION} |
| 884 | 884 | layout={WORK_LOG_LAYOUT_TRANSITION} |
| 885 | className="ml-7 border-l border-adaptive-neutral-300-a60-white-a12 pb-1 pl-3 pt-0.5" | |
| 885 | className="ml-7 border-l border-border pb-1 pl-3 pt-0.5" | |
| 886 | 886 | > |
| 887 | 887 | {row.workEntry.questionAnswer ? ( |
| 888 | 888 | <QuestionAnswerHistory |
| ... | ... | @@ -1037,7 +1037,7 @@ export const ThreadAgentSpawnCard = memo(function ThreadAgentSpawnCard(props: { |
| 1037 | 1037 | props.onToggle(); |
| 1038 | 1038 | }} |
| 1039 | 1039 | onLongPress={props.onCopy} |
| 1040 | className="rounded-xl border border-adaptive-neutral-200-a80-white-a8 bg-card px-2.5 py-2 active:bg-subtle" | |
| 1040 | className="rounded-xl border border-border-subtle bg-card px-2.5 py-2 active:bg-subtle" | |
| 1041 | 1041 | > |
| 1042 | 1042 | <View className="flex-row items-center gap-2"> |
| 1043 | 1043 | <View className="h-6 w-6 shrink-0 items-center justify-center"> |
| ... | ... | @@ -1094,7 +1094,7 @@ export const ThreadAgentSpawnCard = memo(function ThreadAgentSpawnCard(props: { |
| 1094 | 1094 | entering={WORK_LOG_DETAIL_ENTER_TRANSITION} |
| 1095 | 1095 | exiting={WORK_LOG_DETAIL_EXIT_TRANSITION} |
| 1096 | 1096 | layout={WORK_LOG_LAYOUT_TRANSITION} |
| 1097 | className="ml-8 mt-1.5 gap-1.5 border-l border-adaptive-neutral-300-a60-white-a12 pl-3" | |
| 1097 | className="ml-8 mt-1.5 gap-1.5 border-l border-border pl-3" | |
| 1098 | 1098 | > |
| 1099 | 1099 | {summary.members.map((member) => ( |
| 1100 | 1100 | <View key={member.title} className="gap-px"> |
apps/mobile/src/features/threads/threadPresentation.ts+7-23| ... | ... | @@ -12,10 +12,6 @@ export type ThreadStatusKind = |
| 12 | 12 | |
| 13 | 13 | export interface ThreadStatusPresentation extends StatusTone { |
| 14 | 14 | readonly kind: ThreadStatusKind; |
| 15 | /** Foreground color for the leading status icon. */ | |
| 16 | readonly iconColor: string; | |
| 17 | /** Background color for the leading status icon circle. */ | |
| 18 | readonly iconBackground: string; | |
| 19 | 15 | /** Whether the indicator represents in-flight activity. */ |
| 20 | 16 | readonly pulse: boolean; |
| 21 | 17 | } |
| ... | ... | @@ -44,8 +40,6 @@ export function resolveThreadStatus( |
| 44 | 40 | label: "Needs Approval", |
| 45 | 41 | pillClassName: "bg-warning", |
| 46 | 42 | textClassName: "text-warning-foreground", |
| 47 | iconColor: "#ff9f0a", | |
| 48 | iconBackground: "rgba(255,159,10,0.22)", | |
| 49 | 43 | pulse: false, |
| 50 | 44 | }; |
| 51 | 45 | } |
| ... | ... | @@ -54,10 +48,8 @@ export function resolveThreadStatus( |
| 54 | 48 | return { |
| 55 | 49 | kind: "awaiting-input", |
| 56 | 50 | label: "Awaiting Input", |
| 57 | pillClassName: "bg-primary/10", | |
| 58 | textClassName: "text-foreground-secondary", | |
| 59 | iconColor: "#5e5ce6", | |
| 60 | iconBackground: "rgba(94,92,230,0.22)", | |
| 51 | pillClassName: "bg-adaptive-indigo-500-a12-a16", | |
| 52 | textClassName: "text-adaptive-indigo-600-300", | |
| 61 | 53 | pulse: false, |
| 62 | 54 | }; |
| 63 | 55 | } |
| ... | ... | @@ -66,10 +58,8 @@ export function resolveThreadStatus( |
| 66 | 58 | return { |
| 67 | 59 | kind: "working", |
| 68 | 60 | label: "Working", |
| 69 | pillClassName: "bg-primary/10", | |
| 61 | pillClassName: "bg-adaptive-sky-500-a12-a16", | |
| 70 | 62 | textClassName: "text-adaptive-sky-600-400", |
| 71 | iconColor: "#0a84ff", | |
| 72 | iconBackground: "rgba(10,132,255,0.22)", | |
| 73 | 63 | pulse: true, |
| 74 | 64 | }; |
| 75 | 65 | } |
| ... | ... | @@ -78,10 +68,8 @@ export function resolveThreadStatus( |
| 78 | 68 | return { |
| 79 | 69 | kind: "connecting", |
| 80 | 70 | label: "Connecting", |
| 81 | pillClassName: "bg-primary/10", | |
| 82 | textClassName: "text-foreground-secondary", | |
| 83 | iconColor: "#0a84ff", | |
| 84 | iconBackground: "rgba(10,132,255,0.22)", | |
| 71 | pillClassName: "bg-adaptive-sky-500-a12-a16", | |
| 72 | textClassName: "text-adaptive-sky-600-400", | |
| 85 | 73 | pulse: true, |
| 86 | 74 | }; |
| 87 | 75 | } |
| ... | ... | @@ -92,8 +80,6 @@ export function resolveThreadStatus( |
| 92 | 80 | label: "Error", |
| 93 | 81 | pillClassName: "bg-danger", |
| 94 | 82 | textClassName: "text-danger-foreground", |
| 95 | iconColor: "#ff453a", | |
| 96 | iconBackground: "rgba(255,69,58,0.22)", | |
| 97 | 83 | pulse: false, |
| 98 | 84 | }; |
| 99 | 85 | } |
| ... | ... | @@ -106,10 +92,8 @@ export function resolveThreadStatus( |
| 106 | 92 | return { |
| 107 | 93 | kind: "plan-ready", |
| 108 | 94 | label: "Plan Ready", |
| 109 | pillClassName: "bg-primary/10", | |
| 110 | textClassName: "text-foreground-secondary", | |
| 111 | iconColor: "#bf5af2", | |
| 112 | iconBackground: "rgba(191,90,242,0.22)", | |
| 95 | pillClassName: "bg-adaptive-violet-500-a12-a16", | |
| 96 | textClassName: "text-adaptive-violet-600-400", | |
| 113 | 97 | pulse: false, |
| 114 | 98 | }; |
| 115 | 99 | } |
apps/mobile/src/features/threads/worktree-setup-card.tsx+26-28| ... | ... | @@ -72,7 +72,7 @@ export function WorktreeSetupCard(props: WorktreeSetupCardProps) { |
| 72 | 72 | /> |
| 73 | 73 | {!handedOff ? ( |
| 74 | 74 | <Text |
| 75 | className="text-2xs text-adaptive-neutral-600-400" | |
| 75 | className="text-2xs text-foreground-secondary" | |
| 76 | 76 | style={{ fontVariant: ["tabular-nums"] }} |
| 77 | 77 | > |
| 78 | 78 | {elapsed(snapshot.startedAt, snapshot.endedAt, now)} |
| ... | ... | @@ -99,25 +99,21 @@ export function WorktreeSetupCard(props: WorktreeSetupCardProps) { |
| 99 | 99 | {backgroundSetup ? ( |
| 100 | 100 | <ActivityIndicator |
| 101 | 101 | size="small" |
| 102 | colorClassName="accent-adaptive-neutral-500-400" | |
| 102 | colorClassName="accent-icon-muted" | |
| 103 | 103 | style={{ width: 12, height: 12, transform: [{ scale: 0.65 }] }} |
| 104 | 104 | /> |
| 105 | 105 | ) : failed ? ( |
| 106 | 106 | <SymbolView |
| 107 | 107 | name="exclamationmark.circle" |
| 108 | 108 | size={12} |
| 109 | tintColorClassName="accent-adaptive-rose-700-300" | |
| 109 | tintColorClassName="accent-danger-foreground" | |
| 110 | 110 | /> |
| 111 | 111 | ) : null} |
| 112 | <Text numberOfLines={1} className="shrink text-2xs text-adaptive-neutral-600-400"> | |
| 112 | <Text numberOfLines={1} className="shrink text-2xs text-foreground-secondary"> | |
| 113 | 113 | {backgroundSetup ? scriptName : "Details"} |
| 114 | 114 | </Text> |
| 115 | 115 | {!backgroundSetup ? ( |
| 116 | <SymbolView | |
| 117 | name="chevron.right" | |
| 118 | size={10} | |
| 119 | tintColorClassName="accent-adaptive-neutral-500-400" | |
| 120 | /> | |
| 116 | <SymbolView name="chevron.right" size={10} tintColorClassName="accent-icon-muted" /> | |
| 121 | 117 | ) : null} |
| 122 | 118 | </View> |
| 123 | 119 | </Pressable> |
| ... | ... | @@ -166,14 +162,19 @@ function HeaderLabel({ |
| 166 | 162 | failed?: boolean; |
| 167 | 163 | }) { |
| 168 | 164 | return active ? ( |
| 169 | <ShimmeringWorkContent icon="clock" iconSubtleColor="#737373" label={label} showIcon={false} /> | |
| 165 | <ShimmeringWorkContent | |
| 166 | icon="clock" | |
| 167 | iconSubtleColor="transparent" | |
| 168 | label={label} | |
| 169 | showIcon={false} | |
| 170 | /> | |
| 170 | 171 | ) : ( |
| 171 | 172 | <Text |
| 172 | 173 | numberOfLines={1} |
| 173 | 174 | className={ |
| 174 | 175 | failed |
| 175 | ? "flex-1 text-sm text-adaptive-rose-700-300" | |
| 176 | : "flex-1 text-sm text-adaptive-neutral-600-400" | |
| 176 | ? "flex-1 text-sm text-danger-foreground" | |
| 177 | : "flex-1 text-sm text-foreground-secondary" | |
| 177 | 178 | } |
| 178 | 179 | style={{ fontVariant: ["tabular-nums"] }} |
| 179 | 180 | > |
| ... | ... | @@ -232,7 +233,7 @@ function SetupDetailsSheet({ |
| 232 | 233 | </View> |
| 233 | 234 | ))} |
| 234 | 235 | {snapshot.phase === "failed" && snapshot.error ? ( |
| 235 | <Text selectable className="ml-8 mt-2 text-xs text-adaptive-rose-700-300"> | |
| 236 | <Text selectable className="ml-8 mt-2 text-xs text-danger-foreground"> | |
| 236 | 237 | {snapshot.error} |
| 237 | 238 | </Text> |
| 238 | 239 | ) : null} |
| ... | ... | @@ -303,7 +304,7 @@ function StageRow({ |
| 303 | 304 | {stage.status === "running" && animate ? ( |
| 304 | 305 | <ActivityIndicator |
| 305 | 306 | size="small" |
| 306 | colorClassName="accent-adaptive-neutral-500-400" | |
| 307 | colorClassName="accent-icon-muted" | |
| 307 | 308 | style={{ transform: [{ scale: 0.75 }] }} |
| 308 | 309 | /> |
| 309 | 310 | ) : ( |
| ... | ... | @@ -312,10 +313,10 @@ function StageRow({ |
| 312 | 313 | size={14} |
| 313 | 314 | tintColorClassName={ |
| 314 | 315 | stage.status === "failed" |
| 315 | ? "accent-adaptive-rose-700-300" | |
| 316 | ? "accent-danger-foreground" | |
| 316 | 317 | : stage.status === "warning" |
| 317 | 318 | ? "accent-warning-foreground" |
| 318 | : "accent-adaptive-neutral-500-400" | |
| 319 | : "accent-icon-muted" | |
| 319 | 320 | } |
| 320 | 321 | /> |
| 321 | 322 | )} |
| ... | ... | @@ -323,7 +324,7 @@ function StageRow({ |
| 323 | 324 | {stage.status === "running" && animate ? ( |
| 324 | 325 | <ShimmeringWorkContent |
| 325 | 326 | icon="clock" |
| 326 | iconSubtleColor="#737373" | |
| 327 | iconSubtleColor="transparent" | |
| 327 | 328 | label={label} |
| 328 | 329 | showIcon={false} |
| 329 | 330 | /> |
| ... | ... | @@ -332,24 +333,21 @@ function StageRow({ |
| 332 | 333 | numberOfLines={1} |
| 333 | 334 | className={ |
| 334 | 335 | stage.status === "failed" |
| 335 | ? "flex-1 text-sm text-adaptive-rose-700-300" | |
| 336 | : "flex-1 text-sm text-adaptive-neutral-600-400" | |
| 336 | ? "flex-1 text-sm text-danger-foreground" | |
| 337 | : "flex-1 text-sm text-foreground-secondary" | |
| 337 | 338 | } |
| 338 | 339 | > |
| 339 | 340 | {label} |
| 340 | 341 | </Text> |
| 341 | 342 | )} |
| 342 | 343 | {detail ? ( |
| 343 | <Text | |
| 344 | numberOfLines={1} | |
| 345 | className="max-w-[25%] shrink text-2xs text-adaptive-neutral-600-400" | |
| 346 | > | |
| 344 | <Text numberOfLines={1} className="max-w-[25%] shrink text-2xs text-foreground-secondary"> | |
| 347 | 345 | {detail} |
| 348 | 346 | </Text> |
| 349 | 347 | ) : null} |
| 350 | 348 | {stage.status !== "pending" && stage.status !== "skipped" ? ( |
| 351 | 349 | <Text |
| 352 | className="text-2xs text-adaptive-neutral-600-400" | |
| 350 | className="text-2xs text-foreground-secondary" | |
| 353 | 351 | style={{ fontVariant: ["tabular-nums"] }} |
| 354 | 352 | > |
| 355 | 353 | {elapsed(stage.startedAt, stage.endedAt, now)} |
| ... | ... | @@ -368,8 +366,8 @@ function OutputTail({ lines, failed }: { lines: ReadonlyArray<string>; failed: b |
| 368 | 366 | accessibilityLabel="Setup script output" |
| 369 | 367 | className={ |
| 370 | 368 | failed |
| 371 | ? "mb-2 ml-8 rounded-md border border-adaptive-rose-700-300/20 bg-adaptive-rose-50-950 px-3 py-2" | |
| 372 | : "mb-2 ml-8 rounded-md border border-border bg-adaptive-neutral-100-900 px-3 py-2" | |
| 369 | ? "mb-2 ml-8 rounded-md border border-danger-border bg-danger px-3 py-2" | |
| 370 | : "mb-2 ml-8 rounded-md border border-border bg-card-alt px-3 py-2" | |
| 373 | 371 | } |
| 374 | 372 | > |
| 375 | 373 | {OUTPUT_TAIL_SLOTS.map((slot) => ( |
| ... | ... | @@ -379,8 +377,8 @@ function OutputTail({ lines, failed }: { lines: ReadonlyArray<string>; failed: b |
| 379 | 377 | numberOfLines={1} |
| 380 | 378 | className={ |
| 381 | 379 | failed |
| 382 | ? "text-2xs leading-5 text-adaptive-rose-700-300 ios:font-[family-name:Menlo] android:font-mono" | |
| 383 | : "text-2xs leading-5 text-adaptive-neutral-600-400 ios:font-[family-name:Menlo] android:font-mono" | |
| 380 | ? "text-2xs leading-5 text-danger-foreground ios:font-[family-name:Menlo] android:font-mono" | |
| 381 | : "text-2xs leading-5 text-foreground-secondary ios:font-[family-name:Menlo] android:font-mono" | |
| 384 | 382 | } |
| 385 | 383 | > |
| 386 | 384 | {lines[lines.length - OUTPUT_TAIL_SLOTS.length + slot] || "\u00a0"} |
apps/mobile/src/features/voice-input/ComposerDictationControl.tsx+1-1| ... | ... | @@ -300,7 +300,7 @@ export function ComposerDictationStatus(props: { |
| 300 | 300 | <View className="relative h-11 min-w-0 flex-1 justify-center"> |
| 301 | 301 | {isError ? ( |
| 302 | 302 | <View className="min-w-0 flex-row items-center gap-1.5 px-2"> |
| 303 | <Text className="min-w-0 flex-1 text-sm text-red-400" numberOfLines={2}> | |
| 303 | <Text className="min-w-0 flex-1 text-sm text-danger-foreground" numberOfLines={2}> | |
| 304 | 304 | {props.presentation.statusLabel} |
| 305 | 305 | </Text> |
| 306 | 306 | <Pressable |
apps/mobile/src/lib/materialYouTheme.android.ts+12| ... | ... | @@ -36,12 +36,15 @@ export function materialYouPaletteToMobileThemeVariables( |
| 36 | 36 | "--color-sheet": withAlpha(palette.surfaceContainerLow, 0.98), |
| 37 | 37 | "--color-sheet-solid": palette.surfaceContainerLow, |
| 38 | 38 | "--color-card": palette.surfaceContainer, |
| 39 | "--color-grouped-card": palette.surfaceContainer, | |
| 39 | 40 | "--color-card-alt": palette.surfaceContainerHigh, |
| 40 | 41 | "--color-card-translucent": withAlpha(palette.surfaceContainer, 0.8), |
| 41 | 42 | "--color-thread-canvas": palette.surfaceContainerLow, |
| 42 | 43 | "--color-thread-selected": palette.surfaceContainer, |
| 43 | 44 | "--color-thread-selected-foreground": palette.onSurface, |
| 44 | 45 | "--color-thread-selected-foreground-muted": palette.onSurfaceVariant, |
| 46 | "--color-thread-hover": palette.surfaceContainerHigh, | |
| 47 | "--color-row-hover": palette.surfaceContainerHigh, | |
| 45 | 48 | "--color-composer-panel": withAlpha(palette.surfaceContainerLow, dark ? 0.92 : 0.88), |
| 46 | 49 | "--color-composer-surface": withAlpha(palette.surfaceContainerHigh, dark ? 0.9 : 0.94), |
| 47 | 50 | "--color-composer-border": withAlpha(palette.outlineVariant, dark ? 0.34 : 0.46), |
| ... | ... | @@ -50,6 +53,7 @@ export function materialYouPaletteToMobileThemeVariables( |
| 50 | 53 | "--color-foreground-muted": palette.onSurfaceVariant, |
| 51 | 54 | "--color-foreground-tertiary": withAlpha(palette.onSurfaceVariant, 0.58), |
| 52 | 55 | "--color-border": withAlpha(palette.outlineVariant, dark ? 0.5 : 0.65), |
| 56 | "--color-focus": palette.primary, | |
| 53 | 57 | "--color-border-subtle": withAlpha(palette.outlineVariant, dark ? 0.34 : 0.46), |
| 54 | 58 | "--color-separator": withAlpha(palette.outlineVariant, dark ? 0.24 : 0.34), |
| 55 | 59 | "--color-subtle": withAlpha(palette.onSurface, 0.05), |
| ... | ... | @@ -59,6 +63,7 @@ export function materialYouPaletteToMobileThemeVariables( |
| 59 | 63 | "--color-inline-skill-foreground": palette.onTertiaryContainer, |
| 60 | 64 | "--color-primary": palette.primary, |
| 61 | 65 | "--color-primary-foreground": palette.onPrimary, |
| 66 | "--color-primary-text": palette.primary, | |
| 62 | 67 | "--color-primary-shadow": withAlpha(palette.scrim, dark ? 0.22 : 0.18), |
| 63 | 68 | "--color-secondary": palette.secondaryContainer, |
| 64 | 69 | "--color-secondary-foreground": palette.onSecondaryContainer, |
| ... | ... | @@ -70,6 +75,8 @@ export function materialYouPaletteToMobileThemeVariables( |
| 70 | 75 | "--color-danger": palette.errorContainer, |
| 71 | 76 | "--color-danger-border": withAlpha(palette.onErrorContainer, dark ? 0.42 : 0.5), |
| 72 | 77 | "--color-danger-foreground": palette.onErrorContainer, |
| 78 | "--color-update": palette.primaryContainer, | |
| 79 | "--color-update-foreground": palette.onPrimaryContainer, | |
| 73 | 80 | "--color-input": palette.surfaceContainerLowest, |
| 74 | 81 | "--color-input-border": withAlpha(palette.outline, dark ? 0.42 : 0.5), |
| 75 | 82 | "--color-sidebar-search": palette.surfaceContainerLowest, |
| ... | ... | @@ -78,8 +85,10 @@ export function materialYouPaletteToMobileThemeVariables( |
| 78 | 85 | "--color-icon-muted": palette.onSurfaceVariant, |
| 79 | 86 | "--color-icon-subtle": withAlpha(palette.onSurfaceVariant, 0.68), |
| 80 | 87 | "--color-header": palette.surfaceContainerHigh, |
| 88 | "--color-header-foreground": palette.onSurface, | |
| 81 | 89 | "--color-header-border": withAlpha(palette.outlineVariant, dark ? 0.36 : 0.48), |
| 82 | 90 | "--color-glass-surface": withAlpha(palette.surfaceContainerHigh, dark ? 0.78 : 0.72), |
| 91 | "--color-glass-fallback": withAlpha(palette.surfaceContainerHigh, dark ? 0.9 : 0.94), | |
| 83 | 92 | "--color-glass-tint": withAlpha(palette.primaryContainer, dark ? 0.24 : 0.18), |
| 84 | 93 | "--color-status-bar": palette.surfaceContainerLow, |
| 85 | 94 | "--color-md-body": materialTone(palette.onSurfaceVariant, dark ? 90 : 10), |
| ... | ... | @@ -100,6 +109,9 @@ export function materialYouPaletteToMobileThemeVariables( |
| 100 | 109 | "--color-user-bubble-skill-foreground": userBubbleForeground, |
| 101 | 110 | "--color-backdrop": withAlpha(palette.scrim, dark ? 0.48 : 0.22), |
| 102 | 111 | "--color-drawer": palette.surface, |
| 112 | "--color-drawer-foreground": palette.onSurface, | |
| 113 | "--color-drawer-foreground-muted": palette.onSurfaceVariant, | |
| 114 | "--color-drawer-border": withAlpha(palette.outlineVariant, dark ? 0.36 : 0.48), | |
| 103 | 115 | "--color-drawer-shadow": withAlpha(palette.scrim, dark ? 0.32 : 0.12), |
| 104 | 116 | "--color-dot-separator": withAlpha(palette.onSurface, 0.2), |
| 105 | 117 | "--color-wordmark": palette.onSurface, |
apps/mobile/src/lib/materialYouTheme.test.ts+1| ... | ... | @@ -54,6 +54,7 @@ describe("Material You system colors", () => { |
| 54 | 54 | expect(variables["--color-thread-canvas"]).toBe(palette.surfaceContainerLow); |
| 55 | 55 | expect(variables["--color-thread-selected"]).toBe(palette.surfaceContainer); |
| 56 | 56 | expect(variables["--color-header"]).toBe(palette.surfaceContainerHigh); |
| 57 | expect(variables["--color-grouped-card"]).toBe(palette.surfaceContainer); | |
| 57 | 58 | expect(variables["--color-primary"]).toBe(palette.primary); |
| 58 | 59 | expect(variables["--color-placeholder"]).toBe("#1C1B1F9E"); |
| 59 | 60 | }); |
apps/mobile/src/lib/mobileTheme.test-support.ts+13-4| ... | ... | @@ -1,20 +1,29 @@ |
| 1 | 1 | import * as NodeFS from "node:fs"; |
| 2 | 2 | |
| 3 | import type { MobileThemeAppearance, MobileThemeVariables } from "./mobileTheme"; | |
| 3 | import { | |
| 4 | MOBILE_THEME_VARIABLE_NAMES, | |
| 5 | type MobileThemeAppearance, | |
| 6 | type MobileThemeVariables, | |
| 7 | } from "./mobileTheme"; | |
| 4 | 8 | |
| 5 | 9 | export function readDefaultMobileThemeVariables( |
| 6 | 10 | appearance: MobileThemeAppearance, |
| 7 | 11 | ): MobileThemeVariables { |
| 8 | const stylesheet = NodeFS.readFileSync(new URL("../../global.css", import.meta.url), "utf8"); | |
| 12 | const stylesheet = NodeFS.readFileSync( | |
| 13 | new URL("../../generated-uniwind-themes.css", import.meta.url), | |
| 14 | "utf8", | |
| 15 | ); | |
| 9 | 16 | const variant = new RegExp(`@variant ${appearance} \\{([\\s\\S]*?)\\n \\}`, "u").exec( |
| 10 | 17 | stylesheet, |
| 11 | 18 | )?.[1]; |
| 12 | if (variant === undefined) throw new Error(`Missing default ${appearance} theme in global.css.`); | |
| 19 | if (variant === undefined) throw new Error(`Missing generated default ${appearance} theme.`); | |
| 13 | 20 | |
| 14 | 21 | return Object.fromEntries( |
| 15 | 22 | Array.from(variant.matchAll(/(--color-[a-z0-9-]+):\s*([^;]+);/gu), ([, name, value]) => [ |
| 16 | 23 | name, |
| 17 | 24 | (value ?? "").trim(), |
| 18 | ]), | |
| 25 | ]).filter(([name]) => | |
| 26 | MOBILE_THEME_VARIABLE_NAMES.includes(name as (typeof MOBILE_THEME_VARIABLE_NAMES)[number]), | |
| 27 | ), | |
| 19 | 28 | ) as MobileThemeVariables; |
| 20 | 29 | } |
apps/mobile/src/lib/mobileTheme.test.ts+147-10| ... | ... | @@ -1,12 +1,22 @@ |
| 1 | 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | import { BUILT_IN_THEME_IDS, BUILT_IN_THEMES, T3_CHAT_THEME } from "@t3tools/shared/themePalettes"; | |
| 2 | import { | |
| 3 | BUILT_IN_THEME_IDS, | |
| 4 | BUILT_IN_THEMES, | |
| 5 | T3_CHAT_THEME, | |
| 6 | T3_CODE_LIGHT_THEME_COLORS, | |
| 7 | T3_CODE_DARK_THEME_COLORS, | |
| 8 | MOBILE_THEME_IDS, | |
| 9 | getThemeColorsForAppearance, | |
| 10 | } from "@t3tools/shared/themePalettes"; | |
| 3 | 11 | import { readDefaultMobileThemeVariables } from "./mobileTheme.test-support"; |
| 12 | import { getMobileThemeRuntimeVariables } from "./mobileThemeVariables"; | |
| 4 | 13 | |
| 5 | 14 | import { |
| 6 | 15 | createMobileThemePairPatch, |
| 7 | 16 | createMobileThemeSelectionPatch, |
| 8 | 17 | createMobileThemeVariables, |
| 9 | 18 | DEFAULT_MOBILE_THEME_ID, |
| 19 | flattenThemeColor, | |
| 10 | 20 | getMobileThemePreviewColors, |
| 11 | 21 | getMobileThemeVariables, |
| 12 | 22 | normalizeMobileThemeId, |
| ... | ... | @@ -66,12 +76,86 @@ describe("mobile themes", () => { |
| 66 | 76 | } |
| 67 | 77 | }); |
| 68 | 78 | |
| 69 | it("preserves the existing mobile palette as the default", () => { | |
| 70 | expect(readDefaultMobileThemeVariables("light")["--color-screen"]).toBe("#f2f2f7"); | |
| 71 | expect(readDefaultMobileThemeVariables("dark")["--color-screen"]).toBe("#0a0a0a"); | |
| 72 | expect(readDefaultMobileThemeVariables("light")["--color-user-bubble-skill-foreground"]).toBe( | |
| 73 | "#2563eb", | |
| 74 | ); | |
| 79 | it.each(MOBILE_THEME_IDS)("uses the web color roles for %s in both appearances", (themeId) => { | |
| 80 | for (const appearance of ["light", "dark"] as const) { | |
| 81 | const theme = BUILT_IN_THEMES.find((candidate) => candidate.id === themeId); | |
| 82 | const colors = theme | |
| 83 | ? getThemeColorsForAppearance(theme, appearance)! | |
| 84 | : appearance === "dark" | |
| 85 | ? T3_CODE_DARK_THEME_COLORS | |
| 86 | : T3_CODE_LIGHT_THEME_COLORS; | |
| 87 | const variables = | |
| 88 | themeId === DEFAULT_MOBILE_THEME_ID | |
| 89 | ? readDefaultMobileThemeVariables(appearance) | |
| 90 | : getMobileThemeVariables(themeId, appearance); | |
| 91 | expect(variables["--color-screen"]).toBe(themeColorToNativeColor(colors.canvas)); | |
| 92 | expect(variables["--color-thread-canvas"]).toBe(variables["--color-screen"]); | |
| 93 | expect(variables["--color-drawer"]).toBe(themeColorToNativeColor(colors.sidebar)); | |
| 94 | expect(variables["--color-thread-hover"]).toBe( | |
| 95 | themeColorToNativeColor(colors.sidebarRowHover), | |
| 96 | ); | |
| 97 | expect(variables["--color-card"]).toBe(themeColorToNativeColor(colors.surface)); | |
| 98 | expect(variables["--color-composer-surface"]).toBe( | |
| 99 | themeColorWithAlpha( | |
| 100 | themeId === DEFAULT_MOBILE_THEME_ID | |
| 101 | ? variables["--color-grouped-card"] | |
| 102 | : themeColorToNativeColor(colors.surface), | |
| 103 | appearance === "dark" ? 0.9 : 0.94, | |
| 104 | ), | |
| 105 | ); | |
| 106 | expect(variables["--color-thread-selected"]).toBe( | |
| 107 | themeColorToNativeColor(colors.sidebarRowActive), | |
| 108 | ); | |
| 109 | expect(variables["--color-thread-selected-foreground"]).toBe( | |
| 110 | themeColorToNativeColor(colors.sidebarForeground), | |
| 111 | ); | |
| 112 | expect(variables["--color-primary"]).toBe(themeColorToNativeColor(colors.messageAction)); | |
| 113 | if (themeId !== DEFAULT_MOBILE_THEME_ID) { | |
| 114 | expect(variables["--color-user-bubble"]).toBe( | |
| 115 | themeColorToNativeColor(colors.messageSurface), | |
| 116 | ); | |
| 117 | } | |
| 118 | expect( | |
| 119 | contrastRatio(variables["--color-foreground"], variables["--color-screen"]), | |
| 120 | ).toBeGreaterThanOrEqual(4.5); | |
| 121 | for (const [foreground, surface] of [ | |
| 122 | ["--color-drawer-foreground", "--color-drawer"], | |
| 123 | ["--color-drawer-foreground-muted", "--color-drawer"], | |
| 124 | ["--color-drawer-foreground", "--color-thread-hover"], | |
| 125 | ["--color-drawer-foreground-muted", "--color-thread-hover"], | |
| 126 | ["--color-thread-selected-foreground-muted", "--color-thread-selected"], | |
| 127 | ["--color-primary-foreground", "--color-primary"], | |
| 128 | ["--color-primary-text", "--color-screen"], | |
| 129 | ["--color-primary-text", "--color-card"], | |
| 130 | ["--color-primary-text", "--color-card-alt"], | |
| 131 | ["--color-primary-text", "--color-sheet-solid"], | |
| 132 | ["--color-foreground", "--color-sheet-solid"], | |
| 133 | ["--color-foreground-muted", "--color-sheet-solid"], | |
| 134 | ["--color-primary-text", "--color-grouped-card"], | |
| 135 | ["--color-foreground", "--color-grouped-card"], | |
| 136 | ["--color-foreground-muted", "--color-grouped-card"], | |
| 137 | ["--color-placeholder", "--color-grouped-card"], | |
| 138 | ["--color-secondary-foreground", "--color-secondary"], | |
| 139 | ["--color-user-bubble-foreground", "--color-user-bubble"], | |
| 140 | ["--color-warning-foreground", "--color-warning"], | |
| 141 | ["--color-danger-foreground", "--color-danger"], | |
| 142 | ["--color-md-body", "--color-screen"], | |
| 143 | ["--color-md-strong", "--color-screen"], | |
| 144 | ["--color-md-link", "--color-screen"], | |
| 145 | ["--color-md-code-text", "--color-md-code-bg"], | |
| 146 | ] as const) { | |
| 147 | expect( | |
| 148 | contrastRatio(variables[foreground], variables[surface]), | |
| 149 | `${appearance}: ${foreground} on ${surface}`, | |
| 150 | ).toBeGreaterThanOrEqual(4.5); | |
| 151 | } | |
| 152 | expect( | |
| 153 | contrastRatio( | |
| 154 | variables["--color-thread-selected-foreground"], | |
| 155 | variables["--color-thread-selected"], | |
| 156 | ), | |
| 157 | ).toBeGreaterThanOrEqual(4.5); | |
| 158 | } | |
| 75 | 159 | }); |
| 76 | 160 | |
| 77 | 161 | it("applies palette overrides on top of the selected built-in theme", () => { |
| ... | ... | @@ -83,6 +167,62 @@ describe("mobile themes", () => { |
| 83 | 167 | expect(variables["--color-screen"]).toMatch(/^#/); |
| 84 | 168 | }); |
| 85 | 169 | |
| 170 | it.each(["light", "dark"] as const)( | |
| 171 | "separates default settings groups from their %s background", | |
| 172 | (appearance) => { | |
| 173 | const variables = getMobileThemeVariables("t3-code", appearance); | |
| 174 | expect( | |
| 175 | contrastRatio(variables["--color-grouped-card"], variables["--color-sheet-solid"]), | |
| 176 | ).toBeGreaterThanOrEqual(1.06); | |
| 177 | expect(variables["--color-grouped-card"]).not.toBe(variables["--color-card"]); | |
| 178 | for (const platform of ["ios", "android"]) { | |
| 179 | const runtime = getMobileThemeRuntimeVariables("t3-code", appearance, platform); | |
| 180 | const sidebar = flattenThemeColor(runtime["--color-drawer"], runtime["--color-screen"]); | |
| 181 | const chrome = flattenThemeColor( | |
| 182 | runtime[platform === "android" ? "--color-header" : "--color-drawer"], | |
| 183 | runtime["--color-screen"], | |
| 184 | ); | |
| 185 | expect(relativeLuminance(sidebar)).toBeLessThan( | |
| 186 | relativeLuminance(runtime["--color-thread-canvas"]), | |
| 187 | ); | |
| 188 | expect(contrastRatio(chrome, runtime["--color-screen"])).toBeGreaterThanOrEqual(1.06); | |
| 189 | const foregroundRoles = | |
| 190 | platform === "android" | |
| 191 | ? (["--color-header-foreground", "--color-foreground-muted"] as const) | |
| 192 | : (["--color-drawer-foreground", "--color-drawer-foreground-muted"] as const); | |
| 193 | for (const role of foregroundRoles) { | |
| 194 | expect(contrastRatio(runtime[role], chrome)).toBeGreaterThanOrEqual(4.5); | |
| 195 | } | |
| 196 | } | |
| 197 | }, | |
| 198 | ); | |
| 199 | ||
| 200 | it.each(["light", "dark"] as const)( | |
| 201 | "slightly strengthens default %s messages and separates fallback materials", | |
| 202 | (appearance) => { | |
| 203 | const variables = getMobileThemeVariables("t3-code", appearance); | |
| 204 | const desktop = | |
| 205 | appearance === "dark" ? T3_CODE_DARK_THEME_COLORS : T3_CODE_LIGHT_THEME_COLORS; | |
| 206 | const bubbleContrast = contrastRatio( | |
| 207 | variables["--color-user-bubble"], | |
| 208 | variables["--color-screen"], | |
| 209 | ); | |
| 210 | expect(bubbleContrast).toBeGreaterThan(contrastRatio(desktop.messageSurface, desktop.canvas)); | |
| 211 | expect(bubbleContrast).toBeLessThan(1.2); | |
| 212 | for (const role of ["--color-composer-surface", "--color-glass-fallback"] as const) { | |
| 213 | const surface = flattenThemeColor(variables[role], variables["--color-screen"]); | |
| 214 | expect(contrastRatio(surface, variables["--color-screen"])).toBeGreaterThanOrEqual(1.06); | |
| 215 | for (const foreground of [ | |
| 216 | "--color-foreground", | |
| 217 | "--color-placeholder", | |
| 218 | "--color-primary-text", | |
| 219 | ] as const) { | |
| 220 | expect(contrastRatio(variables[foreground], surface)).toBeGreaterThanOrEqual(4.5); | |
| 221 | } | |
| 222 | } | |
| 223 | }, | |
| 224 | ); | |
| 225 | ||
| 86 | 226 | it("uses the same preview roles and standard artwork as desktop", () => { |
| 87 | 227 | expect(getMobileThemePreviewColors(DEFAULT_MOBILE_THEME_ID, "light")).toEqual({ |
| 88 | 228 | canvas: "#fcfcfc", |
| ... | ... | @@ -153,7 +293,6 @@ describe("mobile themes", () => { |
| 153 | 293 | |
| 154 | 294 | it("maps semantic palette roles onto every mobile color variable", () => { |
| 155 | 295 | const variables = createMobileThemeVariables(T3_CHAT_THEME.colors, "light"); |
| 156 | expect(Object.keys(variables)).toHaveLength(75); | |
| 157 | 296 | expect(variables["--color-sheet-solid"]).toBe( |
| 158 | 297 | themeColorToNativeColor(T3_CHAT_THEME.colors.chrome), |
| 159 | 298 | ); |
| ... | ... | @@ -219,8 +358,6 @@ describe("mobile themes", () => { |
| 219 | 358 | } |
| 220 | 359 | }); |
| 221 | 360 | |
| 222 | // The default palette lives in global.css rather than BUILT_IN_THEMES, so the loops above | |
| 223 | // never reached it; it kept an unreadable hardcoded bubble until this covered it. | |
| 224 | 361 | it("keeps the default user bubble readable in both appearances", () => { |
| 225 | 362 | for (const appearance of ["light", "dark"] as const) { |
| 226 | 363 | const variables = readDefaultMobileThemeVariables(appearance); |
apps/mobile/src/lib/mobileTheme.ts+106-43| ... | ... | @@ -1,10 +1,11 @@ |
| 1 | 1 | import { |
| 2 | 2 | BUILT_IN_THEMES, |
| 3 | 3 | T3_CHAT_THEME, |
| 4 | T3_CODE_LIGHT_THEME_COLORS, | |
| 5 | T3_CODE_DARK_THEME_COLORS, | |
| 4 | 6 | getThemeColorsForAppearance, |
| 5 | 7 | MOBILE_DEFAULT_THEME_ID, |
| 6 | 8 | MOBILE_THEME_IDS as SHARED_MOBILE_THEME_IDS, |
| 7 | type BuiltInThemeId, | |
| 8 | 9 | type MobileThemeId as SharedMobileThemeId, |
| 9 | 10 | type ThemeAppearance, |
| 10 | 11 | type ThemeColors, |
| ... | ... | @@ -138,7 +139,7 @@ function withAlpha(color: string, alpha: number): string { |
| 138 | 139 | } |
| 139 | 140 | |
| 140 | 141 | function rgbChannels(color: string): readonly [number, number, number] | null { |
| 141 | const match = /^#([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i.exec(color); | |
| 142 | const match = /^#([\da-f]{2})([\da-f]{2})([\da-f]{2})(?:[\da-f]{2})?$/i.exec(color); | |
| 142 | 143 | if (!match) return null; |
| 143 | 144 | const [, red = "0", green = "0", blue = "0"] = match; |
| 144 | 145 | return [Number.parseInt(red, 16), Number.parseInt(green, 16), Number.parseInt(blue, 16)]; |
| ... | ... | @@ -150,6 +151,14 @@ function rgbChannels(color: string): readonly [number, number, number] | null { |
| 150 | 151 | * colour asked for — so a translucent role like `--color-border` has to be flattened first. |
| 151 | 152 | */ |
| 152 | 153 | export function flattenThemeColor(color: string, surface: string): string { |
| 154 | const alphaHex = /^#([\da-f]{6})([\da-f]{2})$/i.exec(color); | |
| 155 | if (alphaHex) { | |
| 156 | const channels = rgbChannels(color)!; | |
| 157 | return flattenThemeColor( | |
| 158 | `rgba(${channels[0]}, ${channels[1]}, ${channels[2]}, ${Number.parseInt(alphaHex[2]!, 16) / 255})`, | |
| 159 | surface, | |
| 160 | ); | |
| 161 | } | |
| 153 | 162 | const match = /^rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)(?:[,\s/]+([\d.]+))?\s*\)$/i.exec( |
| 154 | 163 | color.trim(), |
| 155 | 164 | ); |
| ... | ... | @@ -182,22 +191,21 @@ function contrastRatio( |
| 182 | 191 | ); |
| 183 | 192 | } |
| 184 | 193 | |
| 185 | /** Preserve the theme's action hue while making it readable as skill text on a message bubble. */ | |
| 186 | function readableMessageAccent(accent: string, surface: string): string { | |
| 194 | /** Preserve the color's hue while giving text 4.5:1 contrast on every supplied surface. */ | |
| 195 | function readableTextColor(accent: string, surface: string | ReadonlyArray<string>): string { | |
| 187 | 196 | const accentChannels = rgbChannels(accent); |
| 188 | const surfaceChannels = rgbChannels(surface); | |
| 189 | if ( | |
| 190 | !accentChannels || | |
| 191 | !surfaceChannels || | |
| 192 | contrastRatio(accentChannels, surfaceChannels) >= 4.5 | |
| 193 | ) { | |
| 197 | const surfaceChannels = (typeof surface === "string" ? [surface] : surface) | |
| 198 | .map(rgbChannels) | |
| 199 | .filter((channels) => channels !== null); | |
| 200 | const minimumContrast = (channels: readonly [number, number, number]) => | |
| 201 | Math.min(...surfaceChannels.map((surface) => contrastRatio(channels, surface))); | |
| 202 | if (!accentChannels || surfaceChannels.length === 0 || minimumContrast(accentChannels) >= 4.5) { | |
| 194 | 203 | return accent; |
| 195 | 204 | } |
| 196 | 205 | |
| 197 | 206 | const black = [0, 0, 0] as const; |
| 198 | 207 | const white = [255, 255, 255] as const; |
| 199 | const target = | |
| 200 | contrastRatio(black, surfaceChannels) >= contrastRatio(white, surfaceChannels) ? black : white; | |
| 208 | const target = minimumContrast(black) >= minimumContrast(white) ? black : white; | |
| 201 | 209 | let readable: readonly [number, number, number] = target; |
| 202 | 210 | let lowerAmount = 0; |
| 203 | 211 | let upperAmount = 1; |
| ... | ... | @@ -208,7 +216,7 @@ function readableMessageAccent(accent: string, surface: string): string { |
| 208 | 216 | Math.round(accentChannels[1] + (target[1] - accentChannels[1]) * amount), |
| 209 | 217 | Math.round(accentChannels[2] + (target[2] - accentChannels[2]) * amount), |
| 210 | 218 | ]; |
| 211 | if (contrastRatio(candidate, surfaceChannels) >= 4.5) { | |
| 219 | if (minimumContrast(candidate) >= 4.5) { | |
| 212 | 220 | readable = candidate; |
| 213 | 221 | upperAmount = amount; |
| 214 | 222 | } else { |
| ... | ... | @@ -227,30 +235,46 @@ export function themeColorWithAlpha(color: string, alpha: number): string { |
| 227 | 235 | return rgb ? `rgba(${rgb[1]}, ${rgb[2]}, ${rgb[3]}, ${alpha})` : color; |
| 228 | 236 | } |
| 229 | 237 | |
| 230 | export function createMobileThemeVariables(colors: ThemeColors, appearance: MobileThemeAppearance) { | |
| 238 | export function createMobileThemeVariables( | |
| 239 | colors: ThemeColors, | |
| 240 | appearance: MobileThemeAppearance, | |
| 241 | groupedCardColor = colors.surface, | |
| 242 | ) { | |
| 231 | 243 | const c = nativeColors(colors); |
| 244 | const groupedCard = themeColorToNativeColor(groupedCardColor); | |
| 245 | const textSurfaces = [c.canvas, c.surface, c.surfaceRaised, c.chrome, groupedCard]; | |
| 232 | 246 | return { |
| 233 | 247 | "--color-screen": c.canvas, |
| 234 | 248 | "--color-sheet": withAlpha(c.chrome, 0.98), |
| 235 | 249 | "--color-sheet-solid": c.chrome, |
| 236 | "--color-card": c.surfaceRaised, | |
| 237 | "--color-card-alt": c.surface, | |
| 238 | "--color-card-translucent": withAlpha(c.surfaceRaised, 0.8), | |
| 239 | "--color-thread-canvas": c.surface, | |
| 240 | "--color-thread-selected": c.surfaceRaised, | |
| 241 | "--color-thread-selected-foreground": c.text, | |
| 242 | "--color-thread-selected-foreground-muted": c.textMuted, | |
| 243 | "--color-composer-panel": themeColorWithAlpha(c.surface, appearance === "dark" ? 0.92 : 0.88), | |
| 250 | "--color-card": c.surface, | |
| 251 | "--color-grouped-card": groupedCard, | |
| 252 | "--color-card-alt": c.surfaceRaised, | |
| 253 | "--color-card-translucent": withAlpha(c.surface, 0.8), | |
| 254 | "--color-thread-canvas": c.canvas, | |
| 255 | "--color-thread-selected": c.sidebarRowActive, | |
| 256 | "--color-thread-selected-foreground": c.sidebarForeground, | |
| 257 | "--color-thread-selected-foreground-muted": readableTextColor( | |
| 258 | c.sidebarMutedForeground, | |
| 259 | c.sidebarRowActive, | |
| 260 | ), | |
| 261 | "--color-thread-hover": c.sidebarRowHover, | |
| 262 | "--color-row-hover": c.toolbarControlHover, | |
| 263 | "--color-composer-panel": themeColorWithAlpha(c.canvas, appearance === "dark" ? 0.92 : 0.88), | |
| 244 | 264 | "--color-composer-surface": themeColorWithAlpha( |
| 245 | c.surfaceRaised, | |
| 265 | groupedCard, | |
| 246 | 266 | appearance === "dark" ? 0.9 : 0.94, |
| 247 | 267 | ), |
| 248 | "--color-composer-border": themeColorWithAlpha(c.border, appearance === "dark" ? 0.46 : 0.54), | |
| 268 | "--color-composer-border": themeColorWithAlpha( | |
| 269 | c.border, | |
| 270 | groupedCard === c.surface ? (appearance === "dark" ? 0.46 : 0.54) : 0.8, | |
| 271 | ), | |
| 249 | 272 | "--color-foreground": c.text, |
| 250 | "--color-foreground-secondary": c.textMuted, | |
| 251 | "--color-foreground-muted": c.mutedForeground, | |
| 273 | "--color-foreground-secondary": readableTextColor(c.textMuted, textSurfaces), | |
| 274 | "--color-foreground-muted": readableTextColor(c.mutedForeground, textSurfaces), | |
| 252 | 275 | "--color-foreground-tertiary": c.secondaryLabel, |
| 253 | 276 | "--color-border": c.border, |
| 277 | "--color-focus": c.focus, | |
| 254 | 278 | "--color-border-subtle": withAlpha(c.border, 0.7), |
| 255 | 279 | "--color-separator": withAlpha(c.border, 0.55), |
| 256 | 280 | "--color-subtle": c.muted, |
| ... | ... | @@ -258,14 +282,15 @@ export function createMobileThemeVariables(colors: ThemeColors, appearance: Mobi |
| 258 | 282 | "--color-inline-skill-background": c.accentSurface, |
| 259 | 283 | "--color-inline-skill-border": withAlpha(c.accent, 0.42), |
| 260 | 284 | "--color-inline-skill-foreground": c.accentSurfaceForeground, |
| 261 | "--color-primary": c.accent, | |
| 262 | "--color-primary-foreground": c.accentForeground, | |
| 285 | "--color-primary": c.messageAction, | |
| 286 | "--color-primary-foreground": c.messageActionForeground, | |
| 287 | "--color-primary-text": readableTextColor(c.messageAction, textSurfaces), | |
| 263 | 288 | "--color-primary-shadow": "#000000", |
| 264 | 289 | "--color-secondary": c.secondary, |
| 265 | 290 | "--color-secondary-foreground": c.secondaryForeground, |
| 266 | 291 | "--color-secondary-border": c.border, |
| 267 | "--color-switch-active-track": c.accent, | |
| 268 | "--color-switch-active-thumb": c.accentForeground, | |
| 292 | "--color-switch-active-track": c.messageAction, | |
| 293 | "--color-switch-active-thumb": c.messageActionForeground, | |
| 269 | 294 | "--color-switch-inactive-track": c.secondary, |
| 270 | 295 | "--color-switch-inactive-thumb": c.mutedForeground, |
| 271 | 296 | "--color-warning": c.warningSurface, |
| ... | ... | @@ -274,21 +299,25 @@ export function createMobileThemeVariables(colors: ThemeColors, appearance: Mobi |
| 274 | 299 | "--color-danger": c.errorSurface, |
| 275 | 300 | "--color-danger-border": withAlpha(c.error, 0.32), |
| 276 | 301 | "--color-danger-foreground": c.errorForeground, |
| 277 | "--color-input": c.surfaceRaised, | |
| 302 | "--color-update": c.updateSurface, | |
| 303 | "--color-update-foreground": c.updateForeground, | |
| 304 | "--color-input": c.surface, | |
| 278 | 305 | "--color-input-border": c.input, |
| 279 | 306 | "--color-sidebar-search": c.sidebarControlSurface, |
| 280 | "--color-placeholder": c.placeholder, | |
| 307 | "--color-placeholder": readableTextColor(c.placeholder, textSurfaces), | |
| 281 | 308 | "--color-icon": c.text, |
| 282 | 309 | "--color-icon-muted": c.iconMuted, |
| 283 | 310 | "--color-icon-subtle": c.secondaryLabel, |
| 284 | 311 | "--color-header": withAlpha(c.toolbar, 0.97), |
| 312 | "--color-header-foreground": c.toolbarForeground, | |
| 285 | 313 | "--color-header-border": c.toolbarBorder, |
| 286 | 314 | "--color-glass-surface": withAlpha(c.surfaceOverlay, 0.74), |
| 315 | "--color-glass-fallback": themeColorWithAlpha(groupedCard, appearance === "dark" ? 0.9 : 0.94), | |
| 287 | 316 | "--color-glass-tint": withAlpha(c.surfaceOverlay, 0.22), |
| 288 | 317 | "--color-status-bar": c.canvas, |
| 289 | 318 | "--color-md-body": c.text, |
| 290 | "--color-md-strong": c.toolbarForeground, | |
| 291 | "--color-md-link": c.accent, | |
| 319 | "--color-md-strong": c.text, | |
| 320 | "--color-md-link": readableTextColor(c.messageAction, c.canvas), | |
| 292 | 321 | "--color-md-blockquote-border": c.border, |
| 293 | 322 | "--color-md-blockquote-bg": c.muted, |
| 294 | 323 | "--color-md-code-bg": c.codeBackground, |
| ... | ... | @@ -301,12 +330,15 @@ export function createMobileThemeVariables(colors: ThemeColors, appearance: Mobi |
| 301 | 330 | "--color-user-bubble": c.messageSurface, |
| 302 | 331 | "--color-user-bubble-foreground": c.messageForeground, |
| 303 | 332 | "--color-user-bubble-foreground-muted": withAlpha(c.messageForeground, 0.78), |
| 304 | "--color-user-bubble-skill-foreground": readableMessageAccent( | |
| 305 | c.messageAction, | |
| 306 | c.messageSurface, | |
| 307 | ), | |
| 333 | "--color-user-bubble-skill-foreground": readableTextColor(c.messageAction, c.messageSurface), | |
| 308 | 334 | "--color-backdrop": withAlpha("#000000", appearance === "dark" ? 0.48 : 0.22), |
| 309 | "--color-drawer": withAlpha(c.sidebar, 0.99), | |
| 335 | "--color-drawer": c.sidebar, | |
| 336 | "--color-drawer-foreground": c.sidebarForeground, | |
| 337 | "--color-drawer-foreground-muted": readableTextColor(c.sidebarMutedForeground, [ | |
| 338 | c.sidebar, | |
| 339 | c.sidebarRowHover, | |
| 340 | ]), | |
| 341 | "--color-drawer-border": c.sidebarBorder, | |
| 310 | 342 | "--color-drawer-shadow": withAlpha("#000000", appearance === "dark" ? 0.32 : 0.12), |
| 311 | 343 | "--color-dot-separator": withAlpha(c.textMuted, 0.35), |
| 312 | 344 | "--color-wordmark": c.text, |
| ... | ... | @@ -318,14 +350,45 @@ export const MOBILE_THEME_VARIABLE_NAMES = Object.keys( |
| 318 | 350 | createMobileThemeVariables(T3_CHAT_THEME.colors, "light"), |
| 319 | 351 | ) as ReadonlyArray<MobileThemeVariable>; |
| 320 | 352 | |
| 353 | export function getMobileThemeColors( | |
| 354 | themeId: SharedMobileThemeId, | |
| 355 | appearance: MobileThemeAppearance, | |
| 356 | ): ThemeColors { | |
| 357 | if (themeId === DEFAULT_MOBILE_THEME_ID) { | |
| 358 | return appearance === "dark" ? T3_CODE_DARK_THEME_COLORS : T3_CODE_LIGHT_THEME_COLORS; | |
| 359 | } | |
| 360 | const theme = BUILT_IN_THEMES.find((candidate) => candidate.id === themeId) ?? T3_CHAT_THEME; | |
| 361 | return getThemeColorsForAppearance(theme, appearance) ?? theme.colors; | |
| 362 | } | |
| 363 | ||
| 321 | 364 | export function getMobileThemeVariables( |
| 322 | themeId: BuiltInThemeId, | |
| 365 | themeId: SharedMobileThemeId, | |
| 323 | 366 | appearance: MobileThemeAppearance, |
| 324 | 367 | overrides: Partial<MobileThemeVariables> | null = null, |
| 325 | 368 | ): MobileThemeVariables { |
| 326 | const theme = BUILT_IN_THEMES.find((candidate) => candidate.id === themeId) ?? T3_CHAT_THEME; | |
| 327 | const colors = getThemeColorsForAppearance(theme, appearance) ?? theme.colors; | |
| 328 | const baseVariables = createMobileThemeVariables(colors, appearance); | |
| 369 | const colors = getMobileThemeColors(themeId, appearance); | |
| 370 | // Mobile settings groups and fallback materials use tonal fills where desktop | |
| 371 | // uses outlined cards. Regular cards retain their shared desktop surface. | |
| 372 | const groupedCard = | |
| 373 | themeId === DEFAULT_MOBILE_THEME_ID | |
| 374 | ? appearance === "light" | |
| 375 | ? colors.toolbarControlHover | |
| 376 | : colors.sidebarRowActive | |
| 377 | : colors.surface; | |
| 378 | const mobileColors = | |
| 379 | themeId === DEFAULT_MOBILE_THEME_ID | |
| 380 | ? { | |
| 381 | ...colors, | |
| 382 | messageSurface: flattenThemeColor( | |
| 383 | themeColorWithAlpha( | |
| 384 | appearance === "dark" ? colors.sidebarRowActive : colors.border, | |
| 385 | 0.3, | |
| 386 | ), | |
| 387 | colors.messageSurface, | |
| 388 | ), | |
| 389 | } | |
| 390 | : colors; | |
| 391 | const baseVariables = createMobileThemeVariables(mobileColors, appearance, groupedCard); | |
| 329 | 392 | |
| 330 | 393 | // The complete base record guarantees that optional overrides cannot leave a token undefined. |
| 331 | 394 | return overrides ? ({ ...baseVariables, ...overrides } as MobileThemeVariables) : baseVariables; |
apps/mobile/src/lib/mobileThemeVariables.test.ts+56-13| ... | ... | @@ -5,11 +5,11 @@ import { getMobileThemeVariables, MOBILE_THEME_IDS, themeColorWithAlpha } from " |
| 5 | 5 | import { getMobileThemeRuntimeVariables } from "./mobileThemeVariables"; |
| 6 | 6 | |
| 7 | 7 | describe("mobile theme runtime variables", () => { |
| 8 | it("derives the standard runtime palette from global.css", () => { | |
| 9 | expect(getMobileThemeRuntimeVariables("t3-code", "light", "ios")).toEqual( | |
| 8 | it("matches the standard base palette to the generated stylesheet", () => { | |
| 9 | expect(getMobileThemeRuntimeVariables("t3-code", "light", "web")).toEqual( | |
| 10 | 10 | readDefaultMobileThemeVariables("light"), |
| 11 | 11 | ); |
| 12 | expect(getMobileThemeRuntimeVariables("t3-code", "dark", "ios")).toEqual( | |
| 12 | expect(getMobileThemeRuntimeVariables("t3-code", "dark", "web")).toEqual( | |
| 13 | 13 | readDefaultMobileThemeVariables("dark"), |
| 14 | 14 | ); |
| 15 | 15 | }); |
| ... | ... | @@ -27,18 +27,22 @@ describe("mobile theme runtime variables", () => { |
| 27 | 27 | "keeps %s colors on Android with an opaque Material frame", |
| 28 | 28 | (themeId) => { |
| 29 | 29 | for (const appearance of ["light", "dark"] as const) { |
| 30 | const ios = getMobileThemeRuntimeVariables(themeId, appearance, "ios"); | |
| 30 | const base = getMobileThemeVariables( | |
| 31 | themeId === "material-you" ? "t3-code" : themeId, | |
| 32 | appearance, | |
| 33 | ); | |
| 31 | 34 | const android = getMobileThemeRuntimeVariables(themeId, appearance, "android"); |
| 32 | 35 | expect(android).toEqual({ |
| 33 | ...ios, | |
| 36 | ...base, | |
| 34 | 37 | "--color-header": themeColorWithAlpha( |
| 35 | ios[ | |
| 38 | base[ | |
| 36 | 39 | themeId === "t3-code" || themeId === "material-you" |
| 37 | ? "--color-card" | |
| 40 | ? "--color-row-hover" | |
| 38 | 41 | : "--color-drawer" |
| 39 | 42 | ], |
| 40 | 43 | 1, |
| 41 | 44 | ), |
| 45 | "--color-header-foreground": base["--color-drawer-foreground"], | |
| 42 | 46 | }); |
| 43 | 47 | expect(android["--color-header"]).toMatch(/^rgba\(\d+, \d+, \d+, 1\)$/); |
| 44 | 48 | } |
| ... | ... | @@ -46,13 +50,52 @@ describe("mobile theme runtime variables", () => { |
| 46 | 50 | ); |
| 47 | 51 | |
| 48 | 52 | it.each(["t3-code", "material-you"] as const)( |
| 49 | "keeps the %s default dark frame distinct from the rounded settings body", | |
| 53 | "keeps the %s default Material frame distinct in both appearances", | |
| 54 | (themeId) => { | |
| 55 | for (const appearance of ["light", "dark"] as const) { | |
| 56 | const variables = getMobileThemeRuntimeVariables(themeId, appearance, "android"); | |
| 57 | expect(variables["--color-header"]).toBe( | |
| 58 | appearance === "light" ? "rgba(244, 244, 245, 1)" : "rgba(20, 20, 20, 1)", | |
| 59 | ); | |
| 60 | for (const pane of ["--color-screen", "--color-sheet-solid", "--color-drawer"] as const) { | |
| 61 | expect(variables["--color-header"]).not.toBe(themeColorWithAlpha(variables[pane], 1)); | |
| 62 | } | |
| 63 | } | |
| 64 | }, | |
| 65 | ); | |
| 66 | ||
| 67 | it.each(["t3-code", "material-you"] as const)( | |
| 68 | "adapts %s iPad chrome without reversing the dark desktop hierarchy", | |
| 50 | 69 | (themeId) => { |
| 51 | const variables = getMobileThemeRuntimeVariables(themeId, "dark", "android"); | |
| 52 | expect(variables["--color-header"]).toBe("rgba(23, 23, 23, 1)"); | |
| 53 | expect(variables["--color-header"]).not.toBe( | |
| 54 | themeColorWithAlpha(variables["--color-sheet-solid"], 1), | |
| 55 | ); | |
| 70 | for (const appearance of ["light", "dark"] as const) { | |
| 71 | const ios = getMobileThemeRuntimeVariables(themeId, appearance, "ios"); | |
| 72 | const android = getMobileThemeRuntimeVariables(themeId, appearance, "android"); | |
| 73 | if (appearance === "light") { | |
| 74 | expect(ios["--color-drawer"]).toBe(android["--color-header"]); | |
| 75 | expect(ios["--color-header"]).toBe(android["--color-header"]); | |
| 76 | expect(ios["--color-header-foreground"]).toBe(ios["--color-drawer-foreground"]); | |
| 77 | } else { | |
| 78 | expect(ios).toEqual(getMobileThemeVariables("t3-code", appearance)); | |
| 79 | expect(ios["--color-drawer"]).toBe(android["--color-drawer"]); | |
| 80 | expect(ios["--color-drawer"]).toBe("#000000"); | |
| 81 | expect(ios["--color-thread-canvas"]).toBe("#0a0a0a"); | |
| 82 | } | |
| 83 | expect(themeColorWithAlpha(ios["--color-thread-hover"], 1)).not.toBe( | |
| 84 | themeColorWithAlpha(ios["--color-drawer"], 1), | |
| 85 | ); | |
| 86 | expect(themeColorWithAlpha(ios["--color-thread-hover"], 1)).not.toBe( | |
| 87 | themeColorWithAlpha(ios["--color-thread-selected"], 1), | |
| 88 | ); | |
| 89 | for (const role of [ | |
| 90 | "--color-screen", | |
| 91 | "--color-thread-canvas", | |
| 92 | "--color-card", | |
| 93 | "--color-composer-surface", | |
| 94 | "--color-grouped-card", | |
| 95 | ] as const) { | |
| 96 | expect(ios[role]).toBe(android[role]); | |
| 97 | } | |
| 98 | } | |
| 56 | 99 | }, |
| 57 | 100 | ); |
| 58 | 101 | }); |
apps/mobile/src/lib/mobileThemeVariables.ts+20-9| ... | ... | @@ -14,8 +14,8 @@ const defaults = defaultThemeVariables as Readonly< |
| 14 | 14 | |
| 15 | 15 | /** |
| 16 | 16 | * Complete palette for native and third-party APIs that cannot consume a |
| 17 | * Uniwind className. The standard palette is generated from global.css; custom | |
| 18 | * palettes share the same source that generates their registered CSS themes. | |
| 17 | * Uniwind className. Every palette shares the source that generates its | |
| 18 | * registered CSS theme. | |
| 19 | 19 | */ |
| 20 | 20 | export function getMobileThemeRuntimeVariables( |
| 21 | 21 | themeId: MobileThemeId, |
| ... | ... | @@ -26,16 +26,27 @@ export function getMobileThemeRuntimeVariables( |
| 26 | 26 | const variables = usesDefaultPalette |
| 27 | 27 | ? defaults[appearance] |
| 28 | 28 | : getMobileThemeVariables(themeId, appearance); |
| 29 | // Android's frame surrounds the sidebar and chat panes. Light iPad sidebars | |
| 30 | // reuse that stronger tonal fill; dark sidebars retain the shared black pane | |
| 31 | // beneath the near-black chat canvas. System colors replace these roles later. | |
| 32 | const frame = themeColorWithAlpha( | |
| 33 | variables[usesDefaultPalette ? "--color-row-hover" : "--color-drawer"], | |
| 34 | 1, | |
| 35 | ); | |
| 36 | if (platform === "ios" && usesDefaultPalette && appearance === "light") { | |
| 37 | return { | |
| 38 | ...variables, | |
| 39 | "--color-header": frame, | |
| 40 | "--color-header-foreground": variables["--color-drawer-foreground"], | |
| 41 | "--color-drawer": frame, | |
| 42 | "--color-drawer-foreground-muted": variables["--color-foreground-muted"], | |
| 43 | }; | |
| 44 | } | |
| 29 | 45 | if (platform !== "android") return variables; |
| 30 | 46 | |
| 31 | // Rounded panes share one opaque frame. The default dark drawer matches the | |
| 32 | // settings body, so use its card tone to keep the rounded edge visible. | |
| 33 | // System colors replace this with their own surfaceContainerHigh afterwards. | |
| 34 | 47 | return { |
| 35 | 48 | ...variables, |
| 36 | "--color-header": themeColorWithAlpha( | |
| 37 | variables[usesDefaultPalette ? "--color-card" : "--color-drawer"], | |
| 38 | 1, | |
| 39 | ), | |
| 49 | "--color-header": frame, | |
| 50 | "--color-header-foreground": variables["--color-drawer-foreground"], | |
| 40 | 51 | }; |
| 41 | 52 | } |
apps/mobile/src/lib/nativeComposerTheme.test.ts created+32| ... | ... | @@ -0,0 +1,32 @@ |
| 1 | import { describe, expect, it } from "vite-plus/test"; | |
| 2 | import { MOBILE_THEME_IDS } from "@t3tools/shared/themePalettes"; | |
| 3 | ||
| 4 | import { getMobileThemeVariables } from "./mobileTheme"; | |
| 5 | import { createNativeComposerTheme } from "./nativeComposerTheme"; | |
| 6 | ||
| 7 | describe("native composer colors", () => { | |
| 8 | it.each(MOBILE_THEME_IDS)("delivers parsable colors for %s in both appearances", (themeId) => { | |
| 9 | for (const appearance of ["light", "dark"] as const) { | |
| 10 | const variables = getMobileThemeVariables(themeId, appearance); | |
| 11 | const theme = createNativeComposerTheme(variables); | |
| 12 | for (const color of Object.values(theme)) expect(color).toMatch(/^#[\da-f]{6}$/i); | |
| 13 | expect(theme.text).toBe(variables["--color-foreground"]); | |
| 14 | expect(theme.skillBorder).not.toBe(theme.skillBackground); | |
| 15 | } | |
| 16 | }); | |
| 17 | ||
| 18 | it("flattens Material You alpha colors over the composer rather than falling back to defaults", () => { | |
| 19 | const theme = createNativeComposerTheme({ | |
| 20 | ...getMobileThemeVariables("t3-code", "light"), | |
| 21 | "--color-screen": "#ffffffff", | |
| 22 | "--color-composer-surface": "#ffffffff", | |
| 23 | "--color-subtle": "#0000000d", | |
| 24 | "--color-placeholder": "#0000009e", | |
| 25 | "--color-inline-skill-background": "#ffffffff", | |
| 26 | "--color-inline-skill-border": "#00000080", | |
| 27 | }); | |
| 28 | expect(theme.chipBackground).toBe("#f2f2f2"); | |
| 29 | expect(theme.placeholder).toBe("#616161"); | |
| 30 | expect(theme.skillBorder).toBe("#7f7f7f"); | |
| 31 | }); | |
| 32 | }); |
apps/mobile/src/lib/nativeComposerTheme.ts created+19| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | import { flattenThemeColor, type MobileThemeVariables } from "./mobileTheme"; | |
| 2 | ||
| 3 | /** Native chip parsers need opaque hex instead of CSS rgba or platform-specific alpha order. */ | |
| 4 | export function createNativeComposerTheme(theme: MobileThemeVariables) { | |
| 5 | const surface = flattenThemeColor(theme["--color-composer-surface"], theme["--color-screen"]); | |
| 6 | const chipBackground = flattenThemeColor(theme["--color-subtle"], surface); | |
| 7 | const skillBackground = flattenThemeColor(theme["--color-inline-skill-background"], surface); | |
| 8 | return { | |
| 9 | text: flattenThemeColor(theme["--color-foreground"], surface), | |
| 10 | placeholder: flattenThemeColor(theme["--color-placeholder"], surface), | |
| 11 | chipBackground, | |
| 12 | chipBorder: flattenThemeColor(theme["--color-border"], chipBackground), | |
| 13 | chipText: flattenThemeColor(theme["--color-foreground"], chipBackground), | |
| 14 | skillBackground, | |
| 15 | skillBorder: flattenThemeColor(theme["--color-inline-skill-border"], skillBackground), | |
| 16 | skillText: flattenThemeColor(theme["--color-inline-skill-foreground"], skillBackground), | |
| 17 | fileTint: flattenThemeColor(theme["--color-icon-muted"], chipBackground), | |
| 18 | }; | |
| 19 | } |
apps/mobile/src/lib/useMobileNavigationTheme.ts+10-7| ... | ... | @@ -10,7 +10,7 @@ import { useUniwindTheme } from "./useUniwindTheme"; |
| 10 | 10 | * preferences provider applies the registered Uniwind theme first, then |
| 11 | 11 | * publishes this matching navigation palette through React. |
| 12 | 12 | */ |
| 13 | export function useMobileNavigationTheme(): Theme { | |
| 13 | export function useMobileNavigationTheme(surface: "screen" | "sidebar" = "screen"): Theme { | |
| 14 | 14 | const { themeAppearance: appearance } = useAppearancePreferences(); |
| 15 | 15 | const variables = useUniwindTheme(); |
| 16 | 16 | return useMemo(() => { |
| ... | ... | @@ -19,13 +19,16 @@ export function useMobileNavigationTheme(): Theme { |
| 19 | 19 | ...base, |
| 20 | 20 | colors: { |
| 21 | 21 | ...base.colors, |
| 22 | primary: variables["--color-primary"], | |
| 23 | background: variables["--color-screen"], | |
| 24 | card: variables["--color-sheet-solid"], | |
| 25 | text: variables["--color-foreground"], | |
| 26 | border: variables["--color-header-border"], | |
| 22 | primary: variables["--color-primary-text"], | |
| 23 | background: variables[surface === "sidebar" ? "--color-drawer" : "--color-screen"], | |
| 24 | card: variables[surface === "sidebar" ? "--color-drawer" : "--color-sheet-solid"], | |
| 25 | text: variables[ | |
| 26 | surface === "sidebar" ? "--color-drawer-foreground" : "--color-header-foreground" | |
| 27 | ], | |
| 28 | border: | |
| 29 | variables[surface === "sidebar" ? "--color-drawer-border" : "--color-header-border"], | |
| 27 | 30 | notification: variables["--color-danger-foreground"], |
| 28 | 31 | }, |
| 29 | 32 | }; |
| 30 | }, [appearance, variables]); | |
| 33 | }, [appearance, surface, variables]); | |
| 31 | 34 | } |
apps/mobile/src/native/SelectableMarkdownText.android.tsx+2-2| ... | ... | @@ -24,8 +24,8 @@ export function hasNativeSelectableMarkdownText(): boolean { |
| 24 | 24 | |
| 25 | 25 | export function SelectableMarkdownText(props: MobileSelectableMarkdownTextProps) { |
| 26 | 26 | const theme = useUniwindTheme(); |
| 27 | const selectionColor = themeColorWithAlpha(theme["--color-primary"], 0.32); | |
| 28 | const selectionHandleColor = theme["--color-primary"]; | |
| 27 | const selectionColor = themeColorWithAlpha(theme["--color-focus"], 0.32); | |
| 28 | const selectionHandleColor = theme["--color-focus"]; | |
| 29 | 29 | const textStyle = useMemo( |
| 30 | 30 | () => ({ selectionColor, selectionHandleColor, ...props.textStyle }), |
| 31 | 31 | [props.textStyle, selectionColor, selectionHandleColor], |
apps/mobile/src/native/T3ComposerEditor.ios.tsx+2-13| ... | ... | @@ -22,7 +22,7 @@ import { |
| 22 | 22 | } from "@t3tools/mobile-markdown-text/markdown"; |
| 23 | 23 | import { resolveMarkdownFileIcon } from "@t3tools/mobile-markdown-text/links"; |
| 24 | 24 | import { useUniwindTheme } from "../lib/useUniwindTheme"; |
| 25 | import { flattenThemeColor } from "../lib/mobileTheme"; | |
| 25 | import { createNativeComposerTheme } from "../lib/nativeComposerTheme"; | |
| 26 | 26 | import { useFontFamily } from "../lib/useFontFamily"; |
| 27 | 27 | import { useScaledTextRole } from "../features/settings/appearance/useScaledTextRole"; |
| 28 | 28 | import { |
| ... | ... | @@ -257,18 +257,7 @@ export function ComposerEditor({ |
| 257 | 257 | }, |
| 258 | 258 | [], |
| 259 | 259 | ); |
| 260 | const themeJson = JSON.stringify({ | |
| 261 | text: theme["--color-foreground"], | |
| 262 | placeholder: theme["--color-placeholder"], | |
| 263 | chipBackground: theme["--color-subtle"], | |
| 264 | // Native chip drawing parses opaque hex only, and this role is translucent. | |
| 265 | chipBorder: flattenThemeColor(theme["--color-border"], theme["--color-user-bubble"]), | |
| 266 | chipText: theme["--color-foreground"], | |
| 267 | skillBackground: theme["--color-inline-skill-background"], | |
| 268 | skillBorder: theme["--color-inline-skill-border"], | |
| 269 | skillText: theme["--color-inline-skill-foreground"], | |
| 270 | fileTint: theme["--color-icon-muted"], | |
| 271 | }); | |
| 260 | const themeJson = JSON.stringify(createNativeComposerTheme(theme)); | |
| 272 | 261 | const resolvedTextStyle = StyleSheet.flatten(textStyle) ?? {}; |
| 273 | 262 | return ( |
| 274 | 263 | <NativeView |
apps/mobile/src/native/T3ComposerEditor.native.tsx+3-12| ... | ... | @@ -27,7 +27,7 @@ import { useNativePaste } from "../lib/useNativePaste"; |
| 27 | 27 | import { useFontFamily } from "../lib/useFontFamily"; |
| 28 | 28 | import { useAppearancePreferences } from "../features/settings/appearance/AppearancePreferencesProvider"; |
| 29 | 29 | import { useUniwindTheme } from "../lib/useUniwindTheme"; |
| 30 | import { flattenThemeColor } from "../lib/mobileTheme"; | |
| 30 | import { createNativeComposerTheme } from "../lib/nativeComposerTheme"; | |
| 31 | 31 | import { |
| 32 | 32 | acknowledgeComposerNativeEvent, |
| 33 | 33 | assumeComposerControlledState, |
| ... | ... | @@ -259,17 +259,8 @@ export function ComposerEditor({ |
| 259 | 259 | ); |
| 260 | 260 | const { systemColorsActive } = useAppearancePreferences(); |
| 261 | 261 | const themeJson = JSON.stringify({ |
| 262 | selection: Platform.OS === "android" || systemColorsActive ? theme["--color-primary"] : null, | |
| 263 | text: theme["--color-foreground"], | |
| 264 | placeholder: theme["--color-placeholder"], | |
| 265 | chipBackground: theme["--color-subtle"], | |
| 266 | // Native chip drawing parses opaque hex only, and this role is translucent. | |
| 267 | chipBorder: flattenThemeColor(theme["--color-border"], theme["--color-user-bubble"]), | |
| 268 | chipText: theme["--color-foreground"], | |
| 269 | skillBackground: theme["--color-inline-skill-background"], | |
| 270 | skillBorder: theme["--color-inline-skill-border"], | |
| 271 | skillText: theme["--color-inline-skill-foreground"], | |
| 272 | fileTint: theme["--color-icon-muted"], | |
| 262 | ...createNativeComposerTheme(theme), | |
| 263 | selection: Platform.OS === "android" || systemColorsActive ? theme["--color-focus"] : null, | |
| 273 | 264 | }); |
| 274 | 265 | const resolvedTextStyle = StyleSheet.flatten(textStyle) ?? {}; |
| 275 | 266 | const regularFontFamily = useFontFamily("regular"); |
apps/web/src/themePalette.test.ts+15| ... | ... | @@ -103,6 +103,21 @@ describe("theme files", () => { |
| 103 | 103 | }); |
| 104 | 104 | }); |
| 105 | 105 | |
| 106 | it("keeps the stock sidebar and chat on distinct surfaces in both appearances", () => { | |
| 107 | expectThemeColors(getStandardThemeColors("light"), { | |
| 108 | canvas: "#fcfcfc", | |
| 109 | sidebar: "#fafafa", | |
| 110 | sidebarRowActive: "#ffffff", | |
| 111 | messageSurface: "#f4f4f5", | |
| 112 | }); | |
| 113 | expectThemeColors(getStandardThemeColors("dark"), { | |
| 114 | canvas: "#0a0a0a", | |
| 115 | sidebar: "#000000", | |
| 116 | sidebarRowActive: "#1a1b1b", | |
| 117 | messageSurface: "#141414", | |
| 118 | }); | |
| 119 | }); | |
| 120 | ||
| 106 | 121 | it("derives readable, distinctive vivid palettes from exact seeds", () => { |
| 107 | 122 | const seeds: ReadonlyArray<["light" | "dark", string, string]> = [ |
| 108 | 123 | ["light", "#f4f9f2", "#1d8a4e"], |
apps/web/src/themePalette.ts+2-127| ... | ... | @@ -9,6 +9,8 @@ import { |
| 9 | 9 | IRIS_THEME, |
| 10 | 10 | OCEAN_THEME, |
| 11 | 11 | T3_CHAT_THEME, |
| 12 | T3_CODE_LIGHT_THEME_COLORS, | |
| 13 | T3_CODE_DARK_THEME_COLORS, | |
| 12 | 14 | RESERVED_THEME_IDS, |
| 13 | 15 | THEME_COLOR_ROLES, |
| 14 | 16 | type ThemeAppearance, |
| ... | ... | @@ -326,133 +328,6 @@ function legacyThemeMode(theme: ThemePreference): ThemeAppearance | null { |
| 326 | 328 | return theme === LEGACY_T3_CHAT_DARK_THEME_ID ? "dark" : null; |
| 327 | 329 | } |
| 328 | 330 | |
| 329 | /** | |
| 330 | * The palette T3 Code wears with no theme installed, captured from the app's | |
| 331 | * stock tokens (index.css) so a draft seeded from the default look paints the | |
| 332 | * pixels the user is already seeing. Alpha-bearing tokens are flattened over | |
| 333 | * their real backdrops (canvas, or the sidebar for its rows) because theme | |
| 334 | * colors are stored as opaque OKLCH tokens. | |
| 335 | */ | |
| 336 | const T3_CODE_LIGHT_THEME_COLORS: ThemeColors = { | |
| 337 | canvas: "#fcfcfc", | |
| 338 | chrome: "#fcfcfc", | |
| 339 | toolbar: "#fcfcfc", | |
| 340 | toolbarForeground: "#27272a", | |
| 341 | toolbarBorder: "#e4e4e7", | |
| 342 | toolbarControl: "#ffffff", | |
| 343 | toolbarControlForeground: "#27272a", | |
| 344 | toolbarControlHover: "#f4f4f5", | |
| 345 | surface: "#ffffff", | |
| 346 | surfaceRaised: "#fcfcfc", | |
| 347 | surfaceOverlay: "#ffffff", | |
| 348 | text: "#27272a", | |
| 349 | textMuted: "#71717b", | |
| 350 | border: "#e4e4e7", | |
| 351 | input: "#d4d4d8", | |
| 352 | focus: "#1b4ed8", | |
| 353 | accent: "#1b4ed8", | |
| 354 | accentForeground: "#ffffff", | |
| 355 | secondary: "#fafafa", | |
| 356 | secondaryForeground: "#27272a", | |
| 357 | muted: "#fafafa", | |
| 358 | mutedForeground: "#71717b", | |
| 359 | placeholder: "#71717b", | |
| 360 | secondaryLabel: "#71717b", | |
| 361 | iconMuted: "#71717b", | |
| 362 | error: "#fb2c36", | |
| 363 | errorForeground: "#c10007", | |
| 364 | errorSurface: "#fcebec", | |
| 365 | warning: "#fe9a00", | |
| 366 | warningForeground: "#bb4d00", | |
| 367 | warningSurface: "#fcf4e8", | |
| 368 | update: "#1b4ed8", | |
| 369 | updateForeground: "#1b4ed8", | |
| 370 | updateSurface: "#e0e6f7", | |
| 371 | accentSurface: "#f4f4f5", | |
| 372 | accentSurfaceForeground: "#18181b", | |
| 373 | messageSurface: "#f4f4f5", | |
| 374 | messageForeground: "#27272a", | |
| 375 | messageAction: "#1b4ed8", | |
| 376 | messageActionForeground: "#ffffff", | |
| 377 | messageActionHover: "#3160db", | |
| 378 | codeBackground: "#ffffff", | |
| 379 | codeForeground: "#27272a", | |
| 380 | sidebar: "#fafafa", | |
| 381 | sidebarForeground: "#27272a", | |
| 382 | sidebarMutedForeground: "#71717b", | |
| 383 | sidebarControlSurface: "#f4f4f5", | |
| 384 | sidebarRowHover: "#fcfcfc", | |
| 385 | sidebarRowActive: "#ffffff", | |
| 386 | sidebarRowSelected: "#ffffff", | |
| 387 | sidebarBorder: "#e4e4e7", | |
| 388 | terminalBackground: "#fcfcfc", | |
| 389 | terminalForeground: "#27272a", | |
| 390 | terminalCursor: "#26384e", | |
| 391 | terminalSelection: "#d0d6dd", | |
| 392 | terminalScrollbar: "#d6d6d6", | |
| 393 | terminalScrollbarHover: "#bdbdbd", | |
| 394 | }; | |
| 395 | ||
| 396 | const T3_CODE_DARK_THEME_COLORS: ThemeColors = { | |
| 397 | canvas: "#0a0a0a", | |
| 398 | chrome: "#0a0a0a", | |
| 399 | toolbar: "#0a0a0a", | |
| 400 | toolbarForeground: "#f5f5f5", | |
| 401 | toolbarBorder: "#191919", | |
| 402 | toolbarControl: "#111111", | |
| 403 | toolbarControlForeground: "#f5f5f5", | |
| 404 | toolbarControlHover: "#141414", | |
| 405 | surface: "#111111", | |
| 406 | surfaceRaised: "#111111", | |
| 407 | surfaceOverlay: "#111111", | |
| 408 | text: "#f5f5f5", | |
| 409 | textMuted: "#818181", | |
| 410 | border: "#191919", | |
| 411 | input: "#1e1e1e", | |
| 412 | focus: "#346bf1", | |
| 413 | accent: "#346bf1", | |
| 414 | accentForeground: "#ffffff", | |
| 415 | secondary: "#111111", | |
| 416 | secondaryForeground: "#f5f5f5", | |
| 417 | muted: "#111111", | |
| 418 | mutedForeground: "#818181", | |
| 419 | placeholder: "#818181", | |
| 420 | secondaryLabel: "#818181", | |
| 421 | iconMuted: "#818181", | |
| 422 | error: "#fb414a", | |
| 423 | errorForeground: "#ff6467", | |
| 424 | errorSurface: "#301214", | |
| 425 | warning: "#fe9a00", | |
| 426 | warningForeground: "#ffb900", | |
| 427 | warningSurface: "#312108", | |
| 428 | update: "#346bf1", | |
| 429 | updateForeground: "#51a2ff", | |
| 430 | updateSurface: "#121b34", | |
| 431 | accentSurface: "#141414", | |
| 432 | accentSurfaceForeground: "#f5f5f5", | |
| 433 | messageSurface: "#141414", | |
| 434 | messageForeground: "#f5f5f5", | |
| 435 | messageAction: "#346bf1", | |
| 436 | messageActionForeground: "#ffffff", | |
| 437 | messageActionHover: "#3061d9", | |
| 438 | codeBackground: "#111111", | |
| 439 | codeForeground: "#f5f5f5", | |
| 440 | sidebar: "#000000", | |
| 441 | sidebarForeground: "#f1f3f7", | |
| 442 | sidebarMutedForeground: "#a3a3a3", | |
| 443 | sidebarControlSurface: "#0a0a0a", | |
| 444 | sidebarRowHover: "#131313", | |
| 445 | sidebarRowActive: "#1a1b1b", | |
| 446 | sidebarRowSelected: "#111111", | |
| 447 | sidebarBorder: "#141414", | |
| 448 | terminalBackground: "#0a0a0a", | |
| 449 | terminalForeground: "#f5f5f5", | |
| 450 | terminalCursor: "#b4cbff", | |
| 451 | terminalSelection: "#343a47", | |
| 452 | terminalScrollbar: "#222222", | |
| 453 | terminalScrollbarHover: "#363636", | |
| 454 | }; | |
| 455 | ||
| 456 | 331 | /** |
| 457 | 332 | * The standard T3 Code look as a theme palette, for seeding a new theme when |
| 458 | 333 | * no theme is installed. Distinct from {@link getDefaultThemeColors}, which |
packages/shared/src/themePalettes.ts+128-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | export const BUILT_IN_THEME_IDS = ["t3-chat", "grove", "ocean", "ember", "iris"] as const; |
| 2 | 2 | |
| 3 | /** The mobile app's own hand-tuned palette, which is not part of the built-in library. */ | |
| 3 | /** The standard T3 Code palette, kept separate from the optional built-in theme library. */ | |
| 4 | 4 | export const MOBILE_DEFAULT_THEME_ID = "t3-code"; |
| 5 | 5 | |
| 6 | 6 | /** |
| ... | ... | @@ -121,6 +121,133 @@ export type ThemeDefinition = Readonly<{ |
| 121 | 121 | managed?: boolean; |
| 122 | 122 | }>; |
| 123 | 123 | |
| 124 | /** | |
| 125 | * The palette T3 Code wears with no theme installed, captured from the app's | |
| 126 | * stock tokens (index.css) so a draft seeded from the default look paints the | |
| 127 | * pixels the user is already seeing. Alpha-bearing tokens are flattened over | |
| 128 | * their real backdrops (canvas, or the sidebar for its rows) because theme | |
| 129 | * colors are stored as opaque OKLCH tokens. | |
| 130 | */ | |
| 131 | export const T3_CODE_LIGHT_THEME_COLORS: ThemeColors = { | |
| 132 | canvas: "#fcfcfc", | |
| 133 | chrome: "#fcfcfc", | |
| 134 | toolbar: "#fcfcfc", | |
| 135 | toolbarForeground: "#27272a", | |
| 136 | toolbarBorder: "#e4e4e7", | |
| 137 | toolbarControl: "#ffffff", | |
| 138 | toolbarControlForeground: "#27272a", | |
| 139 | toolbarControlHover: "#f4f4f5", | |
| 140 | surface: "#ffffff", | |
| 141 | surfaceRaised: "#fcfcfc", | |
| 142 | surfaceOverlay: "#ffffff", | |
| 143 | text: "#27272a", | |
| 144 | textMuted: "#71717b", | |
| 145 | border: "#e4e4e7", | |
| 146 | input: "#d4d4d8", | |
| 147 | focus: "#1b4ed8", | |
| 148 | accent: "#1b4ed8", | |
| 149 | accentForeground: "#ffffff", | |
| 150 | secondary: "#fafafa", | |
| 151 | secondaryForeground: "#27272a", | |
| 152 | muted: "#fafafa", | |
| 153 | mutedForeground: "#71717b", | |
| 154 | placeholder: "#71717b", | |
| 155 | secondaryLabel: "#71717b", | |
| 156 | iconMuted: "#71717b", | |
| 157 | error: "#fb2c36", | |
| 158 | errorForeground: "#c10007", | |
| 159 | errorSurface: "#fcebec", | |
| 160 | warning: "#fe9a00", | |
| 161 | warningForeground: "#bb4d00", | |
| 162 | warningSurface: "#fcf4e8", | |
| 163 | update: "#1b4ed8", | |
| 164 | updateForeground: "#1b4ed8", | |
| 165 | updateSurface: "#e0e6f7", | |
| 166 | accentSurface: "#f4f4f5", | |
| 167 | accentSurfaceForeground: "#18181b", | |
| 168 | messageSurface: "#f4f4f5", | |
| 169 | messageForeground: "#27272a", | |
| 170 | messageAction: "#1b4ed8", | |
| 171 | messageActionForeground: "#ffffff", | |
| 172 | messageActionHover: "#3160db", | |
| 173 | codeBackground: "#ffffff", | |
| 174 | codeForeground: "#27272a", | |
| 175 | sidebar: "#fafafa", | |
| 176 | sidebarForeground: "#27272a", | |
| 177 | sidebarMutedForeground: "#71717b", | |
| 178 | sidebarControlSurface: "#f4f4f5", | |
| 179 | sidebarRowHover: "#fcfcfc", | |
| 180 | sidebarRowActive: "#ffffff", | |
| 181 | sidebarRowSelected: "#ffffff", | |
| 182 | sidebarBorder: "#e4e4e7", | |
| 183 | terminalBackground: "#fcfcfc", | |
| 184 | terminalForeground: "#27272a", | |
| 185 | terminalCursor: "#26384e", | |
| 186 | terminalSelection: "#d0d6dd", | |
| 187 | terminalScrollbar: "#d6d6d6", | |
| 188 | terminalScrollbarHover: "#bdbdbd", | |
| 189 | }; | |
| 190 | ||
| 191 | export const T3_CODE_DARK_THEME_COLORS: ThemeColors = { | |
| 192 | canvas: "#0a0a0a", | |
| 193 | chrome: "#0a0a0a", | |
| 194 | toolbar: "#0a0a0a", | |
| 195 | toolbarForeground: "#f5f5f5", | |
| 196 | toolbarBorder: "#191919", | |
| 197 | toolbarControl: "#111111", | |
| 198 | toolbarControlForeground: "#f5f5f5", | |
| 199 | toolbarControlHover: "#141414", | |
| 200 | surface: "#111111", | |
| 201 | surfaceRaised: "#111111", | |
| 202 | surfaceOverlay: "#111111", | |
| 203 | text: "#f5f5f5", | |
| 204 | textMuted: "#818181", | |
| 205 | border: "#191919", | |
| 206 | input: "#1e1e1e", | |
| 207 | focus: "#346bf1", | |
| 208 | accent: "#346bf1", | |
| 209 | accentForeground: "#ffffff", | |
| 210 | secondary: "#111111", | |
| 211 | secondaryForeground: "#f5f5f5", | |
| 212 | muted: "#111111", | |
| 213 | mutedForeground: "#818181", | |
| 214 | placeholder: "#818181", | |
| 215 | secondaryLabel: "#818181", | |
| 216 | iconMuted: "#818181", | |
| 217 | error: "#fb414a", | |
| 218 | errorForeground: "#ff6467", | |
| 219 | errorSurface: "#301214", | |
| 220 | warning: "#fe9a00", | |
| 221 | warningForeground: "#ffb900", | |
| 222 | warningSurface: "#312108", | |
| 223 | update: "#346bf1", | |
| 224 | updateForeground: "#51a2ff", | |
| 225 | updateSurface: "#121b34", | |
| 226 | accentSurface: "#141414", | |
| 227 | accentSurfaceForeground: "#f5f5f5", | |
| 228 | messageSurface: "#141414", | |
| 229 | messageForeground: "#f5f5f5", | |
| 230 | messageAction: "#346bf1", | |
| 231 | messageActionForeground: "#ffffff", | |
| 232 | messageActionHover: "#3061d9", | |
| 233 | codeBackground: "#111111", | |
| 234 | codeForeground: "#f5f5f5", | |
| 235 | sidebar: "#000000", | |
| 236 | sidebarForeground: "#f1f3f7", | |
| 237 | sidebarMutedForeground: "#a3a3a3", | |
| 238 | sidebarControlSurface: "#0a0a0a", | |
| 239 | sidebarRowHover: "#131313", | |
| 240 | sidebarRowActive: "#1a1b1b", | |
| 241 | sidebarRowSelected: "#111111", | |
| 242 | sidebarBorder: "#141414", | |
| 243 | terminalBackground: "#0a0a0a", | |
| 244 | terminalForeground: "#f5f5f5", | |
| 245 | terminalCursor: "#b4cbff", | |
| 246 | terminalSelection: "#343a47", | |
| 247 | terminalScrollbar: "#222222", | |
| 248 | terminalScrollbarHover: "#363636", | |
| 249 | }; | |
| 250 | ||
| 124 | 251 | export const T3_CHAT_THEME: ThemeDefinition = { |
| 125 | 252 | id: "t3-chat", |
| 126 | 253 | label: "T3 Chat", |