feat(posimai-dev): voice input, bigger Claude icon, aurora bg, session in settings
- Web Speech API mic button (ja-JP, pulses red while listening, hidden if unsupported) - Claude bot icon 15px → 20px, button 32px → 36px - Aurora gradient opacity 7% → 14% (visible through transparent xterm canvas) - Session ID moved to settings panel (hidden in chat bar) - Removed chat input placeholder Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
170dfaa7e0
commit
d7f38faa59
|
|
@ -74,23 +74,38 @@
|
|||
}
|
||||
.status-badge.disconnected { background: rgba(239,68,68,0.12); color: #F87171; }
|
||||
|
||||
/* セッションバッジ(チャットバー内) */
|
||||
.session-badge {
|
||||
font-size: 10px; font-weight: 400; color: rgba(255,255,255,0.25);
|
||||
font-family: monospace; display: none; white-space: nowrap;
|
||||
overflow: hidden; text-overflow: ellipsis; max-width: 180px;
|
||||
}
|
||||
.session-badge.visible { display: block; }
|
||||
|
||||
/* Claude開始ボタン — アイコンのみ */
|
||||
.claude-btn {
|
||||
width: 32px; height: 32px; border-radius: 8px; border: none; cursor: pointer;
|
||||
width: 36px; height: 36px; border-radius: 8px; border: none; cursor: pointer;
|
||||
background: var(--accent-dim); color: var(--accent);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0; transition: background 0.15s;
|
||||
}
|
||||
.claude-btn:hover { background: rgba(167,139,250,0.25); }
|
||||
|
||||
/* マイクボタン */
|
||||
.mic-btn {
|
||||
width: 38px; height: 38px; border-radius: 10px; border: none;
|
||||
background: rgba(255,255,255,0.05); color: rgba(255,255,255,0.35); cursor: pointer;
|
||||
display: none; align-items: center; justify-content: center;
|
||||
flex-shrink: 0; transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
.mic-btn.available { display: flex; }
|
||||
.mic-btn:hover { background: rgba(255,255,255,0.1); color: #F3F4F6; }
|
||||
.mic-btn.listening {
|
||||
background: rgba(239,68,68,0.15); color: #F87171;
|
||||
animation: mic-pulse 1s ease-in-out infinite;
|
||||
}
|
||||
@keyframes mic-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
|
||||
|
||||
/* 設定パネル内セッション表示 */
|
||||
.session-info-row {
|
||||
display: flex; align-items: center; gap: 8px; margin-top: 8px;
|
||||
font-size: 11px; color: rgba(255,255,255,0.35); font-family: monospace;
|
||||
background: rgba(255,255,255,0.04); border-radius: 8px; padding: 8px 10px;
|
||||
}
|
||||
.session-info-row.hidden { display: none; }
|
||||
|
||||
/* ── Terminal ── */
|
||||
#terminal-container {
|
||||
flex: 1;
|
||||
|
|
@ -98,8 +113,8 @@
|
|||
padding: 12px 12px 0;
|
||||
min-height: 0;
|
||||
background:
|
||||
radial-gradient(ellipse at 15% 60%, rgba(34,211,238,0.07) 0%, transparent 55%),
|
||||
radial-gradient(ellipse at 85% 25%, rgba(167,139,250,0.07) 0%, transparent 55%),
|
||||
radial-gradient(ellipse at 15% 60%, rgba(34,211,238,0.14) 0%, transparent 55%),
|
||||
radial-gradient(ellipse at 85% 25%, rgba(167,139,250,0.14) 0%, transparent 55%),
|
||||
var(--dev-bg);
|
||||
}
|
||||
#terminal-container .xterm { height: 100%; }
|
||||
|
|
@ -173,7 +188,11 @@
|
|||
</div>
|
||||
<div style="margin-top:20px">
|
||||
<div class="settings-group-label">セッション</div>
|
||||
<a href="/sessions.html" style="font-size:13px;color:var(--accent);text-decoration:none;display:flex;align-items:center;gap:6px;margin-top:8px">
|
||||
<div class="session-info-row hidden" id="settingsSessionBadge">
|
||||
<i data-lucide="terminal" style="width:12px;height:12px;stroke-width:1.75;color:var(--accent);flex-shrink:0"></i>
|
||||
<span id="settingsSessionId">—</span>
|
||||
</div>
|
||||
<a href="/sessions.html" style="font-size:13px;color:var(--accent);text-decoration:none;display:flex;align-items:center;gap:6px;margin-top:10px" rel="noopener">
|
||||
<i data-lucide="history" style="width:14px;height:14px;stroke-width:1.75"></i>
|
||||
過去のセッションを見る
|
||||
</a>
|
||||
|
|
@ -190,7 +209,7 @@
|
|||
</div>
|
||||
<div class="header-right">
|
||||
<button class="claude-btn" id="claudeBtn" aria-label="Claude 開始">
|
||||
<i data-lucide="bot" style="width:15px;height:15px;stroke-width:1.75"></i>
|
||||
<i data-lucide="bot" style="width:20px;height:20px;stroke-width:1.75"></i>
|
||||
</button>
|
||||
<button class="icon-btn" id="settingsBtn" aria-label="設定" aria-expanded="false">
|
||||
<i data-lucide="settings" style="width:18px;height:18px;stroke-width:1.5"></i>
|
||||
|
|
@ -201,17 +220,18 @@
|
|||
<div id="terminal-container"></div>
|
||||
|
||||
<div class="chat-bar">
|
||||
<span class="session-badge" id="sessionBadge"></span>
|
||||
<input
|
||||
type="text"
|
||||
class="chat-input"
|
||||
id="chatInput"
|
||||
placeholder="Claude に話しかける、またはコマンドを入力..."
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck="false"
|
||||
>
|
||||
<button class="mic-btn" id="micBtn" aria-label="音声入力">
|
||||
<i data-lucide="mic" style="width:16px;height:16px;stroke-width:1.75"></i>
|
||||
</button>
|
||||
<button class="send-btn" id="sendBtn" disabled aria-label="送信">
|
||||
<i data-lucide="arrow-up" style="width:16px;height:16px;stroke-width:2.5"></i>
|
||||
</button>
|
||||
|
|
@ -221,11 +241,13 @@
|
|||
<script>
|
||||
(function () {
|
||||
const statusBadge = document.getElementById('statusBadge');
|
||||
const sessionBadge = document.getElementById('sessionBadge');
|
||||
const settingsSessionRow = document.getElementById('settingsSessionBadge');
|
||||
const settingsSessionId = document.getElementById('settingsSessionId');
|
||||
const container = document.getElementById('terminal-container');
|
||||
const chatInput = document.getElementById('chatInput');
|
||||
const sendBtn = document.getElementById('sendBtn');
|
||||
const claudeBtn = document.getElementById('claudeBtn');
|
||||
const micBtn = document.getElementById('micBtn');
|
||||
|
||||
// xterm.js
|
||||
const term = new Terminal({
|
||||
|
|
@ -277,8 +299,8 @@
|
|||
const msg = JSON.parse(e.data);
|
||||
if (msg.type === 'output') term.write(msg.data);
|
||||
if (msg.type === 'session') {
|
||||
sessionBadge.textContent = msg.id;
|
||||
sessionBadge.classList.add('visible');
|
||||
settingsSessionId.textContent = msg.id;
|
||||
settingsSessionRow.classList.remove('hidden');
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
|
|
@ -319,6 +341,40 @@
|
|||
term.focus();
|
||||
});
|
||||
|
||||
// 音声入力
|
||||
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
|
||||
if (SR) {
|
||||
micBtn.classList.add('available');
|
||||
const recognition = new SR();
|
||||
recognition.lang = 'ja-JP';
|
||||
recognition.continuous = false;
|
||||
recognition.interimResults = false;
|
||||
let listening = false;
|
||||
|
||||
micBtn.addEventListener('click', () => {
|
||||
if (listening) { recognition.stop(); return; }
|
||||
recognition.start();
|
||||
});
|
||||
|
||||
recognition.onstart = () => {
|
||||
listening = true;
|
||||
micBtn.classList.add('listening');
|
||||
};
|
||||
recognition.onend = () => {
|
||||
listening = false;
|
||||
micBtn.classList.remove('listening');
|
||||
};
|
||||
recognition.onresult = (e) => {
|
||||
const transcript = e.results[0][0].transcript;
|
||||
chatInput.value = transcript;
|
||||
chatInput.focus();
|
||||
};
|
||||
recognition.onerror = () => {
|
||||
listening = false;
|
||||
micBtn.classList.remove('listening');
|
||||
};
|
||||
}
|
||||
|
||||
// Resize
|
||||
const ro = new ResizeObserver(() => {
|
||||
fitAddon.fit();
|
||||
|
|
|
|||
Loading…
Reference in New Issue