- 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)
124 lines
3.5 KiB
TypeScript
124 lines
3.5 KiB
TypeScript
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 }
|
|
}
|