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 Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
parent
b81f7d18f6
commit
65103e32e6
@ -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.getByRole("combobox", { name: "Message" })).toBeVisible();
|
||||||
await expect(page.getByTestId("side-panel")).toHaveAttribute("data-panel", "closed");
|
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.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 captureScreenshot(page, testInfo, "create-chat-sidepanel-closed");
|
||||||
|
|
||||||
await page.getByTestId("toggle-bots-sidebar").click();
|
await page.getByTestId("toggle-bots-sidebar").click();
|
||||||
|
|||||||
@ -23,7 +23,6 @@ test("sidebar has expected navigation items and no integrations", async ({ page
|
|||||||
// - textbox "Search" (search input)
|
// - textbox "Search" (search input)
|
||||||
// - button "Heute" (today)
|
// - button "Heute" (today)
|
||||||
// - text "Sprich mit deinen Oo's" (conversations section header)
|
// - text "Sprich mit deinen Oo's" (conversations section header)
|
||||||
// - status "Not configured" (trading bot URL unset)
|
|
||||||
// - NO "Integration(en)" button or text
|
// - NO "Integration(en)" button or text
|
||||||
|
|
||||||
// Search: it's a textbox, not a button
|
// 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");
|
const conversationsText = sidebar.getByText("Sprich mit deinen Oo's");
|
||||||
await expect(conversationsText).toBeVisible({ timeout: 5000 });
|
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
|
// No "Integration(en)" button
|
||||||
const integrationBtn = sidebar.getByRole("button", { name: /Integration(en)?/i });
|
const integrationBtn = sidebar.getByRole("button", { name: /Integration(en)?/i });
|
||||||
await expect(integrationBtn).toHaveCount(0, { timeout: 5000 });
|
await expect(integrationBtn).toHaveCount(0, { timeout: 5000 });
|
||||||
|
|||||||
@ -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
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -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-sidebar-foreground">
|
|
||||||
<CheckCircle2 className="size-3.5 text-success" />
|
|
||||||
<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 rk-sidebar-muted">
|
|
||||||
<Icon className="size-3" />
|
|
||||||
<span className="text-[11px] leading-tight">{label}</span>
|
|
||||||
<span className="text-[10px] leading-tight">· {desc}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@ -139,7 +139,6 @@ import {
|
|||||||
Search,
|
Search,
|
||||||
Settings,
|
Settings,
|
||||||
Smile,
|
Smile,
|
||||||
Sparkles,
|
|
||||||
Square,
|
Square,
|
||||||
TextQuote,
|
TextQuote,
|
||||||
Trash2,
|
Trash2,
|
||||||
@ -178,11 +177,9 @@ import {
|
|||||||
} from "../components/ComputersUnavailableHint";
|
} from "../components/ComputersUnavailableHint";
|
||||||
import { ComputerUpdateProgress } from "../components/ComputerUpdateProgress";
|
import { ComputerUpdateProgress } from "../components/ComputerUpdateProgress";
|
||||||
import { AgentCursor } from "../components/computer/AgentCursor";
|
import { AgentCursor } from "../components/computer/AgentCursor";
|
||||||
import { DataTrustPanel } from "../components/DataTrustPanel";
|
|
||||||
import { MainInbox } from "../components/MainInbox";
|
import { MainInbox } from "../components/MainInbox";
|
||||||
import { MessageHoverMetadata } from "../components/MessageHoverMetadata";
|
import { MessageHoverMetadata } from "../components/MessageHoverMetadata";
|
||||||
import { OoGroupStage, OoStage } from "../components/stage/OoStage";
|
import { OoGroupStage, OoStage } from "../components/stage/OoStage";
|
||||||
import { TradingModeIndicator } from "../components/TradingModeIndicator";
|
|
||||||
import { SkillDraftCard } from "../components/teach/SkillDraftCard";
|
import { SkillDraftCard } from "../components/teach/SkillDraftCard";
|
||||||
import { TeachCaptureOverlay } from "../components/teach/TeachCaptureOverlay";
|
import { TeachCaptureOverlay } from "../components/teach/TeachCaptureOverlay";
|
||||||
import { TeachComputerOverlayControl } from "../components/teach/TeachComputerOverlay";
|
import { TeachComputerOverlayControl } from "../components/teach/TeachComputerOverlay";
|
||||||
@ -4271,8 +4268,6 @@ export function ShellPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<TradingModeIndicator snapshot={tradingStatus} className="mx-3 mb-1 lg:mx-5 2xl:mx-7" />
|
|
||||||
<DataTrustPanel />
|
|
||||||
<Popover open={menuOpen} onOpenChange={setMenuOpen}>
|
<Popover open={menuOpen} onOpenChange={setMenuOpen}>
|
||||||
<PopoverTrigger
|
<PopoverTrigger
|
||||||
data-testid="user-menu-trigger"
|
data-testid="user-menu-trigger"
|
||||||
@ -4633,9 +4628,6 @@ export function ShellPage() {
|
|||||||
activeName={
|
activeName={
|
||||||
inGroup ? (activeGroup?.name ?? activeSnapshot?.groupName) : active?.name
|
inGroup ? (activeGroup?.name ?? activeSnapshot?.groupName) : active?.name
|
||||||
}
|
}
|
||||||
onPromptSelect={(prompt) => {
|
|
||||||
void sendMessage(prompt);
|
|
||||||
}}
|
|
||||||
scrollRef={messageScroll}
|
scrollRef={messageScroll}
|
||||||
artifactTarget={transcriptArtifactTarget}
|
artifactTarget={transcriptArtifactTarget}
|
||||||
onOpenArtifact={wideLayout ? openArtifactBeside : undefined}
|
onOpenArtifact={wideLayout ? openArtifactBeside : undefined}
|
||||||
@ -4684,9 +4676,6 @@ export function ShellPage() {
|
|||||||
{(active || activeGroup) && !showProjectTimeline ? (
|
{(active || activeGroup) && !showProjectTimeline ? (
|
||||||
<Composer
|
<Composer
|
||||||
key={inGroup ? `group:${groupId}` : `bot:${active?.id}`}
|
key={inGroup ? `group:${groupId}` : `bot:${active?.id}`}
|
||||||
activeName={
|
|
||||||
inGroup ? (activeGroup?.name ?? activeSnapshot?.groupName) : active?.name
|
|
||||||
}
|
|
||||||
running={composerRunning}
|
running={composerRunning}
|
||||||
disabled={Boolean(recordingSkill)}
|
disabled={Boolean(recordingSkill)}
|
||||||
pendingAttachments={activePendingAttachments}
|
pendingAttachments={activePendingAttachments}
|
||||||
@ -5813,7 +5802,6 @@ const Transcript = memo(function Transcript({
|
|||||||
speakingMessageId,
|
speakingMessageId,
|
||||||
onSpeak,
|
onSpeak,
|
||||||
activeName,
|
activeName,
|
||||||
onPromptSelect,
|
|
||||||
encounterMode = false,
|
encounterMode = false,
|
||||||
voiceEncounterActive = false,
|
voiceEncounterActive = false,
|
||||||
}: {
|
}: {
|
||||||
@ -5844,7 +5832,6 @@ const Transcript = memo(function Transcript({
|
|||||||
speakingMessageId: string | null;
|
speakingMessageId: string | null;
|
||||||
onSpeak: (message: ThreadMessage) => void;
|
onSpeak: (message: ThreadMessage) => void;
|
||||||
activeName?: string;
|
activeName?: string;
|
||||||
onPromptSelect?: (prompt: string) => void;
|
|
||||||
encounterMode?: boolean;
|
encounterMode?: boolean;
|
||||||
voiceEncounterActive?: boolean;
|
voiceEncounterActive?: boolean;
|
||||||
}) {
|
}) {
|
||||||
@ -6097,87 +6084,11 @@ const Transcript = memo(function Transcript({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{reactionView.visibleMessages.length === 0 && encounterMode ? (
|
{reactionView.visibleMessages.length === 0 && encounterMode ? (
|
||||||
<div className="flex flex-col gap-2 py-2">
|
<p className="py-2 text-ui-body-sm text-muted-foreground">
|
||||||
<p className="text-ui-body-sm text-muted-foreground">
|
{activeName
|
||||||
{activeName
|
? t`Start the conversation — ${activeName} is on stage above.`
|
||||||
? t`Start the conversation — ${activeName} is on stage above.`
|
: t`Start the conversation.`}
|
||||||
: t`Start the conversation.`}
|
</p>
|
||||||
</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-foreground mb-2">
|
|
||||||
{t`Guten Tag.`} evOo {t`ist bereit.`}
|
|
||||||
</h2>
|
|
||||||
<p className="max-w-md text-ui-body leading-relaxed text-foreground/85 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 flex flex-col items-start p-4 rounded-2xl text-start transition-all hover:scale-[1.02] cursor-pointer"
|
|
||||||
>
|
|
||||||
<span className="text-xl mb-2">⚡</span>
|
|
||||||
<span className="text-[14.5px] font-semibold text-foreground">
|
|
||||||
{t`Portfolio-Check`}
|
|
||||||
</span>
|
|
||||||
<span className="text-ui-body-sm text-muted-foreground 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 flex flex-col items-start p-4 rounded-2xl text-start transition-all hover:scale-[1.02] cursor-pointer"
|
|
||||||
>
|
|
||||||
<span className="text-xl mb-2">📈</span>
|
|
||||||
<span className="text-[14.5px] font-semibold text-foreground">
|
|
||||||
{t`Alpha-Signale`}
|
|
||||||
</span>
|
|
||||||
<span className="text-ui-body-sm text-muted-foreground 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 flex flex-col items-start p-4 rounded-2xl text-start transition-all hover:scale-[1.02] cursor-pointer"
|
|
||||||
>
|
|
||||||
<span className="text-xl mb-2">💎</span>
|
|
||||||
<span className="text-[14.5px] font-semibold text-foreground">
|
|
||||||
{t`Steve Jobs Audit`}
|
|
||||||
</span>
|
|
||||||
<span className="text-ui-body-sm text-muted-foreground mt-1 leading-snug">
|
|
||||||
{t`Schlichtheit, Eleganz & Detailtiefe`}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
) : null}
|
||||||
{reactionView.visibleMessages.map((message) => {
|
{reactionView.visibleMessages.map((message) => {
|
||||||
if (!message.blocks.some((block) => !isToolActivityBlock(block))) return null;
|
if (!message.blocks.some((block) => !isToolActivityBlock(block))) return null;
|
||||||
@ -6422,7 +6333,6 @@ const QuoteSelectionButton = memo(function QuoteSelectionButton({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const Composer = memo(function Composer({
|
const Composer = memo(function Composer({
|
||||||
activeName,
|
|
||||||
running,
|
running,
|
||||||
disabled,
|
disabled,
|
||||||
pendingAttachments,
|
pendingAttachments,
|
||||||
@ -6450,7 +6360,6 @@ const Composer = memo(function Composer({
|
|||||||
onSlashOpen,
|
onSlashOpen,
|
||||||
onSlashAction,
|
onSlashAction,
|
||||||
}: {
|
}: {
|
||||||
activeName?: string;
|
|
||||||
running: boolean;
|
running: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
pendingAttachments: PendingAttachment[];
|
pendingAttachments: PendingAttachment[];
|
||||||
@ -6759,28 +6668,6 @@ const Composer = memo(function Composer({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="mx-auto w-full max-w-4xl">
|
<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 ? (
|
{sendError || runError ? (
|
||||||
<div
|
<div
|
||||||
ref={runErrorRef}
|
ref={runErrorRef}
|
||||||
@ -7275,48 +7162,6 @@ function cleanRosterPreview(raw?: string | null): string {
|
|||||||
return text;
|
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({
|
function MessageHoverActions({
|
||||||
message,
|
message,
|
||||||
side,
|
side,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user