mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-04-04 14:46:42 +00:00

* feat: npm package registry API * refactor: move package registry files * refactor: move jsr and npm api to a new package * ci: add verify-package-export * test: implement tests for npm package validation as kunkun extension * chore: add missing dep for package-registry pkg * feat: make provenance an optional input for npm validation function * ci: add verify-package-export as dev dep to 2 packages that uses it * feat: add rekor log API, and return commit from jsr & npm package in validation function * feat: return github repo info from validation function of jsr and npm * feat: extend ExtPublishMetadata to include optional GitHub repository details * fix: eslint for ui package * refactor: format desktop * fix: eslint errors in desktop * format: all code * ci: add lint to CI * feat: add more info to validation function returned from package-registry npm jsr * pnpm lock * feat: add 2 more variables to supabase extension metadata model * format * feat: add provenance card * feat: add workflow path to ExtPublishMetadata and jsr/npm validation * update provenance * feat: make store extension and provenance more responsive * chore: add globals to ui package * fix: remove unnecessary any to fix eslint * fix: svg sanitize * chore: add @typescript-eslint/eslint-plugin to ui package to fix eslint * fix: update eslint dep to fix error * fix: try fixing eslint * fix: update eslint configuration for improved compatibility * chore: add globals package and update README for Discord invite * fix: update eslint rules and upgrade typescript-eslint dependency - Disabled additional eslint rules to resolve errors: - @typescript-eslint/no-unused-expressions - svelte/no-inner-declarations - Upgraded typescript-eslint from version 8.19.1 to 8.20.0 for improved compatibility. * update pnpm lock --------- Co-authored-by: Huakun Shen <huaukun.shen@huakunshen.com>
215 lines
5.8 KiB
Svelte
215 lines
5.8 KiB
Svelte
<script lang="ts">
|
|
import DanceTransition from "@/components/dance/dance-transition.svelte"
|
|
import { appConfig, winExtMap } from "@/stores"
|
|
import { goBackOnEscape } from "@/utils/key"
|
|
import { goHome } from "@/utils/route"
|
|
import { positionToCssStyleString, positionToTailwindClasses } from "@/utils/style"
|
|
import { isInMainWindow } from "@/utils/window"
|
|
import { db } from "@kksh/api/commands"
|
|
import { CustomPosition, ThemeColor, type Position } from "@kksh/api/models"
|
|
import {
|
|
constructJarvisServerAPIWithPermissions,
|
|
// exposeApiToWindow,
|
|
type IApp,
|
|
type IUiIframe
|
|
} from "@kksh/api/ui"
|
|
import { toast, type IUiIframeServer1, type IUiIframeServer2 } from "@kksh/api/ui/iframe"
|
|
import { Button } from "@kksh/svelte5"
|
|
import { cn } from "@kksh/ui/utils"
|
|
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
|
|
import { getCurrentWindow } from "@tauri-apps/api/window"
|
|
import { goto } from "$app/navigation"
|
|
import { IframeParentIO, RPCChannel } from "kkrpc/browser"
|
|
import { ArrowLeftIcon, MoveIcon, RefreshCcwIcon, XIcon } from "lucide-svelte"
|
|
import { onDestroy, onMount } from "svelte"
|
|
import type { PageData } from "./$types"
|
|
|
|
let { data }: { data: PageData } = $props()
|
|
const { loadedExt, url, extPath, extInfoInDB } = data
|
|
const appWin = getCurrentWindow()
|
|
let iframeRef: HTMLIFrameElement
|
|
let uiControl = $state<{
|
|
iframeLoaded: boolean
|
|
showBackBtn: boolean
|
|
showMoveBtn: boolean
|
|
showRefreshBtn: boolean
|
|
backBtnPosition: Position
|
|
moveBtnPosition: Position
|
|
refreshBtnPosition: Position
|
|
transparentBg: boolean
|
|
}>({
|
|
iframeLoaded: false,
|
|
showBackBtn: true, // if open in new window, hide back button
|
|
showMoveBtn: true,
|
|
showRefreshBtn: true,
|
|
backBtnPosition: "top-left",
|
|
moveBtnPosition: "bottom-left",
|
|
refreshBtnPosition: "top-right",
|
|
transparentBg: false
|
|
})
|
|
|
|
const iframeUiAPI: IUiIframeServer2 = {
|
|
goBack: async () => {
|
|
if (isInMainWindow()) {
|
|
goto("/app/")
|
|
} else {
|
|
appWin.close()
|
|
}
|
|
},
|
|
hideBackButton: async () => {
|
|
uiControl.showBackBtn = false
|
|
},
|
|
hideMoveButton: async () => {
|
|
uiControl.showMoveBtn = false
|
|
},
|
|
hideRefreshButton: async () => {
|
|
console.log("hideRefreshButton")
|
|
uiControl.showRefreshBtn = false
|
|
},
|
|
showBackButton: async (position?: Position) => {
|
|
console.log("showBackBtn", position)
|
|
|
|
uiControl.showBackBtn = true
|
|
uiControl.backBtnPosition = position ?? "top-left"
|
|
},
|
|
showMoveButton: async (position?: Position) => {
|
|
uiControl.showMoveBtn = true
|
|
uiControl.moveBtnPosition = position ?? "bottom-left"
|
|
},
|
|
showRefreshButton: async (position?: Position) => {
|
|
uiControl.showRefreshBtn = true
|
|
uiControl.refreshBtnPosition = position ?? "top-right"
|
|
},
|
|
getTheme: () => {
|
|
const theme = $appConfig.theme
|
|
return Promise.resolve({
|
|
theme: theme.theme as ThemeColor,
|
|
radius: theme.radius,
|
|
lightMode: theme.lightMode
|
|
})
|
|
},
|
|
async reloadPage() {
|
|
location.reload()
|
|
},
|
|
async setTransparentWindowBackground(transparent: boolean) {
|
|
if (isInMainWindow()) {
|
|
throw new Error("Cannot set background in main window")
|
|
}
|
|
if (transparent) {
|
|
document.body.style.backgroundColor = "transparent"
|
|
} else {
|
|
document.body.style.backgroundColor = ""
|
|
}
|
|
}
|
|
}
|
|
|
|
const serverAPI: IKunkunFullServerAPI = constructJarvisServerAPIWithPermissions(
|
|
loadedExt.kunkun.permissions,
|
|
loadedExt.extPath
|
|
)
|
|
const serverAPI2 = {
|
|
...serverAPI,
|
|
iframeUi: {
|
|
...serverAPI.iframeUi,
|
|
...iframeUiAPI
|
|
} satisfies IUiIframeServer1 & IUiIframeServer2,
|
|
db: new db.JarvisExtDB(extInfoInDB.extId),
|
|
kv: new db.KV(extInfoInDB.extId),
|
|
app: {
|
|
language: () => Promise.resolve("en") // TODO: get locale
|
|
} satisfies IApp
|
|
}
|
|
|
|
function onBackBtnClicked() {
|
|
if (isInMainWindow()) {
|
|
goHome()
|
|
} else {
|
|
appWin.close()
|
|
}
|
|
}
|
|
|
|
function onIframeLoaded() {
|
|
setTimeout(() => {
|
|
iframeRef.focus()
|
|
uiControl.iframeLoaded = true
|
|
}, 300)
|
|
}
|
|
|
|
onMount(() => {
|
|
setTimeout(() => {
|
|
appWin.show()
|
|
}, 200)
|
|
if (iframeRef?.contentWindow) {
|
|
const io = new IframeParentIO(iframeRef.contentWindow)
|
|
const rpc = new RPCChannel(io, { expose: serverAPI2 })
|
|
} else {
|
|
toast.warning("iframeRef.contentWindow not available")
|
|
}
|
|
|
|
setTimeout(() => {
|
|
if (!uiControl.iframeLoaded) {
|
|
toast.error("Extension failed to load")
|
|
}
|
|
}, 3_000)
|
|
})
|
|
|
|
onDestroy(() => {
|
|
winExtMap.unregisterExtensionFromWindow(appWin.label)
|
|
})
|
|
</script>
|
|
|
|
<svelte:window on:keydown={goBackOnEscape} />
|
|
{#if uiControl.backBtnPosition && uiControl.showBackBtn}
|
|
<Button
|
|
class={cn("absolute", positionToTailwindClasses(uiControl.backBtnPosition))}
|
|
size="icon"
|
|
variant="outline"
|
|
onclick={onBackBtnClicked}
|
|
style={`${positionToCssStyleString(uiControl.backBtnPosition)}`}
|
|
>
|
|
{#if appWin.label === "main"}
|
|
<ArrowLeftIcon class="w-4" />
|
|
{:else}
|
|
<XIcon class="w-4" />
|
|
{/if}
|
|
</Button>
|
|
{/if}
|
|
{#if uiControl.moveBtnPosition && uiControl.showMoveBtn}
|
|
<Button
|
|
class={cn("absolute", positionToTailwindClasses(uiControl.moveBtnPosition))}
|
|
style={`${positionToCssStyleString(uiControl.moveBtnPosition)}`}
|
|
size="icon"
|
|
variant="outline"
|
|
data-tauri-drag-region
|
|
>
|
|
<MoveIcon data-tauri-drag-region class="w-4" />
|
|
</Button>
|
|
{/if}
|
|
{#if uiControl.refreshBtnPosition && uiControl.showRefreshBtn}
|
|
<Button
|
|
class={cn("absolute", positionToTailwindClasses(uiControl.refreshBtnPosition))}
|
|
style={`${positionToCssStyleString(uiControl.refreshBtnPosition)}`}
|
|
size="icon"
|
|
variant="outline"
|
|
onclick={iframeUiAPI.reloadPage}
|
|
>
|
|
<RefreshCcwIcon class="w-4" />
|
|
</Button>
|
|
{/if}
|
|
|
|
<main class="h-screen">
|
|
<DanceTransition delay={300} autoHide={false} show={!uiControl.iframeLoaded} />
|
|
<iframe
|
|
bind:this={iframeRef}
|
|
class={cn("h-full", {
|
|
hidden: !uiControl.iframeLoaded
|
|
})}
|
|
onload={onIframeLoaded}
|
|
width="100%"
|
|
height="100%"
|
|
frameborder="0"
|
|
src={data.url}
|
|
title={data.extPath}
|
|
></iframe>
|
|
</main>
|