fix: ekip panosu ve ust arayuzu rafine et

This commit is contained in:
2026-03-17 00:41:07 +03:00
parent c312b83604
commit 416a994967
5 changed files with 82 additions and 24 deletions

View File

@@ -1,4 +1,4 @@
import { useState } from "react"; import { useEffect, useRef, useState } from "react";
import ShellFrame from "./components/ShellFrame.jsx"; import ShellFrame from "./components/ShellFrame.jsx";
import SessionToolbar from "./components/SessionToolbar.jsx"; import SessionToolbar from "./components/SessionToolbar.jsx";
import ChatStream from "./components/ChatStream.jsx"; import ChatStream from "./components/ChatStream.jsx";
@@ -9,8 +9,9 @@ import { useSession } from "./hooks/useSession.js";
export default function App() { export default function App() {
const { socket, connected } = useSocket(); const { socket, connected } = useSocket();
const { session, chat, error, startSession, stopSession, activateTeam, sendPrompt, clearError } = useSession(socket); const { session, chat, error, startSession, stopSession, sendPrompt, selectProject, clearError } = useSession(socket);
const [busy, setBusy] = useState(false); const [busy, setBusy] = useState(false);
const autoStartedRef = useRef(false);
async function runAction(action) { async function runAction(action) {
setBusy(true); setBusy(true);
@@ -23,12 +24,28 @@ export default function App() {
} }
} }
useEffect(() => {
if (!connected || autoStartedRef.current) {
return;
}
if (session.status === "idle") {
autoStartedRef.current = true;
runAction(startSession).catch(() => {
autoStartedRef.current = false;
});
}
}, [connected, session.status]);
return ( return (
<main className="app-shell"> <main className="app-shell">
<div className="app-shell__header"> <div className="app-shell__header">
<div> <div className="app-shell__title">
<p className="app-shell__eyebrow">1996 COMMAND CENTER</p> <p className="app-shell__eyebrow">1996 COMMAND CENTER</p>
<h1>Retro Claude Team Console</h1> <h1>Retro Claude Team Console</h1>
<p className="app-shell__project" title={session.currentProjectPath ?? "None"}>
<span>Current Project:</span> {session.currentProjectPath ?? "None"}
</p>
</div> </div>
<div className="app-shell__meta"> <div className="app-shell__meta">
<span>LINK: {connected ? "ONLINE" : "OFFLINE"}</span> <span>LINK: {connected ? "ONLINE" : "OFFLINE"}</span>
@@ -54,14 +71,13 @@ export default function App() {
<SessionToolbar <SessionToolbar
session={session} session={session}
busy={busy} busy={busy}
onStart={() => runAction(startSession)}
onActivate={() => runAction(activateTeam)}
onStop={() => runAction(stopSession)} onStop={() => runAction(stopSession)}
onSelectProject={() => runAction(selectProject)}
/> />
} }
/> />
<PromptComposer <PromptComposer
disabled={busy || session.status !== "running"} disabled={busy || session.status !== "running" || !session.teamActivated || !session.currentProjectPath}
onSubmit={(prompt) => runAction(() => sendPrompt(prompt))} onSubmit={(prompt) => runAction(() => sendPrompt(prompt))}
/> />
</div> </div>

View File

@@ -1,20 +1,16 @@
import PixelButton from "./PixelButton.jsx"; import PixelButton from "./PixelButton.jsx";
export default function SessionToolbar({ session, busy, onStart, onActivate, onStop }) { export default function SessionToolbar({ session, busy, onStop, onSelectProject }) {
const isRunning = session.status === "running"; const isRunning = session.status === "running";
const isStarting = session.status === "starting";
return ( return (
<div className="session-toolbar session-toolbar--inline"> <div className="session-toolbar session-toolbar--inline">
<PixelButton tone="green" disabled={busy || isRunning || isStarting} onClick={onStart}>
Start Session
</PixelButton>
<PixelButton tone="cyan" disabled={busy || !isRunning} onClick={onActivate}>
Activate Team
</PixelButton>
<PixelButton tone="red" disabled={busy || (!isRunning && session.status !== "starting")} onClick={onStop}> <PixelButton tone="red" disabled={busy || (!isRunning && session.status !== "starting")} onClick={onStop}>
Stop Session Stop Session
</PixelButton> </PixelButton>
<PixelButton tone="amber" disabled={busy} onClick={onSelectProject}>
Select Project
</PixelButton>
</div> </div>
); );
} }

View File

@@ -21,7 +21,7 @@ function TeamCard({ member }) {
<span>NO SIGNAL YET</span> <span>NO SIGNAL YET</span>
</div> </div>
) : ( ) : (
member.messages.slice(-4).map((message) => ( [...member.messages].slice(-4).reverse().map((message) => (
<article key={message.id} className="team-message"> <article key={message.id} className="team-message">
<span className="team-message__speaker">{message.speaker}:</span> <span className="team-message__speaker">{message.speaker}:</span>
<pre>{message.text}</pre> <pre>{message.text}</pre>

View File

@@ -69,8 +69,7 @@ function shouldBreakCurrentEntry(line) {
/^[-=]{4,}$/.test(trimmed), /^[-=]{4,}$/.test(trimmed),
/^>/.test(trimmed), /^>/.test(trimmed),
/^Kullanici /i.test(trimmed), /^Kullanici /i.test(trimmed),
/^Mazlum nasilsin\?/i.test(trimmed), /^Mazlum nasilsin\?/i.test(trimmed)
/^[A-Za-zÀ-ÿ]+,/.test(trimmed)
].some(Boolean); ].some(Boolean);
} }
@@ -94,11 +93,34 @@ function dedupeMessages(messages) {
const result = []; const result = [];
for (const message of messages) { for (const message of messages) {
const key = `${message.speaker}::${message.text}`; const normalizedText = String(message.text ?? "").replace(/\s+/g, " ").trim();
const firstLine = normalizedText.split("\n")[0]?.trim() ?? "";
const key = `${message.speaker}::${normalizedText}`;
if (seen.has(key)) { if (seen.has(key)) {
continue; continue;
} }
const lastMessage = result[result.length - 1];
if (lastMessage) {
const lastNormalized = String(lastMessage.text ?? "").replace(/\s+/g, " ").trim();
const lastFirstLine = lastNormalized.split("\n")[0]?.trim() ?? "";
if (
lastMessage.speaker === message.speaker &&
firstLine &&
lastFirstLine === firstLine
) {
const mergedText = lastNormalized.length >= normalizedText.length ? lastMessage.text : message.text;
result[result.length - 1] = {
...lastMessage,
text: mergedText
};
seen.add(key);
continue;
}
}
seen.add(key); seen.add(key);
result.push(message); result.push(message);
} }
@@ -112,7 +134,7 @@ export function parseTeamFeed(chat) {
for (const rawLine of String(chat ?? "").split("\n")) { for (const rawLine of String(chat ?? "").split("\n")) {
const line = rawLine.trim(); const line = rawLine.trim();
const speakerMatch = line.match(/^[•*\-⏺]?\s*([A-Za-zÀ-ÿ]+):\s*(.*)$/); const speakerMatch = line.match(/^(?:[•*⏺]\s*)?([A-Za-zÀ-ÿ]+):\s*(.*)$/);
if (speakerMatch) { if (speakerMatch) {
const member = memberMap.get(normalizeSpeaker(speakerMatch[1])); const member = memberMap.get(normalizeSpeaker(speakerMatch[1]));

View File

@@ -35,6 +35,24 @@
font-size: clamp(1.3rem, 2vw, 2rem); font-size: clamp(1.3rem, 2vw, 2rem);
} }
.app-shell__title {
min-width: 0;
}
.app-shell__project {
margin: 10px 0 0;
max-width: min(70vw, 760px);
color: var(--text-dim);
font-size: 0.78rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.app-shell__project span {
color: var(--accent-amber);
}
.app-shell__meta { .app-shell__meta {
display: flex; display: flex;
gap: 10px; gap: 10px;
@@ -107,13 +125,14 @@
.session-toolbar { .session-toolbar {
display: flex; display: flex;
gap: 12px; gap: 8px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.session-toolbar--inline { .session-toolbar--inline {
justify-content: flex-end; justify-content: flex-end;
margin-bottom: 0; margin-bottom: 0;
flex-wrap: nowrap;
} }
.pixel-button { .pixel-button {
@@ -123,17 +142,17 @@
cursor: pointer; cursor: pointer;
text-transform: uppercase; text-transform: uppercase;
background: transparent; background: transparent;
min-width: 156px; min-width: 128px;
} }
.pixel-button span { .pixel-button span {
display: block; display: block;
padding: 14px 16px; padding: 12px 12px;
border: 3px solid var(--border-dark); border: 3px solid var(--border-dark);
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.06); box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.06);
font-family: var(--font-display); font-family: var(--font-display);
font-size: 0.62rem; font-size: 0.56rem;
letter-spacing: 0.14em; letter-spacing: 0.12em;
} }
.pixel-button:hover span { .pixel-button:hover span {
@@ -401,6 +420,10 @@
align-items: stretch; align-items: stretch;
} }
.app-shell__project {
max-width: 100%;
}
.shell-frame__screen { .shell-frame__screen {
min-height: auto; min-height: auto;
padding: 12px; padding: 12px;
@@ -413,5 +436,6 @@
.session-toolbar--inline { .session-toolbar--inline {
justify-content: stretch; justify-content: stretch;
flex-wrap: wrap;
} }
} }