AI chat panel with Edit/Chat modes + verify dropdown
- AI chat panel at bottom of editor (all 3 modes) - Edit mode: AI modifies document directly (no explanations) - Chat mode: AI answers questions about the document - Verify dropdown: Spec Review, Grammar & Spelling, Summary - Enter sends, Shift+Enter for newline - /api/ai/chat endpoint with edit/chat system prompts - Grammar and spec verify actions added to /api/ai/generate
This commit is contained in:
@@ -119,6 +119,30 @@
|
||||
<p v-else style="color:#6c7086;font-style:italic">Preview will appear here...</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- AI Chat Panel -->
|
||||
<div class="ai-chat-panel" v-if="currentFile">
|
||||
<div class="ai-chat-modes">
|
||||
<button :class="{active: aiChatMode === 'edit'}" @click="aiChatMode = 'edit'">Edit</button>
|
||||
<button :class="{active: aiChatMode === 'chat'}" @click="aiChatMode = 'chat'">Chat</button>
|
||||
<select v-model="aiVerifyType" @change="runVerify" class="ai-verify-select">
|
||||
<option value="" disabled>Verify ▾</option>
|
||||
<option value="spec">Spec Review</option>
|
||||
<option value="grammar">Grammar & Spelling</option>
|
||||
<option value="summary">Summary</option>
|
||||
</select>
|
||||
<span v-if="aiChatLoading" class="ai-loading">⏳</span>
|
||||
</div>
|
||||
<div class="ai-chat-output" v-if="aiChatResponse" v-html="renderMarkdown(aiChatResponse)"></div>
|
||||
<div class="ai-chat-input">
|
||||
<textarea
|
||||
v-model="aiChatInput"
|
||||
@keydown="aiChatKeydown"
|
||||
placeholder="Ask AI to edit or chat... (Enter to send, Shift+Enter for newline)"
|
||||
rows="2"
|
||||
></textarea>
|
||||
<button @click="sendAiChat" class="ai-send-btn">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- History Panel -->
|
||||
<div class="history-panel" v-if="showHistory">
|
||||
<div class="panel-header">
|
||||
@@ -943,6 +967,12 @@ async function shareFile() {
|
||||
|
||||
// ─── AI ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
const aiChatMode = ref('edit')
|
||||
const aiChatInput = ref('')
|
||||
const aiChatResponse = ref('')
|
||||
const aiChatLoading = ref(false)
|
||||
const aiVerifyType = ref('')
|
||||
|
||||
async function aiVerify() {
|
||||
if (!currentFile.value) return
|
||||
aiResult.value = 'Verifying...'
|
||||
@@ -954,6 +984,55 @@ async function aiVerify() {
|
||||
}
|
||||
}
|
||||
|
||||
async function runVerify() {
|
||||
if (!aiVerifyType.value || !currentFile.value) return
|
||||
aiChatLoading.value = true
|
||||
aiChatResponse.value = ''
|
||||
try {
|
||||
const res = await api('/api/ai/generate', { path: currentFile.value, action: aiVerifyType.value })
|
||||
aiChatResponse.value = res.result || res.feedback || 'No response'
|
||||
} catch (e) {
|
||||
aiChatResponse.value = 'AI request failed.'
|
||||
}
|
||||
aiChatLoading.value = false
|
||||
aiVerifyType.value = ''
|
||||
}
|
||||
|
||||
function aiChatKeydown(e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
sendAiChat()
|
||||
}
|
||||
}
|
||||
|
||||
async function sendAiChat() {
|
||||
const msg = aiChatInput.value.trim()
|
||||
if (!msg) return
|
||||
aiChatLoading.value = true
|
||||
aiChatResponse.value = ''
|
||||
aiChatInput.value = ''
|
||||
|
||||
const action = aiChatMode.value === 'edit' ? 'edit' : 'chat'
|
||||
try {
|
||||
const res = await api('/api/ai/chat', {
|
||||
path: currentFile.value,
|
||||
content: content.value,
|
||||
message: msg,
|
||||
mode: action,
|
||||
})
|
||||
if (action === 'edit' && res.content) {
|
||||
content.value = res.content
|
||||
isDirty.value = true
|
||||
aiChatResponse.value = 'Document updated.'
|
||||
} else {
|
||||
aiChatResponse.value = res.result || res.content || 'No response'
|
||||
}
|
||||
} catch (e) {
|
||||
aiChatResponse.value = 'AI request failed. Check MH_AI_ENDPOINT.'
|
||||
}
|
||||
aiChatLoading.value = false
|
||||
}
|
||||
|
||||
// ─── Collab ──────────────────────────────────────────────────────────────────
|
||||
|
||||
function toggleCollab() {
|
||||
@@ -1465,6 +1544,85 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; b
|
||||
}
|
||||
.git-btn.dirty { border-color: var(--danger); }
|
||||
|
||||
/* ─── AI Chat Panel ───────────────────────────────────────────────────────── */
|
||||
|
||||
.ai-chat-panel {
|
||||
height: 15%;
|
||||
min-height: 100px;
|
||||
max-height: 200px;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
.ai-chat-modes {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.ai-chat-modes button {
|
||||
background: transparent;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-muted);
|
||||
padding: 2px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
.ai-chat-modes button.active {
|
||||
background: var(--accent);
|
||||
color: var(--bg-primary);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.ai-verify-select {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ai-loading { font-size: 12px; }
|
||||
.ai-chat-output {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.ai-chat-output p { margin: 0 0 6px; }
|
||||
.ai-chat-input {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.ai-chat-input textarea {
|
||||
flex: 1;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
resize: none;
|
||||
outline: none;
|
||||
}
|
||||
.ai-send-btn {
|
||||
background: var(--accent);
|
||||
color: var(--bg-primary);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 4px 12px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.history-panel, .share-dialog, .ai-panel {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
||||
Reference in New Issue
Block a user