Skip to content

Commit 8cdf42d

Browse files
authored
🤖 fix: update InitMessage to match app aesthetic (#1235)
## Summary Update the InitMessage component to match the visual patterns used throughout the rest of the app. ## Changes | Aspect | Before | After | |--------|--------|-------| | **Border** | `border-b` (bottom only) | `rounded border` (all sides, rounded corners) | | **Icons** | Emoji (🔧, ✅) | Lucide icons with semantic colors | | **Running state** | Plain text | Shimmer animation | | **Typography** | `font-mono` everywhere | `font-primary` for text, `font-mono` for paths | | **Color coding** | Minimal | `text-accent` / `text-success` / `text-error` | Follows patterns from `ReasoningMessage`, `StreamErrorMessage`, `TerminalOutput`, and `StreamingBarrier`. --- _Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high`_
1 parent ad4525e commit 8cdf42d

File tree

2 files changed

+164
-16
lines changed

2 files changed

+164
-16
lines changed

src/browser/components/Messages/InitMessage.tsx

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React from "react";
22
import { cn } from "@/common/lib/utils";
33
import type { DisplayedMessage } from "@/common/types/message";
4+
import { Loader2, Wrench, CheckCircle2, AlertCircle } from "lucide-react";
5+
import { Shimmer } from "../ai-elements/shimmer";
46

57
interface InitMessageProps {
68
message: Extract<DisplayedMessage, { type: "workspace-init" }>;
@@ -9,33 +11,53 @@ interface InitMessageProps {
911

1012
export const InitMessage = React.memo<InitMessageProps>(({ message, className }) => {
1113
const isError = message.status === "error";
14+
const isRunning = message.status === "running";
15+
const isSuccess = message.status === "success";
1216

1317
return (
1418
<div
1519
className={cn(
16-
"flex flex-col gap-1.5 border-b p-3 font-mono text-xs text-text-lighter",
17-
isError ? "bg-init-error-bg border-init-error-border" : "bg-init-bg border-init-border",
20+
"my-2 rounded border px-3 py-2",
21+
isError ? "border-init-error-border bg-init-error-bg" : "border-init-border bg-init-bg",
1822
className
1923
)}
2024
>
21-
<div className="text-bright flex items-center gap-2">
22-
<span>🔧</span>
23-
<div>
24-
{message.status === "running" ? (
25-
<span>Running init hook...</span>
26-
) : message.status === "success" ? (
27-
<span>✅ Init hook completed successfully</span>
25+
<div className="flex items-center gap-2">
26+
<span
27+
className={cn(
28+
"flex-shrink-0",
29+
isError ? "text-error" : isSuccess ? "text-success" : "text-accent"
30+
)}
31+
>
32+
{isRunning ? (
33+
<Loader2 className="size-3.5 animate-spin" />
34+
) : isSuccess ? (
35+
<CheckCircle2 className="size-3.5" />
36+
) : isError ? (
37+
<AlertCircle className="size-3.5" />
38+
) : (
39+
<Wrench className="size-3.5" />
40+
)}
41+
</span>
42+
<span className="font-primary text-foreground text-[12px]">
43+
{isRunning ? (
44+
<Shimmer colorClass="var(--color-accent)">Running init hook...</Shimmer>
45+
) : isSuccess ? (
46+
"Init hook completed"
2847
) : (
29-
<span>
30-
Init hook exited with code {message.exitCode}. Workspace is ready, but some setup
31-
failed.
32-
</span>
48+
<span className="text-error">Init hook failed (exit code {message.exitCode})</span>
3349
)}
34-
<div className="text-muted mt-0.5 font-mono text-[11px]">{message.hookPath}</div>
35-
</div>
50+
</span>
3651
</div>
52+
<div className="text-muted mt-1 truncate font-mono text-[11px]">{message.hookPath}</div>
3753
{message.lines.length > 0 && (
38-
<pre className="m-0 max-h-[120px] overflow-auto rounded border border-white/[0.08] bg-black/15 px-2 py-1.5 whitespace-pre-wrap">
54+
<pre
55+
className={cn(
56+
"m-0 mt-2.5 max-h-[120px] overflow-auto rounded-sm",
57+
"bg-black/30 px-2 py-1.5 font-mono text-[11px] leading-relaxed whitespace-pre-wrap",
58+
isError ? "text-danger-soft" : "text-light"
59+
)}
60+
>
3961
{message.lines.join("\n")}
4062
</pre>
4163
)}

src/browser/stories/App.chat.stories.tsx

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
createGenericTool,
1616
createPendingTool,
1717
} from "./mockFactory";
18+
19+
import type { WorkspaceChatMessage } from "@/common/orpc/types";
1820
import { updatePersistedState } from "@/browser/hooks/usePersistedState";
1921
import { getModelKey } from "@/common/constants/storage";
2022
import { setupSimpleChatStory, setupStreamingChatStory, setWorkspaceInput } from "./storyHelpers";
@@ -964,3 +966,127 @@ export const DiffPaddingAlignment: AppStory = {
964966
},
965967
},
966968
};
969+
970+
/**
971+
* Story showing the InitMessage component in success state.
972+
* Tests the workspace init hook display with completed status.
973+
*/
974+
export const InitHookSuccess: AppStory = {
975+
render: () => (
976+
<AppWithMocks
977+
setup={() =>
978+
setupSimpleChatStory({
979+
workspaceId: "ws-init-success",
980+
messages: [
981+
createUserMessage("msg-1", "Start working on the project", {
982+
historySequence: 1,
983+
timestamp: STABLE_TIMESTAMP - 100000,
984+
}),
985+
],
986+
onChat: (_wsId, emit) => {
987+
// Emit init events to show completed init hook
988+
setTimeout(() => {
989+
emit({
990+
type: "init-start",
991+
hookPath: "/home/user/projects/my-app/.mux/init.sh",
992+
timestamp: STABLE_TIMESTAMP - 110000,
993+
} as WorkspaceChatMessage);
994+
emit({
995+
type: "init-output",
996+
line: "Installing dependencies...",
997+
timestamp: STABLE_TIMESTAMP - 109000,
998+
} as WorkspaceChatMessage);
999+
emit({
1000+
type: "init-output",
1001+
line: "Setting up environment variables...",
1002+
timestamp: STABLE_TIMESTAMP - 108000,
1003+
} as WorkspaceChatMessage);
1004+
emit({
1005+
type: "init-output",
1006+
line: "Starting development server...",
1007+
timestamp: STABLE_TIMESTAMP - 107000,
1008+
} as WorkspaceChatMessage);
1009+
emit({
1010+
type: "init-end",
1011+
exitCode: 0,
1012+
timestamp: STABLE_TIMESTAMP - 106000,
1013+
} as WorkspaceChatMessage);
1014+
}, 100);
1015+
},
1016+
})
1017+
}
1018+
/>
1019+
),
1020+
parameters: {
1021+
docs: {
1022+
description: {
1023+
story:
1024+
"Shows the InitMessage component after a successful init hook execution. " +
1025+
"The message displays with a green checkmark, hook path, and output lines.",
1026+
},
1027+
},
1028+
},
1029+
};
1030+
1031+
/**
1032+
* Story showing the InitMessage component in error state.
1033+
* Tests the workspace init hook display with failed status.
1034+
*/
1035+
export const InitHookError: AppStory = {
1036+
render: () => (
1037+
<AppWithMocks
1038+
setup={() =>
1039+
setupSimpleChatStory({
1040+
workspaceId: "ws-init-error",
1041+
messages: [
1042+
createUserMessage("msg-1", "Start working on the project", {
1043+
historySequence: 1,
1044+
timestamp: STABLE_TIMESTAMP - 100000,
1045+
}),
1046+
],
1047+
onChat: (_wsId, emit) => {
1048+
// Emit init events to show failed init hook
1049+
setTimeout(() => {
1050+
emit({
1051+
type: "init-start",
1052+
hookPath: "/home/user/projects/my-app/.mux/init.sh",
1053+
timestamp: STABLE_TIMESTAMP - 110000,
1054+
} as WorkspaceChatMessage);
1055+
emit({
1056+
type: "init-output",
1057+
line: "Installing dependencies...",
1058+
timestamp: STABLE_TIMESTAMP - 109000,
1059+
} as WorkspaceChatMessage);
1060+
emit({
1061+
type: "init-output",
1062+
line: "ERROR: Failed to install package 'missing-dep'",
1063+
timestamp: STABLE_TIMESTAMP - 108000,
1064+
isError: true,
1065+
} as WorkspaceChatMessage);
1066+
emit({
1067+
type: "init-output",
1068+
line: "ERROR: npm ERR! code E404",
1069+
timestamp: STABLE_TIMESTAMP - 107500,
1070+
isError: true,
1071+
} as WorkspaceChatMessage);
1072+
emit({
1073+
type: "init-end",
1074+
exitCode: 1,
1075+
timestamp: STABLE_TIMESTAMP - 107000,
1076+
} as WorkspaceChatMessage);
1077+
}, 100);
1078+
},
1079+
})
1080+
}
1081+
/>
1082+
),
1083+
parameters: {
1084+
docs: {
1085+
description: {
1086+
story:
1087+
"Shows the InitMessage component after a failed init hook execution. " +
1088+
"The message displays with a red alert icon, error styling, and error output.",
1089+
},
1090+
},
1091+
},
1092+
};

0 commit comments

Comments
 (0)