Skip to content

Commit

Permalink
feat: add progress bar component to template worker command
Browse files Browse the repository at this point in the history
  • Loading branch information
HuakunShen committed Sep 14, 2024
1 parent 2f0ac4d commit 81c2e7a
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 72 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@
- [x] Linux
- [x] Windows

## Demo

![](https://i.imgur.com/PRuhafm.gif)
![](./README.assets/main.png)
![](./README.assets/store.png)


![Alt](https://repobeats.axiom.co/api/embed/283a4ee3d0e8777cfadc89752189164a1f9670c9.svg "Repobeats analytics image")

<a href="https://star-history.com/#kunkunsh/kunkun&Date">
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/components/ExtTemplate/ListView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { HTMLElementId } from "@/lib/constants"
import { type Remote } from "@huakunshen/comlink"
import { ListSchema, WorkerExtension } from "@kksh/api/ui/worker"
import { Button } from "@kksh/vue/button"
import { Progress } from "@kksh/vue/progress"
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@kksh/vue/resizable"
import { ArrowLeftIcon } from "@radix-icons/vue"
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
Expand Down Expand Up @@ -38,6 +39,7 @@ const props = defineProps<{
modelValue: ListSchema.List
workerAPI: Remote<WorkerExtension>
loading: boolean
pbar: number | null
class?: HTMLAttributes["class"]
}>()
Expand Down Expand Up @@ -175,6 +177,7 @@ function goBack() {
<ArrowLeftIcon />
</Button>
</CmdInput>
<Progress v-if="pbar" :model-value="pbar" class="h-[1.5px] rounded-none" />
<ResizablePanelGroup direction="horizontal" :class="props.class">
<ResizablePanel :default-size="100 - defaultDetailWidth" ref="panelRef1">
<CommandList class="h-full" @scroll="onScroll">
Expand Down
1 change: 0 additions & 1 deletion apps/desktop/layouts/default.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
import { useGoToSettingShortcuts } from "@/composables/useShortcuts"
import { useTestDB } from "@/lib/dev/exp"
import { installBun } from "@/lib/utils/runtime"
import { Toaster } from "@kksh/vue/sonner"
import { Toaster as Toaster2 } from "@kksh/vue/toast"
import { TooltipProvider } from "@kksh/vue/tooltip"
Expand Down
157 changes: 89 additions & 68 deletions apps/desktop/lib/utils/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,88 +7,109 @@ import { download } from "@tauri-apps/plugin-upload"
import { verifyUrlAlive } from "~/lib/utils/request"
import { Command, executeBashScript, type ChildProcess } from "tauri-plugin-shellx-api"

export function getInferredBunDownloadFilenameStem() {
let _platform: string = platform()
if (_platform === "macos") {
_platform = "darwin"
}
return `bun-${_platform}-${arch()}`
export async function getAppRuntimePath() {
return await join(await appDataDir(), "runtime")
}

export function getInferredBunDownloadFilename(): string {
return `${getInferredBunDownloadFilenameStem()}.zip`
}
abstract class Runtime {
abstract exePath(): Promise<string>

export function getInferredBunDownloadUrl() {
// TODO: Use a proxy or CDN to serve the download, people in China can't access GitHub :(
return `https://github.com/oven-sh/bun/releases/latest/download/${getInferredBunDownloadFilename()}`
}
abstract install(options?: { overwrite: boolean }): Promise<void>

export async function getAppRuntimePath() {
return await join(await appDataDir(), "runtime")
}
abstract installPath(): Promise<string>

export async function getBunInstallPath() {
return await join(await getAppRuntimePath(), getInferredBunDownloadFilenameStem())
}
abstract exists(): Promise<boolean>

export async function getBunExePath() {
return await join(await getBunInstallPath(), "bun")
abstract version(): Promise<string>
}

export async function bunExists(): Promise<boolean> {
return await exists(await getBunInstallPath())
}
export class Bun extends Runtime {
inferredBunDownloadFilenameStem() {
let _platform: string = platform()
if (_platform === "macos") {
_platform = "darwin"
}
return `bun-${_platform}-${arch()}`
}

export async function getBunVersion(): Promise<string> {
let bunExePath = await getBunExePath()
let output: ChildProcess<string> = await Command.create(bunExePath, ["--version"]).execute()
if (output.code !== 0) {
throw new Error(`Fail to get bun version, error: ${output.stderr}`)
async installPath() {
return join(await getAppRuntimePath(), this.inferredBunDownloadFilenameStem())
}
return output.stdout.trim()
}

/**
* Install bun to the runtime directory of appDataDir
* @param options use overwrite to force overwrite existing bun
* @returns installed bun version
*/
export async function installBun(
options: { overwrite: boolean } = { overwrite: false }
): Promise<string> {
const bunInstallPath = await getBunInstallPath()
const bunExePath = await getBunExePath()
if (await exists(bunExePath)) {
if (options.overwrite) {
await remove(bunInstallPath, { recursive: true })
async exePath() {
return join(await this.installPath(), "bun")
}

/**
* Install bun to the runtime directory of appDataDir
* @param options use overwrite to force overwrite existing bun
* @returns installed bun version
*/
async install(options: { overwrite: boolean } = { overwrite: false }) {
const bunInstallPath = await this.installPath()
const bunExePath = await this.exePath()
if (await exists(bunExePath)) {
if (options.overwrite) {
await remove(bunInstallPath, { recursive: true })
} else {
throw new Error(`Bun already exists. Version: ${await this.version()}`)
}
}
// const bunDownloadUrl = getInferredBunDownloadUrl()
const inferredBunDownloadFilename = `${this.inferredBunDownloadFilenameStem()}.zip`
const bunDownloadUrl = `https://github.com/oven-sh/bun/releases/latest/download/${inferredBunDownloadFilename}`
const urlAlive = await verifyUrlAlive(bunDownloadUrl)
if (urlAlive) {
const downloadPath = await join(await tempDir(), inferredBunDownloadFilename)
if (await exists(downloadPath)) {
await remove(downloadPath)
}
await info(`Downloading bun from ${bunDownloadUrl} to ${downloadPath}`)
await download(bunDownloadUrl, downloadPath)
await info(`Downloaded bun zip file to ${downloadPath}`)
// TODO: bash unzip only works on Linux and MacOS, need to support Windows powershell
// TODO: mac comes with unzip, but Linux doesn't, need to install unzip
await unzip(downloadPath, await getAppRuntimePath())
await info(`Unzipped bun to ${await getAppRuntimePath()}`)
// verify installation
if (!(await this.exists())) {
await error(`Fail to install bun, bun not found at ${bunInstallPath}`)
throw new Error(`Fail to install bun, bun not found at ${bunInstallPath}`)
}
await this.version()
} else {
throw new Error(`Bun already exists. Version: ${await getBunVersion()}`)
await error(`Fail to download bun, URL unavailable ${bunDownloadUrl}`)
throw new Error(`Fail to download bun, URL unavailable ${bunDownloadUrl}`)
}
}
const bunDownloadUrl = getInferredBunDownloadUrl()
const bunDownloadFilename = getInferredBunDownloadFilename()
const urlAlive = await verifyUrlAlive(bunDownloadUrl)
if (urlAlive) {
const downloadPath = await join(await tempDir(), bunDownloadFilename)
if (await exists(downloadPath)) {
await remove(downloadPath)
}
await info(`Downloading bun from ${bunDownloadUrl} to ${downloadPath}`)
await download(bunDownloadUrl, downloadPath)
await info(`Downloaded bun zip file to ${downloadPath}`)
// TODO: bash unzip only works on Linux and MacOS, need to support Windows powershell
// TODO: mac comes with unzip, but Linux doesn't, need to install unzip
await unzip(downloadPath, await getAppRuntimePath())
await info(`Unzipped bun to ${await getAppRuntimePath()}`)
// verify installation
if (!(await bunExists())) {
await error(`Fail to install bun, bun not found at ${bunInstallPath}`)
throw new Error(`Fail to install bun, bun not found at ${bunInstallPath}`)
async exists() {
return exists(await this.installPath())
}

async version() {
let bunExePath = await this.exePath()
let output: ChildProcess<string> = await Command.create(bunExePath, ["--version"]).execute()
if (output.code !== 0) {
throw new Error(`Fail to get bun version, error: ${output.stderr}`)
}
return await getBunVersion()
} else {
await error(`Fail to download bun, URL unavailable ${bunDownloadUrl}`)
throw new Error(`Fail to download bun, URL unavailable ${bunDownloadUrl}`)
return output.stdout.trim()
}
}

export class Deno extends Runtime {
override exePath(): Promise<string> {
throw new Error("Method not implemented.")
}
override install(options?: { overwrite: boolean }): Promise<void> {
throw new Error("Method not implemented.")
}
override installPath(): Promise<string> {
throw new Error("Method not implemented.")
}
override exists(): Promise<boolean> {
throw new Error("Method not implemented.")
}
override version(): Promise<string> {
throw new Error("Method not implemented.")
}
}
2 changes: 1 addition & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@kksh/desktop",
"private": true,
"type": "module",
"version": "0.1.8",
"version": "0.1.9-beta.0",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/pages/worker-ext.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const searchTerm = ref("")
const searchBarPlaceholder = ref("")
const listViewRef = ref<{ onActionSelected: () => void }>()
const loadedExt = ref<ExtPackageJsonExtra>()
const pbar = ref<number | null>(null)
let unlistenRefreshWorkerExt: UnlistenFn | undefined
function clearViewContent(keep?: "list" | "form" | "markdown") {
Expand Down Expand Up @@ -166,6 +167,9 @@ const extUiAPI: IUiWorker = {
toast.error(`Unsupported view type: ${view.nodeName}`)
}
},
async setProgressBar(progress: number | null) {
pbar.value = progress
},
async setScrollLoading(_loading: boolean) {
loading.value = _loading
},
Expand Down Expand Up @@ -319,6 +323,7 @@ onUnmounted(() => {
class=""
v-model:search-term="searchTerm"
v-model:search-bar-placeholder="searchBarPlaceholder"
:pbar="pbar"
ref="listViewRef"
:model-value="listViewContent"
:workerAPI="workerAPI!"
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/ui/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export interface IUiWorker {
setScrollLoading: (loading: boolean) => Promise<void>
setSearchTerm: (term: string) => Promise<void>
setSearchBarPlaceholder: (placeholder: string) => Promise<void>
setProgressBar: (progress: number | null) => Promise<void>
}

export interface IUiIframe {
Expand Down

0 comments on commit 81c2e7a

Please sign in to comment.