feat(conversation): cheap-default DeepSeek + Enter-send + model pill

- default-interactive@1 model: claude-haiku-4-5 → deepseek/deepseek-chat
  (input $0.28/$1.12 per 1M; haiku 대비 ~75% 절감).  fallback 은 haiku 로 swap.
- conversation textarea keydown:
  - Enter → 전송 (IME composition 중이면 무시)
  - Shift+Enter → 줄바꿈
  - Cmd/Ctrl+Enter → 전송 (백워드 호환)
  - Placeholder 안내 갱신.
- conversation top-bar 에 model pill 추가 (#session-model-pill) — 현재 세션의
  활성 model 을 monospace badge 로 표시.  헷갈리던 "어느 모델인가?" 해소.
- style.css 에 .conv-model-pill (회색 pill).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chungyeong
2026-05-18 02:02:19 +09:00
parent 9a02f22acb
commit 5cf9ad131a
4 changed files with 39 additions and 15 deletions

View File

@@ -2,8 +2,8 @@ name: default-interactive
version: 1 version: 1
description: "interactive 모드 만능 어시스턴트. 탐색·수정·실행 모두 지원." description: "interactive 모드 만능 어시스턴트. 탐색·수정·실행 모두 지원."
backend: openrouter backend: openrouter
model: "openrouter:anthropic/claude-haiku-4-5" model: "openrouter:deepseek/deepseek-chat"
provider_origin: "US/Anthropic" provider_origin: "CN/DeepSeek"
capabilities: capabilities:
- spec_write - spec_write
- code_edit - code_edit
@@ -42,7 +42,7 @@ allowed_tools:
- write_todos - write_todos
- task - task
deepagents_backend: local_shell deepagents_backend: local_shell
fallback_model: "openrouter:deepseek/deepseek-chat" fallback_model: "openrouter:anthropic/claude-haiku-4-5"
max_cost_per_call_usd: 0.05 max_cost_per_call_usd: 0.05
model_params: model_params:
max_tokens: 2048 max_tokens: 2048

View File

@@ -757,6 +757,21 @@ function updateSessionStatePill(state) {
pill.className = `conv-session-state state-${state}`; pill.className = `conv-session-state state-${state}`;
} }
function updateSessionModelPill(model) {
const pill = $conv("#session-model-pill");
if (!pill) return;
if (!model) {
pill.textContent = "";
pill.className = "conv-model-pill";
return;
}
// Trim the "openrouter:" prefix for display; keep full id in tooltip.
const display = model.replace(/^openrouter:/, "");
pill.textContent = display;
pill.title = `model: ${model}`;
pill.className = "conv-model-pill";
}
async function loadSessionList() { async function loadSessionList() {
try { try {
const list = await jsonFetch("/sessions?limit=50"); const list = await jsonFetch("/sessions?limit=50");
@@ -797,6 +812,7 @@ async function loadAndAttachSession(sessionId) {
return; return;
} }
updateSessionStatePill(detail.session.state); updateSessionStatePill(detail.session.state);
updateSessionModelPill(detail.session.model);
const messages = detail.messages || []; const messages = detail.messages || [];
for (const m of messages) { for (const m of messages) {
@@ -968,17 +984,14 @@ async function bootstrapConversationPage() {
setTimeout(() => { input._composing = false; }, 0); setTimeout(() => { input._composing = false; }, 0);
}); });
input.addEventListener("keydown", (ev) => { input.addEventListener("keydown", (ev) => {
// Honor Cmd/Ctrl+Enter as explicit "send" override even during composition. if (ev.key !== "Enter") return;
if ((ev.metaKey || ev.ctrlKey) && ev.key === "Enter") { // Shift+Enter → newline (let the textarea handle it natively).
ev.preventDefault(); if (ev.shiftKey) return;
sendMessage(ev.target.value); // IME composition (Korean/Japanese/Chinese candidate commit) → never send.
return; if (input._composing) return;
} // Plain Enter (and Cmd/Ctrl+Enter for backwards compat) → send.
// Plain Enter during composition (e.g. Korean IME committing 한글) must ev.preventDefault();
// pass through to the textarea — do nothing. sendMessage(ev.target.value);
if (ev.key === "Enter" && input._composing) {
return;
}
}); });
// v0.3 PR #8: deep link `?session=<id>` auto-loads the named session. // v0.3 PR #8: deep link `?session=<id>` auto-loads the named session.
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);

View File

@@ -26,6 +26,7 @@
<option value="">(세션 선택…)</option> <option value="">(세션 선택…)</option>
</select> </select>
<button id="new-session-btn" type="button" class="conv-action-btn">새 대화</button> <button id="new-session-btn" type="button" class="conv-action-btn">새 대화</button>
<span class="conv-model-pill" id="session-model-pill" title="이 세션의 활성 모델"></span>
<span class="conv-session-state" id="session-state-pill"></span> <span class="conv-session-state" id="session-state-pill"></span>
</div> </div>
@@ -39,7 +40,7 @@
<textarea <textarea
id="message-input" id="message-input"
rows="2" rows="2"
placeholder="메시지를 입력하세요… (Cmd/Ctrl+Enter 로 전송)" placeholder="메시지를 입력하세요… (Enter 전송, Shift+Enter 줄바꿈)"
autocomplete="off" autocomplete="off"
disabled disabled
></textarea> ></textarea>

View File

@@ -1211,3 +1211,13 @@ details[open] summary {
line-height: 1.55; line-height: 1.55;
color: var(--text-muted); color: var(--text-muted);
} }
.conv-model-pill {
font-family: var(--font-mono);
font-size: 11.5px;
padding: 2px 8px;
border-radius: 999px;
background: rgba(0, 0, 0, 0.06);
color: var(--text-muted);
letter-spacing: 0.01em;
}