feat: unified hook notifier, agent auto-detection, terminal transition UI
- Add hooks/notify.ps1 as single hook handler for all events - Refactor settings.local.json to use notify.ps1 instead of inline PS - Add Notification hook, auto-detect agent from session_id/transcript - Rename agent 'main' to 'claude' across server routes and terminal - Add loading overlay and error state for terminal switching transitions - Add transitionError ref to useTranscriptDebug composable
This commit is contained in:
@@ -28,6 +28,8 @@ const {
|
||||
selectedSessionId,
|
||||
conversation,
|
||||
loading,
|
||||
transitioning,
|
||||
transitionError,
|
||||
error,
|
||||
isRealtime,
|
||||
processing,
|
||||
@@ -702,6 +704,22 @@ onBeforeUnmount(() => {
|
||||
<div class="content">
|
||||
<AquaticBackground />
|
||||
<div class="readability-overlay" :style="{ background: `rgba(0, 0, 0, ${overlayOpacity})` }" />
|
||||
<Transition name="terminal-loading">
|
||||
<div v-if="transitioning" class="terminal-loading-overlay">
|
||||
<div class="terminal-loading-spinner" />
|
||||
</div>
|
||||
</Transition>
|
||||
<Transition name="terminal-loading">
|
||||
<div v-if="transitionError" class="terminal-error-overlay" @click="transitionError = null">
|
||||
<div class="terminal-error-content">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f87171" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
|
||||
</svg>
|
||||
<span class="terminal-error-msg">{{ transitionError }}</span>
|
||||
<span class="terminal-error-hint">Click to dismiss</span>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
<ChatContainer
|
||||
ref="chatRef"
|
||||
v-if="conversation"
|
||||
@@ -1180,6 +1198,73 @@ onBeforeUnmount(() => {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ── Terminal switching loading overlay ── */
|
||||
.terminal-loading-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.terminal-loading-spinner {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 2.5px solid rgba(255, 255, 255, 0.15);
|
||||
border-top-color: rgba(255, 255, 255, 0.7);
|
||||
border-radius: 50%;
|
||||
animation: tl-spin 0.7s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes tl-spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.terminal-error-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
backdrop-filter: blur(6px);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.terminal-error-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
max-width: 80%;
|
||||
padding: 1.2rem 1.5rem;
|
||||
background: rgba(30, 30, 30, 0.85);
|
||||
border: 1px solid rgba(248, 113, 113, 0.3);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.terminal-error-msg {
|
||||
font-size: 12px;
|
||||
color: #fca5a5;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.terminal-error-hint {
|
||||
font-size: 10px;
|
||||
color: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
.terminal-loading-enter-active { transition: opacity 0.15s ease; }
|
||||
.terminal-loading-leave-active { transition: opacity 0.25s ease; }
|
||||
.terminal-loading-enter-from,
|
||||
.terminal-loading-leave-to { opacity: 0; }
|
||||
|
||||
/* Override ChatContainer backgrounds: glass-transparent */
|
||||
.content :deep(.chat-container) {
|
||||
background: transparent !important;
|
||||
|
||||
@@ -35,6 +35,7 @@ export function useTranscriptDebug() {
|
||||
const conversation = ref<ParsedConversation | null>(null)
|
||||
const loading = ref(false)
|
||||
const transitioning = ref(false)
|
||||
const transitionError = ref<string | null>(null)
|
||||
const error = ref<string | null>(null)
|
||||
const isRealtime = ref(false)
|
||||
|
||||
@@ -234,9 +235,15 @@ export function useTranscriptDebug() {
|
||||
async function switchToTerminal(transcriptSessionId: string) {
|
||||
if (transcriptSessionId === activeTerminalSessionId.value) return
|
||||
|
||||
transitioning.value = true
|
||||
transitionError.value = null
|
||||
|
||||
// Park current
|
||||
parkCurrentTerminal()
|
||||
|
||||
// Small delay so the fade-out is visible before swapping content
|
||||
await new Promise(r => setTimeout(r, 150))
|
||||
|
||||
// Find the entry — might be from another agent
|
||||
const entry = serverRegistry.value.find(
|
||||
e => e.transcriptSessionId === transcriptSessionId
|
||||
@@ -247,12 +254,28 @@ export function useTranscriptDebug() {
|
||||
await fetchSessions()
|
||||
}
|
||||
|
||||
// Load the target session's transcript
|
||||
// Load the target session's transcript (skip for __new__ — no transcript yet)
|
||||
selectedSessionId.value = transcriptSessionId
|
||||
await fetchSessionContent(transcriptSessionId)
|
||||
if (transcriptSessionId === '__new__') {
|
||||
// Brand-new session: no transcript to load, just clear conversation
|
||||
rawContent.value = ''
|
||||
conversation.value = null
|
||||
error.value = null
|
||||
} else {
|
||||
const ok = await fetchSessionContent(transcriptSessionId)
|
||||
if (!ok) {
|
||||
transitionError.value = error.value || `Failed to load session "${transcriptSessionId}"`
|
||||
transitioning.value = false
|
||||
// Still connect to the terminal so the user can interact
|
||||
connectToTerminal(transcriptSessionId)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to the terminal
|
||||
connectToTerminal(transcriptSessionId)
|
||||
|
||||
transitioning.value = false
|
||||
}
|
||||
|
||||
async function disposeAllLocalTerminals() {
|
||||
@@ -583,6 +606,7 @@ export function useTranscriptDebug() {
|
||||
|
||||
selectedAgent.value = agent
|
||||
error.value = null
|
||||
transitionError.value = null
|
||||
loading.value = true
|
||||
transitioning.value = true
|
||||
|
||||
@@ -618,6 +642,7 @@ export function useTranscriptDebug() {
|
||||
parkCurrentTerminal()
|
||||
|
||||
error.value = null
|
||||
transitionError.value = null
|
||||
loading.value = true
|
||||
transitioning.value = true
|
||||
|
||||
@@ -927,6 +952,7 @@ export function useTranscriptDebug() {
|
||||
conversation,
|
||||
loading,
|
||||
transitioning,
|
||||
transitionError,
|
||||
error,
|
||||
lineCount,
|
||||
isRealtime,
|
||||
|
||||
Reference in New Issue
Block a user