redesign(p0): land Dawn dark-theme foundation with one-shot migration and edge-to-edge polish
This commit is contained in:
parent
0404965f44
commit
b41fbfabec
12 changed files with 201 additions and 179 deletions
|
|
@ -4,6 +4,8 @@ import android.os.Bundle;
|
|||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
import com.getcapacitor.BridgeActivity;
|
||||
|
||||
public class MainActivity extends BridgeActivity {
|
||||
|
|
@ -40,6 +42,14 @@ public class MainActivity extends BridgeActivity {
|
|||
registerPlugin(CallForegroundPlugin.class);
|
||||
EdgeToEdge.enable(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
// Force light icons on both system bars: our CSS is permanently dark
|
||||
// (Dawn redesign), but EdgeToEdge.enable auto-detects icon tint from
|
||||
// the device uiMode — on a light-mode device that gives dark icons
|
||||
// over our dark bars and they vanish.
|
||||
WindowInsetsControllerCompat controller =
|
||||
WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView());
|
||||
controller.setAppearanceLightStatusBars(false);
|
||||
controller.setAppearanceLightNavigationBars(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -20,6 +20,7 @@
|
|||
"@capacitor/preferences": "8.0.1",
|
||||
"@capacitor/push-notifications": "8.0.3",
|
||||
"@capacitor/toast": "8.0.1",
|
||||
"@fontsource-variable/jetbrains-mono": "5.2.5",
|
||||
"@fontsource/inter": "4.5.14",
|
||||
"@tanstack/react-query": "5.24.1",
|
||||
"@tanstack/react-query-devtools": "5.24.1",
|
||||
|
|
@ -2427,6 +2428,15 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource-variable/jetbrains-mono": {
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/jetbrains-mono/-/jetbrains-mono-5.2.5.tgz",
|
||||
"integrity": "sha512-G3sN1xq1moZd0JL+hFaA4MEdsiQS+JXC/z7m+EqA5/Fzn5CQlXGUaaNKFGQdDsFuLTnCfW0KOOSWHjygNfjEPw==",
|
||||
"license": "OFL-1.1",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource/inter": {
|
||||
"version": "4.5.14",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-4.5.14.tgz",
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
"@capacitor/preferences": "8.0.1",
|
||||
"@capacitor/push-notifications": "8.0.3",
|
||||
"@capacitor/toast": "8.0.1",
|
||||
"@fontsource-variable/jetbrains-mono": "5.2.5",
|
||||
"@fontsource/inter": "4.5.14",
|
||||
"@tanstack/react-query": "5.24.1",
|
||||
"@tanstack/react-query-devtools": "5.24.1",
|
||||
|
|
|
|||
|
|
@ -36,111 +36,21 @@ import { DateFormat, MessageLayout, MessageSpacing, settingsAtom } from '../../.
|
|||
import { SettingTile } from '../../../components/setting-tile';
|
||||
import { KeySymbol } from '../../../utils/key-symbol';
|
||||
import { isMacOS } from '../../../utils/user-agent';
|
||||
import {
|
||||
DarkTheme,
|
||||
LightTheme,
|
||||
useThemes,
|
||||
} from '../../../hooks/useTheme';
|
||||
import { stopPropagation } from '../../../utils/keyboard';
|
||||
import { useMessageLayoutItems } from '../../../hooks/useMessageLayout';
|
||||
import { useMessageSpacingItems } from '../../../hooks/useMessageSpacing';
|
||||
import { useDateFormatItems } from '../../../hooks/useDateFormat';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
|
||||
const SYSTEM_THEME_ID = 'system';
|
||||
|
||||
const THEME_I18N_KEYS: Record<string, string> = {
|
||||
[LightTheme.id]: 'Settings.theme_light',
|
||||
[DarkTheme.id]: 'Settings.theme_dark',
|
||||
};
|
||||
|
||||
function ThemeSelect() {
|
||||
// Theme switching is locked to dark while the Dawn redesign rolls out — Vojo
|
||||
// light is a separate plan. Kept as a read-only label so the existing
|
||||
// SettingTile layout in the Appearance section still renders.
|
||||
const { t } = useTranslation();
|
||||
const themes = useThemes();
|
||||
const [systemTheme, setSystemTheme] = useSetting(settingsAtom, 'useSystemTheme');
|
||||
const [themeId, setThemeId] = useSetting(settingsAtom, 'themeId');
|
||||
const [menuCords, setMenuCords] = useState<RectCords>();
|
||||
|
||||
const themeName = (id: string) => t(THEME_I18N_KEYS[id] ?? id);
|
||||
|
||||
const currentLabel = systemTheme
|
||||
? t('Settings.system_theme')
|
||||
: themeName(themeId ?? LightTheme.id);
|
||||
|
||||
const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||
setMenuCords(evt.currentTarget.getBoundingClientRect());
|
||||
};
|
||||
|
||||
const handleSelect = (id: string) => {
|
||||
if (id === SYSTEM_THEME_ID) {
|
||||
setSystemTheme(true);
|
||||
} else {
|
||||
setSystemTheme(false);
|
||||
setThemeId(id);
|
||||
}
|
||||
setMenuCords(undefined);
|
||||
};
|
||||
|
||||
const selectedId = systemTheme ? SYSTEM_THEME_ID : (themeId ?? LightTheme.id);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
size="300"
|
||||
variant="Primary"
|
||||
outlined
|
||||
fill="Soft"
|
||||
radii="300"
|
||||
after={<Icon size="300" src={Icons.ChevronBottom} />}
|
||||
onClick={handleOpenMenu}
|
||||
>
|
||||
<Text size="T300">{currentLabel}</Text>
|
||||
</Button>
|
||||
<PopOut
|
||||
anchor={menuCords}
|
||||
offset={5}
|
||||
position="Bottom"
|
||||
align="End"
|
||||
content={
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
initialFocus: false,
|
||||
onDeactivate: () => setMenuCords(undefined),
|
||||
clickOutsideDeactivates: true,
|
||||
isKeyForward: (evt: KeyboardEvent) =>
|
||||
evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
|
||||
isKeyBackward: (evt: KeyboardEvent) =>
|
||||
evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
|
||||
escapeDeactivates: stopPropagation,
|
||||
}}
|
||||
>
|
||||
<Menu>
|
||||
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
||||
<MenuItem
|
||||
size="300"
|
||||
variant={selectedId === SYSTEM_THEME_ID ? 'Primary' : 'Surface'}
|
||||
radii="300"
|
||||
onClick={() => handleSelect(SYSTEM_THEME_ID)}
|
||||
>
|
||||
<Text size="T300">{t('Settings.system_theme')}</Text>
|
||||
</MenuItem>
|
||||
{themes.map((theme) => (
|
||||
<MenuItem
|
||||
key={theme.id}
|
||||
size="300"
|
||||
variant={selectedId === theme.id ? 'Primary' : 'Surface'}
|
||||
radii="300"
|
||||
onClick={() => handleSelect(theme.id)}
|
||||
>
|
||||
<Text size="T300">{themeName(theme.id)}</Text>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Box>
|
||||
</Menu>
|
||||
</FocusTrap>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
<Text size="T300" priority="300">
|
||||
{t('Settings.theme_dark')}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ export function WelcomePage() {
|
|||
<PageHero
|
||||
icon={<img width="70" height="70" src={VojoSVG} alt="Vojo Logo" />}
|
||||
title="Welcome to Vojo"
|
||||
subTitle={<span>{__APP_VERSION__}</span>}
|
||||
subTitle={
|
||||
<span style={{ fontFamily: '"JetBrains Mono Variable", ui-monospace, monospace' }}>
|
||||
{__APP_VERSION__}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</PageHeroSection>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { useSearchParamsViaServers } from '../../../hooks/router/useSearchParams
|
|||
import { mDirectAtom } from '../../../state/mDirectList';
|
||||
import { getDirectRoomPath } from '../../pathUtils';
|
||||
import { getCanonicalAliasOrRoomId } from '../../../utils/matrix';
|
||||
import { isDirectInvite } from '../../../utils/room';
|
||||
|
||||
export function HomeRouteRoomProvider({ children }: { children: ReactNode }) {
|
||||
const mx = useMatrixClient();
|
||||
|
|
@ -21,7 +22,18 @@ export function HomeRouteRoomProvider({ children }: { children: ReactNode }) {
|
|||
const roomId = useSelectedRoom();
|
||||
const room = mx.getRoom(roomId);
|
||||
|
||||
if (room && mDirects.has(room.roomId)) {
|
||||
// Cold-start push routing lands on /home/{roomId} (sw.ts has no access to
|
||||
// mDirectAtom). For DM rooms we redirect to /direct/. mDirectAtom is hydrated
|
||||
// from useEffect so it can still be empty on the first frame after a fresh
|
||||
// invite — fall back to the synchronous SDK signals (invite-state DMInviter
|
||||
// or m.room.member content with is_direct: true) so the redirect lands on
|
||||
// the first paint instead of one frame later. See plan §6.5 / §6.7.
|
||||
if (
|
||||
room &&
|
||||
(mDirects.has(room.roomId) ||
|
||||
!!room.getDMInviter() ||
|
||||
isDirectInvite(room, mx.getUserId()))
|
||||
) {
|
||||
const alias = getCanonicalAliasOrRoomId(mx, room.roomId);
|
||||
return <Navigate to={getDirectRoomPath(alias, eventId)} replace />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,12 @@ export interface Settings {
|
|||
dateFormatString: string;
|
||||
|
||||
developerTools: boolean;
|
||||
|
||||
migrationsApplied?: Record<string, boolean>;
|
||||
}
|
||||
|
||||
const DAWN_MIGRATION_KEY = 'dawn-redesign-v1';
|
||||
|
||||
const defaultSettings: Settings = {
|
||||
themeId: undefined,
|
||||
useSystemTheme: true,
|
||||
|
|
@ -77,19 +81,42 @@ const defaultSettings: Settings = {
|
|||
developerTools: false,
|
||||
};
|
||||
|
||||
export const getSettings = () => {
|
||||
const settings = localStorage.getItem(STORAGE_KEY);
|
||||
if (settings === null) return defaultSettings;
|
||||
return {
|
||||
...defaultSettings,
|
||||
...(JSON.parse(settings) as Settings),
|
||||
};
|
||||
};
|
||||
|
||||
export const setSettings = (settings: Settings) => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
||||
};
|
||||
|
||||
export const getSettings = (): Settings => {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
|
||||
let parsed: Partial<Settings> | null = null;
|
||||
if (raw !== null) {
|
||||
try {
|
||||
parsed = JSON.parse(raw) as Partial<Settings>;
|
||||
} catch {
|
||||
parsed = null;
|
||||
}
|
||||
}
|
||||
|
||||
const merged: Settings = { ...defaultSettings, ...(parsed ?? {}) };
|
||||
|
||||
// One-shot Dawn redesign migration: force the dark theme on every existing
|
||||
// user so they see the redesigned palette. Stamped so we run it exactly once;
|
||||
// future Vojo-light themes will not be overwritten on subsequent loads.
|
||||
// Synchronous — runs at settingsAtom creation, before useActiveTheme reads
|
||||
// the value during the first render.
|
||||
if (!merged.migrationsApplied?.[DAWN_MIGRATION_KEY]) {
|
||||
merged.useSystemTheme = false;
|
||||
merged.themeId = 'dark-theme';
|
||||
merged.migrationsApplied = {
|
||||
...(merged.migrationsApplied ?? {}),
|
||||
[DAWN_MIGRATION_KEY]: true,
|
||||
};
|
||||
setSettings(merged);
|
||||
}
|
||||
|
||||
return merged;
|
||||
};
|
||||
|
||||
const baseSettings = atom<Settings>(getSettings());
|
||||
export const settingsAtom = atom<Settings, [Settings], undefined>(
|
||||
(get) => get(baseSettings),
|
||||
|
|
|
|||
27
src/app/styles/global.css.ts
Normal file
27
src/app/styles/global.css.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { globalStyle } from '@vanilla-extract/css';
|
||||
|
||||
// Vojo-owned safe-area background. Used by `body { background-color: ... }`
|
||||
// in `src/index.css` to paint Android edge-to-edge cutout / nav-bar zones.
|
||||
// Matches `Background.Container` from the Dawn palette in `src/colors.css.ts`
|
||||
// (`#0d0e11`, canon DAWN.bg2). The safe-area zone reads as a continuation of
|
||||
// the sidebar / nav panels — no visible color seam at the system-bar boundary.
|
||||
//
|
||||
// Why a custom var (and not `color.Background.Container`):
|
||||
// 1. Folds emits its color tokens scoped to a theme class (e.g. `--oq6d070`
|
||||
// lives inside the `.lightTheme` selector). Before useTheme appends a
|
||||
// theme class to body, `var(--oq6d070)` resolves to its initial value
|
||||
// and `background-color` falls back to transparent — system bars show
|
||||
// through unpainted on Android edge-to-edge. A `:root`-level var here
|
||||
// resolves from the very first paint.
|
||||
// 2. `--oq6d070` is a folds@2.6 internal vanilla-extract debugId hash and
|
||||
// will rotate on any folds upgrade.
|
||||
//
|
||||
// The hardcoded #0d0e11 in `index.css` is the matching fallback for the
|
||||
// instant before this CSS file is parsed.
|
||||
//
|
||||
// See docs/plans/dm_1x1_redesign.md §6.6 / R13.
|
||||
globalStyle(':root', {
|
||||
vars: {
|
||||
'--vojo-safe-area-bg': '#0d0e11',
|
||||
},
|
||||
});
|
||||
|
|
@ -1,104 +1,118 @@
|
|||
import { createTheme } from '@vanilla-extract/css';
|
||||
import { color } from 'folds';
|
||||
|
||||
// Базовая тёмная палитра приложения
|
||||
const navDark = '#121314'; // левая панель (навигация)
|
||||
const contentDark = '#0d0d0e'; // правая часть (контент/чат)
|
||||
// Dawn / Stream-v2 palette — see docs/plans/dm_1x1_redesign.md §1, §6 and
|
||||
// docs/design/new-direct-messages-design/project/stream-v2-dawn.jsx const DAWN.
|
||||
//
|
||||
// Tier mapping vs. the canon (DAWN.{bg2, bg, surface}):
|
||||
// bg2 #0d0e11 — app/window deepest level. The DM list panel AND the chat
|
||||
// content area share this tone in the canon (ChatListDawn
|
||||
// line 130 `<div ... background: DAWN.bg2 ...>` ===
|
||||
// DawnDesktopV3 line 220 `<WindowFrame bg={DAWN.bg2} ...>`).
|
||||
// We use it for both `Background.Container` (sidebar / nav
|
||||
// panels) and `Surface.Container` (room view) so the two big
|
||||
// areas read as one uniform surface, divided only by a
|
||||
// 1-pixel line — matches the canon.
|
||||
// bg #181a20 — raised one notch. In the canon this paints chat bubbles,
|
||||
// file cards, the composer container. Mapped to
|
||||
// `SurfaceVariant.Container`.
|
||||
// surface #21232b — raised two notches. Used for chips, reactions, active /
|
||||
// hover rows. Mapped to `*.ContainerActive` family.
|
||||
|
||||
const darkThemeData = {
|
||||
Background: {
|
||||
Container: navDark,
|
||||
ContainerHover: '#262626',
|
||||
ContainerActive: '#333333',
|
||||
ContainerLine: '#404040',
|
||||
OnContainer: '#F2F2F2',
|
||||
Container: '#0d0e11',
|
||||
ContainerHover: '#181a20',
|
||||
ContainerActive: '#21232b',
|
||||
ContainerLine: '#1f2228',
|
||||
OnContainer: '#e6e6e9',
|
||||
},
|
||||
|
||||
Surface: {
|
||||
Container: contentDark,
|
||||
ContainerHover: '#333333',
|
||||
ContainerActive: '#404040',
|
||||
ContainerLine: '#4D4D4D',
|
||||
OnContainer: '#F2F2F2',
|
||||
Container: '#0d0e11',
|
||||
ContainerHover: '#181a20',
|
||||
ContainerActive: '#21232b',
|
||||
ContainerLine: '#1f2228',
|
||||
OnContainer: '#e6e6e9',
|
||||
},
|
||||
|
||||
SurfaceVariant: {
|
||||
Container: '#333333',
|
||||
ContainerHover: '#404040',
|
||||
ContainerActive: '#4D4D4D',
|
||||
ContainerLine: '#595959',
|
||||
OnContainer: '#F2F2F2',
|
||||
Container: '#181a20',
|
||||
ContainerHover: '#21232b',
|
||||
ContainerActive: '#2a2d36',
|
||||
ContainerLine: '#2f3340',
|
||||
OnContainer: '#e6e6e9',
|
||||
},
|
||||
|
||||
Primary: {
|
||||
Main: '#BDB6EC',
|
||||
MainHover: '#B2AAE9',
|
||||
MainActive: '#ADA3E8',
|
||||
MainLine: '#A79DE6',
|
||||
OnMain: '#2C2843',
|
||||
Container: '#413C65',
|
||||
ContainerHover: '#494370',
|
||||
ContainerActive: '#50497B',
|
||||
ContainerLine: '#575086',
|
||||
OnContainer: '#E3E1F7',
|
||||
Main: '#9580ff',
|
||||
MainHover: '#a59cff',
|
||||
MainActive: '#b0a8ff',
|
||||
MainLine: '#bcb5ff',
|
||||
OnMain: '#0c0c0e',
|
||||
Container: '#3a3260',
|
||||
ContainerHover: '#443878',
|
||||
ContainerActive: '#4d3f87',
|
||||
ContainerLine: '#564796',
|
||||
OnContainer: '#e0dcff',
|
||||
},
|
||||
|
||||
Secondary: {
|
||||
Main: '#FFFFFF',
|
||||
MainHover: '#E5E5E5',
|
||||
MainActive: '#D9D9D9',
|
||||
MainLine: '#CCCCCC',
|
||||
OnMain: '#1A1A1A',
|
||||
Container: '#404040',
|
||||
ContainerHover: '#4D4D4D',
|
||||
ContainerActive: '#595959',
|
||||
ContainerLine: '#666666',
|
||||
OnContainer: '#F2F2F2',
|
||||
Main: '#e6e6e9',
|
||||
MainHover: '#d6d6d9',
|
||||
MainActive: '#c6c6c9',
|
||||
MainLine: '#b6b6b9',
|
||||
OnMain: '#181a20',
|
||||
Container: '#21232b',
|
||||
ContainerHover: '#2a2d36',
|
||||
ContainerActive: '#34384a',
|
||||
ContainerLine: '#3a3e54',
|
||||
OnContainer: '#e6e6e9',
|
||||
},
|
||||
|
||||
Success: {
|
||||
Main: '#85E0BA',
|
||||
MainHover: '#70DBAF',
|
||||
MainActive: '#66D9A9',
|
||||
MainLine: '#5CD6A3',
|
||||
OnMain: '#0F3D2A',
|
||||
Container: '#175C3F',
|
||||
ContainerHover: '#1A6646',
|
||||
ContainerActive: '#1C704D',
|
||||
ContainerLine: '#1F7A54',
|
||||
OnContainer: '#CCF2E2',
|
||||
Main: '#7dd3a8',
|
||||
MainHover: '#90dcb5',
|
||||
MainActive: '#9ee0bf',
|
||||
MainLine: '#abe5c8',
|
||||
OnMain: '#0e2e1f',
|
||||
Container: '#1a4a32',
|
||||
ContainerHover: '#1f5638',
|
||||
ContainerActive: '#23613f',
|
||||
ContainerLine: '#286c46',
|
||||
OnContainer: '#caf2d9',
|
||||
},
|
||||
|
||||
Warning: {
|
||||
Main: '#E3BA91',
|
||||
MainHover: '#DFAF7E',
|
||||
MainActive: '#DDA975',
|
||||
MainLine: '#DAA36C',
|
||||
OnMain: '#3F2A15',
|
||||
Container: '#5E3F20',
|
||||
ContainerHover: '#694624',
|
||||
ContainerActive: '#734D27',
|
||||
ContainerLine: '#7D542B',
|
||||
OnContainer: '#F3E2D1',
|
||||
Main: '#d4b88a',
|
||||
MainHover: '#dabf95',
|
||||
MainActive: '#dfc59e',
|
||||
MainLine: '#e3cba8',
|
||||
OnMain: '#3a2c14',
|
||||
Container: '#5a4422',
|
||||
ContainerHover: '#664e27',
|
||||
ContainerActive: '#71562b',
|
||||
ContainerLine: '#7c5e30',
|
||||
OnContainer: '#f3e2c5',
|
||||
},
|
||||
|
||||
Critical: {
|
||||
Main: '#E69D9D',
|
||||
MainHover: '#E28D8D',
|
||||
MainActive: '#E08585',
|
||||
MainLine: '#DE7D7D',
|
||||
OnMain: '#401C1C',
|
||||
Container: '#602929',
|
||||
ContainerHover: '#6B2E2E',
|
||||
ContainerActive: '#763333',
|
||||
ContainerLine: '#803737',
|
||||
OnContainer: '#F5D6D6',
|
||||
Main: '#c08e7b',
|
||||
MainHover: '#c89a88',
|
||||
MainActive: '#cea591',
|
||||
MainLine: '#d4ad9a',
|
||||
OnMain: '#3a1f17',
|
||||
Container: '#592e22',
|
||||
ContainerHover: '#653527',
|
||||
ContainerActive: '#6f3a2a',
|
||||
ContainerLine: '#7a402d',
|
||||
OnContainer: '#f0d4c8',
|
||||
},
|
||||
|
||||
Other: {
|
||||
FocusRing: 'rgba(255, 255, 255, 0.5)',
|
||||
FocusRing: 'rgba(149, 128, 255, 0.5)',
|
||||
Shadow: 'rgba(0, 0, 0, 1)',
|
||||
Overlay: 'rgba(0, 0, 0, 0.8)',
|
||||
Overlay: 'rgba(0, 0, 0, 0.85)',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ body {
|
|||
padding: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: var(--oq6d070);
|
||||
background-color: var(--vojo-safe-area-bg, #0d0e11);
|
||||
font-family: var(--font-secondary);
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ import React from 'react';
|
|||
import { createRoot } from 'react-dom/client';
|
||||
import { enableMapSet } from 'immer';
|
||||
import '@fontsource/inter/variable.css';
|
||||
import '@fontsource-variable/jetbrains-mono/index.css';
|
||||
import 'folds/dist/style.css';
|
||||
import { configClass, varsClass } from 'folds';
|
||||
|
||||
enableMapSet();
|
||||
|
||||
import './app/styles/global.css';
|
||||
import './index.css';
|
||||
|
||||
import { trimTrailingSlash } from './app/utils/common';
|
||||
|
|
|
|||
|
|
@ -15,7 +15,12 @@ import buildConfig from './build.config';
|
|||
function resolveAppVersion() {
|
||||
if (process.env.VITE_APP_VERSION) return process.env.VITE_APP_VERSION;
|
||||
try {
|
||||
const raw = execSync('git describe --tags --always --dirty', {
|
||||
// --match 'v*' filters out non-semver tags (e.g. `redesign-p0-done`,
|
||||
// `pre-redesign`) so they never shadow the version-derivation chain.
|
||||
// Without it, `git describe` would pick the nearest tag of any shape and
|
||||
// the regex below would fall through to `return raw`, leaking the human
|
||||
// tag name into __APP_VERSION__ on the Welcome screen.
|
||||
const raw = execSync("git describe --tags --match 'v*' --always --dirty", {
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
})
|
||||
.toString()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue