feat: compact boundary divider, overlay fix, approval window, PiP, Tauri enhancements
- Add CompactBoundaryDivider component for compact_boundary system messages - Fix readability overlay: v-if removes element entirely at 0% opacity - Add approval page and window composable - Add PiP window support and loading screen - Tauri: add window management commands and capabilities - Disable Ctrl+1..5 shortcuts in Tauri (handled by global shortcuts)
This commit is contained in:
123
frontend/src/composables/usePipWindow.ts
Normal file
123
frontend/src/composables/usePipWindow.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { ref } from 'vue'
|
||||
import { isTauri, getTauriStore } from '@/lib/tauri'
|
||||
import { trackLoading } from '@/lib/loadingWindow'
|
||||
|
||||
const STORE_KEY_PREFIX = 'pipWindow.geometry.'
|
||||
|
||||
interface WindowGeometry {
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
const pipWindows = ref<Map<number, boolean>>(new Map())
|
||||
|
||||
async function loadGeometry(idx: number): Promise<WindowGeometry | null> {
|
||||
try {
|
||||
const store = await getTauriStore()
|
||||
return await store.get<WindowGeometry>(`${STORE_KEY_PREFIX}${idx}`) ?? null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function saveGeometry(idx: number, geo: WindowGeometry): Promise<void> {
|
||||
try {
|
||||
const store = await getTauriStore()
|
||||
await store.set(`${STORE_KEY_PREFIX}${idx}`, geo)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export function usePipWindow() {
|
||||
async function openPip(terminalIndex: number): Promise<boolean> {
|
||||
if (!isTauri) return false
|
||||
|
||||
const pipLabel = `pip-terminal-${terminalIndex}`
|
||||
|
||||
try {
|
||||
const { WebviewWindow } = await import('@tauri-apps/api/webviewWindow')
|
||||
|
||||
const existing = await WebviewWindow.getByLabel(pipLabel)
|
||||
if (existing) {
|
||||
await existing.setFocus()
|
||||
return true
|
||||
}
|
||||
|
||||
const pipUrl = `/transcript-debug/${terminalIndex}?pip=1`
|
||||
|
||||
// Restore last position/size or use defaults
|
||||
const saved = await loadGeometry(terminalIndex)
|
||||
const x = saved?.x ?? (window.screen.width - 400)
|
||||
const y = saved?.y ?? (60 + (terminalIndex - 1) * 40)
|
||||
const width = saved?.width ?? 380
|
||||
const height = saved?.height ?? 620
|
||||
|
||||
// Track in the shared loading indicator (bottom-right spinner)
|
||||
const dismissLoading = await trackLoading(pipLabel)
|
||||
|
||||
// Create real window hidden
|
||||
const pip = new WebviewWindow(pipLabel, {
|
||||
url: pipUrl,
|
||||
title: `T${terminalIndex} - Agent UI`,
|
||||
width,
|
||||
height,
|
||||
x,
|
||||
y,
|
||||
visible: false,
|
||||
alwaysOnTop: false,
|
||||
decorations: false,
|
||||
resizable: true,
|
||||
focus: true,
|
||||
})
|
||||
|
||||
pipWindows.value.set(terminalIndex, true)
|
||||
|
||||
// When real window is ready: show it, remove from loading tracker
|
||||
pip.once('tauri://webview-created', () => {
|
||||
setTimeout(async () => {
|
||||
try { await pip.show() } catch {}
|
||||
try { await pip.setFocus() } catch {}
|
||||
await dismissLoading()
|
||||
}, 200)
|
||||
})
|
||||
|
||||
// Persist geometry on close
|
||||
pip.onCloseRequested(async () => {
|
||||
try {
|
||||
const pos = await pip.outerPosition()
|
||||
const size = await pip.outerSize()
|
||||
await saveGeometry(terminalIndex, {
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
})
|
||||
} catch {}
|
||||
pipWindows.value.delete(terminalIndex)
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (e) {
|
||||
console.warn(`Failed to open PiP window for T${terminalIndex}:`, e)
|
||||
pipWindows.value.delete(terminalIndex)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function closePip(terminalIndex: number): Promise<void> {
|
||||
const pipLabel = `pip-terminal-${terminalIndex}`
|
||||
try {
|
||||
const { WebviewWindow } = await import('@tauri-apps/api/webviewWindow')
|
||||
const existing = await WebviewWindow.getByLabel(pipLabel)
|
||||
if (existing) await existing.close()
|
||||
} catch {}
|
||||
pipWindows.value.delete(terminalIndex)
|
||||
}
|
||||
|
||||
function isPipOpen(terminalIndex: number): boolean {
|
||||
return pipWindows.value.get(terminalIndex) ?? false
|
||||
}
|
||||
|
||||
return { openPip, closePip, isPipOpen, pipWindows }
|
||||
}
|
||||
Reference in New Issue
Block a user