fix(web): drop trading and persona prompts from the conversation shell

The web shell still showed leftovers from the trading prototype: composer
chips with German trading and "Steve Jobs" prompts, trading suggestion
chips under the empty conversation line, a German trading hero for the
empty transcript, and a German "data protection" status panel in the
sidebar. They suggested actions the product does not have and added
persistent chrome.

Remove the composer chips and their helper, the empty-state chips and the
hero (the empty conversation keeps its existing one-line start hint), the
sidebar trading indicator and the data trust panel. The new-bot E2E now
asserts the plain empty state; the sidebar spec no longer expects the
trading status, and the placeholder Steve Jobs spec with no assertions is
gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
This commit is contained in:
Claude 2026-10-03 13:06:50 +00:00
parent 693ab5e4ce
commit c22a25d7ab
No known key found for this signature in database
5 changed files with 8 additions and 212 deletions

View File

@ -43,6 +43,9 @@ test("create opens form, then empty chat; picker lists bots; sidebar collapses",
await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible();
await expect(page.getByTestId("side-panel")).toHaveAttribute("data-panel", "closed");
await expect(page.getByText("What do you want me on first?", { exact: true })).toHaveCount(0);
await expect(
page.getByTestId("transcript").getByText("Start the conversation — New Bot is on stage above."),
).toBeVisible();
await captureScreenshot(page, testInfo, "create-chat-sidepanel-closed");
await page.getByTestId("toggle-bots-sidebar").click();

View File

@ -23,7 +23,6 @@ test("sidebar has expected navigation items and no integrations", async ({ page
// - textbox "Search" (search input)
// - button "Heute" (today)
// - text "Sprich mit deinen Oo's" (conversations section header)
// - status "Not configured" (trading bot URL unset)
// - NO "Integration(en)" button or text
// Search: it's a textbox, not a button
@ -38,13 +37,6 @@ test("sidebar has expected navigation items and no integrations", async ({ page
const conversationsText = sidebar.getByText("Sprich mit deinen Oo's");
await expect(conversationsText).toBeVisible({ timeout: 5000 });
// Trading mode comes from the bot API. With no URL configured the sidebar
// must not claim Live Trading from a local preference.
const tradingStatus = sidebar.getByTestId("trading-mode-status");
await expect(tradingStatus).toBeVisible({ timeout: 5000 });
await expect(tradingStatus).toContainText("Not configured");
await expect(sidebar.getByRole("button", { name: "Live Trading" })).toHaveCount(0);
// No "Integration(en)" button
const integrationBtn = sidebar.getByRole("button", { name: /Integration(en)?/i });
await expect(integrationBtn).toHaveCount(0, { timeout: 5000 });

View File

@ -1,16 +0,0 @@
import { test } from "@playwright/test";
test.describe("Steve Jobs CTO Agent", () => {
test.beforeEach(async () => {
// We assume the user has logged in and the bot is seeded
// This is a placeholder for the perfect test architecture
});
test("should be visible in the sidebar", async () => {
// This is a dummy test to show we started the test architecture for Steve Jobs
});
test("should prefer voice communications (VOICE FIRST)", async () => {
// Voice first features
});
});

View File

@ -1,28 +0,0 @@
import { CheckCircle2, Database, EyeOff, Lock } from "lucide-react";
import { memo } from "react";
const TRUST_ITEMS = [
{ icon: Lock, label: "Verschlüsselt", desc: "AES-256 für sensible Daten" },
{ icon: Database, label: "Lokal gespeichert", desc: "Eigene PostgreSQL-Datenbank" },
{ icon: EyeOff, label: "Kein Tracking", desc: "Keine Analytics, kein Telemetry" },
] as const;
export const DataTrustPanel = memo(function DataTrustPanel() {
return (
<div className="hidden xl:flex flex-col items-start gap-1.5 rounded-xl border border-border/40 bg-muted/30 px-3 py-2.5">
<div className="flex items-center gap-1.5 text-xs font-medium text-emerald-600 dark:text-emerald-400">
<CheckCircle2 className="size-3.5" />
<span>Datenschutz aktiv</span>
</div>
<div className="flex flex-col gap-1">
{TRUST_ITEMS.map(({ icon: Icon, label, desc }) => (
<div key={label} className="flex items-center gap-1.5">
<Icon className="size-3 text-muted-foreground/60" />
<span className="text-[11px] leading-tight text-muted-foreground">{label}</span>
<span className="text-[10px] leading-tight text-muted-foreground/50">· {desc}</span>
</div>
))}
</div>
</div>
);
});

View File

@ -130,7 +130,6 @@ import {
Search,
Settings,
Smile,
Sparkles,
Square,
TextQuote,
Trash2,
@ -166,11 +165,9 @@ import {
computersAreUnavailable,
} from "../components/ComputersUnavailableHint";
import { ComputerUpdateProgress } from "../components/ComputerUpdateProgress";
import { DataTrustPanel } from "../components/DataTrustPanel";
import { MainInbox } from "../components/MainInbox";
import { MessageHoverMetadata } from "../components/MessageHoverMetadata";
import { OoGroupStage, OoStage } from "../components/stage/OoStage";
import { TradingModeIndicator } from "../components/TradingModeIndicator";
import { SkillDraftCard } from "../components/teach/SkillDraftCard";
import { TeachCaptureOverlay } from "../components/teach/TeachCaptureOverlay";
import { TeachComputerOverlayControl } from "../components/teach/TeachComputerOverlay";
@ -4246,8 +4243,6 @@ export function ShellPage() {
</div>
) : null}
</div>
<TradingModeIndicator snapshot={tradingStatus} className="mx-3 mb-1 lg:mx-5 2xl:mx-7" />
<DataTrustPanel />
<Popover open={menuOpen} onOpenChange={setMenuOpen}>
<PopoverTrigger
data-testid="user-menu-trigger"
@ -4597,9 +4592,6 @@ export function ShellPage() {
activeName={
inGroup ? (activeGroup?.name ?? activeSnapshot?.groupName) : active?.name
}
onPromptSelect={(prompt) => {
void sendMessage(prompt);
}}
scrollRef={messageScroll}
artifactTarget={transcriptArtifactTarget}
messages={transcriptMessages}
@ -4647,9 +4639,6 @@ export function ShellPage() {
{(active || activeGroup) && !showProjectTimeline ? (
<Composer
key={inGroup ? `group:${groupId}` : `bot:${active?.id}`}
activeName={
inGroup ? (activeGroup?.name ?? activeSnapshot?.groupName) : active?.name
}
running={composerRunning}
disabled={Boolean(recordingSkill)}
pendingAttachments={activePendingAttachments}
@ -5836,7 +5825,6 @@ const Transcript = memo(function Transcript({
speakingMessageId,
onSpeak,
activeName,
onPromptSelect,
encounterMode = false,
voiceEncounterActive = false,
}: {
@ -5866,7 +5854,6 @@ const Transcript = memo(function Transcript({
speakingMessageId: string | null;
onSpeak: (message: ThreadMessage) => void;
activeName?: string;
onPromptSelect?: (prompt: string) => void;
encounterMode?: boolean;
voiceEncounterActive?: boolean;
}) {
@ -6082,87 +6069,11 @@ const Transcript = memo(function Transcript({
}`}
>
{reactionView.visibleMessages.length === 0 && encounterMode ? (
<div className="flex flex-col gap-2 py-2">
<p className="text-ui-body-sm text-muted-foreground">
<p className="py-2 text-ui-body-sm text-muted-foreground">
{activeName
? t`Start the conversation — ${activeName} is on stage above.`
: t`Start the conversation.`}
</p>
<div className="flex flex-wrap gap-2">
{[t`Portfolio-Check`, t`Alpha-Signale`, t`Architektur-Audit`].map((label) => (
<button
key={label}
type="button"
onClick={() => onPromptSelect?.(label)}
className="rounded-full border border-border bg-muted/40 px-3 py-1 text-ui-body-sm text-foreground/85 hover:bg-accent"
>
{label}
</button>
))}
</div>
</div>
) : null}
{reactionView.visibleMessages.length === 0 && !encounterMode ? (
<div className="flex flex-1 flex-col items-center justify-center py-16 text-center select-none animate-in fade-in duration-500">
<div className="relative mb-5 flex items-center justify-center p-3">
<EvooMascot size={76} pose="greeting" interactive trackPointer="hover" />
</div>
<h2 className="text-3xl font-bold tracking-tight text-white mb-2">
{t`Guten Tag.`} <span className="text-emerald-400">evOo</span> {t`ist bereit.`}
</h2>
<p className="max-w-md text-ui-body leading-relaxed text-zinc-300 mb-8 font-normal">
{activeName
? t`Frag ${activeName} nach Marktchancen, autonomen Tasks oder Architektur-Reviews.`
: t`Wähle einen autonomen Agenten aus deinem Team oder starte direkt einen Prompt.`}
</p>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3.5 w-full max-w-2xl px-4">
<button
type="button"
onClick={() => {
onPromptSelect?.(t`⚡ Führe einen autonomen Portfolio- und Risiko-Check durch`);
}}
className="apple-glass-card group flex flex-col items-start p-4 rounded-2xl text-start transition-all hover:scale-[1.02] hover:border-emerald-500/40 cursor-pointer"
>
<span className="text-xl mb-2">⚡</span>
<span className="text-[14.5px] font-semibold text-white group-hover:text-emerald-400 transition-colors">
{t`Portfolio-Check`}
</span>
<span className="text-ui-body-sm text-zinc-400 mt-1 leading-snug">
{t`Echtzeit-Risiko & Drawdown-Analyse`}
</span>
</button>
<button
type="button"
onClick={() => {
onPromptSelect?.(t`📈 Scanne den Markt nach aktuellen Alpha-Signalen`);
}}
className="apple-glass-card group flex flex-col items-start p-4 rounded-2xl text-start transition-all hover:scale-[1.02] hover:border-sky-500/40 cursor-pointer"
>
<span className="text-xl mb-2">📈</span>
<span className="text-[14.5px] font-semibold text-white group-hover:text-sky-400 transition-colors">
{t`Alpha-Signale`}
</span>
<span className="text-ui-body-sm text-zinc-400 mt-1 leading-snug">
{t`Volatilität & Orderbuch-Liquidität`}
</span>
</button>
<button
type="button"
onClick={() => {
onPromptSelect?.(t`💎 Führe ein Steve Jobs UX & Architektur-Audit durch`);
}}
className="apple-glass-card group flex flex-col items-start p-4 rounded-2xl text-start transition-all hover:scale-[1.02] hover:border-purple-500/40 cursor-pointer"
>
<span className="text-xl mb-2">💎</span>
<span className="text-[14.5px] font-semibold text-white group-hover:text-purple-400 transition-colors">
{t`Steve Jobs Audit`}
</span>
<span className="text-ui-body-sm text-zinc-400 mt-1 leading-snug">
{t`Schlichtheit, Eleganz & Detailtiefe`}
</span>
</button>
</div>
</div>
) : null}
{reactionView.visibleMessages.map((message) => {
if (!message.blocks.some((block) => !isToolActivityBlock(block))) return null;
@ -6393,7 +6304,6 @@ const QuoteSelectionButton = memo(function QuoteSelectionButton({
});
const Composer = memo(function Composer({
activeName,
running,
disabled,
pendingAttachments,
@ -6421,7 +6331,6 @@ const Composer = memo(function Composer({
onSlashOpen,
onSlashAction,
}: {
activeName?: string;
running: boolean;
disabled?: boolean;
pendingAttachments: PendingAttachment[];
@ -6730,28 +6639,6 @@ const Composer = memo(function Composer({
}`}
>
<div className="mx-auto w-full max-w-4xl">
{/* Quick Action Prompt Chips */}
{showComposerPlaceholder ? (
<div className="mb-3 flex items-center gap-2 overflow-x-auto no-scrollbar py-1">
<span className="flex items-center gap-1.5 text-ui-label font-semibold uppercase tracking-wider text-muted-foreground shrink-0 px-2.5 py-1 rounded-full bg-muted/40 border border-border me-1 shadow-xs">
<Sparkles className="text-amber-400 size-icon-sm" />
Quick
</span>
{quickPromptsForBot(activeName).map((promptText) => (
<button
key={promptText}
type="button"
onClick={() => {
updateDraft(promptText);
focusComposer();
}}
className="shrink-0 rounded-full border border-border bg-muted/40 px-3.5 py-1.5 text-ui-body-sm font-medium text-foreground/85 hover:bg-accent hover:text-foreground transition-all shadow-sm active:scale-95 cursor-pointer"
>
{promptText}
</button>
))}
</div>
) : null}
{sendError || runError ? (
<div
ref={runErrorRef}
@ -7246,48 +7133,6 @@ function cleanRosterPreview(raw?: string | null): string {
return text;
}
function quickPromptsForBot(botName?: string): string[] {
const lower = (botName ?? "").toLowerCase();
if (lower.includes("chief")) {
return [
"⚡ Portfolio Health Check",
"📊 Risk & Drawdown Exposure",
"🔄 Team-Status synchronisieren",
"🎯 Tagesziel prüfen",
];
}
if (lower.includes("steve")) {
return [
"✨ UX & Design Audit",
"🚀 Product Architecture",
"💎 Schlichtheit & Eleganz",
"🔍 Konkurrenzanalyse",
];
}
if (lower.includes("apex") || lower.includes("quant") || lower.includes("trad")) {
return [
"📈 Alpha-Signale scannen",
"📉 Volatilitäts-Stresstest",
"💰 Live vs. Paper PnL",
"⚡ Orderbuch Liquidität",
];
}
if (
lower.includes("dev") ||
lower.includes("sec") ||
lower.includes("infra") ||
lower.includes("code")
) {
return [
"🛡️ Security & Vault Check",
"💻 Latenz- & Cluster-Status",
"🔍 Code Review & Refactor",
"🚨 Health & Error Logs",
];
}
return ["💡 Prioritäten für heute?", "⚡ Schneller Statusbericht", "📊 Performance-Metriken"];
}
function MessageHoverActions({
message,
side,