Supra_App/src/routes/(authed)/+layout.svelte
pakintada@gmail.com 1156b5e6c3 feat: scrcpy
- add: scrcpy for previewing android screen.
- change: notification of android connection fail to dialog.
- wip: firmware packing (on file listing issue)
- add: reject button on announcement dialog with callback.

Signed-off-by: pakintada@gmail.com <Pakin>
2026-07-16 14:52:54 +07:00

150 lines
4.4 KiB
Svelte

<!-- navbar select menus -->
<script lang="ts">
import { logger } from '$lib/core/utils/logger';
import favicon from '$lib/assets/favicon.svg';
import AppAccountSelect from '$lib/components/app-account-select.svelte';
import AppSidebar from '$lib/components/app-sidebar.svelte';
import * as Sidebar from '$lib/components/ui/sidebar/index';
import '../layout.css';
import ErrorLayout from '$lib/components/error-layout.svelte';
import { sidebarStore } from '$lib/core/stores/sidebar';
import { auth } from '$lib/core/stores/auth';
import { connectToWebsocket } from '$lib/core/stores/websocketStore';
import * as adb from '$lib/core/adb/adb';
import { adbConnectionStatus } from '$lib/core/stores/adbConnectionStore';
import { addNotification } from '$lib/core/stores/noti';
import { page } from '$app/stores';
import {
AdbDaemonWebUsbDevice,
AdbDaemonWebUsbDeviceManager
} from '@yume-chan/adb-daemon-webusb';
import AdbWebCredentialStore from '@yume-chan/adb-credential-web';
import { deviceCredentialManager } from '$lib/core/adb/deviceCredManager';
import { browser } from '$app/environment';
import { onMount } from 'svelte';
import AdbReconnectBanner from '$lib/components/adb-reconnect-banner.svelte';
let { children } = $props();
let websocketConnectedForUid = $state('');
let adbReconnectTriedForUid = $state('');
let TerminalDrawerComponent: any = $state(null);
// Dynamic import: TerminalDrawer depends on @xterm/xterm which references
// browser globals (self, window). Static import would crash SSR evaluation.
onMount(async () => {
if (browser) {
const mod = await import('$lib/components/terminal-drawer.svelte');
TerminalDrawerComponent = mod.default;
}
});
function getAutoConnectChannel(pathname: string) {
if (pathname.startsWith('/tools/create-menu')) {
return 'recipe';
}
if (pathname.startsWith('/tools/brew')) {
return 'brew';
}
return 'adb';
}
async function tryAutoConnect() {
try {
if (adb.getAdbInstance()) return true;
if (!('usb' in navigator) || !AdbDaemonWebUsbDeviceManager.BROWSER) {
throw new Error('WebUSB not supported, try using fallback or different browser');
}
const devices = await AdbDaemonWebUsbDeviceManager.BROWSER.getDevices();
if (!devices || devices.length == 0) {
throw new Error('No device found');
}
if (devices.length > 1) {
throw new Error('Too many connected devices');
}
const device = devices[0];
const credStore = new AdbWebCredentialStore();
try {
const channel = getAutoConnectChannel($page.url.pathname);
if (channel === 'recipe') {
await adb.connectRecipeMenuDeviceByCred(device, credStore);
} else {
await adb.connectDeviceByCred(device, credStore, channel === 'brew');
}
return true;
} catch (e: any) {
if (e.message === 'CREDENTIAL_EXPIRED') {
try {
await deviceCredentialManager.clearAllCredentials();
} catch (ignored) {}
}
if (e instanceof AdbDaemonWebUsbDevice.DeviceBusyError) {
addNotification(
'ERR:Device is already in use by another program, check if machine has been installed `adb`, kill the process and try again.'
);
}
return false;
}
} catch (e) {
logger.error('error on auto connect brew page', e);
addNotification('ERROR:Failed to auto connect, please try again');
}
}
$effect(() => {
const currentUser = $auth;
if (!currentUser) {
websocketConnectedForUid = '';
adbReconnectTriedForUid = '';
return;
}
if (websocketConnectedForUid !== currentUser.uid) {
websocketConnectedForUid = currentUser.uid;
logger.info('connect ws after auth ready');
void currentUser.getIdToken(true).then(async (idToken) => {
await connectToWebsocket(idToken);
});
}
if (adbReconnectTriedForUid !== currentUser.uid && !adb.getAdbInstance()) {
adbReconnectTriedForUid = currentUser.uid;
void tryAutoConnect();
}
});
</script>
<svelte:head>
<link rel="icon" href={favicon} />
<link href="https://fonts.googleapis.com/css2?family=Roboto+Flex" rel="stylesheet" />
<title>Taobin Management Tools</title>
</svelte:head>
<Sidebar.Provider
onOpenChange={(open) => {
sidebarStore.set(open);
}}
>
<AppSidebar />
<main class="h-screen w-screen overflow-auto">
<Sidebar.Trigger />
{@render children()}
</main>
{#if TerminalDrawerComponent}
<svelte:component this={TerminalDrawerComponent} />
{/if}
</Sidebar.Provider>
<AdbReconnectBanner />