| 1 | import { Hct, argbFromHex, hexFromArgb } from "@material/material-color-utilities"; |
| 2 | |
| 3 | import type { MobileThemeAppearance, MobileThemeVariables } from "./mobileTheme"; |
| 4 | import type { MaterialYouPalette } from "./materialYouPalette"; |
| 5 | |
| 6 | function withAlpha(color: string, alpha: number): string { |
| 7 | const match = /^#([\da-f]{6})(?:[\da-f]{2})?$/iu.exec(color); |
| 8 | if (!match?.[1]) return color; |
| 9 | const alphaByte = Math.round(Math.min(1, Math.max(0, alpha)) * 255) |
| 10 | .toString(16) |
| 11 | .padStart(2, "0") |
| 12 | .toUpperCase(); |
| 13 | return `#${match[1]}${alphaByte}`; |
| 14 | } |
| 15 | |
| 16 | function materialTone(color: string, tone: number): string { |
| 17 | const match = /^#([\da-f]{6})(?:[\da-f]{2})?$/iu.exec(color); |
| 18 | if (!match?.[1]) return color; |
| 19 | |
| 20 | const source = Hct.fromInt(argbFromHex(match[1])); |
| 21 | return `${hexFromArgb(Hct.from(source.hue, source.chroma, tone).toInt()).toUpperCase()}FF`; |
| 22 | } |
| 23 | |
| 24 | export function materialYouPaletteToMobileThemeVariables( |
| 25 | palette: MaterialYouPalette, |
| 26 | appearance: MobileThemeAppearance, |
| 27 | base: MobileThemeVariables, |
| 28 | ): MobileThemeVariables { |
| 29 | const dark = appearance === "dark"; |
| 30 | const userBubble = dark ? materialTone(palette.inversePrimary, 30) : palette.primary; |
| 31 | const userBubbleForeground = dark ? materialTone(palette.inversePrimary, 90) : palette.onPrimary; |
| 32 | |
| 33 | return { |
| 34 | ...base, |
| 35 | "--color-screen": palette.surface, |
| 36 | "--color-sheet": withAlpha(palette.surfaceContainerLow, 0.98), |
| 37 | "--color-sheet-solid": palette.surfaceContainerLow, |
| 38 | "--color-card": palette.surfaceContainer, |
| 39 | "--color-grouped-card": palette.surfaceContainer, |
| 40 | "--color-card-alt": palette.surfaceContainerHigh, |
| 41 | "--color-card-translucent": withAlpha(palette.surfaceContainer, 0.8), |
| 42 | "--color-thread-canvas": palette.surfaceContainerLow, |
| 43 | "--color-thread-selected": palette.surfaceContainer, |
| 44 | "--color-thread-selected-foreground": palette.onSurface, |
| 45 | "--color-thread-selected-foreground-muted": palette.onSurfaceVariant, |
| 46 | "--color-thread-hover": palette.surfaceContainerHigh, |
| 47 | "--color-row-hover": palette.surfaceContainerHigh, |
| 48 | "--color-composer-panel": withAlpha(palette.surfaceContainerLow, dark ? 0.92 : 0.88), |
| 49 | "--color-composer-surface": withAlpha(palette.surfaceContainerHigh, dark ? 0.9 : 0.94), |
| 50 | "--color-composer-border": withAlpha(palette.outlineVariant, dark ? 0.34 : 0.46), |
| 51 | "--color-foreground": palette.onSurface, |
| 52 | "--color-foreground-secondary": palette.onSurfaceVariant, |
| 53 | "--color-foreground-muted": palette.onSurfaceVariant, |
| 54 | "--color-foreground-tertiary": withAlpha(palette.onSurfaceVariant, 0.58), |
| 55 | "--color-border": withAlpha(palette.outlineVariant, dark ? 0.5 : 0.65), |
| 56 | "--color-focus": palette.primary, |
| 57 | "--color-border-subtle": withAlpha(palette.outlineVariant, dark ? 0.34 : 0.46), |
| 58 | "--color-separator": withAlpha(palette.outlineVariant, dark ? 0.24 : 0.34), |
| 59 | "--color-subtle": withAlpha(palette.onSurface, 0.05), |
| 60 | "--color-subtle-strong": withAlpha(palette.onSurface, 0.1), |
| 61 | "--color-inline-skill-background": withAlpha(palette.tertiaryContainer, 0.52), |
| 62 | "--color-inline-skill-border": withAlpha(palette.tertiary, 0.38), |
| 63 | "--color-inline-skill-foreground": palette.onTertiaryContainer, |
| 64 | "--color-primary": palette.primary, |
| 65 | "--color-primary-foreground": palette.onPrimary, |
| 66 | "--color-primary-text": palette.primary, |
| 67 | "--color-primary-shadow": withAlpha(palette.scrim, dark ? 0.22 : 0.18), |
| 68 | "--color-secondary": palette.secondaryContainer, |
| 69 | "--color-secondary-foreground": palette.onSecondaryContainer, |
| 70 | "--color-secondary-border": withAlpha(palette.outlineVariant, dark ? 0.42 : 0.56), |
| 71 | "--color-switch-active-track": palette.primary, |
| 72 | "--color-switch-active-thumb": palette.onPrimary, |
| 73 | "--color-switch-inactive-track": palette.surfaceContainerHighest, |
| 74 | "--color-switch-inactive-thumb": palette.outline, |
| 75 | "--color-danger": palette.errorContainer, |
| 76 | "--color-danger-border": withAlpha(palette.onErrorContainer, dark ? 0.42 : 0.5), |
| 77 | "--color-danger-foreground": palette.onErrorContainer, |
| 78 | "--color-update": palette.primaryContainer, |
| 79 | "--color-update-foreground": palette.onPrimaryContainer, |
| 80 | "--color-input": palette.surfaceContainerLowest, |
| 81 | "--color-input-border": withAlpha(palette.outline, dark ? 0.42 : 0.5), |
| 82 | "--color-sidebar-search": palette.surfaceContainerLowest, |
| 83 | "--color-placeholder": withAlpha(palette.onSurface, 0.62), |
| 84 | "--color-icon": palette.onSurface, |
| 85 | "--color-icon-muted": palette.onSurfaceVariant, |
| 86 | "--color-icon-subtle": withAlpha(palette.onSurfaceVariant, 0.68), |
| 87 | "--color-header": palette.surfaceContainerHigh, |
| 88 | "--color-header-foreground": palette.onSurface, |
| 89 | "--color-header-border": withAlpha(palette.outlineVariant, dark ? 0.36 : 0.48), |
| 90 | "--color-glass-surface": withAlpha(palette.surfaceContainerHigh, dark ? 0.78 : 0.72), |
| 91 | "--color-glass-fallback": withAlpha(palette.surfaceContainerHigh, dark ? 0.9 : 0.94), |
| 92 | "--color-glass-tint": withAlpha(palette.primaryContainer, dark ? 0.24 : 0.18), |
| 93 | "--color-status-bar": palette.surfaceContainerLow, |
| 94 | "--color-md-body": materialTone(palette.onSurfaceVariant, dark ? 90 : 10), |
| 95 | "--color-md-strong": materialTone(palette.onSurfaceVariant, dark ? 90 : 10), |
| 96 | "--color-md-link": palette.primary, |
| 97 | "--color-md-blockquote-border": withAlpha(palette.primary, dark ? 0.34 : 0.26), |
| 98 | "--color-md-blockquote-bg": withAlpha(palette.primaryContainer, dark ? 0.14 : 0.2), |
| 99 | "--color-md-code-bg": withAlpha(palette.onSurface, dark ? 0.08 : 0.06), |
| 100 | "--color-md-code-text": palette.onSurface, |
| 101 | "--color-md-user-code-bg": withAlpha(userBubbleForeground, dark ? 0.18 : 0.22), |
| 102 | "--color-md-user-code-text": userBubbleForeground, |
| 103 | "--color-md-user-fence-bg": withAlpha(userBubbleForeground, dark ? 0.14 : 0.18), |
| 104 | "--color-md-user-fence-text": userBubbleForeground, |
| 105 | "--color-md-hr": withAlpha(palette.outlineVariant, dark ? 0.48 : 0.6), |
| 106 | "--color-user-bubble": userBubble, |
| 107 | "--color-user-bubble-foreground": userBubbleForeground, |
| 108 | "--color-user-bubble-foreground-muted": withAlpha(userBubbleForeground, 0.78), |
| 109 | "--color-user-bubble-skill-foreground": userBubbleForeground, |
| 110 | "--color-backdrop": withAlpha(palette.scrim, dark ? 0.48 : 0.22), |
| 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), |
| 115 | "--color-drawer-shadow": withAlpha(palette.scrim, dark ? 0.32 : 0.12), |
| 116 | "--color-dot-separator": withAlpha(palette.onSurface, 0.2), |
| 117 | "--color-wordmark": palette.onSurface, |
| 118 | "--color-chevron": withAlpha(palette.onSurface, 0.2), |
| 119 | }; |
| 120 | } |