mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-04-11 17:29:44 +00:00
fix: listview action menu (#64)
* refactor: add a valibot schema for package registry validation * fix: list view action menu * chore: bump version to 0.1.16 in package.json * refactor: extract supabase package from api * ci: remove NODE_OPTIONS from build step and improve error handling in getLatestNpmPkgVersion function
This commit is contained in:
parent
21a90259ac
commit
7a3b6f3983
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -51,8 +51,6 @@ jobs:
|
|||||||
- name: Setup
|
- name: Setup
|
||||||
run: pnpm prepare
|
run: pnpm prepare
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
|
||||||
NODE_OPTIONS: --max-old-space-size=4096
|
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
- name: JS Test
|
- name: JS Test
|
||||||
if: matrix.os == 'ubuntu-24.04'
|
if: matrix.os == 'ubuntu-24.04'
|
||||||
|
@ -11,9 +11,11 @@ export function getLatestNpmPkgInfo(pkgName: string): Promise<Record<string, any
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getLatestNpmPkgVersion(pkgName: string): Promise<string> {
|
export function getLatestNpmPkgVersion(pkgName: string): Promise<string> {
|
||||||
return getLatestNpmPkgInfo(pkgName).then(
|
return getLatestNpmPkgInfo(pkgName)
|
||||||
(data) => v.parse(v.object({ version: v.string() }), data).version
|
.then((data) => v.parse(v.object({ version: v.string() }), data).version)
|
||||||
)
|
.catch((err) => {
|
||||||
|
throw new Error(`Failed to get latest version of ${pkgName}: ${err.message}`)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@kksh/desktop",
|
"name": "@kksh/desktop",
|
||||||
"version": "0.1.15",
|
"version": "0.1.16",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -16,7 +16,7 @@ interface AppStateAPI {
|
|||||||
clearSearchTerm: () => void
|
clearSearchTerm: () => void
|
||||||
get: () => AppState
|
get: () => AppState
|
||||||
setLoadingBar: (loadingBar: boolean) => void
|
setLoadingBar: (loadingBar: boolean) => void
|
||||||
setDefaultAction: (defaultAction: string) => void
|
setDefaultAction: (defaultAction: string | null) => void
|
||||||
setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => void
|
setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ function createAppState(): Writable<AppState> & AppStateAPI {
|
|||||||
setLoadingBar: (loadingBar: boolean) => {
|
setLoadingBar: (loadingBar: boolean) => {
|
||||||
store.update((state) => ({ ...state, loadingBar }))
|
store.update((state) => ({ ...state, loadingBar }))
|
||||||
},
|
},
|
||||||
setDefaultAction: (defaultAction: string) => {
|
setDefaultAction: (defaultAction: string | null) => {
|
||||||
store.update((state) => ({ ...state, defaultAction }))
|
store.update((state) => ({ ...state, defaultAction }))
|
||||||
},
|
},
|
||||||
setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => {
|
setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => {
|
||||||
|
26
apps/desktop/src/lib/stores/keys.ts
Normal file
26
apps/desktop/src/lib/stores/keys.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { get, writable, type Writable } from "svelte/store"
|
||||||
|
|
||||||
|
export interface KeyStoreAPI {
|
||||||
|
get: () => string[]
|
||||||
|
getSet: () => Set<string>
|
||||||
|
keydown: (key: string) => void
|
||||||
|
keyup: (key: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function createKeysStore(): Writable<string[]> & KeyStoreAPI {
|
||||||
|
const store = writable<string[]>([])
|
||||||
|
|
||||||
|
return {
|
||||||
|
...store,
|
||||||
|
get: () => get(store),
|
||||||
|
getSet: () => new Set(get(store)),
|
||||||
|
keydown: (key: string) => {
|
||||||
|
store.update((state) => [...state, key])
|
||||||
|
},
|
||||||
|
keyup: (key: string) => {
|
||||||
|
store.update((state) => state.filter((k) => k !== key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const keys = createKeysStore()
|
@ -1,4 +1,5 @@
|
|||||||
import { appState } from "@/stores"
|
import { appState } from "@/stores"
|
||||||
|
import { keys } from "@/stores/keys"
|
||||||
import { toggleDevTools } from "@kksh/api/commands"
|
import { toggleDevTools } from "@kksh/api/commands"
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
||||||
import { getCurrentWindow } from "@tauri-apps/api/window"
|
import { getCurrentWindow } from "@tauri-apps/api/window"
|
||||||
@ -72,6 +73,7 @@ export function goHomeOrCloseOnEscapeWithInput(e: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function globalKeyDownHandler(e: KeyboardEvent) {
|
export async function globalKeyDownHandler(e: KeyboardEvent) {
|
||||||
|
keys.keydown(e.key)
|
||||||
const _platform = platform()
|
const _platform = platform()
|
||||||
if ((_platform === "macos" && e.metaKey) || (_platform === "windows" && e.ctrlKey)) {
|
if ((_platform === "macos" && e.metaKey) || (_platform === "windows" && e.ctrlKey)) {
|
||||||
if (e.key === ",") {
|
if (e.key === ",") {
|
||||||
@ -96,6 +98,10 @@ export async function globalKeyDownHandler(e: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function globalKeyUpHandler(e: KeyboardEvent) {
|
||||||
|
keys.keyup(e.key)
|
||||||
|
}
|
||||||
|
|
||||||
export function isLetter(letter: string): boolean {
|
export function isLetter(letter: string): boolean {
|
||||||
if (letter.length != 1) return false
|
if (letter.length != 1) return false
|
||||||
return letter.match(/[a-zA-Z]/) ? true : false
|
return letter.match(/[a-zA-Z]/) ? true : false
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { appConfig, appState, extensions, quickLinks, winExtMap } 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, globalKeyUpHandler, 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 { listenToKillProcessEvent, listenToRecordExtensionProcessEvent } from "@kksh/api/events"
|
||||||
@ -97,7 +97,7 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:keydown={globalKeyDownHandler} />
|
<svelte:window on:keydown={globalKeyDownHandler} on:keyup={globalKeyUpHandler} />
|
||||||
<ViewTransition />
|
<ViewTransition />
|
||||||
<AppContext {appConfig} {appState}>
|
<AppContext {appConfig} {appState}>
|
||||||
{@render children()}
|
{@render children()}
|
||||||
|
@ -4,8 +4,7 @@
|
|||||||
import { supabaseAPI } from "@/supabase"
|
import { supabaseAPI } from "@/supabase"
|
||||||
import { goBackOnEscapeClearSearchTerm, goHomeOnEscapeClearSearchTerm } from "@/utils/key"
|
import { goBackOnEscapeClearSearchTerm, goHomeOnEscapeClearSearchTerm } from "@/utils/key"
|
||||||
import { goBack, goHome } from "@/utils/route"
|
import { goBack, goHome } from "@/utils/route"
|
||||||
import { SBExt, type Tables } from "@kksh/api/supabase"
|
import { SBExt, type Tables } from "@kksh/supabase"
|
||||||
import { isUpgradable } from "@kksh/extension"
|
|
||||||
import type { ExtPublishMetadata } from "@kksh/supabase/models"
|
import type { ExtPublishMetadata } from "@kksh/supabase/models"
|
||||||
import { Button, Command } from "@kksh/svelte5"
|
import { Button, Command } from "@kksh/svelte5"
|
||||||
import { Constants } from "@kksh/ui"
|
import { Constants } from "@kksh/ui"
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { appConfig, extensions, installedStoreExts } from "@/stores"
|
import { appConfig, extensions, installedStoreExts } from "@/stores"
|
||||||
import { supabaseAPI } from "@/supabase"
|
import { supabaseAPI } from "@/supabase"
|
||||||
import type { ExtPackageJsonExtra } from "@kksh/api/models"
|
import type { ExtPackageJsonExtra } from "@kksh/api/models"
|
||||||
import { SBExt } from "@kksh/api/supabase"
|
|
||||||
import { isExtPathInDev, isUpgradable } from "@kksh/extension"
|
import { isExtPathInDev, isUpgradable } from "@kksh/extension"
|
||||||
|
import { SBExt } from "@kksh/supabase"
|
||||||
import { error } from "@sveltejs/kit"
|
import { error } from "@sveltejs/kit"
|
||||||
import { derived, get, type Readable } from "svelte/store"
|
import { derived, get, type Readable } from "svelte/store"
|
||||||
import type { PageLoad } from "./$types"
|
import type { PageLoad } from "./$types"
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
import { extensions, installedStoreExts } from "@/stores/extensions.js"
|
import { extensions, installedStoreExts } from "@/stores/extensions.js"
|
||||||
import { supabaseAPI } from "@/supabase"
|
import { supabaseAPI } from "@/supabase"
|
||||||
import { goBack } from "@/utils/route.js"
|
import { goBack } from "@/utils/route.js"
|
||||||
import type { Tables } from "@kksh/api/supabase/types"
|
|
||||||
import { ExtPublishMetadata } from "@kksh/supabase/models"
|
import { ExtPublishMetadata } from "@kksh/supabase/models"
|
||||||
|
import type { Tables } from "@kksh/supabase/types"
|
||||||
import { Button } from "@kksh/svelte5"
|
import { Button } from "@kksh/svelte5"
|
||||||
import { cn } from "@kksh/svelte5/utils"
|
import { cn } from "@kksh/svelte5/utils"
|
||||||
import { Constants } from "@kksh/ui"
|
import { Constants } from "@kksh/ui"
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { extensions } from "@/stores"
|
import { extensions } from "@/stores"
|
||||||
import { supabaseAPI } from "@/supabase"
|
import { supabaseAPI } from "@/supabase"
|
||||||
import { KunkunExtManifest, type ExtPackageJsonExtra } from "@kksh/api/models"
|
import { KunkunExtManifest, type ExtPackageJsonExtra } from "@kksh/api/models"
|
||||||
import type { Tables } from "@kksh/api/supabase/types"
|
|
||||||
import { ExtPublishMetadata } from "@kksh/supabase/models"
|
import { ExtPublishMetadata } from "@kksh/supabase/models"
|
||||||
|
import type { Tables } from "@kksh/supabase/types"
|
||||||
import { error } from "@sveltejs/kit"
|
import { error } from "@sveltejs/kit"
|
||||||
import { toast } from "svelte-sonner"
|
import { toast } from "svelte-sonner"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Tables } from "@kksh/api/supabase/types"
|
|
||||||
import type { ExtPublishMetadata } from "@kksh/supabase/models"
|
import type { ExtPublishMetadata } from "@kksh/supabase/models"
|
||||||
|
import type { Tables } from "@kksh/supabase/types"
|
||||||
|
|
||||||
export async function getInstallExtras(
|
export async function getInstallExtras(
|
||||||
ext: Tables<"ext_publish"> & { metadata?: ExtPublishMetadata }
|
ext: Tables<"ext_publish"> & { metadata?: ExtPublishMetadata }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { appState } from "@/stores/appState.js"
|
import { appState } from "@/stores/appState.js"
|
||||||
|
import { keys } from "@/stores/keys"
|
||||||
import { winExtMap } from "@/stores/winExtMap.js"
|
import { winExtMap } from "@/stores/winExtMap.js"
|
||||||
import { listenToFileDrop, listenToRefreshDevExt } from "@/utils/tauri-events.js"
|
import { listenToFileDrop, listenToRefreshDevExt } from "@/utils/tauri-events.js"
|
||||||
import { isInMainWindow } from "@/utils/window.js"
|
import { isInMainWindow } from "@/utils/window.js"
|
||||||
@ -23,13 +24,16 @@
|
|||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
||||||
import { readTextFile } from "@tauri-apps/plugin-fs"
|
import { readTextFile } from "@tauri-apps/plugin-fs"
|
||||||
import { debug } from "@tauri-apps/plugin-log"
|
import { debug } from "@tauri-apps/plugin-log"
|
||||||
|
import { platform } from "@tauri-apps/plugin-os"
|
||||||
import { goto } from "$app/navigation"
|
import { goto } from "$app/navigation"
|
||||||
import { RPCChannel, WorkerParentIO } from "kkrpc/browser"
|
import { RPCChannel, WorkerParentIO } from "kkrpc/browser"
|
||||||
import { onDestroy, onMount } from "svelte"
|
import { onDestroy, onMount, tick } from "svelte"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
|
|
||||||
const { data } = $props()
|
const { data } = $props()
|
||||||
|
let listviewInputRef = $state<HTMLInputElement | null>(null)
|
||||||
let { loadedExt, scriptPath, extInfoInDB } = $derived(data)
|
let { loadedExt, scriptPath, extInfoInDB } = $derived(data)
|
||||||
|
let actionPanelOpen = $state(false)
|
||||||
let workerAPI: WorkerExtension | undefined = undefined
|
let workerAPI: WorkerExtension | undefined = undefined
|
||||||
let unlistenRefreshWorkerExt: UnlistenFn | undefined
|
let unlistenRefreshWorkerExt: UnlistenFn | undefined
|
||||||
let unlistenFileDrop: UnlistenFn | undefined
|
let unlistenFileDrop: UnlistenFn | undefined
|
||||||
@ -46,6 +50,7 @@
|
|||||||
const loadingBar = $derived($appState.loadingBar || extensionLoadingBar)
|
const loadingBar = $derived($appState.loadingBar || extensionLoadingBar)
|
||||||
let loaded = $state(false)
|
let loaded = $state(false)
|
||||||
let listview: Templates.ListView | undefined = $state(undefined)
|
let listview: Templates.ListView | undefined = $state(undefined)
|
||||||
|
const _platform = platform()
|
||||||
|
|
||||||
async function goBack() {
|
async function goBack() {
|
||||||
if (isInMainWindow()) {
|
if (isInMainWindow()) {
|
||||||
@ -134,6 +139,14 @@
|
|||||||
listViewContent = parsedListView
|
listViewContent = parsedListView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// on each render, also update the default action and action panel
|
||||||
|
if (listViewContent?.defaultAction) {
|
||||||
|
appState.setDefaultAction(listViewContent.defaultAction)
|
||||||
|
}
|
||||||
|
if (listViewContent?.actions) {
|
||||||
|
appState.setActionPanel(listViewContent.actions)
|
||||||
|
}
|
||||||
|
|
||||||
// if (parsedListView.updateDetailOnly) {
|
// if (parsedListView.updateDetailOnly) {
|
||||||
// if (listViewContent) {
|
// if (listViewContent) {
|
||||||
// listViewContent.detail = parsedListView.detail
|
// listViewContent.detail = parsedListView.detail
|
||||||
@ -187,6 +200,8 @@
|
|||||||
worker.terminate()
|
worker.terminate()
|
||||||
worker = undefined
|
worker = undefined
|
||||||
}
|
}
|
||||||
|
appState.setDefaultAction(null)
|
||||||
|
appState.setActionPanel(undefined)
|
||||||
const workerScript = await readTextFile(scriptPath)
|
const workerScript = await readTextFile(scriptPath)
|
||||||
const blob = new Blob([workerScript], { type: "application/javascript" })
|
const blob = new Blob([workerScript], { type: "application/javascript" })
|
||||||
const blobURL = URL.createObjectURL(blob)
|
const blobURL = URL.createObjectURL(blob)
|
||||||
@ -248,13 +263,44 @@
|
|||||||
extensionLoadingBar = false
|
extensionLoadingBar = false
|
||||||
appState.setActionPanel(undefined)
|
appState.setActionPanel(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
void $keys
|
||||||
|
const keySet = keys.getSet()
|
||||||
|
if (
|
||||||
|
keySet.size === 2 &&
|
||||||
|
keySet.has(_platform === "macos" ? "Meta" : "Control") &&
|
||||||
|
keySet.has("k")
|
||||||
|
) {
|
||||||
|
console.log("open action panel")
|
||||||
|
actionPanelOpen = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function onActionPanelBlur() {
|
||||||
|
setTimeout(() => {
|
||||||
|
listviewInputRef?.focus()
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onkeydown(e: KeyboardEvent) {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
console.log(document.activeElement)
|
||||||
|
console.log(document.activeElement?.nodeName)
|
||||||
|
if (document.activeElement?.nodeName === "INPUT") {
|
||||||
|
console.log("input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:keydown={onkeydown} />
|
||||||
{#if loadingBar}
|
{#if loadingBar}
|
||||||
<LoadingBar class="fixed left-0 top-0 w-full" color="white" />
|
<LoadingBar class="fixed left-0 top-0 w-full" color="white" />
|
||||||
{/if}
|
{/if}
|
||||||
{#if loaded && listViewContent !== undefined}
|
{#if loaded && listViewContent !== undefined}
|
||||||
<Templates.ListView
|
<Templates.ListView
|
||||||
|
bind:inputRef={listviewInputRef}
|
||||||
bind:searchTerm
|
bind:searchTerm
|
||||||
bind:searchBarPlaceholder
|
bind:searchBarPlaceholder
|
||||||
bind:this={listview}
|
bind:this={listview}
|
||||||
@ -275,19 +321,33 @@
|
|||||||
workerAPI?.onSearchTermChange(searchTerm)
|
workerAPI?.onSearchTermChange(searchTerm)
|
||||||
}}
|
}}
|
||||||
onHighlightedItemChanged={(value: string) => {
|
onHighlightedItemChanged={(value: string) => {
|
||||||
workerAPI?.onHighlightedListItemChanged(value)
|
// workerAPI?.onHighlightedListItemChanged(value)
|
||||||
if (listViewContent?.defaultAction) {
|
// if (listViewContent?.defaultAction) {
|
||||||
appState.setDefaultAction(listViewContent.defaultAction)
|
// appState.setDefaultAction(listViewContent.defaultAction)
|
||||||
}
|
// }
|
||||||
if (listViewContent?.actions) {
|
// if (listViewContent?.actions) {
|
||||||
appState.setActionPanel(listViewContent.actions)
|
// appState.setActionPanel(listViewContent.actions)
|
||||||
|
// }
|
||||||
|
try {
|
||||||
|
const parsedItem = v.parse(ListSchema.Item, JSON.parse(value))
|
||||||
|
if (parsedItem.defaultAction) {
|
||||||
|
appState.setDefaultAction(parsedItem.defaultAction)
|
||||||
|
}
|
||||||
|
if (parsedItem.actions) {
|
||||||
|
appState.setActionPanel(parsedItem.actions)
|
||||||
|
}
|
||||||
|
workerAPI?.onHighlightedListItemChanged(parsedItem.value)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#snippet footer()}
|
{#snippet footer()}
|
||||||
<GlobalCommandPaletteFooter
|
<GlobalCommandPaletteFooter
|
||||||
defaultAction={$appState.defaultAction}
|
bind:actionPanelOpen
|
||||||
actionPanel={$appState.actionPanel}
|
actionPanel={$appState.actionPanel}
|
||||||
|
defaultAction={$appState.defaultAction ?? undefined}
|
||||||
|
{onActionPanelBlur}
|
||||||
onDefaultActionSelected={() => {
|
onDefaultActionSelected={() => {
|
||||||
workerAPI?.onEnterPressedOnSearchBar()
|
workerAPI?.onEnterPressedOnSearchBar()
|
||||||
}}
|
}}
|
||||||
|
@ -14,8 +14,6 @@
|
|||||||
"./permissions": "./src/permissions/index.ts",
|
"./permissions": "./src/permissions/index.ts",
|
||||||
"./dev": "./src/dev/index.ts",
|
"./dev": "./src/dev/index.ts",
|
||||||
"./events": "./src/events.ts",
|
"./events": "./src/events.ts",
|
||||||
"./supabase": "./src/supabase/index.ts",
|
|
||||||
"./supabase/types": "./src/supabase/database.types.ts",
|
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
export * from "./model"
|
|
||||||
export * from "./database.types"
|
|
@ -1,19 +0,0 @@
|
|||||||
import { Icon } from "@kksh/api/models"
|
|
||||||
import * as v from "valibot"
|
|
||||||
|
|
||||||
/***
|
|
||||||
* Correspond to `extensions` table in supabase
|
|
||||||
*/
|
|
||||||
export const SBExt = v.object({
|
|
||||||
identifier: v.string(),
|
|
||||||
name: v.string(),
|
|
||||||
created_at: v.string(),
|
|
||||||
downloads: v.number(),
|
|
||||||
short_description: v.string(),
|
|
||||||
long_description: v.string(),
|
|
||||||
version: v.string(),
|
|
||||||
api_version: v.optional(v.string()),
|
|
||||||
icon: Icon
|
|
||||||
})
|
|
||||||
|
|
||||||
export type SBExt = v.InferOutput<typeof SBExt>
|
|
@ -8,7 +8,8 @@
|
|||||||
"exports": {
|
"exports": {
|
||||||
"./jsr": "./src/jsr/index.ts",
|
"./jsr": "./src/jsr/index.ts",
|
||||||
"./npm": "./src/npm/index.ts",
|
"./npm": "./src/npm/index.ts",
|
||||||
"./github": "./src/github.ts"
|
"./github": "./src/github.ts",
|
||||||
|
"./models": "./src/models.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
import { ExtPackageJson } from "@kksh/api/models"
|
import { ExtPackageJson } from "@kksh/api/models"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
import { authenticatedUserIsMemberOfGitHubOrg, userIsPublicMemberOfGitHubOrg } from "../github"
|
import { authenticatedUserIsMemberOfGitHubOrg, userIsPublicMemberOfGitHubOrg } from "../github"
|
||||||
|
import type { ExtensionPublishValidationData } from "../models"
|
||||||
import type { NpmPkgMetadata } from "../npm/models"
|
import type { NpmPkgMetadata } from "../npm/models"
|
||||||
import { getInfoFromRekorLog } from "../sigstore"
|
import { getInfoFromRekorLog } from "../sigstore"
|
||||||
import { getTarballSize } from "../utils"
|
import { getTarballSize } from "../utils"
|
||||||
@ -220,21 +221,7 @@ export async function validateJsrPackageAsKunkunExtension(payload: {
|
|||||||
githubToken?: string
|
githubToken?: string
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
error?: string
|
error?: string
|
||||||
data?: {
|
data?: ExtensionPublishValidationData
|
||||||
pkgJson: ExtPackageJson
|
|
||||||
tarballUrl: string
|
|
||||||
shasum: string
|
|
||||||
apiVersion: string
|
|
||||||
tarballSize: number
|
|
||||||
rekorLogIndex: string
|
|
||||||
github: {
|
|
||||||
githubActionInvocationId: string
|
|
||||||
commit: string
|
|
||||||
repo: string
|
|
||||||
owner: string
|
|
||||||
workflowPath: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}> {
|
}> {
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* check if jsr package exists */
|
/* check if jsr package exists */
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ExtPackageJson } from "@kksh/api/models"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
|
|
||||||
export const RawRekorLogEntry = v.object({
|
export const RawRekorLogEntry = v.object({
|
||||||
@ -58,3 +59,19 @@ export const SigstoreAttestation = v.object({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
export type SigstoreAttestation = v.InferOutput<typeof SigstoreAttestation>
|
export type SigstoreAttestation = v.InferOutput<typeof SigstoreAttestation>
|
||||||
|
export const ExtensionPublishValidationData = v.object({
|
||||||
|
pkgJson: ExtPackageJson,
|
||||||
|
tarballUrl: v.string(),
|
||||||
|
shasum: v.string(),
|
||||||
|
apiVersion: v.string(),
|
||||||
|
rekorLogIndex: v.string(),
|
||||||
|
tarballSize: v.number(),
|
||||||
|
github: v.object({
|
||||||
|
githubActionInvocationId: v.string(),
|
||||||
|
commit: v.string(),
|
||||||
|
repo: v.string(),
|
||||||
|
owner: v.string(),
|
||||||
|
workflowPath: v.string()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
export type ExtensionPublishValidationData = v.InferOutput<typeof ExtensionPublishValidationData>
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
parseGitHubRepoFromUri,
|
parseGitHubRepoFromUri,
|
||||||
userIsPublicMemberOfGitHubOrg
|
userIsPublicMemberOfGitHubOrg
|
||||||
} from "../github"
|
} from "../github"
|
||||||
|
import type { ExtensionPublishValidationData } from "../models"
|
||||||
import { getInfoFromRekorLog } from "../sigstore"
|
import { getInfoFromRekorLog } from "../sigstore"
|
||||||
import {
|
import {
|
||||||
NpmPkgMetadata,
|
NpmPkgMetadata,
|
||||||
@ -116,21 +117,7 @@ export async function validateNpmPackageAsKunkunExtension(payload: {
|
|||||||
provenance?: Provenance // provenance API has cors policy, when we run this validation on client side, a provenance should be passed in
|
provenance?: Provenance // provenance API has cors policy, when we run this validation on client side, a provenance should be passed in
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
error?: string
|
error?: string
|
||||||
data?: {
|
data?: ExtensionPublishValidationData
|
||||||
pkgJson: ExtPackageJson
|
|
||||||
tarballUrl: string
|
|
||||||
shasum: string
|
|
||||||
apiVersion: string
|
|
||||||
rekorLogIndex: string
|
|
||||||
tarballSize: number
|
|
||||||
github: {
|
|
||||||
githubActionInvocationId: string
|
|
||||||
commit: string
|
|
||||||
repo: string
|
|
||||||
owner: string
|
|
||||||
workflowPath: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}> {
|
}> {
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* check if npm package exist */
|
/* check if npm package exist */
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
"./models": "./src/models.ts"
|
"./models": "./src/models.ts",
|
||||||
|
"./types": "./src/database.types.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kksh/api": "workspace:*",
|
"@kksh/api": "workspace:*",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { SBExt } from "@kksh/api/supabase"
|
|
||||||
import type { Database, Tables } from "@kksh/api/supabase/types"
|
|
||||||
import type { PostgrestSingleResponse, SupabaseClient } from "@supabase/supabase-js"
|
import type { PostgrestSingleResponse, SupabaseClient } from "@supabase/supabase-js"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
|
import type { Database, Tables } from "./database.types"
|
||||||
|
import { SBExt } from "./models"
|
||||||
|
|
||||||
export class SupabaseAPI {
|
export class SupabaseAPI {
|
||||||
constructor(private supabase: SupabaseClient<Database>) {}
|
constructor(private supabase: SupabaseClient<Database>) {}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Database } from "@kksh/api/supabase/types"
|
|
||||||
import { createClient } from "@supabase/supabase-js"
|
import { createClient } from "@supabase/supabase-js"
|
||||||
|
import type { Database } from "./database.types"
|
||||||
|
|
||||||
export function createSB(supabaseUrl: string, supabaseAnonKey: string) {
|
export function createSB(supabaseUrl: string, supabaseAnonKey: string) {
|
||||||
return createClient<Database>(supabaseUrl, supabaseAnonKey, {
|
return createClient<Database>(supabaseUrl, supabaseAnonKey, {
|
||||||
@ -10,5 +10,5 @@ export function createSB(supabaseUrl: string, supabaseAnonKey: string) {
|
|||||||
}
|
}
|
||||||
export { SupabaseAPI } from "./api"
|
export { SupabaseAPI } from "./api"
|
||||||
|
|
||||||
export type { Database, Tables } from "@kksh/api/supabase/types"
|
export type { Database, Tables } from "./database.types"
|
||||||
export { SBExt } from "@kksh/api/supabase"
|
export { SBExt } from "./models"
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
* @module @kksh/supabase/models
|
* @module @kksh/supabase/models
|
||||||
* This module contains some models for supabase database that cannot be code generated, such as JSON fields.
|
* This module contains some models for supabase database that cannot be code generated, such as JSON fields.
|
||||||
*/
|
*/
|
||||||
|
import { Icon } from "@kksh/api/models"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
|
|
||||||
export enum ExtPublishSourceTypeEnum {
|
export enum ExtPublishSourceTypeEnum {
|
||||||
@ -24,3 +25,20 @@ export const ExtPublishMetadata = v.object({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
export type ExtPublishMetadata = v.InferOutput<typeof ExtPublishMetadata>
|
export type ExtPublishMetadata = v.InferOutput<typeof ExtPublishMetadata>
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Correspond to `extensions` table in supabase
|
||||||
|
*/
|
||||||
|
export const SBExt = v.object({
|
||||||
|
identifier: v.string(),
|
||||||
|
name: v.string(),
|
||||||
|
created_at: v.string(),
|
||||||
|
downloads: v.number(),
|
||||||
|
short_description: v.string(),
|
||||||
|
long_description: v.string(),
|
||||||
|
version: v.string(),
|
||||||
|
api_version: v.optional(v.string()),
|
||||||
|
icon: Icon
|
||||||
|
})
|
||||||
|
|
||||||
|
export type SBExt = v.InferOutput<typeof SBExt>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
## Permission Table
|
## Permission Table
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
@ -7,7 +6,6 @@
|
|||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
|
@ -4,6 +4,6 @@ export interface AppState {
|
|||||||
searchTerm: string
|
searchTerm: string
|
||||||
highlightedCmd: string
|
highlightedCmd: string
|
||||||
loadingBar: boolean
|
loadingBar: boolean
|
||||||
defaultAction: string
|
defaultAction: string | null
|
||||||
actionPanel?: ActionSchema.ActionPanel
|
actionPanel?: ActionSchema.ActionPanel
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from "@iconify/svelte"
|
import Icon from "@iconify/svelte"
|
||||||
import { Icon as TIcon } from "@kksh/api/models"
|
import { Icon as TIcon } from "@kksh/api/models"
|
||||||
import { SBExt } from "@kksh/api/supabase"
|
import { SBExt } from "@kksh/supabase"
|
||||||
import { Button, Command } from "@kksh/svelte5"
|
import { Button, Command } from "@kksh/svelte5"
|
||||||
import { Constants, IconMultiplexer } from "@kksh/ui"
|
import { Constants, IconMultiplexer } from "@kksh/ui"
|
||||||
import { cn, humanReadableNumber } from "@kksh/ui/utils"
|
import { cn, humanReadableNumber } from "@kksh/ui/utils"
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
import autoAnimate from "@formkit/auto-animate"
|
import autoAnimate from "@formkit/auto-animate"
|
||||||
import Icon from "@iconify/svelte"
|
import Icon from "@iconify/svelte"
|
||||||
import { ExtPackageJson, IconEnum, KunkunExtManifest } from "@kksh/api/models"
|
import { ExtPackageJson, IconEnum, KunkunExtManifest } from "@kksh/api/models"
|
||||||
import { type Tables } from "@kksh/api/supabase/types"
|
|
||||||
import { ExtPublishMetadata, ExtPublishSourceTypeEnum } from "@kksh/supabase/models"
|
import { ExtPublishMetadata, ExtPublishSourceTypeEnum } from "@kksh/supabase/models"
|
||||||
|
import { type Tables } from "@kksh/supabase/types"
|
||||||
import { Badge, Button, ScrollArea, Separator } from "@kksh/svelte5"
|
import { Badge, Button, ScrollArea, Separator } from "@kksh/svelte5"
|
||||||
import { Constants, IconMultiplexer } from "@kksh/ui"
|
import { Constants, IconMultiplexer } from "@kksh/ui"
|
||||||
import { cn } from "@kksh/ui/utils"
|
import { cn } from "@kksh/ui/utils"
|
||||||
@ -163,7 +163,7 @@
|
|||||||
href={`https://github.com/${metadata.git.owner}/${metadata.git.repo}/tree/${metadata.git.commit}`}
|
href={`https://github.com/${metadata.git.owner}/${metadata.git.repo}/tree/${metadata.git.commit}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<Badge class="h-8 space-x-2">
|
<Badge class="h-8 space-x-2" variant="secondary">
|
||||||
<Icon class="h-6 w-6" icon="mdi:github" />
|
<Icon class="h-6 w-6" icon="mdi:github" />
|
||||||
<span>{metadata.git.owner}/{metadata.git.repo}</span>
|
<span>{metadata.git.owner}/{metadata.git.repo}</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
import { Button, Checkbox, Form, Input, Label, Select } from "@kksh/svelte5"
|
import { Button, Checkbox, Form, Input, Label, Select } from "@kksh/svelte5"
|
||||||
import { DatePickerWithPreset, Shiki } from "@kksh/ui"
|
import { DatePickerWithPreset, Shiki } from "@kksh/ui"
|
||||||
import { buildFormSchema, cn } from "@kksh/ui/utils"
|
import { buildFormSchema, cn } from "@kksh/ui/utils"
|
||||||
import { onMount } from "svelte"
|
import { onMount, tick } from "svelte"
|
||||||
import SuperDebug, { defaults, superForm } from "sveltekit-superforms"
|
import SuperDebug, { defaults, superForm } from "sveltekit-superforms"
|
||||||
import { valibot, valibotClient } from "sveltekit-superforms/adapters"
|
import { valibot, valibotClient } from "sveltekit-superforms/adapters"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
@ -20,9 +20,17 @@
|
|||||||
class?: string
|
class?: string
|
||||||
onSubmit?: (formData: Record<string, string | number | boolean>) => void
|
onSubmit?: (formData: Record<string, string | number | boolean>) => void
|
||||||
} = $props()
|
} = $props()
|
||||||
|
let formRef = $state<HTMLFormElement | null>(null)
|
||||||
const formSchema = $derived(buildFormSchema(formViewContent))
|
const formSchema = $derived(buildFormSchema(formViewContent))
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
console.log(formSchema)
|
// auto focus the first input element
|
||||||
|
const input = formRef?.querySelector("input")
|
||||||
|
setTimeout(() => {
|
||||||
|
if (input) {
|
||||||
|
input.focus()
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
})
|
})
|
||||||
const form = $derived(
|
const form = $derived(
|
||||||
superForm(defaults(valibot(formSchema)), {
|
superForm(defaults(valibot(formSchema)), {
|
||||||
@ -51,7 +59,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/snippet}
|
{/snippet}
|
||||||
{#key formViewContent}
|
{#key formViewContent}
|
||||||
<form class={cn("flex flex-col gap-2", className)} use:enhance>
|
<form class={cn("flex flex-col gap-2", className)} use:enhance bind:this={formRef}>
|
||||||
{#each formViewContent.fields as field}
|
{#each formViewContent.fields as field}
|
||||||
{@const _field = field as FormSchema.BaseField}
|
{@const _field = field as FormSchema.BaseField}
|
||||||
{#if _field.label && !_field.hideLabel}
|
{#if _field.label && !_field.hideLabel}
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (highlightedValue.startsWith("{")) {
|
if (highlightedValue.startsWith("{")) {
|
||||||
onHighlightedItemChanged?.(JSON.parse(highlightedValue).value)
|
onHighlightedItemChanged?.(highlightedValue)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -10,11 +10,13 @@
|
|||||||
let {
|
let {
|
||||||
actionPanel,
|
actionPanel,
|
||||||
open = $bindable(false),
|
open = $bindable(false),
|
||||||
onActionSelected
|
onActionSelected,
|
||||||
|
onBlur
|
||||||
}: {
|
}: {
|
||||||
actionPanel?: ActionSchema.ActionPanel
|
actionPanel?: ActionSchema.ActionPanel
|
||||||
open?: boolean
|
open?: boolean
|
||||||
onActionSelected?: (value: string) => void
|
onActionSelected?: (value: string) => void
|
||||||
|
onBlur?: () => void
|
||||||
} = $props()
|
} = $props()
|
||||||
|
|
||||||
let value = $state("")
|
let value = $state("")
|
||||||
@ -24,16 +26,19 @@
|
|||||||
// an item from the list so users can continue navigating the
|
// an item from the list so users can continue navigating the
|
||||||
// rest of the form with the keyboard.
|
// rest of the form with the keyboard.
|
||||||
function closeAndFocusTrigger() {
|
function closeAndFocusTrigger() {
|
||||||
|
console.log("closeAndFocusTrigger")
|
||||||
open = false
|
open = false
|
||||||
tick().then(() => {
|
onBlur?.()
|
||||||
triggerRef.focus()
|
// tick().then(() => {
|
||||||
})
|
// triggerRef.focus()
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Popover.Root bind:open>
|
<Popover.Root bind:open>
|
||||||
<Popover.Trigger bind:ref={triggerRef}>
|
<Popover.Trigger bind:ref={triggerRef}>
|
||||||
{#snippet child({ props })}
|
<!-- eslint-disable-next-line @typescript-eslint/no-explicit-any -->
|
||||||
|
{#snippet child({ props }: { props: any })}
|
||||||
<Button variant="ghost" class="" {...props} role="combobox" aria-expanded={open}>
|
<Button variant="ghost" class="" {...props} role="combobox" aria-expanded={open}>
|
||||||
Actions
|
Actions
|
||||||
<span class="flex items-center gap-0.5" data-tauri-drag-region>
|
<span class="flex items-center gap-0.5" data-tauri-drag-region>
|
||||||
@ -46,7 +51,14 @@
|
|||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
<Popover.Content class="w-64 p-0">
|
<Popover.Content class="w-64 p-0">
|
||||||
<Command.Root>
|
<Command.Root>
|
||||||
<Command.Input placeholder="Select an Action" />
|
<Command.Input
|
||||||
|
placeholder="Select an Action"
|
||||||
|
onkeydown={(e) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
closeAndFocusTrigger()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Command.List>
|
<Command.List>
|
||||||
<Command.Empty>No action found.</Command.Empty>
|
<Command.Empty>No action found.</Command.Empty>
|
||||||
<Command.Group>
|
<Command.Group>
|
||||||
|
@ -6,15 +6,19 @@
|
|||||||
import Kbd from "../common/Kbd.svelte"
|
import Kbd from "../common/Kbd.svelte"
|
||||||
import ActionPanel from "./ActionPanel.svelte"
|
import ActionPanel from "./ActionPanel.svelte"
|
||||||
|
|
||||||
const {
|
let {
|
||||||
class: className,
|
class: className,
|
||||||
defaultAction,
|
defaultAction,
|
||||||
actionPanel,
|
actionPanel,
|
||||||
|
actionPanelOpen = $bindable(false),
|
||||||
|
onActionPanelBlur,
|
||||||
onDefaultActionSelected,
|
onDefaultActionSelected,
|
||||||
onActionSelected
|
onActionSelected
|
||||||
}: {
|
}: {
|
||||||
class?: string
|
class?: string
|
||||||
defaultAction?: string
|
defaultAction?: string | null
|
||||||
|
actionPanelOpen?: boolean
|
||||||
|
onActionPanelBlur?: () => void
|
||||||
actionPanel?: ActionSchema.ActionPanel
|
actionPanel?: ActionSchema.ActionPanel
|
||||||
onDefaultActionSelected?: () => void
|
onDefaultActionSelected?: () => void
|
||||||
onActionSelected?: (value: string) => void
|
onActionSelected?: (value: string) => void
|
||||||
@ -40,7 +44,12 @@
|
|||||||
</Button>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if actionPanel}
|
{#if actionPanel}
|
||||||
<ActionPanel {actionPanel} {onActionSelected} />
|
<ActionPanel
|
||||||
|
{actionPanel}
|
||||||
|
{onActionSelected}
|
||||||
|
bind:open={actionPanelOpen}
|
||||||
|
onBlur={onActionPanelBlur}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user