diff --git a/apps/mobile/src/lib/materialYouTheme.android.ts b/apps/mobile/src/lib/materialYouTheme.android.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce16b23c89ecc5cc8cf409025c7cad77fa2fab37 --- /dev/null +++ b/apps/mobile/src/lib/materialYouTheme.android.ts @@ -0,0 +1,108 @@ +import { Hct, argbFromHex, hexFromArgb } from "@material/material-color-utilities"; + +import type { MobileThemeAppearance, MobileThemeVariables } from "./mobileTheme"; +import type { MaterialYouPalette } from "./materialYouPalette"; + +function withAlpha(color: string, alpha: number): string { + const match = /^#([\da-f]{6})(?:[\da-f]{2})?$/iu.exec(color); + if (!match?.[1]) return color; + const alphaByte = Math.round(Math.min(1, Math.max(0, alpha)) * 255) + .toString(16) + .padStart(2, "0") + .toUpperCase(); + return `#${match[1]}${alphaByte}`; +} + +function materialTone(color: string, tone: number): string { + const match = /^#([\da-f]{6})(?:[\da-f]{2})?$/iu.exec(color); + if (!match?.[1]) return color; + + const source = Hct.fromInt(argbFromHex(match[1])); + return `${hexFromArgb(Hct.from(source.hue, source.chroma, tone).toInt()).toUpperCase()}FF`; +} + +export function materialYouPaletteToMobileThemeVariables( + palette: MaterialYouPalette, + appearance: MobileThemeAppearance, + base: MobileThemeVariables, +): MobileThemeVariables { + const dark = appearance === "dark"; + const userBubble = dark ? materialTone(palette.inversePrimary, 30) : palette.primary; + const userBubbleForeground = dark ? materialTone(palette.inversePrimary, 90) : palette.onPrimary; + + return { + ...base, + "--color-screen": palette.surface, + "--color-sheet": withAlpha(palette.surfaceContainerLow, 0.98), + "--color-sheet-solid": palette.surfaceContainerLow, + "--color-card": palette.surfaceContainer, + "--color-card-alt": palette.surfaceContainerHigh, + "--color-card-translucent": withAlpha(palette.surfaceContainer, 0.8), + "--color-thread-canvas": palette.surfaceContainerLow, + "--color-thread-selected": palette.surfaceContainer, + "--color-thread-selected-foreground": palette.onSurface, + "--color-thread-selected-foreground-muted": palette.onSurfaceVariant, + "--color-composer-panel": withAlpha(palette.surfaceContainerLow, dark ? 0.92 : 0.88), + "--color-composer-surface": withAlpha(palette.surfaceContainerHigh, dark ? 0.9 : 0.94), + "--color-composer-border": withAlpha(palette.outlineVariant, dark ? 0.34 : 0.46), + "--color-foreground": palette.onSurface, + "--color-foreground-secondary": palette.onSurfaceVariant, + "--color-foreground-muted": palette.onSurfaceVariant, + "--color-foreground-tertiary": withAlpha(palette.onSurfaceVariant, 0.58), + "--color-border": withAlpha(palette.outlineVariant, dark ? 0.5 : 0.65), + "--color-border-subtle": withAlpha(palette.outlineVariant, dark ? 0.34 : 0.46), + "--color-separator": withAlpha(palette.outlineVariant, dark ? 0.24 : 0.34), + "--color-subtle": withAlpha(palette.onSurface, 0.05), + "--color-subtle-strong": withAlpha(palette.onSurface, 0.1), + "--color-inline-skill-background": withAlpha(palette.tertiaryContainer, 0.52), + "--color-inline-skill-border": withAlpha(palette.tertiary, 0.38), + "--color-inline-skill-foreground": palette.onTertiaryContainer, + "--color-primary": palette.primary, + "--color-primary-foreground": palette.onPrimary, + "--color-primary-shadow": withAlpha(palette.scrim, dark ? 0.22 : 0.18), + "--color-secondary": palette.secondaryContainer, + "--color-secondary-foreground": palette.onSecondaryContainer, + "--color-secondary-border": withAlpha(palette.outlineVariant, dark ? 0.42 : 0.56), + "--color-switch-active-track": palette.primary, + "--color-switch-active-thumb": palette.onPrimary, + "--color-switch-inactive-track": palette.surfaceContainerHighest, + "--color-switch-inactive-thumb": palette.outline, + "--color-danger": palette.errorContainer, + "--color-danger-border": withAlpha(palette.onErrorContainer, dark ? 0.42 : 0.5), + "--color-danger-foreground": palette.onErrorContainer, + "--color-input": palette.surfaceContainerLowest, + "--color-input-border": withAlpha(palette.outline, dark ? 0.42 : 0.5), + "--color-sidebar-search": palette.surfaceContainerLowest, + "--color-placeholder": withAlpha(palette.onSurface, 0.62), + "--color-icon": palette.onSurface, + "--color-icon-muted": palette.onSurfaceVariant, + "--color-icon-subtle": withAlpha(palette.onSurfaceVariant, 0.68), + "--color-header": palette.surfaceContainerHigh, + "--color-header-border": withAlpha(palette.outlineVariant, dark ? 0.36 : 0.48), + "--color-glass-surface": withAlpha(palette.surfaceContainerHigh, dark ? 0.78 : 0.72), + "--color-glass-tint": withAlpha(palette.primaryContainer, dark ? 0.24 : 0.18), + "--color-status-bar": palette.surfaceContainerLow, + "--color-md-body": materialTone(palette.onSurfaceVariant, dark ? 90 : 10), + "--color-md-strong": materialTone(palette.onSurfaceVariant, dark ? 90 : 10), + "--color-md-link": palette.primary, + "--color-md-blockquote-border": withAlpha(palette.primary, dark ? 0.34 : 0.26), + "--color-md-blockquote-bg": withAlpha(palette.primaryContainer, dark ? 0.14 : 0.2), + "--color-md-code-bg": withAlpha(palette.onSurface, dark ? 0.08 : 0.06), + "--color-md-code-text": palette.onSurface, + "--color-md-user-code-bg": withAlpha(userBubbleForeground, dark ? 0.18 : 0.22), + "--color-md-user-code-text": userBubbleForeground, + "--color-md-user-fence-bg": withAlpha(userBubbleForeground, dark ? 0.14 : 0.18), + "--color-md-user-fence-text": userBubbleForeground, + "--color-md-hr": withAlpha(palette.outlineVariant, dark ? 0.48 : 0.6), + "--color-user-bubble": userBubble, + "--color-user-bubble-foreground": userBubbleForeground, + "--color-user-bubble-foreground-muted": withAlpha(userBubbleForeground, 0.78), + "--color-user-bubble-skill-foreground": userBubbleForeground, + "--color-backdrop": withAlpha(palette.scrim, dark ? 0.48 : 0.22), + "--color-drawer": palette.surface, + "--color-drawer-shadow": withAlpha(palette.scrim, dark ? 0.32 : 0.12), + "--color-dot-separator": withAlpha(palette.onSurface, 0.2), + "--color-wordmark": palette.onSurface, + "--color-chevron": withAlpha(palette.onSurface, 0.2), + }; +} diff --git a/apps/mobile/src/lib/materialYouTheme.test.ts b/apps/mobile/src/lib/materialYouTheme.test.ts index 16618ea55d48c0bd5505aff404c0f769fb126ee3..dc99f9badecda70fb1a504bac42a0dea5846f07b 100644 --- a/apps/mobile/src/lib/materialYouTheme.test.ts +++ b/apps/mobile/src/lib/materialYouTheme.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vite-plus/test"; import { getMobileThemeRuntimeVariables } from "./mobileThemeVariables"; import type { MaterialYouPalette } from "./materialYouPalette"; -import { materialYouPaletteToMobileThemeVariables } from "./materialYouTheme"; +import { materialYouPaletteToMobileThemeVariables } from "./materialYouTheme.android"; const palette: MaterialYouPalette = { primary: "#6750A4FF", diff --git a/apps/mobile/src/lib/materialYouTheme.ts b/apps/mobile/src/lib/materialYouTheme.ts index ce16b23c89ecc5cc8cf409025c7cad77fa2fab37..64d4338d1140048754e01bfcd582e81f796e6ab9 100644 --- a/apps/mobile/src/lib/materialYouTheme.ts +++ b/apps/mobile/src/lib/materialYouTheme.ts @@ -1,108 +1,10 @@ -import { Hct, argbFromHex, hexFromArgb } from "@material/material-color-utilities"; - import type { MobileThemeAppearance, MobileThemeVariables } from "./mobileTheme"; import type { MaterialYouPalette } from "./materialYouPalette"; -function withAlpha(color: string, alpha: number): string { - const match = /^#([\da-f]{6})(?:[\da-f]{2})?$/iu.exec(color); - if (!match?.[1]) return color; - const alphaByte = Math.round(Math.min(1, Math.max(0, alpha)) * 255) - .toString(16) - .padStart(2, "0") - .toUpperCase(); - return `#${match[1]}${alphaByte}`; -} - -function materialTone(color: string, tone: number): string { - const match = /^#([\da-f]{6})(?:[\da-f]{2})?$/iu.exec(color); - if (!match?.[1]) return color; - - const source = Hct.fromInt(argbFromHex(match[1])); - return `${hexFromArgb(Hct.from(source.hue, source.chroma, tone).toInt()).toUpperCase()}FF`; -} - export function materialYouPaletteToMobileThemeVariables( - palette: MaterialYouPalette, - appearance: MobileThemeAppearance, + _palette: MaterialYouPalette, + _appearance: MobileThemeAppearance, base: MobileThemeVariables, ): MobileThemeVariables { - const dark = appearance === "dark"; - const userBubble = dark ? materialTone(palette.inversePrimary, 30) : palette.primary; - const userBubbleForeground = dark ? materialTone(palette.inversePrimary, 90) : palette.onPrimary; - - return { - ...base, - "--color-screen": palette.surface, - "--color-sheet": withAlpha(palette.surfaceContainerLow, 0.98), - "--color-sheet-solid": palette.surfaceContainerLow, - "--color-card": palette.surfaceContainer, - "--color-card-alt": palette.surfaceContainerHigh, - "--color-card-translucent": withAlpha(palette.surfaceContainer, 0.8), - "--color-thread-canvas": palette.surfaceContainerLow, - "--color-thread-selected": palette.surfaceContainer, - "--color-thread-selected-foreground": palette.onSurface, - "--color-thread-selected-foreground-muted": palette.onSurfaceVariant, - "--color-composer-panel": withAlpha(palette.surfaceContainerLow, dark ? 0.92 : 0.88), - "--color-composer-surface": withAlpha(palette.surfaceContainerHigh, dark ? 0.9 : 0.94), - "--color-composer-border": withAlpha(palette.outlineVariant, dark ? 0.34 : 0.46), - "--color-foreground": palette.onSurface, - "--color-foreground-secondary": palette.onSurfaceVariant, - "--color-foreground-muted": palette.onSurfaceVariant, - "--color-foreground-tertiary": withAlpha(palette.onSurfaceVariant, 0.58), - "--color-border": withAlpha(palette.outlineVariant, dark ? 0.5 : 0.65), - "--color-border-subtle": withAlpha(palette.outlineVariant, dark ? 0.34 : 0.46), - "--color-separator": withAlpha(palette.outlineVariant, dark ? 0.24 : 0.34), - "--color-subtle": withAlpha(palette.onSurface, 0.05), - "--color-subtle-strong": withAlpha(palette.onSurface, 0.1), - "--color-inline-skill-background": withAlpha(palette.tertiaryContainer, 0.52), - "--color-inline-skill-border": withAlpha(palette.tertiary, 0.38), - "--color-inline-skill-foreground": palette.onTertiaryContainer, - "--color-primary": palette.primary, - "--color-primary-foreground": palette.onPrimary, - "--color-primary-shadow": withAlpha(palette.scrim, dark ? 0.22 : 0.18), - "--color-secondary": palette.secondaryContainer, - "--color-secondary-foreground": palette.onSecondaryContainer, - "--color-secondary-border": withAlpha(palette.outlineVariant, dark ? 0.42 : 0.56), - "--color-switch-active-track": palette.primary, - "--color-switch-active-thumb": palette.onPrimary, - "--color-switch-inactive-track": palette.surfaceContainerHighest, - "--color-switch-inactive-thumb": palette.outline, - "--color-danger": palette.errorContainer, - "--color-danger-border": withAlpha(palette.onErrorContainer, dark ? 0.42 : 0.5), - "--color-danger-foreground": palette.onErrorContainer, - "--color-input": palette.surfaceContainerLowest, - "--color-input-border": withAlpha(palette.outline, dark ? 0.42 : 0.5), - "--color-sidebar-search": palette.surfaceContainerLowest, - "--color-placeholder": withAlpha(palette.onSurface, 0.62), - "--color-icon": palette.onSurface, - "--color-icon-muted": palette.onSurfaceVariant, - "--color-icon-subtle": withAlpha(palette.onSurfaceVariant, 0.68), - "--color-header": palette.surfaceContainerHigh, - "--color-header-border": withAlpha(palette.outlineVariant, dark ? 0.36 : 0.48), - "--color-glass-surface": withAlpha(palette.surfaceContainerHigh, dark ? 0.78 : 0.72), - "--color-glass-tint": withAlpha(palette.primaryContainer, dark ? 0.24 : 0.18), - "--color-status-bar": palette.surfaceContainerLow, - "--color-md-body": materialTone(palette.onSurfaceVariant, dark ? 90 : 10), - "--color-md-strong": materialTone(palette.onSurfaceVariant, dark ? 90 : 10), - "--color-md-link": palette.primary, - "--color-md-blockquote-border": withAlpha(palette.primary, dark ? 0.34 : 0.26), - "--color-md-blockquote-bg": withAlpha(palette.primaryContainer, dark ? 0.14 : 0.2), - "--color-md-code-bg": withAlpha(palette.onSurface, dark ? 0.08 : 0.06), - "--color-md-code-text": palette.onSurface, - "--color-md-user-code-bg": withAlpha(userBubbleForeground, dark ? 0.18 : 0.22), - "--color-md-user-code-text": userBubbleForeground, - "--color-md-user-fence-bg": withAlpha(userBubbleForeground, dark ? 0.14 : 0.18), - "--color-md-user-fence-text": userBubbleForeground, - "--color-md-hr": withAlpha(palette.outlineVariant, dark ? 0.48 : 0.6), - "--color-user-bubble": userBubble, - "--color-user-bubble-foreground": userBubbleForeground, - "--color-user-bubble-foreground-muted": withAlpha(userBubbleForeground, 0.78), - "--color-user-bubble-skill-foreground": userBubbleForeground, - "--color-backdrop": withAlpha(palette.scrim, dark ? 0.48 : 0.22), - "--color-drawer": palette.surface, - "--color-drawer-shadow": withAlpha(palette.scrim, dark ? 0.32 : 0.12), - "--color-dot-separator": withAlpha(palette.onSurface, 0.2), - "--color-wordmark": palette.onSurface, - "--color-chevron": withAlpha(palette.onSurface, 0.2), - }; + return base; }