Workflow run 페이지를 archive 로 격하시키고, 사용자가 처음 보는 화면을
chat-style 대화 thread 로 전환. Claude Code 의 Web GUI 와 동일한 UX.
핵심 동작:
- 새 페이지 `/conversation.html` 에서 세션을 picker 로 고르거나 "새 대화"
버튼으로 만들고 메시지 입력. Cmd/Ctrl+Enter 로 전송.
- POST /api/sessions/{id}/messages 가 user MessageRow 를 영속한 즉시 200 응답
후 `asyncio.create_task(invoke_session_agent(...))` 로 백그라운드 invoke 발사.
- 백그라운드 task 는 lifespan 에서 1회 열어둔 LangGraph saver 를 재사용하고
agent.ainvoke → assistant MessageRow 영속 → 자동 compaction 까지 처리.
- 기존 SSE 스트림 (`/api/sessions/{id}/stream`) 이 새 메시지를 push,
프론트엔드의 `EventSource` 가 받아 thread 에 렌더.
신규 / 수정 파일:
- `static/conversation.html` (신규): chat UI 마크업. data-page="conversation".
- `static/app.js`: 새 페이지 핸들러 `bootstrapConversationPage` +
세션 picker + 메시지 thread 렌더 + SSE 구독 + Cmd/Ctrl+Enter 단축키.
XSS 정책 동일: 모든 사용자 콘텐츠는 `textContent` 만 사용.
- `static/style.css`: `.messages-thread`, `.msg-bubble`, `.conv-topbar`,
`.conv-input-bar` 등 chat UI 스타일.
- `api/app.py`: lifespan 에서 LangGraph saver 를 1회 열어 `app.state.saver`
에 보관 (Postgres 일 때만).
- `api/agent_runner.py` (신규): `invoke_session_agent(...)` — REPL 의
`InteractiveSession + _invoke_and_stream` 와 동일한 stack 을 HTTP background
context 용으로 재구성. 실패는 로깅 + return.
- `api/routes/sessions.py`: POST /messages 가 background task 발사 + ref 를
`app.state.pending_invocations` set 에 보관 (RUF006 / GC drop 방지).
테스트 (`tests/integration/test_conversation_gui.py`, 4 케이스):
- GET /conversation.html → 200 + 필수 마크업
- POST /messages → 200 + user row 영속 + 스텁 runner 호출 확인
- 백그라운드 task ref 가 `pending_invocations` 에 잡혀있고 완료 후 자동 discard
- 스텁 runner 가 assistant row 영속 → user + assistant 시퀀스 검증
게이트:
- ruff check / format --check / mypy: PASS
- pytest -q --ignore=tests/integration/test_e2e_workflow.py
--ignore=tests/integration/test_openrouter_smoke.py: 675 passed (4 신규 포함)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
965 lines
18 KiB
CSS
965 lines
18 KiB
CSS
/* my-deepagent Web GUI — v0.2 PR #3 (Claude design pass).
|
||
*
|
||
* Visual reference: Anthropic / Claude design language
|
||
* - Warm cream paper background (#F5F0E8 family)
|
||
* - Charcoal warm-tinted ink for body
|
||
* - Tiempos / Source Serif headings (serif heads, sans body)
|
||
* - Rust / terracotta accent (#CC5500 family)
|
||
* - Generous whitespace, soft 1px borders, minimal shadow
|
||
* - Rounded corners 6–12 px, no heavy elevation
|
||
*
|
||
* Vanilla CSS only. No framework, no build system.
|
||
*/
|
||
|
||
/* ---------- Reset + tokens ---------- */
|
||
|
||
*,
|
||
*::before,
|
||
*::after {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
:root {
|
||
/* Paper / surface */
|
||
--bg: #f5f0e8;
|
||
--surface-1: #fbfaf6;
|
||
--surface-2: #efe9de;
|
||
--surface-3: #e7e0d3;
|
||
--surface-hover: #eee7da;
|
||
|
||
/* Borders */
|
||
--border: #e2dccf;
|
||
--border-strong: #cfc7b6;
|
||
|
||
/* Ink */
|
||
--text-primary: #1b1916;
|
||
--text-secondary: #54514a;
|
||
--text-muted: #8a857a;
|
||
--text-faint: #b6b0a3;
|
||
|
||
/* Accents (rust + supporting palette) */
|
||
--accent: #c14a1a;
|
||
--accent-hover: #a73d12;
|
||
--accent-bg: rgba(193, 74, 26, 0.1);
|
||
|
||
--success: #4a6f2a;
|
||
--success-bg: rgba(74, 111, 42, 0.1);
|
||
--warning: #a86c1f;
|
||
--warning-bg: rgba(168, 108, 31, 0.12);
|
||
--danger: #a33419;
|
||
--danger-bg: rgba(163, 52, 25, 0.1);
|
||
--info: #4a5d8c;
|
||
--info-bg: rgba(74, 93, 140, 0.1);
|
||
|
||
/* Type */
|
||
--font-serif: "Tiempos Headline", "Source Serif Pro", "IBM Plex Serif",
|
||
"Georgia", "Apple SD Gothic Neo", "Noto Serif KR", serif;
|
||
--font-sans: "Styrene B", "Styrene A", "Inter", "Pretendard",
|
||
-apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo",
|
||
"Noto Sans KR", "Segoe UI", Helvetica, Arial, sans-serif;
|
||
--font-mono: "JetBrains Mono", "SF Mono", "Menlo", "Monaco", "Consolas",
|
||
monospace;
|
||
|
||
/* Geometry */
|
||
--radius-sm: 6px;
|
||
--radius: 8px;
|
||
--radius-lg: 12px;
|
||
|
||
--shadow-card: 0 1px 0 rgba(28, 25, 22, 0.04);
|
||
}
|
||
|
||
/* ---------- Base ---------- */
|
||
|
||
html,
|
||
body {
|
||
margin: 0;
|
||
padding: 0;
|
||
background: var(--bg);
|
||
color: var(--text-primary);
|
||
font-family: var(--font-sans);
|
||
font-size: 15px;
|
||
line-height: 1.55;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
body {
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
a {
|
||
color: var(--accent);
|
||
text-decoration: none;
|
||
transition: color 0.15s ease;
|
||
}
|
||
|
||
a:hover {
|
||
color: var(--accent-hover);
|
||
text-decoration: underline;
|
||
text-decoration-thickness: 1px;
|
||
text-underline-offset: 3px;
|
||
}
|
||
|
||
code,
|
||
kbd,
|
||
.mono {
|
||
font-family: var(--font-mono);
|
||
font-size: 0.86em;
|
||
letter-spacing: -0.005em;
|
||
}
|
||
|
||
::selection {
|
||
background: var(--accent);
|
||
color: var(--surface-1);
|
||
}
|
||
|
||
/* ---------- Header / nav ---------- */
|
||
|
||
header {
|
||
background: transparent;
|
||
border-bottom: 1px solid var(--border);
|
||
padding: 20px 36px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: baseline;
|
||
gap: 24px;
|
||
}
|
||
|
||
header h1 {
|
||
margin: 0;
|
||
font-family: var(--font-serif);
|
||
font-size: 22px;
|
||
font-weight: 500;
|
||
letter-spacing: -0.02em;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
header h1 a {
|
||
color: inherit;
|
||
}
|
||
|
||
header h1 a:hover {
|
||
text-decoration: none;
|
||
color: var(--accent);
|
||
}
|
||
|
||
header nav {
|
||
display: flex;
|
||
gap: 4px;
|
||
align-self: center;
|
||
}
|
||
|
||
header nav a {
|
||
color: var(--text-secondary);
|
||
padding: 6px 12px;
|
||
border-radius: var(--radius-sm);
|
||
font-size: 14px;
|
||
font-weight: 400;
|
||
transition: all 0.15s ease;
|
||
}
|
||
|
||
header nav a:hover {
|
||
color: var(--text-primary);
|
||
background: var(--surface-2);
|
||
text-decoration: none;
|
||
}
|
||
|
||
header nav a.active {
|
||
color: var(--accent);
|
||
background: var(--accent-bg);
|
||
}
|
||
|
||
/* ---------- Main ---------- */
|
||
|
||
main {
|
||
flex: 1;
|
||
max-width: 1180px;
|
||
width: 100%;
|
||
margin: 0 auto;
|
||
padding: 40px 36px 64px;
|
||
}
|
||
|
||
.page-title {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
margin-bottom: 28px;
|
||
border-bottom: 1px solid var(--border);
|
||
padding-bottom: 16px;
|
||
}
|
||
|
||
.page-title h2 {
|
||
margin: 0;
|
||
font-family: var(--font-serif);
|
||
font-size: 30px;
|
||
font-weight: 500;
|
||
letter-spacing: -0.02em;
|
||
text-transform: none;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.page-subtitle {
|
||
color: var(--text-muted);
|
||
font-size: 14px;
|
||
}
|
||
|
||
h2 {
|
||
margin: 36px 0 14px;
|
||
font-family: var(--font-serif);
|
||
font-size: 18px;
|
||
font-weight: 500;
|
||
letter-spacing: -0.01em;
|
||
color: var(--text-primary);
|
||
text-transform: none;
|
||
}
|
||
|
||
h2.section-title {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 10px;
|
||
}
|
||
|
||
h2.section-title .hint {
|
||
font-family: var(--font-sans);
|
||
font-size: 13px;
|
||
color: var(--text-muted);
|
||
font-weight: 400;
|
||
margin-left: 4px;
|
||
}
|
||
|
||
/* ---------- Cards / tables ---------- */
|
||
|
||
.card {
|
||
background: var(--surface-1);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow-card);
|
||
overflow: hidden;
|
||
}
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: separate;
|
||
border-spacing: 0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
th,
|
||
td {
|
||
text-align: left;
|
||
padding: 14px 18px;
|
||
border-bottom: 1px solid var(--border);
|
||
vertical-align: middle;
|
||
}
|
||
|
||
tbody tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
tbody tr {
|
||
transition: background 0.12s ease;
|
||
}
|
||
|
||
tbody tr:hover {
|
||
background: var(--surface-2);
|
||
}
|
||
|
||
th {
|
||
background: var(--surface-2);
|
||
color: var(--text-secondary);
|
||
font-weight: 500;
|
||
font-size: 12px;
|
||
letter-spacing: 0.03em;
|
||
text-transform: uppercase;
|
||
padding-top: 11px;
|
||
padding-bottom: 11px;
|
||
border-bottom: 1px solid var(--border-strong);
|
||
}
|
||
|
||
td .mono {
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
td a {
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* ---------- State badges (pill) ---------- */
|
||
|
||
.badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 3px 11px;
|
||
border-radius: 999px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
letter-spacing: 0.01em;
|
||
border: 1px solid transparent;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.badge::before {
|
||
content: "";
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: currentColor;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.badge.state-completed,
|
||
.badge.state-ok {
|
||
color: var(--success);
|
||
background: var(--success-bg);
|
||
border-color: rgba(74, 111, 42, 0.2);
|
||
}
|
||
|
||
.badge.state-running,
|
||
.badge.state-executing,
|
||
.badge.state-validating,
|
||
.badge.state-awaiting_artifact,
|
||
.badge.state-awaiting_approval {
|
||
color: var(--warning);
|
||
background: var(--warning-bg);
|
||
border-color: rgba(168, 108, 31, 0.22);
|
||
}
|
||
|
||
.badge.state-failed,
|
||
.badge.state-aborted {
|
||
color: var(--danger);
|
||
background: var(--danger-bg);
|
||
border-color: rgba(163, 52, 25, 0.2);
|
||
}
|
||
|
||
.badge.state-pending,
|
||
.badge.state-created,
|
||
.badge.state-bound,
|
||
.badge.state-planning,
|
||
.badge.state-paused,
|
||
.badge.state-skipped {
|
||
color: var(--info);
|
||
background: var(--info-bg);
|
||
border-color: rgba(74, 93, 140, 0.2);
|
||
}
|
||
|
||
.badge.state-running::before,
|
||
.badge.state-executing::before,
|
||
.badge.state-validating::before,
|
||
.badge.state-awaiting_artifact::before {
|
||
animation: pulse 1.6s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; transform: scale(1); }
|
||
50% { opacity: 0.45; transform: scale(0.85); }
|
||
}
|
||
|
||
/* ---------- Buttons ---------- */
|
||
|
||
button,
|
||
.button {
|
||
appearance: none;
|
||
background: var(--surface-1);
|
||
color: var(--text-primary);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--radius-sm);
|
||
padding: 8px 16px;
|
||
font-family: inherit;
|
||
font-size: 13.5px;
|
||
font-weight: 500;
|
||
letter-spacing: -0.005em;
|
||
cursor: pointer;
|
||
transition: all 0.15s ease;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
text-decoration: none;
|
||
}
|
||
|
||
button:hover:not(:disabled),
|
||
.button:hover {
|
||
background: var(--surface-hover);
|
||
border-color: var(--text-faint);
|
||
color: var(--text-primary);
|
||
text-decoration: none;
|
||
}
|
||
|
||
button:active:not(:disabled) {
|
||
transform: translateY(0.5px);
|
||
}
|
||
|
||
button:disabled {
|
||
opacity: 0.4;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
button.primary {
|
||
background: var(--accent);
|
||
border-color: var(--accent);
|
||
color: var(--surface-1);
|
||
font-weight: 500;
|
||
}
|
||
|
||
button.primary:hover:not(:disabled) {
|
||
background: var(--accent-hover);
|
||
border-color: var(--accent-hover);
|
||
color: var(--surface-1);
|
||
}
|
||
|
||
button.danger {
|
||
background: transparent;
|
||
color: var(--danger);
|
||
border-color: rgba(163, 52, 25, 0.32);
|
||
}
|
||
|
||
button.danger:hover:not(:disabled) {
|
||
background: var(--danger-bg);
|
||
border-color: var(--danger);
|
||
}
|
||
|
||
/* ---------- Forms ---------- */
|
||
|
||
label {
|
||
display: block;
|
||
margin: 0 0 6px;
|
||
color: var(--text-secondary);
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
label .hint {
|
||
color: var(--text-muted);
|
||
font-weight: 400;
|
||
margin-left: 6px;
|
||
font-size: 12.5px;
|
||
}
|
||
|
||
input[type="text"],
|
||
input[type="number"],
|
||
textarea,
|
||
select {
|
||
width: 100%;
|
||
background: var(--surface-1);
|
||
color: var(--text-primary);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius-sm);
|
||
padding: 10px 13px;
|
||
font-family: inherit;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
|
||
}
|
||
|
||
input:focus,
|
||
textarea:focus,
|
||
select:focus {
|
||
outline: none;
|
||
border-color: var(--accent);
|
||
box-shadow: 0 0 0 3px var(--accent-bg);
|
||
}
|
||
|
||
input::placeholder,
|
||
textarea::placeholder {
|
||
color: var(--text-faint);
|
||
}
|
||
|
||
textarea {
|
||
resize: vertical;
|
||
min-height: 110px;
|
||
font-family: var(--font-mono);
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
select {
|
||
appearance: none;
|
||
background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%2354514a' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
|
||
background-repeat: no-repeat;
|
||
background-position: right 14px center;
|
||
padding-right: 36px;
|
||
}
|
||
|
||
.form-row {
|
||
margin-bottom: 18px;
|
||
}
|
||
|
||
.form-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 220px;
|
||
gap: 16px;
|
||
}
|
||
|
||
.action-bar {
|
||
display: flex;
|
||
gap: 8px;
|
||
margin: 28px 0 0;
|
||
padding-top: 18px;
|
||
border-top: 1px solid var(--border);
|
||
}
|
||
|
||
.action-bar.no-top-border {
|
||
border-top: none;
|
||
padding-top: 0;
|
||
}
|
||
|
||
/* ---------- Meta panel (key/value lists) ---------- */
|
||
|
||
.meta-panel {
|
||
background: var(--surface-1);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
padding: 4px 0;
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.meta-row {
|
||
display: grid;
|
||
grid-template-columns: 150px 1fr;
|
||
gap: 18px;
|
||
padding: 12px 18px;
|
||
border-bottom: 1px solid var(--border);
|
||
align-items: center;
|
||
}
|
||
|
||
.meta-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.meta-row .key {
|
||
color: var(--text-muted);
|
||
font-size: 12.5px;
|
||
font-weight: 500;
|
||
letter-spacing: 0.02em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.meta-row .value {
|
||
color: var(--text-primary);
|
||
font-size: 14px;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.meta-row .value.mono {
|
||
font-family: var(--font-mono);
|
||
font-size: 12.5px;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.meta-row .value.dim {
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
/* ---------- Budget summary cards ---------- */
|
||
|
||
.budget-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||
gap: 14px;
|
||
}
|
||
|
||
.budget-card {
|
||
background: var(--surface-1);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
padding: 18px;
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.budget-card .scope {
|
||
color: var(--text-muted);
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
letter-spacing: 0.04em;
|
||
text-transform: uppercase;
|
||
margin-bottom: 8px;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 1;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.budget-card .amount {
|
||
font-family: var(--font-serif);
|
||
font-size: 26px;
|
||
font-weight: 500;
|
||
letter-spacing: -0.02em;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.budget-card .amount.warn { color: var(--warning); }
|
||
.budget-card .amount.over { color: var(--danger); }
|
||
|
||
.budget-card .cap {
|
||
font-family: var(--font-sans);
|
||
color: var(--text-muted);
|
||
font-size: 13px;
|
||
font-weight: 400;
|
||
margin-left: 6px;
|
||
}
|
||
|
||
.budget-card .bar {
|
||
height: 4px;
|
||
background: var(--surface-3);
|
||
border-radius: 2px;
|
||
margin-top: 12px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.budget-card .bar > div {
|
||
height: 100%;
|
||
background: var(--success);
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.budget-card .bar.warn > div { background: var(--warning); }
|
||
.budget-card .bar.over > div { background: var(--danger); }
|
||
|
||
/* ---------- Event log (SSE) ---------- */
|
||
|
||
.events {
|
||
background: var(--surface-1);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
max-height: 60vh;
|
||
overflow-y: auto;
|
||
padding: 8px 0;
|
||
font-family: var(--font-mono);
|
||
font-size: 12.5px;
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.event-line {
|
||
display: grid;
|
||
grid-template-columns: 80px 1fr;
|
||
gap: 14px;
|
||
padding: 6px 18px;
|
||
border-bottom: 1px solid transparent;
|
||
}
|
||
|
||
.event-line:hover {
|
||
background: var(--surface-2);
|
||
}
|
||
|
||
.event-line .ts {
|
||
color: var(--text-faint);
|
||
font-size: 11.5px;
|
||
}
|
||
|
||
.event-line .body .type {
|
||
color: var(--accent);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.event-line .body .payload {
|
||
color: var(--text-muted);
|
||
margin-left: 8px;
|
||
font-size: 11.5px;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.event-line.run-completed .body .type { color: var(--success); }
|
||
.event-line.run-failed .body .type,
|
||
.event-line.run-aborted .body .type { color: var(--danger); }
|
||
.event-line.run-resumed .body .type { color: var(--warning); }
|
||
|
||
.events::-webkit-scrollbar {
|
||
width: 10px;
|
||
}
|
||
|
||
.events::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.events::-webkit-scrollbar-thumb {
|
||
background: var(--border-strong);
|
||
border-radius: 5px;
|
||
border: 2px solid var(--surface-1);
|
||
}
|
||
|
||
/* ---------- Empty / error states ---------- */
|
||
|
||
.empty {
|
||
padding: 56px 24px;
|
||
text-align: center;
|
||
color: var(--text-muted);
|
||
font-size: 14px;
|
||
}
|
||
|
||
.empty .empty-icon {
|
||
font-family: var(--font-serif);
|
||
font-size: 30px;
|
||
margin-bottom: 10px;
|
||
opacity: 0.5;
|
||
color: var(--accent);
|
||
}
|
||
|
||
.empty .cta {
|
||
margin-top: 18px;
|
||
}
|
||
|
||
.error-banner {
|
||
background: var(--danger-bg);
|
||
border: 1px solid rgba(163, 52, 25, 0.28);
|
||
border-radius: var(--radius);
|
||
padding: 12px 16px;
|
||
margin-bottom: 16px;
|
||
color: var(--danger);
|
||
font-size: 13.5px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.error-banner::before {
|
||
content: "!";
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 18px;
|
||
height: 18px;
|
||
background: var(--danger);
|
||
color: var(--surface-1);
|
||
border-radius: 50%;
|
||
font-weight: 700;
|
||
font-size: 11px;
|
||
flex-shrink: 0;
|
||
font-family: var(--font-sans);
|
||
}
|
||
|
||
/* ---------- Tag chip (per-role override input) ---------- */
|
||
|
||
.chips {
|
||
display: grid;
|
||
grid-template-columns: 160px 1fr;
|
||
gap: 14px;
|
||
padding: 14px 18px;
|
||
border-bottom: 1px solid var(--border);
|
||
align-items: center;
|
||
}
|
||
|
||
.chips:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.chips .role {
|
||
font-size: 13px;
|
||
color: var(--text-secondary);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.chips .role .hint {
|
||
display: block;
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
font-weight: 400;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.chips input {
|
||
font-family: var(--font-mono);
|
||
font-size: 12.5px;
|
||
}
|
||
|
||
/* ---------- Responsive ---------- */
|
||
|
||
@media (max-width: 720px) {
|
||
main { padding: 24px 16px 40px; }
|
||
header { padding: 16px 16px; }
|
||
.page-title h2 { font-size: 24px; }
|
||
.form-grid { grid-template-columns: 1fr; }
|
||
.meta-row { grid-template-columns: 1fr; gap: 4px; padding: 12px 14px; }
|
||
.event-line { grid-template-columns: 1fr; gap: 2px; }
|
||
.chips { grid-template-columns: 1fr; gap: 6px; }
|
||
}
|
||
|
||
/* =================================================================
|
||
v0.3 PR #8 — Conversation page
|
||
================================================================= */
|
||
|
||
.conversation-main {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: calc(100vh - 80px);
|
||
padding-bottom: 0;
|
||
}
|
||
|
||
.conv-topbar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px 16px;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
margin-bottom: 12px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.conv-label {
|
||
font-size: 13px;
|
||
color: var(--text-muted);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.conv-picker {
|
||
flex: 1;
|
||
min-width: 240px;
|
||
padding: 6px 10px;
|
||
font-family: var(--font-mono);
|
||
font-size: 13px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
background: var(--bg);
|
||
}
|
||
|
||
.conv-action-btn {
|
||
padding: 6px 14px;
|
||
font-size: 13px;
|
||
background: var(--accent);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.conv-action-btn:hover { filter: brightness(1.08); }
|
||
|
||
.conv-session-state {
|
||
font-size: 11px;
|
||
padding: 2px 8px;
|
||
border-radius: 999px;
|
||
text-transform: lowercase;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
|
||
.conv-session-state.state-active {
|
||
background: rgba(34,197,94,0.12);
|
||
color: rgb(22,163,74);
|
||
}
|
||
|
||
.conv-session-state.state-ended {
|
||
background: rgba(100,116,139,0.12);
|
||
color: rgb(71,85,105);
|
||
}
|
||
|
||
.messages-thread {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 16px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
background: var(--bg);
|
||
margin-bottom: 12px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.conv-empty {
|
||
color: var(--text-muted);
|
||
text-align: center;
|
||
padding: 40px 16px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.msg-bubble {
|
||
max-width: 80%;
|
||
padding: 10px 14px;
|
||
border-radius: 12px;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.msg-bubble.role-user {
|
||
align-self: flex-end;
|
||
background: var(--accent);
|
||
color: white;
|
||
}
|
||
|
||
.msg-bubble.role-assistant {
|
||
align-self: flex-start;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
}
|
||
|
||
.msg-bubble.role-system {
|
||
align-self: center;
|
||
max-width: 90%;
|
||
font-style: italic;
|
||
font-size: 12.5px;
|
||
background: rgba(245,158,11,0.08);
|
||
border: 1px dashed rgba(245,158,11,0.4);
|
||
color: rgb(120,53,15);
|
||
}
|
||
|
||
.msg-bubble.pending {
|
||
opacity: 0.6;
|
||
font-size: 20px;
|
||
padding: 6px 14px;
|
||
}
|
||
|
||
.msg-meta {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 11px;
|
||
opacity: 0.6;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.msg-role {
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
.conv-input-bar {
|
||
display: flex;
|
||
gap: 8px;
|
||
padding: 12px;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.conv-input-bar textarea {
|
||
flex: 1;
|
||
font-family: var(--font-body);
|
||
font-size: 14px;
|
||
padding: 8px 10px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
resize: vertical;
|
||
min-height: 44px;
|
||
}
|
||
|
||
.conv-input-bar textarea:disabled {
|
||
background: var(--bg);
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.conv-input-bar button {
|
||
padding: 0 18px;
|
||
font-size: 13px;
|
||
background: var(--accent);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.conv-input-bar button:disabled {
|
||
opacity: 0.4;
|
||
cursor: not-allowed;
|
||
}
|
||
|