Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
54d761463This file is pinned to the selected release. Download the original to open it in your design tool.
import loadPreferences, { loadModConfig, loadPreferenceConfig } from 'loadPreference'
import { runContextCreatedBehaviors, type StackchanAppBehavior } from 'app-behavior'
import { resolveAppBehaviors } from 'app-behavior-resolver'
import defaultBehavior from 'app-default-behavior'
import { installLaunchShortcut, type LaunchShortcutButton, prepareAppLaunch } from 'app-launch'
import { type BootWiFiStatus, startHostBootServices } from 'boot-services'
import type { StackchanContext } from 'capabilities'
import { createStackchanContext, getHostDeviceEnvironment } from 'compose'
import { DOMAIN } from 'consts'
import { type StackchanDockRuntime, startStackchanDock } from 'dock'
import { prepareExperimentalMiniApps, registerExperimentalMiniApps } from 'experimental-mini-app-loader'
import { initializeLocalization } from 'localization'
import Modules from 'modules'
import { showStartupSplash, showWiFiConnectionStatus, showWiFiRecoveryChoice } from 'startup-splash'
import { applyTimezone } from 'timezone-settings'
type DeviceButton = {
onChanged: (this: DeviceButton) => void
}
type GlobalEnvironment = {
application?: ReturnType<typeof showStartupSplash>
button?: Partial<Record<'a' | 'c', DeviceButton>> & { power?: LaunchShortcutButton }
System: { restart(): void }
}
const globalEnv = globalThis as typeof globalThis & GlobalEnvironment
const noopButtonHandler = () => undefined
function installPlatformInputBridge(): void {
if (!Modules.has('wasm-button-bridge')) return
const bridge = Modules.importNow('wasm-button-bridge') as { installWasmButtons?: () => void }
bridge.installWasmButtons?.()
trace('[main] installed WASM button bridge\n')
}
function loadAppBehaviors(): StackchanAppBehavior[] {
trace('[main] checking mod override\n')
return resolveAppBehaviors(Modules, defaultBehavior, (error) => {
trace(`[main] MOD override unavailable: ${error instanceof Error ? error.message : String(error)}\n`)
})
}
function installModManagerShortcut(): void {
const powerButton = globalEnv.button?.power
if (!powerButton || !Modules.has('mod-manager')) return
installLaunchShortcut(powerButton, async () => {
try {
const startModManager = Modules.importNow('mod-manager') as (
application: ReturnType<typeof showStartupSplash>,
) => Promise<'back'>
await startModManager(globalEnv.application ?? showStartupSplash())
globalEnv.System.restart()
} catch (error) {
trace(`[mods] shortcut failed: ${error instanceof Error ? error.message : String(error)}\n`)
}
})
}
function waitForBootWiFiRecoveryChoice(status: BootWiFiStatus & { reason: string }): Promise<'retry' | 'offline'> {
return new Promise((resolve) => {
let resolved = false
const previousAHandler = globalEnv.button?.a?.onChanged
const previousCHandler = globalEnv.button?.c?.onChanged
const restoreButtons = () => {
if (globalEnv.button?.a) {
globalEnv.button.a.onChanged = previousAHandler ?? noopButtonHandler
}
if (globalEnv.button?.c) {
globalEnv.button.c.onChanged = previousCHandler ?? noopButtonHandler
}
}
const choose = (choice: 'retry' | 'offline') => {
if (resolved) return
resolved = true
restoreButtons()
resolve(choice)
}
trace(`[network] ${status.message}: ${status.reason}\n`)
showWiFiRecoveryChoice({
message: status.message,
onRetry: () => choose('retry'),
onOffline: () => choose('offline'),
})
if (globalEnv.button?.a) {
globalEnv.button.a.onChanged = () => choose('retry')
}
if (globalEnv.button?.c) {
globalEnv.button.c.onChanged = () => choose('offline')
}
})
}
async function main() {
trace('[main] start\n')
let dockRuntime: StackchanDockRuntime | undefined
let context: StackchanContext | undefined
try {
dockRuntime = startStackchanDock(Modules, loadModConfig())
if (dockRuntime) trace('[main] Stackchan Dock started\n')
installPlatformInputBridge()
initializeLocalization(loadPreferences(DOMAIN.ui).language)
applyTimezone(loadPreferences(DOMAIN.time).timezone)
trace('[main] loading app behaviors\n')
const appBehaviors = loadAppBehaviors()
// Launch behaviors run before startHostBootServices so the splash screen is
// visible while network setup blocks.
const launch = await prepareAppLaunch(appBehaviors, prepareExperimentalMiniApps)
trace(`[main] onLaunch shouldCreateContext=${launch.shouldCreateContext}\n`)
if (!launch.shouldCreateContext) {
installModManagerShortcut()
const unownedDock = dockRuntime
dockRuntime = undefined
unownedDock?.close()
return
}
const experimentalMiniApps = launch.prepared
const bootServices = startHostBootServices({
wifi: {
onStatusChanged: showWiFiConnectionStatus,
promptRecoveryChoice: waitForBootWiFiRecoveryChoice,
},
})
const networkReady = await bootServices.connectivity.network.ready
trace(`[main] network ready: ${networkReady.status}\n`)
const preferences = loadPreferenceConfig()
const ownedDock = dockRuntime
context = createStackchanContext(preferences, {
connectivity: bootServices.connectivity,
remoteConversationSession: ownedDock?.remoteConversationSession,
closeHandlers: ownedDock ? [() => ownedDock.close()] : undefined,
})
ownedDock?.onContextCreated(context)
registerExperimentalMiniApps(experimentalMiniApps, context.ui.miniApps)
trace('[main] app context created\n')
await runContextCreatedBehaviors(appBehaviors, context, {
device: getHostDeviceEnvironment(),
config: preferences,
})
trace('[main] app behaviors ready\n')
installModManagerShortcut()
} catch (error) {
try {
if (context) await context.lifecycle.close()
else dockRuntime?.close()
} catch (closeError) {
trace(`[main] cleanup error ${closeError instanceof Error ? closeError.message : String(closeError)}\n`)
}
installModManagerShortcut()
throw error
}
}
main().catch((error) => {
trace(`[main] error ${error?.message ?? error}\n`)
})