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

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

View File

@@ -26,6 +26,7 @@
<option value="">(세션 선택…)</option>
</select>
<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>
</div>
@@ -39,7 +40,7 @@
<textarea
id="message-input"
rows="2"
placeholder="메시지를 입력하세요… (Cmd/Ctrl+Enter 로 전송)"
placeholder="메시지를 입력하세요… (Enter 전송, Shift+Enter 줄바꿈)"
autocomplete="off"
disabled
></textarea>

View File

@@ -1211,3 +1211,13 @@ details[open] summary {
line-height: 1.55;
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;
}