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:
@@ -37,7 +37,8 @@ import {
|
|||||||
FolderPlus,
|
FolderPlus,
|
||||||
FolderGit2,
|
FolderGit2,
|
||||||
GitPullRequest,
|
GitPullRequest,
|
||||||
Terminal,
|
Globe,
|
||||||
|
SquareTerminal,
|
||||||
Monitor,
|
Monitor,
|
||||||
MoreVertical,
|
MoreVertical,
|
||||||
Plus,
|
Plus,
|
||||||
@@ -969,6 +970,9 @@ function WorkspaceRowInner({
|
|||||||
|
|
||||||
const isDesktop = !isTouchPlatform;
|
const isDesktop = !isTouchPlatform;
|
||||||
const showScriptsIcon = isDesktop && workspace.hasRunningScripts;
|
const showScriptsIcon = isDesktop && workspace.hasRunningScripts;
|
||||||
|
const hasRunningService = workspace.scripts.some(
|
||||||
|
(s) => s.lifecycle === "running" && (s.type ?? "service") === "service",
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WorkspaceHoverCard workspace={workspace} prHint={prHint} isDragging={isDragging}>
|
<WorkspaceHoverCard workspace={workspace} prHint={prHint} isDragging={isDragging}>
|
||||||
@@ -1018,7 +1022,11 @@ function WorkspaceRowInner({
|
|||||||
<View style={styles.workspaceRowRight}>
|
<View style={styles.workspaceRowRight}>
|
||||||
{showScriptsIcon ? (
|
{showScriptsIcon ? (
|
||||||
<View testID="workspace-globe-icon" accessibilityLabel="Scripts available">
|
<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>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
{isCreating ? <Text style={styles.workspaceCreatingText}>Creating...</Text> : null}
|
{isCreating ? <Text style={styles.workspaceCreatingText}>Creating...</Text> : null}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useMutation } from "@tanstack/react-query";
|
|||||||
import { Dimensions, Platform, Text, View } from "react-native";
|
import { Dimensions, Platform, Text, View } from "react-native";
|
||||||
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
|
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
|
||||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
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 { GitHubIcon } from "@/components/icons/github-icon";
|
||||||
import { DiffStat } from "@/components/diff-stat";
|
import { DiffStat } from "@/components/diff-stat";
|
||||||
import { Pressable } from "react-native";
|
import { Pressable } from "react-native";
|
||||||
@@ -200,7 +200,7 @@ function getScriptHealthColor(input: {
|
|||||||
theme: ReturnType<typeof useUnistyles>["theme"];
|
theme: ReturnType<typeof useUnistyles>["theme"];
|
||||||
}): string {
|
}): string {
|
||||||
if (input.health === "healthy") {
|
if (input.health === "healthy") {
|
||||||
return input.theme.colors.palette.green[500];
|
return input.theme.colors.palette.blue[500];
|
||||||
}
|
}
|
||||||
if (input.health === "unhealthy") {
|
if (input.health === "unhealthy") {
|
||||||
return input.theme.colors.palette.red[500];
|
return input.theme.colors.palette.red[500];
|
||||||
@@ -349,7 +349,7 @@ function WorkspaceHoverCardContent({
|
|||||||
<>
|
<>
|
||||||
<View style={styles.separator} />
|
<View style={styles.separator} />
|
||||||
<View style={styles.sectionLabelRow}>
|
<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>
|
<Text style={styles.sectionLabel}>Scripts</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.sectionList} testID="hover-card-script-list">
|
<View style={styles.sectionList} testID="hover-card-script-list">
|
||||||
@@ -365,7 +365,7 @@ function WorkspaceHoverCardContent({
|
|||||||
? getScriptHealthColor({ health: script.health, theme })
|
? getScriptHealthColor({ health: script.health, theme })
|
||||||
: theme.colors.foregroundMuted;
|
: theme.colors.foregroundMuted;
|
||||||
} else if (isRunning) {
|
} else if (isRunning) {
|
||||||
dotColor = theme.colors.palette.green[500];
|
dotColor = theme.colors.palette.blue[500];
|
||||||
} else if (exitCode === 0) {
|
} else if (exitCode === 0) {
|
||||||
dotColor = theme.colors.palette.green[500];
|
dotColor = theme.colors.palette.green[500];
|
||||||
} else if (exitCode !== null) {
|
} else if (exitCode !== null) {
|
||||||
@@ -402,13 +402,21 @@ function WorkspaceHoverCardContent({
|
|||||||
>
|
>
|
||||||
{({ hovered }) => (
|
{({ hovered }) => (
|
||||||
<>
|
<>
|
||||||
<View
|
{isService ? (
|
||||||
testID={`hover-card-script-health-${script.scriptName}`}
|
<Globe
|
||||||
style={[
|
size={14}
|
||||||
styles.statusDot,
|
color={dotColor}
|
||||||
{ backgroundColor: 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
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.listRowLabel,
|
styles.listRowLabel,
|
||||||
@@ -666,12 +674,8 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
},
|
},
|
||||||
statusDot: {
|
scriptIcon: {
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
borderRadius: 4,
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
marginLeft: 2,
|
|
||||||
},
|
},
|
||||||
checksSummaryRow: {
|
checksSummaryRow: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
|||||||
@@ -2124,12 +2124,6 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
|
|||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<View style={styles.headerRight}>
|
<View style={styles.headerRight}>
|
||||||
{!isMobile ? (
|
|
||||||
<WorkspaceOpenInEditorButton
|
|
||||||
serverId={normalizedServerId}
|
|
||||||
cwd={normalizedWorkspaceId}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{!isMobile && workspaceDescriptor && workspaceDescriptor.scripts.length > 0 ? (
|
{!isMobile && workspaceDescriptor && workspaceDescriptor.scripts.length > 0 ? (
|
||||||
<WorkspaceScriptsButton
|
<WorkspaceScriptsButton
|
||||||
serverId={normalizedServerId}
|
serverId={normalizedServerId}
|
||||||
@@ -2137,6 +2131,12 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
|
|||||||
scripts={workspaceDescriptor.scripts}
|
scripts={workspaceDescriptor.scripts}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
{!isMobile ? (
|
||||||
|
<WorkspaceOpenInEditorButton
|
||||||
|
serverId={normalizedServerId}
|
||||||
|
cwd={normalizedWorkspaceId}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
{!isMobile && isGitCheckout ? (
|
{!isMobile && isGitCheckout ? (
|
||||||
<>
|
<>
|
||||||
{workspaceDirectory ? (
|
{workspaceDirectory ? (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { type ReactElement } from "react";
|
import { type ReactElement } from "react";
|
||||||
import { Pressable, Text, View } from "react-native";
|
import { Pressable, Text, View } from "react-native";
|
||||||
import { useMutation } from "@tanstack/react-query";
|
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 { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||||
import type { WorkspaceDescriptor } from "@/stores/session-store";
|
import type { WorkspaceDescriptor } from "@/stores/session-store";
|
||||||
import { useSessionStore } from "@/stores/session-store";
|
import { useSessionStore } from "@/stores/session-store";
|
||||||
@@ -20,7 +20,7 @@ function getScriptHealthColor(
|
|||||||
theme: ReturnType<typeof useUnistyles>["theme"],
|
theme: ReturnType<typeof useUnistyles>["theme"],
|
||||||
): string {
|
): string {
|
||||||
if (health === "healthy") {
|
if (health === "healthy") {
|
||||||
return theme.colors.palette.green[500];
|
return theme.colors.palette.blue[500];
|
||||||
}
|
}
|
||||||
if (health === "unhealthy") {
|
if (health === "unhealthy") {
|
||||||
return theme.colors.palette.red[500];
|
return theme.colors.palette.red[500];
|
||||||
@@ -82,16 +82,16 @@ export function WorkspaceScriptsButton({
|
|||||||
accessibilityLabel="Workspace scripts"
|
accessibilityLabel="Workspace scripts"
|
||||||
>
|
>
|
||||||
<View style={styles.splitButtonContent}>
|
<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>
|
<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} />
|
<ChevronDown size={14} color={theme.colors.foregroundMuted} />
|
||||||
</View>
|
</View>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
@@ -114,7 +114,7 @@ export function WorkspaceScriptsButton({
|
|||||||
? getScriptHealthColor(script.health, theme)
|
? getScriptHealthColor(script.health, theme)
|
||||||
: theme.colors.foregroundMuted;
|
: theme.colors.foregroundMuted;
|
||||||
} else if (isRunning) {
|
} else if (isRunning) {
|
||||||
dotColor = theme.colors.palette.green[500];
|
dotColor = theme.colors.palette.blue[500];
|
||||||
} else if (exitCode === 0) {
|
} else if (exitCode === 0) {
|
||||||
dotColor = theme.colors.palette.green[500];
|
dotColor = theme.colors.palette.green[500];
|
||||||
} else if (exitCode !== null) {
|
} else if (exitCode !== null) {
|
||||||
@@ -138,12 +138,11 @@ export function WorkspaceScriptsButton({
|
|||||||
>
|
>
|
||||||
{({ hovered }) => (
|
{({ hovered }) => (
|
||||||
<>
|
<>
|
||||||
<View
|
{isService ? (
|
||||||
style={[
|
<Globe size={14} color={dotColor} style={styles.scriptIcon} />
|
||||||
styles.statusDot,
|
) : (
|
||||||
{ backgroundColor: dotColor },
|
<SquareTerminal size={14} color={dotColor} style={styles.scriptIcon} />
|
||||||
]}
|
)}
|
||||||
/>
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.scriptName,
|
styles.scriptName,
|
||||||
@@ -274,11 +273,6 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
gap: theme.spacing[1.5],
|
gap: theme.spacing[1.5],
|
||||||
},
|
},
|
||||||
runningDot: {
|
|
||||||
width: 6,
|
|
||||||
height: 6,
|
|
||||||
borderRadius: 3,
|
|
||||||
},
|
|
||||||
scriptList: {
|
scriptList: {
|
||||||
paddingVertical: theme.spacing[1],
|
paddingVertical: theme.spacing[1],
|
||||||
},
|
},
|
||||||
@@ -293,11 +287,7 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
scriptRowHovered: {
|
scriptRowHovered: {
|
||||||
backgroundColor: theme.colors.surface2,
|
backgroundColor: theme.colors.surface2,
|
||||||
},
|
},
|
||||||
statusDot: {
|
scriptIcon: {
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
borderRadius: 4,
|
|
||||||
marginLeft: 2,
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
},
|
},
|
||||||
scriptName: {
|
scriptName: {
|
||||||
|
|||||||
Reference in New Issue
Block a user