Compact dashboard room progress UI
Compact the Rooms progress panel into a Discord-like current progress strip, show only the latest output by default, and move history/compose controls behind details.
This commit is contained in:
@@ -1315,56 +1315,63 @@ function RoomActivityPanel({
|
|||||||
const task = activity.pairedTask;
|
const task = activity.pairedTask;
|
||||||
const turn = task?.currentTurn ?? null;
|
const turn = task?.currentTurn ?? null;
|
||||||
const outputs = task?.outputs ?? [];
|
const outputs = task?.outputs ?? [];
|
||||||
|
const latestOutput = outputs.at(-1) ?? null;
|
||||||
|
const progressText = turn
|
||||||
|
? `${turn.role} · ${turn.intentKind} · ${t.rooms.attempt} ${turn.attemptNo}`
|
||||||
|
: t.rooms.noTurn;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="room-activity">
|
<div className="room-activity">
|
||||||
<div className="room-activity-grid">
|
<div className="room-progress-strip">
|
||||||
<span>
|
|
||||||
<small>{t.rooms.task}</small>
|
|
||||||
<strong>{task?.title || task?.id || t.rooms.noTask}</strong>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<small>{t.rooms.currentTurn}</small>
|
|
||||||
<strong>
|
|
||||||
{turn
|
|
||||||
? `${turn.role} · ${t.rooms.attempt} ${turn.attemptNo}`
|
|
||||||
: t.rooms.noTurn}
|
|
||||||
</strong>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<small>{t.rooms.round}</small>
|
|
||||||
<strong>{task ? task.roundTripCount : '-'}</strong>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<small>{t.rooms.updated}</small>
|
|
||||||
<strong>
|
|
||||||
{formatDate(turn?.updatedAt ?? task?.updatedAt, locale)}
|
|
||||||
</strong>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{turn ? (
|
{turn ? (
|
||||||
<div className="room-turn-line">
|
|
||||||
<span className={`pill pill-${turn.state}`}>
|
<span className={`pill pill-${turn.state}`}>
|
||||||
{statusLabel(turn.state, t)}
|
{statusLabel(turn.state, t)}
|
||||||
</span>
|
</span>
|
||||||
<span>{turn.intentKind}</span>
|
) : (
|
||||||
{turn.executorServiceId ? (
|
<span className="pill pill-inactive">{t.rooms.noTurn}</span>
|
||||||
<span>{turn.executorServiceId}</span>
|
)}
|
||||||
|
<strong>{progressText}</strong>
|
||||||
|
<span>{task ? `${t.rooms.round} ${task.roundTripCount}` : '-'}</span>
|
||||||
|
{turn?.executorServiceId ? <span>{turn.executorServiceId}</span> : null}
|
||||||
|
<time>{formatDate(turn?.updatedAt ?? task?.updatedAt, locale)}</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section className="room-latest-output">
|
||||||
|
<header>
|
||||||
|
<strong>{t.rooms.latestOutput}</strong>
|
||||||
|
{latestOutput ? (
|
||||||
|
<span>
|
||||||
|
#{latestOutput.turnNumber} · {latestOutput.role}
|
||||||
|
{latestOutput.verdict ? ` · ${latestOutput.verdict}` : ''}
|
||||||
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
{turn.activeRunId ? (
|
</header>
|
||||||
|
{latestOutput ? (
|
||||||
|
<p>{latestOutput.outputText}</p>
|
||||||
|
) : (
|
||||||
|
<p className="room-muted">{t.rooms.noOutput}</p>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<details className="room-activity-details">
|
||||||
|
<summary>{t.rooms.details}</summary>
|
||||||
|
<div className="room-detail-grid">
|
||||||
|
<section>
|
||||||
|
<strong>{t.rooms.task}</strong>
|
||||||
|
<p className="room-muted">
|
||||||
|
{task?.title || task?.id || t.rooms.noTask}
|
||||||
|
</p>
|
||||||
|
<div className="room-turn-line">
|
||||||
|
{turn?.activeRunId ? (
|
||||||
<span className="mono-chip">{turn.activeRunId}</span>
|
<span className="mono-chip">{turn.activeRunId}</span>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{turn?.lastError ? (
|
{turn?.lastError ? (
|
||||||
<p className="room-activity-error">{turn.lastError}</p>
|
<span className="room-activity-error">{turn.lastError}</span>
|
||||||
) : null}
|
) : null}
|
||||||
|
</div>
|
||||||
<div className="room-activity-columns">
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<strong>{t.rooms.output}</strong>
|
<strong>{t.rooms.outputHistory}</strong>
|
||||||
{outputs.length === 0 ? (
|
{outputs.length === 0 ? (
|
||||||
<p className="room-muted">{t.rooms.noOutput}</p>
|
<p className="room-muted">{t.rooms.noOutput}</p>
|
||||||
) : (
|
) : (
|
||||||
@@ -1385,7 +1392,7 @@ function RoomActivityPanel({
|
|||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<strong>{t.rooms.recentMessages}</strong>
|
<strong>{t.rooms.messageHistory}</strong>
|
||||||
{activity.messages.length === 0 ? (
|
{activity.messages.length === 0 ? (
|
||||||
<p className="room-muted">{t.rooms.noMessages}</p>
|
<p className="room-muted">{t.rooms.noMessages}</p>
|
||||||
) : (
|
) : (
|
||||||
@@ -1408,10 +1415,38 @@ function RoomActivityPanel({
|
|||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
</details>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RoomComposeDetails({
|
||||||
|
busy,
|
||||||
|
onChange,
|
||||||
|
onSubmit,
|
||||||
|
t,
|
||||||
|
value,
|
||||||
|
}: {
|
||||||
|
busy: boolean;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
onSubmit: () => void;
|
||||||
|
t: Messages;
|
||||||
|
value: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<details className="room-compose-details">
|
||||||
|
<summary>{t.rooms.message}</summary>
|
||||||
|
<RoomMessageForm
|
||||||
|
busy={busy}
|
||||||
|
onChange={onChange}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
t={t}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</details>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function RoomPanel({
|
function RoomPanel({
|
||||||
onSendRoomMessage,
|
onSendRoomMessage,
|
||||||
roomActivity,
|
roomActivity,
|
||||||
@@ -1502,8 +1537,8 @@ function RoomPanel({
|
|||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td className="room-message-cell">
|
||||||
<RoomMessageForm
|
<RoomComposeDetails
|
||||||
busy={roomMessageKey === entry.jid}
|
busy={roomMessageKey === entry.jid}
|
||||||
onChange={(value) => setDraft(entry.jid, value)}
|
onChange={(value) => setDraft(entry.jid, value)}
|
||||||
onSubmit={() => void submitRoomMessage(entry.jid)}
|
onSubmit={() => void submitRoomMessage(entry.jid)}
|
||||||
@@ -1550,7 +1585,7 @@ function RoomPanel({
|
|||||||
locale={locale}
|
locale={locale}
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
<RoomMessageForm
|
<RoomComposeDetails
|
||||||
busy={roomMessageKey === entry.jid}
|
busy={roomMessageKey === entry.jid}
|
||||||
onChange={(value) => setDraft(entry.jid, value)}
|
onChange={(value) => setDraft(entry.jid, value)}
|
||||||
onSubmit={() => void submitRoomMessage(entry.jid)}
|
onSubmit={() => void submitRoomMessage(entry.jid)}
|
||||||
|
|||||||
@@ -158,8 +158,11 @@ export interface Messages {
|
|||||||
attempt: string;
|
attempt: string;
|
||||||
updated: string;
|
updated: string;
|
||||||
output: string;
|
output: string;
|
||||||
|
latestOutput: string;
|
||||||
|
outputHistory: string;
|
||||||
noOutput: string;
|
noOutput: string;
|
||||||
recentMessages: string;
|
recentMessages: string;
|
||||||
|
messageHistory: string;
|
||||||
noMessages: string;
|
noMessages: string;
|
||||||
details: string;
|
details: string;
|
||||||
message: string;
|
message: string;
|
||||||
@@ -446,8 +449,11 @@ export const messages = {
|
|||||||
attempt: '시도',
|
attempt: '시도',
|
||||||
updated: '갱신',
|
updated: '갱신',
|
||||||
output: '출력',
|
output: '출력',
|
||||||
|
latestOutput: '마지막 출력',
|
||||||
|
outputHistory: '출력 기록',
|
||||||
noOutput: '출력 없음',
|
noOutput: '출력 없음',
|
||||||
recentMessages: '최근 메시지',
|
recentMessages: '메시지 기록',
|
||||||
|
messageHistory: '메시지 기록',
|
||||||
noMessages: '메시지 없음',
|
noMessages: '메시지 없음',
|
||||||
details: '세부',
|
details: '세부',
|
||||||
message: 'Message',
|
message: 'Message',
|
||||||
@@ -718,8 +724,11 @@ export const messages = {
|
|||||||
attempt: 'attempt',
|
attempt: 'attempt',
|
||||||
updated: 'updated',
|
updated: 'updated',
|
||||||
output: 'output',
|
output: 'output',
|
||||||
|
latestOutput: 'latest output',
|
||||||
|
outputHistory: 'output log',
|
||||||
noOutput: 'No output',
|
noOutput: 'No output',
|
||||||
recentMessages: 'Recent messages',
|
recentMessages: 'Message log',
|
||||||
|
messageHistory: 'Message log',
|
||||||
noMessages: 'No messages',
|
noMessages: 'No messages',
|
||||||
details: 'details',
|
details: 'details',
|
||||||
message: 'message',
|
message: 'message',
|
||||||
@@ -990,8 +999,11 @@ export const messages = {
|
|||||||
attempt: '尝试',
|
attempt: '尝试',
|
||||||
updated: '更新',
|
updated: '更新',
|
||||||
output: '输出',
|
output: '输出',
|
||||||
|
latestOutput: '最新输出',
|
||||||
|
outputHistory: '输出记录',
|
||||||
noOutput: '暂无输出',
|
noOutput: '暂无输出',
|
||||||
recentMessages: '最近消息',
|
recentMessages: '消息记录',
|
||||||
|
messageHistory: '消息记录',
|
||||||
noMessages: '暂无消息',
|
noMessages: '暂无消息',
|
||||||
details: '详情',
|
details: '详情',
|
||||||
message: 'Message',
|
message: 'Message',
|
||||||
@@ -1262,8 +1274,11 @@ export const messages = {
|
|||||||
attempt: '試行',
|
attempt: '試行',
|
||||||
updated: '更新',
|
updated: '更新',
|
||||||
output: '出力',
|
output: '出力',
|
||||||
|
latestOutput: '最新出力',
|
||||||
|
outputHistory: '出力履歴',
|
||||||
noOutput: '出力なし',
|
noOutput: '出力なし',
|
||||||
recentMessages: '最近のメッセージ',
|
recentMessages: 'メッセージ履歴',
|
||||||
|
messageHistory: 'メッセージ履歴',
|
||||||
noMessages: 'メッセージなし',
|
noMessages: 'メッセージなし',
|
||||||
details: '詳細',
|
details: '詳細',
|
||||||
message: 'Message',
|
message: 'Message',
|
||||||
|
|||||||
@@ -1328,16 +1328,16 @@ progress::-moz-progress-bar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.room-activity-cell {
|
.room-activity-cell {
|
||||||
min-width: 360px;
|
min-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-activity {
|
.room-activity {
|
||||||
display: grid;
|
display: grid;
|
||||||
min-width: min(360px, 100%);
|
min-width: min(300px, 100%);
|
||||||
gap: 9px;
|
gap: 7px;
|
||||||
padding: 10px;
|
padding: 8px;
|
||||||
border: 1px solid rgba(43, 55, 38, 0.1);
|
border: 1px solid rgba(43, 55, 38, 0.1);
|
||||||
border-radius: 18px;
|
border-radius: 15px;
|
||||||
background: rgba(255, 250, 240, 0.58);
|
background: rgba(255, 250, 240, 0.58);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1348,38 +1348,28 @@ progress::-moz-progress-bar {
|
|||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-activity-grid {
|
.room-progress-strip {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: auto minmax(96px, 1fr) auto auto auto;
|
||||||
gap: 7px;
|
gap: 6px;
|
||||||
}
|
align-items: center;
|
||||||
|
|
||||||
.room-activity-grid > span {
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 8px;
|
color: var(--muted);
|
||||||
border-radius: 13px;
|
font-size: 11px;
|
||||||
background: rgba(43, 55, 38, 0.045);
|
font-weight: 850;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-activity-grid small,
|
.room-progress-strip strong,
|
||||||
.room-activity-grid strong {
|
.room-progress-strip span,
|
||||||
display: block;
|
.room-progress-strip time {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-activity-grid small {
|
.room-progress-strip strong {
|
||||||
color: var(--muted);
|
color: var(--ink);
|
||||||
font-size: 10px;
|
|
||||||
font-weight: 850;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.room-activity-grid strong {
|
|
||||||
margin-top: 3px;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1398,33 +1388,85 @@ progress::-moz-progress-bar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.room-activity-error {
|
.room-activity-error {
|
||||||
margin: 0;
|
|
||||||
color: #7c2518;
|
color: #7c2518;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 850;
|
font-weight: 850;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-activity-columns {
|
.room-latest-output {
|
||||||
display: grid;
|
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
||||||
gap: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.room-activity-columns > section {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
align-content: start;
|
padding: 8px;
|
||||||
gap: 7px;
|
border-radius: 12px;
|
||||||
|
background: rgba(43, 55, 38, 0.045);
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-activity-columns > section > strong {
|
.room-latest-output header {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 850;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-latest-output strong,
|
||||||
|
.room-detail-grid > section > strong {
|
||||||
color: var(--bg-ink);
|
color: var(--bg-ink);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.06em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.room-latest-output header span,
|
||||||
|
.room-latest-output p {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-latest-output p {
|
||||||
|
display: -webkit-box;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--ink);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.32;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-activity-details,
|
||||||
|
.room-compose-details {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-activity-details > summary,
|
||||||
|
.room-compose-details > summary {
|
||||||
|
min-height: 32px;
|
||||||
|
color: var(--accent);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 900;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-detail-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-detail-grid > section {
|
||||||
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
|
align-content: start;
|
||||||
|
gap: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
.room-output-list,
|
.room-output-list,
|
||||||
.room-message-list {
|
.room-message-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -1486,6 +1528,14 @@ progress::-moz-progress-bar {
|
|||||||
-webkit-line-clamp: 4;
|
-webkit-line-clamp: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.room-message-cell {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-compose-details .room-compose {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.task-board {
|
.task-board {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
@@ -2165,9 +2215,13 @@ progress::-moz-progress-bar {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-activity-grid,
|
.room-progress-strip {
|
||||||
.room-activity-columns {
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||||
grid-template-columns: 1fr;
|
}
|
||||||
|
|
||||||
|
.room-progress-strip span:nth-of-type(n + 3),
|
||||||
|
.room-progress-strip time {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inbox-summary,
|
.inbox-summary,
|
||||||
|
|||||||
Reference in New Issue
Block a user