refactor: strip useUnistyles in favor of withUnistyles + StyleSheet
Replaces broad useUnistyles() subscriptions in composer, workspace, and related components with withUnistyles wrappers and StyleSheet.create so re-renders stay scoped to the tokens actually consumed. Lifts the design scales (spacing, iconSize, fontSize, etc.) to top-level exports in theme.ts so component literals share a single source of truth with the unistyles theme.
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
import { useCallback, useMemo, type ReactElement, type ReactNode } from "react";
|
||||
import { Pressable, Text, View, type PressableStateCallbackType } from "react-native";
|
||||
import { Check } from "lucide-react-native";
|
||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||
import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
||||
import invariant from "tiny-invariant";
|
||||
import { SyncedLoader } from "@/components/synced-loader";
|
||||
import { ensurePanelsRegistered } from "@/panels/register-panels";
|
||||
import { getPanelRegistration } from "@/panels/panel-registry";
|
||||
import type { WorkspaceTabDescriptor } from "@/screens/workspace/workspace-tabs-types";
|
||||
import type { SidebarStateBucket } from "@/utils/sidebar-agent-state";
|
||||
import { getStatusDotColor, isEmphasizedStatusDotBucket } from "@/utils/status-dot-color";
|
||||
import { isEmphasizedStatusDotBucket } from "@/utils/status-dot-color";
|
||||
import { shouldRenderSyncedStatusLoader } from "@/utils/status-loader";
|
||||
import type { Theme } from "@/styles/theme";
|
||||
|
||||
export interface WorkspaceTabPresentation {
|
||||
key: string;
|
||||
@@ -103,22 +104,22 @@ interface WorkspaceTabIconProps {
|
||||
statusDotBorderColor?: string;
|
||||
}
|
||||
|
||||
const ThemedCheckIcon = withUnistyles(Check);
|
||||
const mutedColorMapping = (theme: Theme) => ({ color: theme.colors.foregroundMuted });
|
||||
|
||||
export function WorkspaceTabIcon({
|
||||
presentation,
|
||||
active = false,
|
||||
size = 14,
|
||||
statusDotBorderColor,
|
||||
}: WorkspaceTabIconProps): ReactElement {
|
||||
const { theme } = useUnistyles();
|
||||
const iconColor = active ? theme.colors.foreground : theme.colors.foregroundMuted;
|
||||
const statusDotColor =
|
||||
presentation.statusBucket === null
|
||||
? null
|
||||
: getStatusDotColor({
|
||||
theme,
|
||||
bucket: presentation.statusBucket,
|
||||
showDoneAsInactive: false,
|
||||
});
|
||||
const iconColor = active ? styles.iconActive.color : styles.iconInactive.color;
|
||||
const bucket = presentation.statusBucket;
|
||||
let statusDotColor: string | null = null;
|
||||
if (bucket === "needs_input") statusDotColor = styles.statusDotNeedsInput.color;
|
||||
else if (bucket === "failed") statusDotColor = styles.statusDotFailed.color;
|
||||
else if (bucket === "running") statusDotColor = styles.statusDotRunning.color;
|
||||
else if (bucket === "attention") statusDotColor = styles.statusDotAttention.color;
|
||||
const statusDotSize = isEmphasizedStatusDotBucket(presentation.statusBucket)
|
||||
? EMPHASIZED_STATUS_DOT_SIZE
|
||||
: DEFAULT_STATUS_DOT_SIZE;
|
||||
@@ -139,27 +140,20 @@ export function WorkspaceTabIcon({
|
||||
styles.statusDot,
|
||||
{
|
||||
backgroundColor: statusDotColor ?? undefined,
|
||||
borderColor: statusDotBorderColor ?? theme.colors.surface0,
|
||||
borderColor: statusDotBorderColor ?? styles.statusDotBorderDefault.borderColor,
|
||||
width: statusDotSize,
|
||||
height: statusDotSize,
|
||||
right: statusDotOffset,
|
||||
bottom: statusDotOffset,
|
||||
},
|
||||
],
|
||||
[statusDotColor, statusDotBorderColor, theme.colors.surface0, statusDotSize, statusDotOffset],
|
||||
[statusDotColor, statusDotBorderColor, statusDotSize, statusDotOffset],
|
||||
);
|
||||
|
||||
if (shouldShowLoader) {
|
||||
return (
|
||||
<View style={agentIconWrapperStyle}>
|
||||
<SyncedLoader
|
||||
size={size - 1}
|
||||
color={
|
||||
theme.colorScheme === "light"
|
||||
? theme.colors.palette.amber[700]
|
||||
: theme.colors.palette.amber[500]
|
||||
}
|
||||
/>
|
||||
<SyncedLoader size={size - 1} color={styles.syncedLoader.color} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -187,7 +181,6 @@ export function WorkspaceTabOptionRow({
|
||||
onPress,
|
||||
trailingAccessory,
|
||||
}: WorkspaceTabOptionRowProps): ReactElement {
|
||||
const { theme } = useUnistyles();
|
||||
const pressableStyle = useCallback(
|
||||
({ hovered, pressed }: PressableStateCallbackType & { hovered?: boolean }) => [
|
||||
styles.optionMainPressable,
|
||||
@@ -213,7 +206,7 @@ export function WorkspaceTabOptionRow({
|
||||
</Pressable>
|
||||
{selected ? (
|
||||
<View style={styles.optionTrailingSlot}>
|
||||
<Check size={16} color={theme.colors.foregroundMuted} />
|
||||
<ThemedCheckIcon size={16} uniProps={mutedColorMapping} />
|
||||
</View>
|
||||
) : null}
|
||||
{trailingAccessory ? (
|
||||
@@ -238,6 +231,33 @@ const styles = StyleSheet.create((theme) => ({
|
||||
borderRadius: theme.borderRadius.full,
|
||||
borderWidth: 1,
|
||||
},
|
||||
statusDotBorderDefault: {
|
||||
borderColor: theme.colors.surface0,
|
||||
},
|
||||
statusDotNeedsInput: {
|
||||
color: theme.colors.palette.amber[500],
|
||||
},
|
||||
statusDotFailed: {
|
||||
color: theme.colors.palette.red[500],
|
||||
},
|
||||
statusDotRunning: {
|
||||
color: theme.colors.palette.blue[500],
|
||||
},
|
||||
statusDotAttention: {
|
||||
color: theme.colors.palette.green[500],
|
||||
},
|
||||
iconActive: {
|
||||
color: theme.colors.foreground,
|
||||
},
|
||||
iconInactive: {
|
||||
color: theme.colors.foregroundMuted,
|
||||
},
|
||||
syncedLoader: {
|
||||
color:
|
||||
theme.colorScheme === "light"
|
||||
? theme.colors.palette.amber[700]
|
||||
: theme.colors.palette.amber[500],
|
||||
},
|
||||
optionRow: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
|
||||
Reference in New Issue
Block a user