mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-04-11 17:29:44 +00:00
Feature: on boarding page (#46)
* feat: add deno install page * feat: add deno install onboarding page * feat: add ffmpeg, deno, brew install help page * feat: improve on boarding page with deno install, setting, ffmpeg install * refactor: update app configuration and onboarding flow - Improved the onboarding page layout by adding a draggable region. - Introduced a new writable store `appConfigLoaded` to track the loading status of app configuration. - Updated the main application page to subscribe to `appConfigLoaded` for better handling of onboarding logic. - Minor formatting changes in the ffmpeg installation help page for consistency.
This commit is contained in:
parent
f89cf8fe6a
commit
6ce27244a5
@ -159,6 +159,61 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
},
|
},
|
||||||
keywords: ["extension", "window", "troubleshooter"]
|
keywords: ["extension", "window", "troubleshooter"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Help (Install Deno)",
|
||||||
|
icon: {
|
||||||
|
type: IconEnum.Iconify,
|
||||||
|
value: "simple-icons:deno"
|
||||||
|
},
|
||||||
|
description: "",
|
||||||
|
function: async () => {
|
||||||
|
appState.clearSearchTerm()
|
||||||
|
goto("/app/help/deno-install")
|
||||||
|
},
|
||||||
|
keywords: ["help", "deno", "install"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Help (Install ffmpeg)",
|
||||||
|
icon: {
|
||||||
|
type: IconEnum.Iconify,
|
||||||
|
value: "logos:ffmpeg-icon"
|
||||||
|
},
|
||||||
|
description: "",
|
||||||
|
function: async () => {
|
||||||
|
appState.clearSearchTerm()
|
||||||
|
goto("/app/help/ffmpeg-install")
|
||||||
|
},
|
||||||
|
keywords: ["help", "ffmpeg", "install"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Help (Install homebrew)",
|
||||||
|
icon: {
|
||||||
|
type: IconEnum.Iconify,
|
||||||
|
value: "devicon:homebrew"
|
||||||
|
},
|
||||||
|
description: "",
|
||||||
|
function: async () => {
|
||||||
|
appState.clearSearchTerm()
|
||||||
|
goto("/app/help/brew-install")
|
||||||
|
},
|
||||||
|
keywords: ["help", "brew", "install", "homebrew"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "On Boarding (Dev Only)",
|
||||||
|
icon: {
|
||||||
|
type: IconEnum.Iconify,
|
||||||
|
value: "fluent-mdl2:onboarding"
|
||||||
|
},
|
||||||
|
description: "",
|
||||||
|
function: async () => {
|
||||||
|
appState.clearSearchTerm()
|
||||||
|
goto("/app/help/onboarding")
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
dev: true,
|
||||||
|
developer: true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Extension Permission Inspector",
|
name: "Extension Permission Inspector",
|
||||||
icon: {
|
icon: {
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "@/utils"
|
||||||
|
import { Button } from "@kksh/svelte5"
|
||||||
|
import { Shiki } from "@kksh/ui"
|
||||||
|
import { confirm } from "@tauri-apps/plugin-dialog"
|
||||||
|
import { platform } from "@tauri-apps/plugin-os"
|
||||||
|
import { toast } from "svelte-sonner"
|
||||||
|
import { writeText } from "tauri-plugin-clipboard-api"
|
||||||
|
import {
|
||||||
|
executeBashScript,
|
||||||
|
executePowershellScript,
|
||||||
|
fixPathEnv,
|
||||||
|
type ChildProcess
|
||||||
|
} from "tauri-plugin-shellx-api"
|
||||||
|
|
||||||
|
let {
|
||||||
|
code,
|
||||||
|
autoInstallable,
|
||||||
|
alreadyInstalled,
|
||||||
|
lang,
|
||||||
|
class: className,
|
||||||
|
onSuccess
|
||||||
|
}: {
|
||||||
|
code: string
|
||||||
|
autoInstallable?: boolean
|
||||||
|
alreadyInstalled?: boolean
|
||||||
|
lang: "bash" | "powershell"
|
||||||
|
class?: string
|
||||||
|
onSuccess?: () => void
|
||||||
|
} = $props()
|
||||||
|
|
||||||
|
function copy() {
|
||||||
|
return writeText(code).then(() => toast.info("Copied to clipboard", { description: code }))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function autoInstall() {
|
||||||
|
let cmd: ChildProcess<string> | undefined
|
||||||
|
if (alreadyInstalled) {
|
||||||
|
const ans = await confirm("Already installed, do you really want to run this command?")
|
||||||
|
if (!ans) return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
toast.info("Installing...")
|
||||||
|
if (platform() === "macos") {
|
||||||
|
cmd = await executeBashScript(code)
|
||||||
|
} else if (platform() === "windows") {
|
||||||
|
cmd = await executePowershellScript(code)
|
||||||
|
} else if (platform() === "linux") {
|
||||||
|
cmd = await executeBashScript(code)
|
||||||
|
} else {
|
||||||
|
return toast.error("Unsupported platform")
|
||||||
|
}
|
||||||
|
if (cmd) {
|
||||||
|
if (cmd.code === 0) {
|
||||||
|
console.log(cmd.stdout)
|
||||||
|
toast.success("Installed successfully", { description: `Status Code: ${cmd.code}` })
|
||||||
|
onSuccess?.()
|
||||||
|
} else {
|
||||||
|
console.log(cmd.stdout)
|
||||||
|
console.log(cmd.stderr)
|
||||||
|
toast.error("Failed to install", { description: cmd.stderr })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast.error("Failed to install, Unknown Error")
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast.error("Failed to install", {
|
||||||
|
description: error instanceof Error ? error.message : "Unknown Error"
|
||||||
|
})
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("flex items-center gap-2", className)}>
|
||||||
|
<Shiki class={cn("w-full overflow-x-scroll rounded-md p-1 px-2")} {code} {lang} />
|
||||||
|
<Button class="" size="sm" variant="secondary" onclick={copy}>Copy</Button>
|
||||||
|
<Button class="" size="sm" variant="secondary" onclick={autoInstall} disabled={!autoInstallable}>
|
||||||
|
Auto Install
|
||||||
|
</Button>
|
||||||
|
</div>
|
@ -0,0 +1,53 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import HotkeyPick from "@/components/standalone/settings/hotkey-pick.svelte"
|
||||||
|
import { appConfig } from "@/stores"
|
||||||
|
import { Button, Switch } from "@kksh/svelte5"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ul class="rounded-lg border">
|
||||||
|
<li>
|
||||||
|
<span>Launch at Login</span>
|
||||||
|
<Switch bind:checked={$appConfig.launchAtLogin} />
|
||||||
|
</li>
|
||||||
|
<li class="">
|
||||||
|
<span>Hotkey</span>
|
||||||
|
<HotkeyPick />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span>Menu Bar Icon</span>
|
||||||
|
<Switch bind:checked={$appConfig.showInTray} />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span>Hide On Blur</span>
|
||||||
|
<Switch bind:checked={$appConfig.hideOnBlur} />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span>Extension Auto Upgrade</span>
|
||||||
|
<Switch bind:checked={$appConfig.extensionAutoUpgrade} />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span>Dev Extension HMR</span>
|
||||||
|
<Switch bind:checked={$appConfig.hmr} />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span>Join Beta Updates</span>
|
||||||
|
<Switch bind:checked={$appConfig.joinBetaProgram} />
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<span>Developer Mode</span>
|
||||||
|
<Switch bind:checked={$appConfig.developerMode} />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
li {
|
||||||
|
@apply flex items-center justify-between border-b px-3 py-3;
|
||||||
|
}
|
||||||
|
ul li:last-child {
|
||||||
|
@apply border-b-0;
|
||||||
|
}
|
||||||
|
li > span {
|
||||||
|
@apply text-sm;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,59 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import InstallCodeBlock from "@/components/common/install-code-block.svelte"
|
||||||
|
import Icon from "@iconify/svelte"
|
||||||
|
import { IconEnum } from "@kksh/api/models"
|
||||||
|
import { Button, Tabs } from "@kksh/svelte5"
|
||||||
|
import { TauriLink } from "@kksh/ui"
|
||||||
|
import { platform } from "@tauri-apps/plugin-os"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import { toast } from "svelte-sonner"
|
||||||
|
import { whereIsCommand } from "tauri-plugin-shellx-api"
|
||||||
|
|
||||||
|
let brewPath = $state("")
|
||||||
|
let _platform = $state(platform())
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
brewPath = await whereIsCommand("brew")
|
||||||
|
})
|
||||||
|
|
||||||
|
function onInstallSuccess() {}
|
||||||
|
let alreadyInstalled = $derived(brewPath != "")
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1 class="font-mono text-2xl font-bold">Install Homebrew</h1>
|
||||||
|
<TauriLink
|
||||||
|
href="/app/help/brew-install"
|
||||||
|
icon={IconEnum.Iconify}
|
||||||
|
iconValue="devicon:homebrew"
|
||||||
|
class="flex items-center"
|
||||||
|
>
|
||||||
|
<span class="text-lg">Homebrew Website</span>
|
||||||
|
<Icon icon="devicon:homebrew" class="h-6 w-6" />
|
||||||
|
</TauriLink>
|
||||||
|
{#if _platform !== "macos"}
|
||||||
|
<p class="font-mono text-sm text-red-500">Homebrew is only available on MacOS.</p>
|
||||||
|
{/if}
|
||||||
|
{#if alreadyInstalled}
|
||||||
|
<div class="flex items-center gap-2 font-mono text-sm">
|
||||||
|
<span>✅</span>
|
||||||
|
<span>Homebrew is already installed at </span>
|
||||||
|
<pre class="text-sm">{brewPath}</pre>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="flex items-center gap-2 font-mono text-sm">
|
||||||
|
<span>❌</span>
|
||||||
|
<span>Homebrew is not installed</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<p class="font-mono text-sm">
|
||||||
|
Some extensions require Homebrew to enable advanced features. Homebrew is optional but
|
||||||
|
recommended.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code={`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`}
|
||||||
|
lang="bash"
|
||||||
|
{alreadyInstalled}
|
||||||
|
autoInstallable={!alreadyInstalled}
|
||||||
|
/>
|
@ -0,0 +1,161 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import InstallCodeBlock from "@/components/common/install-code-block.svelte"
|
||||||
|
import { goBackOnEscape } from "@/utils/key"
|
||||||
|
import { goBack } from "@/utils/route"
|
||||||
|
import { Button, Tabs } from "@kksh/svelte5"
|
||||||
|
import { platform } from "@tauri-apps/plugin-os"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import ArrowLeft from "svelte-radix/ArrowLeft.svelte"
|
||||||
|
import { toast } from "svelte-sonner"
|
||||||
|
import { whereIsCommand } from "tauri-plugin-shellx-api"
|
||||||
|
|
||||||
|
let brewPath = $state("")
|
||||||
|
let denoPath = $state("")
|
||||||
|
let cargoPath = $state("")
|
||||||
|
let scoopPath = $state("")
|
||||||
|
let chocoPath = $state("")
|
||||||
|
let wingetPath = $state("")
|
||||||
|
let _platform = $state(platform())
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
;[denoPath, brewPath, cargoPath, scoopPath, chocoPath, wingetPath] = await Promise.all([
|
||||||
|
whereIsCommand("deno"),
|
||||||
|
whereIsCommand("brew"),
|
||||||
|
whereIsCommand("cargo"),
|
||||||
|
whereIsCommand("scoop"),
|
||||||
|
whereIsCommand("choco"),
|
||||||
|
whereIsCommand("winget")
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
async function onInstallSuccess() {
|
||||||
|
denoPath = await whereIsCommand("deno")
|
||||||
|
console.log("new denoPath", denoPath)
|
||||||
|
if (!denoPath) {
|
||||||
|
toast.warning("Installation succeeds, but deno is not found in your PATH", {
|
||||||
|
description: "Please verify by yourself, and restart this app"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let alreadyInstalled = $derived(denoPath != "")
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:keydown={goBackOnEscape} />
|
||||||
|
<Button variant="outline" size="icon" onclick={goBack} class="absolute left-2 top-2">
|
||||||
|
<ArrowLeft class="size-4" />
|
||||||
|
</Button>
|
||||||
|
<h1 class="font-mono text-2xl font-bold">Install Deno</h1>
|
||||||
|
<p class="font-mono text-sm">
|
||||||
|
Some extensions require Deno to enable advanced features. Deno provides a secure, sandboxed
|
||||||
|
runtime environment for executing extension code safely. It is optional but recommended.
|
||||||
|
</p>
|
||||||
|
<p class="font-mono text-sm">Choose any installation method below.</p>
|
||||||
|
<p class="font-mono text-sm">
|
||||||
|
If you are unsure, you can use <strong class="text-lg">Auto Install</strong>.
|
||||||
|
</p>
|
||||||
|
<p class="font-mono text-sm text-red-400">
|
||||||
|
After installation, ensure the `deno` command is accessible from your system's PATH.
|
||||||
|
</p>
|
||||||
|
{#if _platform === "macos" || _platform === "linux"}
|
||||||
|
<p class="font-mono text-sm text-red-400">
|
||||||
|
Installation with <span class="font-bold text-green-500">curl</span> command likely requires manual
|
||||||
|
configuration. So auto install is disabled. Please copy the command and run it in a terminal.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
{#if denoPath}
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span>✅</span>
|
||||||
|
<span>Deno is already installed at </span>
|
||||||
|
<pre class="text-sm">{denoPath}</pre>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span>❌</span>
|
||||||
|
<span>Deno is not installed</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<Tabs.Root value={_platform} class="mt-2 w-full">
|
||||||
|
<div class="flex w-full justify-center">
|
||||||
|
<Tabs.List>
|
||||||
|
<Tabs.Trigger value="windows">Windows</Tabs.Trigger>
|
||||||
|
<Tabs.Trigger value="macos">MacOS</Tabs.Trigger>
|
||||||
|
<Tabs.Trigger value="linux">Linux</Tabs.Trigger>
|
||||||
|
</Tabs.List>
|
||||||
|
</div>
|
||||||
|
<Tabs.Content value="macos" class="space-y-2">
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code="curl -fsSL https://deno.land/install.sh | sh"
|
||||||
|
lang="bash"
|
||||||
|
{alreadyInstalled}
|
||||||
|
/>
|
||||||
|
{#if brewPath}
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code="brew install deno"
|
||||||
|
lang="bash"
|
||||||
|
{alreadyInstalled}
|
||||||
|
autoInstallable={true}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</Tabs.Content>
|
||||||
|
<Tabs.Content value="windows" class="space-y-2">
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code="irm https://deno.land/install.ps1 | iex"
|
||||||
|
lang="bash"
|
||||||
|
{alreadyInstalled}
|
||||||
|
/>
|
||||||
|
{#if scoopPath}
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code="scoop install deno"
|
||||||
|
lang="bash"
|
||||||
|
autoInstallable={true}
|
||||||
|
{alreadyInstalled}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{#if chocoPath}
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code="choco install deno"
|
||||||
|
lang="bash"
|
||||||
|
autoInstallable={true}
|
||||||
|
{alreadyInstalled}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{#if wingetPath}
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code="winget install deno"
|
||||||
|
lang="bash"
|
||||||
|
autoInstallable={true}
|
||||||
|
{alreadyInstalled}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</Tabs.Content>
|
||||||
|
<Tabs.Content value="linux" class="space-y-2">
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code="curl -fsSL https://deno.land/install.sh | sh"
|
||||||
|
lang="bash"
|
||||||
|
{alreadyInstalled}
|
||||||
|
/>
|
||||||
|
</Tabs.Content>
|
||||||
|
</Tabs.Root>
|
||||||
|
{#if cargoPath}
|
||||||
|
<p class="mt-2 font-mono text-sm">
|
||||||
|
Seeing this message means `cargo` is detected and you are a programmer. `cargo install` allows
|
||||||
|
you to install `deno` from rust source code. But rust compiles super slow (a few minutes), so
|
||||||
|
auto install is disabled. If you really want to use this method, please copy the command and run
|
||||||
|
it in a terminal.
|
||||||
|
</p>
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
class="mt-2"
|
||||||
|
code="cargo install deno --locked"
|
||||||
|
lang="bash"
|
||||||
|
{alreadyInstalled}
|
||||||
|
/>
|
||||||
|
{/if}
|
@ -0,0 +1,121 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import InstallCodeBlock from "@/components/common/install-code-block.svelte"
|
||||||
|
import Icon from "@iconify/svelte"
|
||||||
|
import { IconEnum } from "@kksh/api/models"
|
||||||
|
import { Button, Tabs } from "@kksh/svelte5"
|
||||||
|
import { TauriLink } from "@kksh/ui"
|
||||||
|
import { platform } from "@tauri-apps/plugin-os"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import { toast } from "svelte-sonner"
|
||||||
|
import { whereIsCommand } from "tauri-plugin-shellx-api"
|
||||||
|
|
||||||
|
let brewPath = $state("")
|
||||||
|
let chocoPath = $state("")
|
||||||
|
let ffmpegPath = $state("")
|
||||||
|
let aptPath = $state("")
|
||||||
|
let _platform = $state(platform())
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
;[ffmpegPath, brewPath, chocoPath, aptPath] = await Promise.all([
|
||||||
|
whereIsCommand("ffmpeg"),
|
||||||
|
whereIsCommand("brew"),
|
||||||
|
whereIsCommand("choco"),
|
||||||
|
whereIsCommand("apt")
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
function onInstallSuccess() {}
|
||||||
|
let alreadyInstalled = $derived(ffmpegPath != "")
|
||||||
|
|
||||||
|
let command = {
|
||||||
|
macos: "brew install ffmpeg",
|
||||||
|
windows: "choco install ffmpeg",
|
||||||
|
linux: `sudo apt update && sudo apt upgrade && sudo apt install ffmpeg`
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1 class="font-mono text-2xl font-bold">Install ffmpeg</h1>
|
||||||
|
<p class="font-mono text-sm">
|
||||||
|
Some extensions require ffmpeg to enable advanced features. ffmpeg is optional but recommended.
|
||||||
|
</p>
|
||||||
|
<p class="font-mono text-sm">
|
||||||
|
For example, the YouTube video downloader extension requires `ffmpeg` to merge audio and video;
|
||||||
|
`ffmpeg` is also used in video processing extensions.
|
||||||
|
</p>
|
||||||
|
{#if alreadyInstalled}
|
||||||
|
<div class="flex items-center gap-2 font-mono text-sm">
|
||||||
|
<span>✅</span>
|
||||||
|
<span>ffmpeg is already installed at </span>
|
||||||
|
<pre class="text-sm">{ffmpegPath}</pre>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="flex items-center gap-2 font-mono text-sm">
|
||||||
|
<span>❌</span>
|
||||||
|
<span>ffmpeg is not installed</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<TauriLink
|
||||||
|
href="/app/help/ffmpeg-install"
|
||||||
|
icon={IconEnum.Iconify}
|
||||||
|
iconValue="logos:ffmpeg-icon"
|
||||||
|
class="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<span class="font-mono text-lg font-bold">ffmpeg Website</span>
|
||||||
|
<Icon icon="logos:ffmpeg-icon" class="h-6 w-6" />
|
||||||
|
</TauriLink>
|
||||||
|
<p class="font-mono text-sm">
|
||||||
|
You can install ffmpeg from the official website, but it's much easier if you use a package
|
||||||
|
manager of your platform.
|
||||||
|
</p>
|
||||||
|
<Tabs.Root value={_platform} class="mt-2 w-full">
|
||||||
|
<div class="flex w-full justify-center">
|
||||||
|
<Tabs.List>
|
||||||
|
<Tabs.Trigger value="windows">Windows</Tabs.Trigger>
|
||||||
|
<Tabs.Trigger value="macos">MacOS</Tabs.Trigger>
|
||||||
|
<Tabs.Trigger value="linux">Linux</Tabs.Trigger>
|
||||||
|
</Tabs.List>
|
||||||
|
</div>
|
||||||
|
<Tabs.Content value="macos" class="space-y-2">
|
||||||
|
{#if !brewPath}
|
||||||
|
<p class="font-mono text-sm text-red-400">
|
||||||
|
Homebrew is not installed. Please install Homebrew first.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code={command.macos}
|
||||||
|
lang="bash"
|
||||||
|
{alreadyInstalled}
|
||||||
|
autoInstallable={true}
|
||||||
|
/>
|
||||||
|
</Tabs.Content>
|
||||||
|
<Tabs.Content value="windows" class="space-y-2">
|
||||||
|
{#if !chocoPath}
|
||||||
|
<p class="font-mono text-sm text-red-400">
|
||||||
|
Chocolatey is not installed. Please install Chocolatey first.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code={command.windows}
|
||||||
|
lang="bash"
|
||||||
|
{alreadyInstalled}
|
||||||
|
/>
|
||||||
|
</Tabs.Content>
|
||||||
|
<Tabs.Content value="linux" class="space-y-2">
|
||||||
|
{#if !aptPath}
|
||||||
|
<p class="font-mono text-sm text-red-400">
|
||||||
|
`apt` is not installed. Please install `apt` first.
|
||||||
|
</p>
|
||||||
|
<p class="font-mono text-sm text-red-400">
|
||||||
|
If you are on a different distro, I believe you can figure it out as a Linux user.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
<InstallCodeBlock
|
||||||
|
onSuccess={onInstallSuccess}
|
||||||
|
code={command.linux}
|
||||||
|
lang="bash"
|
||||||
|
{alreadyInstalled}
|
||||||
|
/>
|
||||||
|
</Tabs.Content>
|
||||||
|
</Tabs.Root>
|
@ -5,7 +5,7 @@ import { PersistedAppConfig, type AppConfig } from "@kksh/types"
|
|||||||
import { debug, error } from "@tauri-apps/plugin-log"
|
import { debug, error } from "@tauri-apps/plugin-log"
|
||||||
import * as os from "@tauri-apps/plugin-os"
|
import * as os from "@tauri-apps/plugin-os"
|
||||||
import { load } from "@tauri-apps/plugin-store"
|
import { load } from "@tauri-apps/plugin-store"
|
||||||
import { get } from "svelte/store"
|
import { get, writable } from "svelte/store"
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
|
|
||||||
export const defaultAppConfig: AppConfig = {
|
export const defaultAppConfig: AppConfig = {
|
||||||
@ -29,12 +29,15 @@ export const defaultAppConfig: AppConfig = {
|
|||||||
developerMode: false
|
developerMode: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const appConfigLoaded = writable(false)
|
||||||
|
|
||||||
interface AppConfigAPI {
|
interface AppConfigAPI {
|
||||||
init: () => Promise<void>
|
init: () => Promise<void>
|
||||||
get: () => AppConfig
|
get: () => AppConfig
|
||||||
setTheme: (theme: ThemeConfig) => void
|
setTheme: (theme: ThemeConfig) => void
|
||||||
setDevExtensionPath: (devExtensionPath: string | null) => void
|
setDevExtensionPath: (devExtensionPath: string | null) => void
|
||||||
setTriggerHotkey: (triggerHotkey: string[]) => void
|
setTriggerHotkey: (triggerHotkey: string[]) => void
|
||||||
|
setOnBoarded: (onBoarded: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAppConfig(): WithSyncStore<AppConfig> & AppConfigAPI {
|
function createAppConfig(): WithSyncStore<AppConfig> & AppConfigAPI {
|
||||||
@ -61,7 +64,7 @@ function createAppConfig(): WithSyncStore<AppConfig> & AppConfigAPI {
|
|||||||
await persistStore.clear()
|
await persistStore.clear()
|
||||||
await persistStore.set("config", v.parse(PersistedAppConfig, defaultAppConfig))
|
await persistStore.set("config", v.parse(PersistedAppConfig, defaultAppConfig))
|
||||||
}
|
}
|
||||||
|
appConfigLoaded.set(true)
|
||||||
store.subscribe(async (config) => {
|
store.subscribe(async (config) => {
|
||||||
console.log("Saving app config", config)
|
console.log("Saving app config", config)
|
||||||
await persistStore.set("config", config)
|
await persistStore.set("config", config)
|
||||||
@ -80,6 +83,9 @@ function createAppConfig(): WithSyncStore<AppConfig> & AppConfigAPI {
|
|||||||
setTriggerHotkey: (triggerHotkey: string[]) => {
|
setTriggerHotkey: (triggerHotkey: string[]) => {
|
||||||
store.update((config) => ({ ...config, triggerHotkey }))
|
store.update((config) => ({ ...config, triggerHotkey }))
|
||||||
},
|
},
|
||||||
|
setOnBoarded: (onBoarded: boolean) => {
|
||||||
|
store.update((config) => ({ ...config, onBoarded }))
|
||||||
|
},
|
||||||
init
|
init
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@
|
|||||||
|
|
||||||
<svelte:window on:keydown={globalKeyDownHandler} />
|
<svelte:window on:keydown={globalKeyDownHandler} />
|
||||||
<ViewTransition />
|
<ViewTransition />
|
||||||
<Toaster richColors />
|
<Toaster richColors closeButton />
|
||||||
<AppContext {appConfig} {appState}>
|
<AppContext {appConfig} {appState}>
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</AppContext>
|
</AppContext>
|
||||||
|
@ -3,7 +3,14 @@
|
|||||||
import { commandLaunchers } from "@/cmds"
|
import { commandLaunchers } from "@/cmds"
|
||||||
import { builtinCmds } from "@/cmds/builtin"
|
import { builtinCmds } from "@/cmds/builtin"
|
||||||
import { systemCommands } from "@/cmds/system"
|
import { systemCommands } from "@/cmds/system"
|
||||||
import { appConfig, appState, devStoreExts, installedStoreExts, quickLinks } from "@/stores"
|
import {
|
||||||
|
appConfig,
|
||||||
|
appConfigLoaded,
|
||||||
|
appState,
|
||||||
|
devStoreExts,
|
||||||
|
installedStoreExts,
|
||||||
|
quickLinks
|
||||||
|
} from "@/stores"
|
||||||
import { cmdQueries } from "@/stores/cmdQuery"
|
import { cmdQueries } from "@/stores/cmdQuery"
|
||||||
import { isKeyboardEventFromInputElement } from "@/utils/dom"
|
import { isKeyboardEventFromInputElement } from "@/utils/dom"
|
||||||
import Icon from "@iconify/svelte"
|
import Icon from "@iconify/svelte"
|
||||||
@ -22,6 +29,7 @@
|
|||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
||||||
import { getCurrentWindow, Window } from "@tauri-apps/api/window"
|
import { getCurrentWindow, Window } from "@tauri-apps/api/window"
|
||||||
import { exit } from "@tauri-apps/plugin-process"
|
import { exit } from "@tauri-apps/plugin-process"
|
||||||
|
import { goto } from "$app/navigation"
|
||||||
import { ArrowBigUpIcon, CircleXIcon, EllipsisVerticalIcon, RefreshCcwIcon } from "lucide-svelte"
|
import { ArrowBigUpIcon, CircleXIcon, EllipsisVerticalIcon, RefreshCcwIcon } from "lucide-svelte"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
@ -36,12 +44,23 @@
|
|||||||
onMount(() => {
|
onMount(() => {
|
||||||
Promise.all([Window.getByLabel("splashscreen"), getCurrentWindow()]).then(
|
Promise.all([Window.getByLabel("splashscreen"), getCurrentWindow()]).then(
|
||||||
([splashscreenWin, mainWin]) => {
|
([splashscreenWin, mainWin]) => {
|
||||||
mainWin.show()
|
|
||||||
if (splashscreenWin) {
|
if (splashscreenWin) {
|
||||||
splashscreenWin.close()
|
splashscreenWin.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mainWin.show()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
appConfigLoaded.subscribe((loaded) => {
|
||||||
|
// wait for appConfig store to be loaded, it's async and saved to disk when changed, so we use another store appConfigLoaded
|
||||||
|
// to keep track of the loading status
|
||||||
|
if (loaded) {
|
||||||
|
if (!appConfig.get().onBoarded) {
|
||||||
|
goto("/app/help/onboarding")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
16
apps/desktop/src/routes/app/help/brew-install/+page.svelte
Normal file
16
apps/desktop/src/routes/app/help/brew-install/+page.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import BrewInstall from "@/components/standalone/help/brew-install.svelte"
|
||||||
|
import { goBackOnEscape } from "@/utils/key"
|
||||||
|
import { goBack } from "@/utils/route"
|
||||||
|
import { Button } from "@kksh/svelte5"
|
||||||
|
import ArrowLeft from "svelte-radix/ArrowLeft.svelte"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:keydown={goBackOnEscape} />
|
||||||
|
<Button variant="outline" size="icon" onclick={goBack} class="absolute left-2 top-2">
|
||||||
|
<ArrowLeft class="size-4" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<main class="container pt-12">
|
||||||
|
<BrewInstall />
|
||||||
|
</main>
|
15
apps/desktop/src/routes/app/help/deno-install/+page.svelte
Normal file
15
apps/desktop/src/routes/app/help/deno-install/+page.svelte
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import DenoInstall from "@/components/standalone/help/deno-install.svelte"
|
||||||
|
import { goBackOnEscape } from "@/utils/key"
|
||||||
|
import { goBack } from "@/utils/route"
|
||||||
|
import { Button } from "@kksh/svelte5"
|
||||||
|
import ArrowLeft from "svelte-radix/ArrowLeft.svelte"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:keydown={goBackOnEscape} />
|
||||||
|
<Button variant="outline" size="icon" onclick={goBack} class="absolute left-2 top-2">
|
||||||
|
<ArrowLeft class="size-4" />
|
||||||
|
</Button>
|
||||||
|
<main class="container pt-12">
|
||||||
|
<DenoInstall />
|
||||||
|
</main>
|
15
apps/desktop/src/routes/app/help/ffmpeg-install/+page.svelte
Normal file
15
apps/desktop/src/routes/app/help/ffmpeg-install/+page.svelte
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FFmpegInstall from "@/components/standalone/help/ffmpeg-install.svelte"
|
||||||
|
import { goBackOnEscape } from "@/utils/key"
|
||||||
|
import { goBack } from "@/utils/route"
|
||||||
|
import { Button } from "@kksh/svelte5"
|
||||||
|
import ArrowLeft from "svelte-radix/ArrowLeft.svelte"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:keydown={goBackOnEscape} />
|
||||||
|
<Button variant="outline" size="icon" onclick={goBack} class="absolute left-2 top-2">
|
||||||
|
<ArrowLeft class="size-4" />
|
||||||
|
</Button>
|
||||||
|
<main class="container pt-12">
|
||||||
|
<FFmpegInstall />
|
||||||
|
</main>
|
64
apps/desktop/src/routes/app/help/onboarding/+page.svelte
Normal file
64
apps/desktop/src/routes/app/help/onboarding/+page.svelte
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import GeneralSettings from "@/components/standalone/general-settings.svelte"
|
||||||
|
import DenoInstall from "@/components/standalone/help/deno-install.svelte"
|
||||||
|
import FFmpegInstall from "@/components/standalone/help/ffmpeg-install.svelte"
|
||||||
|
import { appConfig } from "@/stores/appConfig"
|
||||||
|
import { Button } from "@kksh/svelte5"
|
||||||
|
import { goto } from "$app/navigation"
|
||||||
|
import { ArrowRightIcon } from "lucide-svelte"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import { fade } from "svelte/transition"
|
||||||
|
import { whereIsCommand } from "tauri-plugin-shellx-api"
|
||||||
|
import { Step } from "./steps"
|
||||||
|
|
||||||
|
let step = $state(0)
|
||||||
|
let denoPath = $state("")
|
||||||
|
let ffmpegPath = $state("")
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
denoPath = await whereIsCommand("deno")
|
||||||
|
ffmpegPath = await whereIsCommand("ffmpeg")
|
||||||
|
})
|
||||||
|
|
||||||
|
function nextStep() {
|
||||||
|
step++
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (step === Step.DenoInstall) {
|
||||||
|
if (denoPath) {
|
||||||
|
step++
|
||||||
|
}
|
||||||
|
} else if (step === Step.FFmpegInstall) {
|
||||||
|
if (ffmpegPath) {
|
||||||
|
step++
|
||||||
|
}
|
||||||
|
} else if (step > Step.FFmpegInstall) {
|
||||||
|
appConfig.setOnBoarded(true)
|
||||||
|
goto("/app")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
<div class="left-0 top-0 h-10 w-full" data-tauri-drag-region></div>
|
||||||
|
{#if step === Step.Welcome}
|
||||||
|
<h1 class="text-3xl font-bold">Welcome to Kunkun</h1>
|
||||||
|
<p>
|
||||||
|
This is a on boarding page to help you set up Kunkun with some basic settings and optional
|
||||||
|
dependencies.
|
||||||
|
</p>
|
||||||
|
<div>Click <strong>Next</strong> to continue</div>
|
||||||
|
{:else if step === Step.GeneralSettings}
|
||||||
|
<h1 class="text-2xl font-bold">General Settings</h1>
|
||||||
|
<small> You can change these settings later in the settings page. </small>
|
||||||
|
<GeneralSettings />
|
||||||
|
{:else if step === Step.DenoInstall}
|
||||||
|
<DenoInstall />
|
||||||
|
{:else if step === Step.FFmpegInstall}
|
||||||
|
<FFmpegInstall />
|
||||||
|
{/if}
|
||||||
|
</main>
|
||||||
|
<Button class="fixed bottom-4 right-4" variant="outline" size="icon" onclick={nextStep}>
|
||||||
|
<ArrowRightIcon />
|
||||||
|
</Button>
|
6
apps/desktop/src/routes/app/help/onboarding/steps.ts
Normal file
6
apps/desktop/src/routes/app/help/onboarding/steps.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export enum Step {
|
||||||
|
Welcome = 0,
|
||||||
|
GeneralSettings = 1,
|
||||||
|
DenoInstall = 2,
|
||||||
|
FFmpegInstall = 3
|
||||||
|
}
|
@ -1,70 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import HotkeyInputPopover from "@/components/common/HotkeyInputPopover.svelte"
|
import GeneralSettings from "@/components/standalone/general-settings.svelte"
|
||||||
import HotkeyPick from "@/components/standalone/settings/hotkey-pick.svelte"
|
|
||||||
import { appConfig } from "@/stores"
|
|
||||||
import { Button, Switch } from "@kksh/svelte5"
|
|
||||||
import { Shiki } from "@kksh/ui"
|
|
||||||
import { dev } from "$app/environment"
|
|
||||||
import { onMount, type Snippet } from "svelte"
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="container flex flex-col space-y-2">
|
<main class="container flex flex-col space-y-2">
|
||||||
<!-- <h3 class="text-mg mb-2 ml-3 font-bold">App Updates</h3> -->
|
<GeneralSettings />
|
||||||
<ul class="rounded-lg border">
|
|
||||||
<li>
|
|
||||||
<span>Launch at Login</span>
|
|
||||||
<Switch bind:checked={$appConfig.launchAtLogin} />
|
|
||||||
</li>
|
|
||||||
<li class="">
|
|
||||||
<span>Hotkey</span>
|
|
||||||
<!-- <HotkeyInput bind:keys={$appConfig.triggerHotkey} /> -->
|
|
||||||
<!-- <HotkeyInputPopover class="" /> -->
|
|
||||||
<HotkeyPick />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span>Menu Bar Icon</span>
|
|
||||||
<Switch bind:checked={$appConfig.showInTray} />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span>Hide On Blur</span>
|
|
||||||
<Switch bind:checked={$appConfig.hideOnBlur} />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span>Extension Auto Upgrade</span>
|
|
||||||
<Switch bind:checked={$appConfig.extensionAutoUpgrade} />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span>Dev Extension HMR</span>
|
|
||||||
<Switch bind:checked={$appConfig.hmr} />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span>Join Beta Updates</span>
|
|
||||||
<Switch bind:checked={$appConfig.joinBetaProgram} />
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<span>Developer Mode</span>
|
|
||||||
<Switch bind:checked={$appConfig.developerMode} />
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<!-- <li>
|
|
||||||
<span>Language</span>
|
|
||||||
<Switch bind:checked={$appConfig} />
|
|
||||||
</li> -->
|
|
||||||
</ul>
|
|
||||||
<!-- {#if dev}
|
|
||||||
<Shiki class="w-full overflow-x-auto" lang="json" code={JSON.stringify($appConfig, null, 2)} />
|
|
||||||
{/if} -->
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
li {
|
|
||||||
@apply flex items-center justify-between border-b px-3 py-3;
|
|
||||||
}
|
|
||||||
ul li:last-child {
|
|
||||||
@apply border-b-0;
|
|
||||||
}
|
|
||||||
li > span {
|
|
||||||
@apply text-sm;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
## Permission Table
|
## Permission Table
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
@ -6,6 +7,7 @@
|
|||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
"@internationalized/date": "^3.6.0",
|
"@internationalized/date": "^3.6.0",
|
||||||
"@std/semver": "npm:@jsr/std__semver@^1.0.3",
|
"@std/semver": "npm:@jsr/std__semver@^1.0.3",
|
||||||
"gsap": "^3.12.5",
|
"gsap": "^3.12.5",
|
||||||
|
"shiki-magic-move": "^0.5.2",
|
||||||
"svelte-markdown": "^0.4.1"
|
"svelte-markdown": "^0.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,49 +2,39 @@
|
|||||||
<!-- https://shiki.style/guide/bundles#fine-grained-bundle -->
|
<!-- https://shiki.style/guide/bundles#fine-grained-bundle -->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { cn } from "@kksh/ui/utils"
|
import { cn } from "@kksh/ui/utils"
|
||||||
import { mode } from "mode-watcher"
|
import { getSingletonHighlighter } from "shiki"
|
||||||
import { createHighlighterCore, type HighlighterCore } from "shiki/core"
|
import { ShikiMagicMove } from "shiki-magic-move/svelte"
|
||||||
import { createOnigurumaEngine } from "shiki/engine/oniguruma"
|
import "shiki-magic-move/dist/style.css"
|
||||||
import { onMount } from "svelte"
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
code,
|
code,
|
||||||
lang,
|
lang,
|
||||||
theme,
|
theme,
|
||||||
|
lineNumbers,
|
||||||
class: className
|
class: className
|
||||||
}: {
|
}: {
|
||||||
code: string
|
code: string
|
||||||
lang: "json" | "typescript"
|
lang: "json" | "typescript" | "bash" | "powershell"
|
||||||
theme?: "vitesse-dark" | "vitesse-light"
|
theme?: "vitesse-dark" | "vitesse-light"
|
||||||
|
lineNumbers?: boolean
|
||||||
class?: string
|
class?: string
|
||||||
} = $props()
|
} = $props()
|
||||||
let html = $state("")
|
|
||||||
let highlighter: HighlighterCore
|
|
||||||
|
|
||||||
function refresh() {
|
const highlighter2 = getSingletonHighlighter({
|
||||||
html = highlighter.codeToHtml(code, {
|
themes: ["vitesse-dark", "vitesse-light"],
|
||||||
lang,
|
langs: ["typescript", "bash", "powershell", "json"]
|
||||||
theme: theme ?? ($mode === "dark" ? "vitesse-dark" : "vitesse-light")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onMount(async () => {
|
|
||||||
highlighter = await createHighlighterCore({
|
|
||||||
themes: [import("shiki/themes/vitesse-dark.mjs"), import("shiki/themes/vitesse-light.mjs")],
|
|
||||||
langs: [import("shiki/langs/json.mjs"), import("shiki/langs/typescript.mjs")],
|
|
||||||
engine: createOnigurumaEngine(import("shiki/wasm"))
|
|
||||||
})
|
|
||||||
refresh()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
$effect(() => {
|
let code2 = $state(`const hello = 'world'`)
|
||||||
code // keep this here to watch for code changes
|
|
||||||
highlighter
|
|
||||||
if (highlighter) {
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={cn("", className)}>
|
{#await highlighter2 then highlighter}
|
||||||
{@html html}
|
<ShikiMagicMove
|
||||||
</div>
|
class={cn("", className)}
|
||||||
|
{lang}
|
||||||
|
theme={theme ?? "vitesse-dark"}
|
||||||
|
{highlighter}
|
||||||
|
{code}
|
||||||
|
options={{ duration: 800, stagger: 0.3, lineNumbers: lineNumbers ?? false }}
|
||||||
|
/>
|
||||||
|
{/await}
|
||||||
|
74
pnpm-lock.yaml
generated
74
pnpm-lock.yaml
generated
@ -1091,6 +1091,9 @@ importers:
|
|||||||
gsap:
|
gsap:
|
||||||
specifier: ^3.12.5
|
specifier: ^3.12.5
|
||||||
version: 3.12.5
|
version: 3.12.5
|
||||||
|
shiki-magic-move:
|
||||||
|
specifier: ^0.5.2
|
||||||
|
version: 0.5.2(react@18.3.1)(shiki@1.24.2)(svelte@5.16.2)(vue@3.5.13(typescript@5.7.2))
|
||||||
svelte-markdown:
|
svelte-markdown:
|
||||||
specifier: ^0.4.1
|
specifier: ^0.4.1
|
||||||
version: 0.4.1(svelte@5.16.2)
|
version: 0.4.1(svelte@5.16.2)
|
||||||
@ -6059,6 +6062,9 @@ packages:
|
|||||||
didyoumean@1.2.2:
|
didyoumean@1.2.2:
|
||||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||||
|
|
||||||
|
diff-match-patch-es@0.1.1:
|
||||||
|
resolution: {integrity: sha512-+wE0HYKRuRdfsnpEFh41kTd0GlYFSDQacz2bQ4dwMDvYGtofqtYdJ6Gl4ZOgUPqPi7v8LSqMY0+/OedmIPHBZw==}
|
||||||
|
|
||||||
diff@7.0.0:
|
diff@7.0.0:
|
||||||
resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==}
|
resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==}
|
||||||
engines: {node: '>=0.3.1'}
|
engines: {node: '>=0.3.1'}
|
||||||
@ -9175,6 +9181,26 @@ packages:
|
|||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
shiki-magic-move@0.5.2:
|
||||||
|
resolution: {integrity: sha512-Y5EHPD+IPiUUFFMEKu6RE8wELsKp8CYgf420Z+EXVljOvyBakiR9rjt/1Cm0VcSr9rkyQANw6fTE1PqcNOnAGA==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18.2.0 || ^19.0.0
|
||||||
|
shiki: ^1.1.6
|
||||||
|
solid-js: ^1.9.1
|
||||||
|
svelte: ^5.0.0-0
|
||||||
|
vue: ^3.4.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react:
|
||||||
|
optional: true
|
||||||
|
shiki:
|
||||||
|
optional: true
|
||||||
|
solid-js:
|
||||||
|
optional: true
|
||||||
|
svelte:
|
||||||
|
optional: true
|
||||||
|
vue:
|
||||||
|
optional: true
|
||||||
|
|
||||||
shiki@1.24.2:
|
shiki@1.24.2:
|
||||||
resolution: {integrity: sha512-TR1fi6mkRrzW+SKT5G6uKuc32Dj2EEa7Kj0k8kGqiBINb+C1TiflVOiT9ta6GqOJtC4fraxO5SLUaKBcSY38Fg==}
|
resolution: {integrity: sha512-TR1fi6mkRrzW+SKT5G6uKuc32Dj2EEa7Kj0k8kGqiBINb+C1TiflVOiT9ta6GqOJtC4fraxO5SLUaKBcSY38Fg==}
|
||||||
|
|
||||||
@ -15653,6 +15679,13 @@ snapshots:
|
|||||||
'@vue/shared': 3.5.13
|
'@vue/shared': 3.5.13
|
||||||
vue: 3.5.13(typescript@5.6.3)
|
vue: 3.5.13(typescript@5.6.3)
|
||||||
|
|
||||||
|
'@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.7.2))':
|
||||||
|
dependencies:
|
||||||
|
'@vue/compiler-ssr': 3.5.13
|
||||||
|
'@vue/shared': 3.5.13
|
||||||
|
vue: 3.5.13(typescript@5.7.2)
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@vue/shared@3.5.13': {}
|
'@vue/shared@3.5.13': {}
|
||||||
|
|
||||||
'@vueuse/core@10.11.1(vue@3.5.13(typescript@5.6.3))':
|
'@vueuse/core@10.11.1(vue@3.5.13(typescript@5.6.3))':
|
||||||
@ -16830,6 +16863,8 @@ snapshots:
|
|||||||
|
|
||||||
didyoumean@1.2.2: {}
|
didyoumean@1.2.2: {}
|
||||||
|
|
||||||
|
diff-match-patch-es@0.1.1: {}
|
||||||
|
|
||||||
diff@7.0.0: {}
|
diff@7.0.0: {}
|
||||||
|
|
||||||
dir-glob@3.0.1:
|
dir-glob@3.0.1:
|
||||||
@ -17157,8 +17192,8 @@ snapshots:
|
|||||||
'@typescript-eslint/parser': 8.15.0(eslint@8.57.1)(typescript@5.6.3)
|
'@typescript-eslint/parser': 8.15.0(eslint@8.57.1)(typescript@5.6.3)
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||||
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
|
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
|
||||||
eslint-plugin-react: 7.37.2(eslint@8.57.1)
|
eslint-plugin-react: 7.37.2(eslint@8.57.1)
|
||||||
eslint-plugin-react-hooks: 5.0.0(eslint@8.57.1)
|
eslint-plugin-react-hooks: 5.0.0(eslint@8.57.1)
|
||||||
@ -17186,37 +17221,37 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1):
|
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nolyfill/is-core-module': 1.0.39
|
'@nolyfill/is-core-module': 1.0.39
|
||||||
debug: 4.3.7
|
debug: 4.3.7
|
||||||
enhanced-resolve: 5.17.1
|
enhanced-resolve: 5.17.1
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||||
fast-glob: 3.3.2
|
fast-glob: 3.3.2
|
||||||
get-tsconfig: 4.8.1
|
get-tsconfig: 4.8.1
|
||||||
is-bun-module: 1.2.1
|
is-bun-module: 1.2.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@typescript-eslint/parser'
|
- '@typescript-eslint/parser'
|
||||||
- eslint-import-resolver-node
|
- eslint-import-resolver-node
|
||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
|
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.15.0(eslint@8.57.1)(typescript@5.6.3)
|
'@typescript-eslint/parser': 8.15.0(eslint@8.57.1)(typescript@5.6.3)
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
|
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rtsao/scc': 1.1.0
|
'@rtsao/scc': 1.1.0
|
||||||
array-includes: 3.1.8
|
array-includes: 3.1.8
|
||||||
@ -17227,7 +17262,7 @@ snapshots:
|
|||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 8.57.1
|
eslint: 8.57.1
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
|
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
is-core-module: 2.15.1
|
is-core-module: 2.15.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@ -20464,6 +20499,16 @@ snapshots:
|
|||||||
interpret: 1.4.0
|
interpret: 1.4.0
|
||||||
rechoir: 0.6.2
|
rechoir: 0.6.2
|
||||||
|
|
||||||
|
shiki-magic-move@0.5.2(react@18.3.1)(shiki@1.24.2)(svelte@5.16.2)(vue@3.5.13(typescript@5.7.2)):
|
||||||
|
dependencies:
|
||||||
|
diff-match-patch-es: 0.1.1
|
||||||
|
ohash: 1.1.4
|
||||||
|
optionalDependencies:
|
||||||
|
react: 18.3.1
|
||||||
|
shiki: 1.24.2
|
||||||
|
svelte: 5.16.2
|
||||||
|
vue: 3.5.13(typescript@5.7.2)
|
||||||
|
|
||||||
shiki@1.24.2:
|
shiki@1.24.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/core': 1.24.2
|
'@shikijs/core': 1.24.2
|
||||||
@ -22130,6 +22175,17 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.6.3
|
typescript: 5.6.3
|
||||||
|
|
||||||
|
vue@3.5.13(typescript@5.7.2):
|
||||||
|
dependencies:
|
||||||
|
'@vue/compiler-dom': 3.5.13
|
||||||
|
'@vue/compiler-sfc': 3.5.13
|
||||||
|
'@vue/runtime-dom': 3.5.13
|
||||||
|
'@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.7.2))
|
||||||
|
'@vue/shared': 3.5.13
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.7.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
walkdir@0.4.1: {}
|
walkdir@0.4.1: {}
|
||||||
|
|
||||||
wcwidth@1.0.1:
|
wcwidth@1.0.1:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user