Replace script status dots with colored type-aware icons

Use Globe for services and SquareTerminal for scripts instead of
plain status dots. Play icon for the Scripts section title. Running
status uses blue instead of green. Scripts button moved before Open
in Editor in the workspace header.
This commit is contained in:
Mohamed Boudra
2026-04-10 22:03:39 +07:00
parent 944978ddb6
commit 7f63fc6ceb
4 changed files with 54 additions and 52 deletions

View File

@@ -37,7 +37,8 @@ import {
FolderPlus,
FolderGit2,
GitPullRequest,
Terminal,
Globe,
SquareTerminal,
Monitor,
MoreVertical,
Plus,
@@ -969,6 +970,9 @@ function WorkspaceRowInner({
const isDesktop = !isTouchPlatform;
const showScriptsIcon = isDesktop && workspace.hasRunningScripts;
const hasRunningService = workspace.scripts.some(
(s) => s.lifecycle === "running" && (s.type ?? "service") === "service",
);
return (
<WorkspaceHoverCard workspace={workspace} prHint={prHint} isDragging={isDragging}>
@@ -1018,7 +1022,11 @@ function WorkspaceRowInner({
<View style={styles.workspaceRowRight}>
{showScriptsIcon ? (
<View testID="workspace-globe-icon" accessibilityLabel="Scripts available">
<Terminal size={12} color={theme.colors.foregroundMuted} />
{hasRunningService ? (
<Globe size={12} color={theme.colors.palette.blue[500]} />
) : (
<SquareTerminal size={12} color={theme.colors.palette.blue[500]} />
)}
</View>
) : null}
{isCreating ? <Text style={styles.workspaceCreatingText}>Creating...</Text> : null}

View File

@@ -10,7 +10,7 @@ import { useMutation } from "@tanstack/react-query";
import { Dimensions, Platform, Text, View } from "react-native";
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { ExternalLink, LoaderCircle, Play, Terminal } from "lucide-react-native";
import { ExternalLink, Globe, LoaderCircle, Play, SquareTerminal } from "lucide-react-native";
import { GitHubIcon } from "@/components/icons/github-icon";
import { DiffStat } from "@/components/diff-stat";
import { Pressable } from "react-native";
@@ -200,7 +200,7 @@ function getScriptHealthColor(input: {
theme: ReturnType<typeof useUnistyles>["theme"];
}): string {
if (input.health === "healthy") {
return input.theme.colors.palette.green[500];
return input.theme.colors.palette.blue[500];
}
if (input.health === "unhealthy") {
return input.theme.colors.palette.red[500];
@@ -349,7 +349,7 @@ function WorkspaceHoverCardContent({
<>
<View style={styles.separator} />
<View style={styles.sectionLabelRow}>
<Terminal size={12} color={theme.colors.foregroundMuted} />
<Play size={12} color={theme.colors.foregroundMuted} fill="transparent" />
<Text style={styles.sectionLabel}>Scripts</Text>
</View>
<View style={styles.sectionList} testID="hover-card-script-list">
@@ -365,7 +365,7 @@ function WorkspaceHoverCardContent({
? getScriptHealthColor({ health: script.health, theme })
: theme.colors.foregroundMuted;
} else if (isRunning) {
dotColor = theme.colors.palette.green[500];
dotColor = theme.colors.palette.blue[500];
} else if (exitCode === 0) {
dotColor = theme.colors.palette.green[500];
} else if (exitCode !== null) {
@@ -402,13 +402,21 @@ function WorkspaceHoverCardContent({
>
{({ hovered }) => (
<>
<View
testID={`hover-card-script-health-${script.scriptName}`}
style={[
styles.statusDot,
{ backgroundColor: dotColor },
]}
/>
{isService ? (
<Globe
size={14}
color={dotColor}
testID={`hover-card-script-health-${script.scriptName}`}
style={styles.scriptIcon}
/>
) : (
<SquareTerminal
size={14}
color={dotColor}
testID={`hover-card-script-health-${script.scriptName}`}
style={styles.scriptIcon}
/>
)}
<Text
style={[
styles.listRowLabel,
@@ -666,12 +674,8 @@ const styles = StyleSheet.create((theme) => ({
flex: 1,
minWidth: 0,
},
statusDot: {
width: 8,
height: 8,
borderRadius: 4,
scriptIcon: {
flexShrink: 0,
marginLeft: 2,
},
checksSummaryRow: {
flexDirection: "row",

View File

@@ -2124,12 +2124,6 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
}
right={
<View style={styles.headerRight}>
{!isMobile ? (
<WorkspaceOpenInEditorButton
serverId={normalizedServerId}
cwd={normalizedWorkspaceId}
/>
) : null}
{!isMobile && workspaceDescriptor && workspaceDescriptor.scripts.length > 0 ? (
<WorkspaceScriptsButton
serverId={normalizedServerId}
@@ -2137,6 +2131,12 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
scripts={workspaceDescriptor.scripts}
/>
) : null}
{!isMobile ? (
<WorkspaceOpenInEditorButton
serverId={normalizedServerId}
cwd={normalizedWorkspaceId}
/>
) : null}
{!isMobile && isGitCheckout ? (
<>
{workspaceDirectory ? (

View File

@@ -1,7 +1,7 @@
import { type ReactElement } from "react";
import { Pressable, Text, View } from "react-native";
import { useMutation } from "@tanstack/react-query";
import { ChevronDown, ExternalLink, LoaderCircle, Play, Terminal } from "lucide-react-native";
import { ChevronDown, ExternalLink, Globe, LoaderCircle, Play, SquareTerminal } from "lucide-react-native";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import type { WorkspaceDescriptor } from "@/stores/session-store";
import { useSessionStore } from "@/stores/session-store";
@@ -20,7 +20,7 @@ function getScriptHealthColor(
theme: ReturnType<typeof useUnistyles>["theme"],
): string {
if (health === "healthy") {
return theme.colors.palette.green[500];
return theme.colors.palette.blue[500];
}
if (health === "unhealthy") {
return theme.colors.palette.red[500];
@@ -82,16 +82,16 @@ export function WorkspaceScriptsButton({
accessibilityLabel="Workspace scripts"
>
<View style={styles.splitButtonContent}>
<Terminal size={14} color={theme.colors.foregroundMuted} />
<Play
size={14}
color={
hasAnyRunning
? theme.colors.palette.blue[500]
: theme.colors.foregroundMuted
}
fill="transparent"
/>
<Text style={styles.splitButtonText}>Scripts</Text>
{hasAnyRunning ? (
<View
style={[
styles.runningDot,
{ backgroundColor: theme.colors.palette.green[500] },
]}
/>
) : null}
<ChevronDown size={14} color={theme.colors.foregroundMuted} />
</View>
</DropdownMenuTrigger>
@@ -114,7 +114,7 @@ export function WorkspaceScriptsButton({
? getScriptHealthColor(script.health, theme)
: theme.colors.foregroundMuted;
} else if (isRunning) {
dotColor = theme.colors.palette.green[500];
dotColor = theme.colors.palette.blue[500];
} else if (exitCode === 0) {
dotColor = theme.colors.palette.green[500];
} else if (exitCode !== null) {
@@ -138,12 +138,11 @@ export function WorkspaceScriptsButton({
>
{({ hovered }) => (
<>
<View
style={[
styles.statusDot,
{ backgroundColor: dotColor },
]}
/>
{isService ? (
<Globe size={14} color={dotColor} style={styles.scriptIcon} />
) : (
<SquareTerminal size={14} color={dotColor} style={styles.scriptIcon} />
)}
<Text
style={[
styles.scriptName,
@@ -274,11 +273,6 @@ const styles = StyleSheet.create((theme) => ({
justifyContent: "center",
gap: theme.spacing[1.5],
},
runningDot: {
width: 6,
height: 6,
borderRadius: 3,
},
scriptList: {
paddingVertical: theme.spacing[1],
},
@@ -293,11 +287,7 @@ const styles = StyleSheet.create((theme) => ({
scriptRowHovered: {
backgroundColor: theme.colors.surface2,
},
statusDot: {
width: 8,
height: 8,
borderRadius: 4,
marginLeft: 2,
scriptIcon: {
flexShrink: 0,
},
scriptName: {