fix: knowledge view — chat input styling, autofocus, graph component (no iframe)
- Mini-chat input bar now uses shared --color-input-bar-* CSS variables and the same chat-input-bar pill pattern as all other chat interfaces - chatInputEl focused on mount (autofocus on page load) - Graph panel: replaced iframe (blocked by X-Frame-Options: DENY) with inline GraphView component; CSS :deep override constrains its height to fill the panel instead of 100vh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { apiGet, transcribeAudio } from "@/api/client";
|
||||
import { useChatStore } from "@/stores/chat";
|
||||
import { useSettingsStore } from "@/stores/settings";
|
||||
import { useVoiceRecorder } from "@/composables/useVoiceRecorder";
|
||||
import GraphView from "@/views/GraphView.vue";
|
||||
|
||||
const router = useRouter();
|
||||
const chatStore = useChatStore();
|
||||
@@ -254,6 +255,7 @@ onMounted(() => {
|
||||
fetchItems(true);
|
||||
fetchTags();
|
||||
fetchTodayBar();
|
||||
nextTick(() => chatInputEl.value?.focus());
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
@@ -426,12 +428,9 @@ onUnmounted(() => {
|
||||
<span>Graph</span>
|
||||
<button class="btn-icon-sm" @click="toggleGraph">✕</button>
|
||||
</div>
|
||||
<iframe
|
||||
ref="graphFrame"
|
||||
src="/graph"
|
||||
class="graph-iframe"
|
||||
title="Knowledge graph"
|
||||
></iframe>
|
||||
<div class="graph-embed">
|
||||
<GraphView :embedded="true" />
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
</div><!-- end knowledge-layout -->
|
||||
@@ -458,40 +457,47 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Input bar -->
|
||||
<!-- Input bar — matches shared chat-input-bar pattern -->
|
||||
<div class="minichat-input-row">
|
||||
<textarea
|
||||
ref="chatInputEl"
|
||||
v-model="chatInput"
|
||||
@keydown="onChatKeydown"
|
||||
@input="chatAutoResize"
|
||||
class="minichat-input"
|
||||
placeholder="Ask anything…"
|
||||
rows="1"
|
||||
></textarea>
|
||||
<button
|
||||
v-if="voiceEnabled && recorder.isSupported"
|
||||
class="minichat-btn btn-mic"
|
||||
:class="{ recording: recorder.recording.value, transcribing: transcribingVoice }"
|
||||
@mousedown.prevent="startPtt"
|
||||
@mouseup.prevent="stopPtt"
|
||||
@touchstart.prevent="startPtt"
|
||||
@touchend.prevent="stopPtt"
|
||||
title="Hold to speak"
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm-1-9c0-.55.45-1 1-1s1 .45 1 1v6c0 .55-.45 1-1 1s-1-.45-1-1V5zm6 6c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="minichat-btn btn-send"
|
||||
:disabled="!chatInput.trim() || chatSending || chatStore.streaming"
|
||||
@click="sendChatMessage"
|
||||
title="Send"
|
||||
>↑</button>
|
||||
<div class="chat-input-bar">
|
||||
<textarea
|
||||
ref="chatInputEl"
|
||||
v-model="chatInput"
|
||||
@keydown="onChatKeydown"
|
||||
@input="chatAutoResize"
|
||||
placeholder="Ask anything…"
|
||||
:disabled="chatStore.streaming"
|
||||
rows="1"
|
||||
></textarea>
|
||||
<button
|
||||
v-if="voiceEnabled && recorder.isSupported"
|
||||
class="btn-attach btn-mic-ptt"
|
||||
:class="{ 'mic-recording': recorder.recording.value, 'mic-transcribing': transcribingVoice }"
|
||||
@mousedown.prevent="startPtt"
|
||||
@mouseup.prevent="stopPtt"
|
||||
@touchstart.prevent="startPtt"
|
||||
@touchend.prevent="stopPtt"
|
||||
:disabled="transcribingVoice"
|
||||
title="Hold to speak"
|
||||
aria-label="Push to talk"
|
||||
>
|
||||
<svg v-if="!transcribingVoice" width="17" height="17" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm-1-9c0-.55.45-1 1-1s1 .45 1 1v6c0 .55-.45 1-1 1s-1-.45-1-1V5zm6 6c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/>
|
||||
</svg>
|
||||
<svg v-else width="17" height="17" viewBox="0 0 24 24" fill="currentColor">
|
||||
<circle cx="12" cy="12" r="8" opacity="0.35"/><circle cx="12" cy="12" r="4"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="btn-send"
|
||||
:disabled="!chatInput.trim() || chatSending || chatStore.streaming"
|
||||
@click="sendChatMessage"
|
||||
title="Send"
|
||||
>↑</button>
|
||||
</div>
|
||||
<button
|
||||
v-if="chatOpen"
|
||||
class="minichat-btn btn-close"
|
||||
class="btn-close-chat"
|
||||
@click="closeChat"
|
||||
title="Close chat"
|
||||
>✕</button>
|
||||
@@ -879,10 +885,14 @@ onUnmounted(() => {
|
||||
transition: color 0.15s;
|
||||
}
|
||||
.btn-icon-sm:hover { color: var(--color-text); }
|
||||
.graph-iframe {
|
||||
.graph-embed {
|
||||
flex: 1;
|
||||
border: none;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
/* Override GraphView's 100vh height so it fills the panel instead */
|
||||
.graph-embed :deep(.graph-page) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* ── Mini-chat ───────────────────────────────────────────── */
|
||||
@@ -956,58 +966,84 @@ onUnmounted(() => {
|
||||
|
||||
.minichat-input-row {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
.minichat-input {
|
||||
/* Reuse the shared chat-input-bar pattern */
|
||||
.chat-input-bar {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 0.5rem 0.5rem 0.75rem;
|
||||
background: var(--color-input-bar-bg);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 2px 12px var(--color-shadow);
|
||||
}
|
||||
.chat-input-bar textarea {
|
||||
flex: 1;
|
||||
resize: none;
|
||||
padding: 8px 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--color-border, rgba(255,255,255,0.1));
|
||||
background: var(--color-bg-tertiary, rgba(255,255,255,0.04));
|
||||
color: var(--color-text);
|
||||
font-size: 0.9rem;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-family: inherit;
|
||||
line-height: 1.45;
|
||||
font-size: 0.95rem;
|
||||
background: transparent;
|
||||
color: var(--color-input-bar-text);
|
||||
outline: none;
|
||||
min-height: 38px;
|
||||
max-height: 120px;
|
||||
transition: border-color 0.15s;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.minichat-input:focus { border-color: var(--color-primary, #6366f1); }
|
||||
.minichat-btn {
|
||||
flex-shrink: 0;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 8px;
|
||||
.chat-input-bar textarea::placeholder { color: var(--color-input-bar-placeholder); }
|
||||
.chat-input-bar textarea:disabled { opacity: 0.5; }
|
||||
.btn-attach {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--color-input-bar-text);
|
||||
opacity: 0.6;
|
||||
padding: 0.25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.btn-attach:hover { opacity: 1; }
|
||||
.btn-attach:disabled { opacity: 0.3; cursor: default; }
|
||||
.btn-mic-ptt { transition: color 0.15s, opacity 0.15s; }
|
||||
.btn-mic-ptt.mic-recording { color: #ef4444 !important; opacity: 1 !important; }
|
||||
.btn-mic-ptt.mic-transcribing { color: var(--color-primary); opacity: 1 !important; }
|
||||
.btn-send {
|
||||
background: linear-gradient(135deg, #6366f1, #4f46e5);
|
||||
width: 34px;
|
||||
min-width: 34px;
|
||||
height: 34px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
font-size: 1.1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.btn-send:disabled { opacity: 0.4; cursor: not-allowed; }
|
||||
.btn-send:not(:disabled):hover { box-shadow: 0 0 12px rgba(99,102,241,0.5); }
|
||||
.btn-mic {
|
||||
background: rgba(255,255,255,0.05);
|
||||
.btn-send:disabled { opacity: 0.35; cursor: default; }
|
||||
.btn-close-chat {
|
||||
flex-shrink: 0;
|
||||
background: none;
|
||||
border: 1px solid var(--color-border, rgba(255,255,255,0.1));
|
||||
border-radius: 8px;
|
||||
color: var(--color-muted);
|
||||
cursor: pointer;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.8rem;
|
||||
transition: color 0.15s;
|
||||
}
|
||||
.btn-mic.recording { color: #ef4444; border-color: #ef4444; }
|
||||
.btn-close {
|
||||
background: rgba(255,255,255,0.05);
|
||||
border: 1px solid var(--color-border, rgba(255,255,255,0.1));
|
||||
color: var(--color-muted);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
.btn-close:hover { color: var(--color-text); }
|
||||
.btn-close-chat:hover { color: var(--color-text); }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user