Add beenvoice mobile companion app with full dark mode support.

Expo app with dashboard, time clock, invoices, and settings — native tabs, glass UI, theme-aware components, and iOS Live Activities.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-17 22:36:37 -04:00
parent 8a7a8df477
commit 14c880123c
93 changed files with 8849 additions and 7849 deletions
+99
View File
@@ -0,0 +1,99 @@
import type { ColorSchemeName } from "react-native";
import * as light from "@/lib/beenvoice-theme";
/** Dark palette — mirrors `globals.css` `:root.dark` */
export const dark = {
background: "#09090B",
foreground: "#FAFAFA",
primary: "#FAFAFA",
primaryForeground: "#18181B",
muted: "#27272A",
mutedForeground: "#A1A1AA",
border: "#27272A",
border50: "rgba(39, 39, 42, 0.5)",
surface80: "rgba(9, 9, 11, 0.8)",
chrome80: "rgba(9, 9, 11, 0.8)",
gridLine: "rgba(128, 128, 128, 0.12)",
blobCore: "rgba(115, 115, 115, 0.3)",
destructive: "#F87171",
destructiveForeground: "#FAFAFA",
destructiveBg: "#450A0A",
success: "#4ADE80",
successBg: "#052E16",
warning: "#FBBF24",
warningBg: "#422006",
} as const;
export type ThemeColors = {
background: string;
backgroundMuted: string;
foreground: string;
card: string;
cardGlass: string;
primary: string;
primaryForeground: string;
muted: string;
mutedForeground: string;
border: string;
borderGlass: string;
secondary: string;
secondaryForeground: string;
accent: string;
destructive: string;
destructiveForeground: string;
destructiveBg: string;
success: string;
successBg: string;
warning: string;
warningBg: string;
brand: string;
brandDark: string;
text: string;
textMuted: string;
};
export function getThemeColors(scheme: ColorSchemeName): ThemeColors {
const isDark = scheme === "dark";
const palette = isDark ? dark : null;
return {
background: palette?.background ?? light.background,
backgroundMuted: palette?.muted ?? light.muted,
foreground: palette?.foreground ?? light.foreground,
card: palette?.background ?? light.background,
cardGlass: palette?.surface80 ?? light.surface80,
primary: palette?.primary ?? light.primary,
primaryForeground: palette?.primaryForeground ?? light.primaryForeground,
muted: palette?.muted ?? light.muted,
mutedForeground: palette?.mutedForeground ?? light.mutedForeground,
border: palette?.border ?? light.border,
borderGlass: palette?.border50 ?? light.border50,
secondary: palette?.border ?? light.border,
secondaryForeground: palette?.primary ?? light.primary,
accent: palette?.muted ?? light.muted,
destructive: palette?.destructive ?? "#EF4444",
destructiveForeground: palette?.destructiveForeground ?? light.primaryForeground,
destructiveBg: palette?.destructiveBg ?? "#FEF2F2",
success: palette?.success ?? "#16A34A",
successBg: palette?.successBg ?? "#F0FDF4",
warning: palette?.warning ?? "#D97706",
warningBg: palette?.warningBg ?? "#FFFBEB",
brand: palette?.primary ?? light.primary,
brandDark: palette?.foreground ?? light.foreground,
text: palette?.foreground ?? light.foreground,
textMuted: palette?.mutedForeground ?? light.mutedForeground,
};
}
export function getBackgroundTokens(scheme: ColorSchemeName) {
const isDark = scheme === "dark";
return {
background: isDark ? dark.background : light.background,
gridLine: isDark ? dark.gridLine : light.gridLine,
blobCore: isDark ? dark.blobCore : light.blobCore,
gridSize: light.gridSize,
blobDiameter: light.blobDiameter,
blobAnimation: light.blobAnimation,
};
}