mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-04-11 17:29:44 +00:00
feat: implement extension spawned process auto cleanup
If extension doesn't kill the processes it spawns, Kunkun will auto clean up all spawned processes on exit/window close
This commit is contained in:
parent
fb0e5761c9
commit
7b9be980b9
@ -63,7 +63,6 @@ export async function onCustomUiCmdSelect(
|
|||||||
const newUrl = `http://${addr}`
|
const newUrl = `http://${addr}`
|
||||||
url2 = `/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
|
url2 = `/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
|
||||||
}
|
}
|
||||||
console.log("URL 2", url2)
|
|
||||||
const window = launchNewExtWindow(winLabel, url2, cmd.window)
|
const window = launchNewExtWindow(winLabel, url2, cmd.window)
|
||||||
window.onCloseRequested(async (event) => {
|
window.onCloseRequested(async (event) => {
|
||||||
await winExtMap.unregisterExtensionFromWindow(winLabel)
|
await winExtMap.unregisterExtensionFromWindow(winLabel)
|
||||||
@ -81,7 +80,6 @@ export async function onCustomUiCmdSelect(
|
|||||||
const newUrl = `http://${addr}`
|
const newUrl = `http://${addr}`
|
||||||
url2 = `/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
|
url2 = `/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
|
||||||
}
|
}
|
||||||
console.log("URL 2", url2)
|
|
||||||
goto(url2)
|
goto(url2)
|
||||||
}
|
}
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
|
@ -30,6 +30,7 @@ type API = {
|
|||||||
dist?: string
|
dist?: string
|
||||||
}) => Promise<string>
|
}) => Promise<string>
|
||||||
unregisterExtensionFromWindow: (windowLabel: string) => Promise<void>
|
unregisterExtensionFromWindow: (windowLabel: string) => Promise<void>
|
||||||
|
cleanupProcessesFromWindow: (windowLabel: string) => Promise<void>
|
||||||
registerProcess: (windowLabel: string, pid: number) => Promise<void>
|
registerProcess: (windowLabel: string, pid: number) => Promise<void>
|
||||||
unregisterProcess: (pid: number) => Promise<void>
|
unregisterProcess: (pid: number) => Promise<void>
|
||||||
}
|
}
|
||||||
@ -84,11 +85,10 @@ function createWinExtMapStore(): Writable<WinExtMap> & API {
|
|||||||
if (winExtMap[windowLabel]) {
|
if (winExtMap[windowLabel]) {
|
||||||
// clean up processes spawned by extension but not killed by itself
|
// clean up processes spawned by extension but not killed by itself
|
||||||
const extLabelMap = await getExtLabelMap() // realtime data from core process
|
const extLabelMap = await getExtLabelMap() // realtime data from core process
|
||||||
Object.entries(extLabelMap).forEach(([label, ext]) => {
|
if (extLabelMap[windowLabel]) {
|
||||||
if (label === windowLabel) {
|
console.log("kill processes", extLabelMap[windowLabel].processes)
|
||||||
killProcesses(ext.processes)
|
killProcesses(extLabelMap[windowLabel].processes)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
await unregisterExtensionWindow(windowLabel)
|
await unregisterExtensionWindow(windowLabel)
|
||||||
delete winExtMap[windowLabel]
|
delete winExtMap[windowLabel]
|
||||||
store.set(winExtMap)
|
store.set(winExtMap)
|
||||||
@ -96,9 +96,15 @@ function createWinExtMapStore(): Writable<WinExtMap> & API {
|
|||||||
warn(`Window ${windowLabel} does not have an extension registered`)
|
warn(`Window ${windowLabel} does not have an extension registered`)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
cleanupProcessesFromWindow: async (windowLabel: string) => {
|
||||||
|
const winExtMap = get(store)
|
||||||
|
if (winExtMap[windowLabel]) {
|
||||||
|
await killProcesses(winExtMap[windowLabel].pids)
|
||||||
|
}
|
||||||
|
},
|
||||||
registerProcess: async (windowLabel: string, pid: number) => {
|
registerProcess: async (windowLabel: string, pid: number) => {
|
||||||
const winExtMap = get(store)
|
const winExtMap = get(store)
|
||||||
registerExtensionSpawnedProcess(windowLabel, pid)
|
await registerExtensionSpawnedProcess(windowLabel, pid)
|
||||||
if (!winExtMap[windowLabel]) {
|
if (!winExtMap[windowLabel]) {
|
||||||
throw new Error(`Window ${windowLabel} does not have an extension registered`)
|
throw new Error(`Window ${windowLabel} does not have an extension registered`)
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ export function positionToTailwindClasses(position: Position) {
|
|||||||
if (parseOutput.output.left) {
|
if (parseOutput.output.left) {
|
||||||
className += ` left-[${parseOutput.output.left / 4}rem]`
|
className += ` left-[${parseOutput.output.left / 4}rem]`
|
||||||
}
|
}
|
||||||
console.log(position, className)
|
|
||||||
return className
|
return className
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AppContext from "@/components/context/AppContext.svelte"
|
import AppContext from "@/components/context/AppContext.svelte"
|
||||||
import "../app.css"
|
import "../app.css"
|
||||||
import { appConfig, appState, extensions, quickLinks } from "@/stores"
|
import { appConfig, appState, extensions, quickLinks, winExtMap } from "@/stores"
|
||||||
import { initDeeplink } from "@/utils/deeplink"
|
import { initDeeplink } from "@/utils/deeplink"
|
||||||
import { updateAppHotkey } from "@/utils/hotkey"
|
import { updateAppHotkey } from "@/utils/hotkey"
|
||||||
import { globalKeyDownHandler, goBackOrCloseOnEscape } from "@/utils/key"
|
import { globalKeyDownHandler, goBackOrCloseOnEscape } from "@/utils/key"
|
||||||
import { listenToWindowBlur } from "@/utils/tauri-events"
|
import { listenToWindowBlur } from "@/utils/tauri-events"
|
||||||
import { isInMainWindow } from "@/utils/window"
|
import { isInMainWindow } from "@/utils/window"
|
||||||
|
import { listenToKillProcessEvent, listenToRecordExtensionProcessEvent } from "@kksh/api/events"
|
||||||
import {
|
import {
|
||||||
ModeWatcher,
|
ModeWatcher,
|
||||||
themeConfigStore,
|
themeConfigStore,
|
||||||
@ -78,6 +79,18 @@
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
extensions.init()
|
extensions.init()
|
||||||
|
unlisteners.push(
|
||||||
|
await listenToRecordExtensionProcessEvent(async (event) => {
|
||||||
|
console.log("record extension process event", event)
|
||||||
|
winExtMap.registerProcess(event.payload.windowLabel, event.payload.pid)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
unlisteners.push(
|
||||||
|
await listenToKillProcessEvent((event) => {
|
||||||
|
console.log("kill process event", event)
|
||||||
|
winExtMap.unregisterProcess(event.payload.pid)
|
||||||
|
})
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
getCurrentWebviewWindow().show()
|
getCurrentWebviewWindow().show()
|
||||||
|
@ -5,13 +5,10 @@
|
|||||||
import { systemCommands } from "@/cmds/system"
|
import { systemCommands } from "@/cmds/system"
|
||||||
import { appConfig, appState, devStoreExts, installedStoreExts, quickLinks } from "@/stores"
|
import { appConfig, appState, devStoreExts, installedStoreExts, quickLinks } from "@/stores"
|
||||||
import { cmdQueries } from "@/stores/cmdQuery"
|
import { cmdQueries } from "@/stores/cmdQuery"
|
||||||
import { getActiveElementNodeName, isKeyboardEventFromInputElement } from "@/utils/dom"
|
import { isKeyboardEventFromInputElement } from "@/utils/dom"
|
||||||
import Icon from "@iconify/svelte"
|
import Icon from "@iconify/svelte"
|
||||||
import { openDevTools, toggleDevTools } from "@kksh/api/commands"
|
import { toggleDevTools } from "@kksh/api/commands"
|
||||||
import type { ExtPackageJsonExtra } from "@kksh/api/models"
|
|
||||||
import { isExtPathInDev } from "@kksh/extension/utils"
|
|
||||||
import { Button, Command, DropdownMenu } from "@kksh/svelte5"
|
import { Button, Command, DropdownMenu } from "@kksh/svelte5"
|
||||||
import type { AppConfig, AppState } from "@kksh/types"
|
|
||||||
import {
|
import {
|
||||||
BuiltinCmds,
|
BuiltinCmds,
|
||||||
CustomCommandInput,
|
CustomCommandInput,
|
||||||
@ -20,12 +17,11 @@
|
|||||||
QuickLinks,
|
QuickLinks,
|
||||||
SystemCmds
|
SystemCmds
|
||||||
} from "@kksh/ui/main"
|
} from "@kksh/ui/main"
|
||||||
import type { BuiltinCmd, CmdValue, CommandLaunchers } from "@kksh/ui/types"
|
import type { CmdValue } from "@kksh/ui/types"
|
||||||
import { cn, commandScore } from "@kksh/ui/utils"
|
import { cn, commandScore } from "@kksh/ui/utils"
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
||||||
import { exit } from "@tauri-apps/plugin-process"
|
import { exit } from "@tauri-apps/plugin-process"
|
||||||
import { ArrowBigUpIcon, CircleXIcon, EllipsisVerticalIcon, RefreshCcwIcon } from "lucide-svelte"
|
import { ArrowBigUpIcon, CircleXIcon, EllipsisVerticalIcon, RefreshCcwIcon } from "lucide-svelte"
|
||||||
import { onMount } from "svelte"
|
|
||||||
|
|
||||||
let inputEle: HTMLInputElement | null = null
|
let inputEle: HTMLInputElement | null = null
|
||||||
function onKeyDown(event: KeyboardEvent) {
|
function onKeyDown(event: KeyboardEvent) {
|
||||||
@ -136,7 +132,7 @@
|
|||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon={$appConfig.hmr ? "fontisto:toggle-on" : "fontisto:toggle-off"}
|
icon={$appConfig.hmr ? "fontisto:toggle-on" : "fontisto:toggle-off"}
|
||||||
class={cn("mr-1 h-5 w-5", $appConfig.hmr ? "text-green-500" : "")}
|
class={cn("mr-1 h-5 w-5", { "text-green-500": $appConfig.hmr })}
|
||||||
/>
|
/>
|
||||||
Toggle Dev Extension HMR
|
Toggle Dev Extension HMR
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
@ -112,7 +112,6 @@
|
|||||||
} satisfies IApp
|
} satisfies IApp
|
||||||
|
|
||||||
function onBackBtnClicked() {
|
function onBackBtnClicked() {
|
||||||
console.log("onBackBtnClicked")
|
|
||||||
if (isInMainWindow()) {
|
if (isInMainWindow()) {
|
||||||
goHome()
|
goHome()
|
||||||
} else {
|
} else {
|
||||||
|
@ -56,14 +56,9 @@
|
|||||||
let loaded = $state(false)
|
let loaded = $state(false)
|
||||||
|
|
||||||
async function goBack() {
|
async function goBack() {
|
||||||
console.log("goBack")
|
|
||||||
if (isInMainWindow()) {
|
if (isInMainWindow()) {
|
||||||
console.log("goBack in main window")
|
|
||||||
// if in main window, then winExtMap store must contain this
|
|
||||||
// winExtMap.unregisterExtensionFromWindow(appWin.label)
|
|
||||||
goto("/")
|
goto("/")
|
||||||
} else {
|
} else {
|
||||||
console.log("goBack in webview window")
|
|
||||||
appWin.close()
|
appWin.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,6 +230,7 @@
|
|||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
unlistenRefreshWorkerExt?.()
|
unlistenRefreshWorkerExt?.()
|
||||||
|
winExtMap.unregisterExtensionFromWindow(appWin.label)
|
||||||
extensionLoadingBar = false
|
extensionLoadingBar = false
|
||||||
appState.setActionPanel(undefined)
|
appState.setActionPanel(undefined)
|
||||||
})
|
})
|
||||||
|
@ -38,12 +38,12 @@
|
|||||||
<IconMultiplexer icon={cmd.icon ?? ext.kunkun.icon} class="!h-5 !w-5 shrink-0" />
|
<IconMultiplexer icon={cmd.icon ?? ext.kunkun.icon} class="!h-5 !w-5 shrink-0" />
|
||||||
<span>{cmd.name}</span>
|
<span>{cmd.name}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="flex gap-2">
|
<span class="flex gap-1">
|
||||||
{#if isDev}
|
{#if isDev}
|
||||||
<Badge class="rounded-sm bg-green-500 px-1">Dev</Badge>
|
<Badge class="scale-75 rounded-sm bg-green-500 px-1">Dev</Badge>
|
||||||
{/if}
|
{/if}
|
||||||
{#if hmr}
|
{#if hmr}
|
||||||
<Badge class="rounded-sm px-1">HMR</Badge>
|
<Badge class="scale-75 rounded-sm px-1">HMR</Badge>
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
</Command.Item>
|
</Command.Item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user