mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-04-16 03:34:35 +00:00
feature: splashscreen (#36)
* feat: move all pages to app folder, add splashscreen * feat: use Dance as splashscreen * feat: add zoom in for splashscreen logo * refactor: move svelte files into app folder * fix: url prefix with /app * refactor: remove platform-specific tauri conf Merge windows back to main tauri config. The reason I separated them was because I need decoration: true on mac and false on windows and linux. Now I use tauri rust API to set decorations to false for win and linux.
This commit is contained in:
parent
80ad705f7c
commit
caa252b4dd
@ -2,7 +2,7 @@
|
|||||||
"$schema": "../gen/schemas/desktop-schema.json",
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
"identifier": "default",
|
"identifier": "default",
|
||||||
"description": "Capability for the main window",
|
"description": "Capability for the main window",
|
||||||
"windows": ["main*"],
|
"windows": ["main*", "splashscreen"],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"identifier": "http:default",
|
"identifier": "http:default",
|
||||||
|
@ -53,7 +53,19 @@ impl<R: Runtime> WindowExt for WebviewWindow<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_window<R: Runtime>(app: &AppHandle<R>) {
|
pub fn setup_window<R: Runtime>(app: &AppHandle<R>) {
|
||||||
let window = app.get_webview_window("main").unwrap();
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
window.set_transparent_titlebar(true, true);
|
{
|
||||||
|
let main_win = app.get_webview_window("main").unwrap();
|
||||||
|
main_win.set_transparent_titlebar(true, true);
|
||||||
|
let splashscreen_win = app.get_webview_window("splashscreen").unwrap();
|
||||||
|
splashscreen_win.set_transparent_titlebar(true, true);
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
{
|
||||||
|
// on linux or windows, set decorations to false
|
||||||
|
let main_win = app.get_webview_window("main").unwrap();
|
||||||
|
main_win
|
||||||
|
.set_decorations(false)
|
||||||
|
.expect("Failed to set decorations");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,23 @@
|
|||||||
"macOSPrivateApi": true,
|
"macOSPrivateApi": true,
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null
|
"csp": null
|
||||||
}
|
},
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"hiddenTitle": true,
|
||||||
|
"url": "/app",
|
||||||
|
"title": "Kunkun",
|
||||||
|
"width": 800,
|
||||||
|
"visible": false,
|
||||||
|
"height": 600,
|
||||||
|
"decorations": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/splashscreen",
|
||||||
|
"visible": false,
|
||||||
|
"label": "splashscreen"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
"createUpdaterArtifacts": true,
|
"createUpdaterArtifacts": true,
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"identifier": "sh.kunkun.desktop",
|
|
||||||
"app": {
|
|
||||||
"windows": [
|
|
||||||
{
|
|
||||||
"hiddenTitle": true,
|
|
||||||
"title": "Kunkun",
|
|
||||||
"width": 800,
|
|
||||||
"height": 600,
|
|
||||||
"decorations": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"identifier": "sh.kunkun.desktop",
|
|
||||||
"app": {
|
|
||||||
"windows": [
|
|
||||||
{
|
|
||||||
"hiddenTitle": true,
|
|
||||||
"title": "Kunkun",
|
|
||||||
"width": 800,
|
|
||||||
"height": 600
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"identifier": "sh.kunkun.desktop",
|
|
||||||
"app": {
|
|
||||||
"windows": [
|
|
||||||
{
|
|
||||||
"hiddenTitle": true,
|
|
||||||
"title": "Kunkun",
|
|
||||||
"width": 800,
|
|
||||||
"height": 600,
|
|
||||||
"decorations": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,7 +13,6 @@ import { derived } from "svelte/store"
|
|||||||
import * as clipboard from "tauri-plugin-clipboard-api"
|
import * as clipboard from "tauri-plugin-clipboard-api"
|
||||||
import { open } from "tauri-plugin-shellx-api"
|
import { open } from "tauri-plugin-shellx-api"
|
||||||
import { v4 as uuidv4 } from "uuid"
|
import { v4 as uuidv4 } from "uuid"
|
||||||
import { hexColor } from "valibot"
|
|
||||||
|
|
||||||
export const rawBuiltinCmds: BuiltinCmd[] = [
|
export const rawBuiltinCmds: BuiltinCmd[] = [
|
||||||
{
|
{
|
||||||
@ -25,7 +24,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
description: "Go to Extension Store",
|
description: "Go to Extension Store",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
goto("/extension/store")
|
goto("/app/extension/store")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -36,7 +35,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
},
|
},
|
||||||
description: "",
|
description: "",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
goto("/auth")
|
goto("/app/auth")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -73,6 +72,23 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
}, 2_000)
|
}, 2_000)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Splashscreen (Dev)",
|
||||||
|
icon: {
|
||||||
|
type: IconEnum.Iconify,
|
||||||
|
value: "material-symbols:skeleton"
|
||||||
|
},
|
||||||
|
description: "",
|
||||||
|
flags: {
|
||||||
|
dev: true
|
||||||
|
},
|
||||||
|
function: async () => {
|
||||||
|
new WebviewWindow(`splashscreen`, {
|
||||||
|
url: "/splashscreen"
|
||||||
|
})
|
||||||
|
appState.clearSearchTerm()
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "File Transfer",
|
name: "File Transfer",
|
||||||
icon: {
|
icon: {
|
||||||
@ -81,7 +97,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
},
|
},
|
||||||
description: "",
|
description: "",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
goto("/extension/file-transfer")
|
goto("/app/extension/file-transfer")
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -95,7 +111,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
description: "",
|
description: "",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
goto("/settings/add-dev-extension")
|
goto("/app/settings/add-dev-extension")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -120,7 +136,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
function: async () => {
|
function: async () => {
|
||||||
// const appStateStore = useAppStateStore()
|
// const appStateStore = useAppStateStore()
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
goto("/settings/set-dev-ext-path")
|
goto("/app/settings/set-dev-ext-path")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -132,11 +148,11 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
description: "",
|
description: "",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
// goto("/window-troubleshooter")
|
// goto("/app/window-troubleshooter")
|
||||||
const winLabel = `main:extension-window-troubleshooter-${uuidv4()}`
|
const winLabel = `main:extension-window-troubleshooter-${uuidv4()}`
|
||||||
console.log(winLabel)
|
console.log(winLabel)
|
||||||
new WebviewWindow(winLabel, {
|
new WebviewWindow(winLabel, {
|
||||||
url: "/troubleshooters/extension-window",
|
url: "/app/troubleshooters/extension-window",
|
||||||
title: "Extension Window Troubleshooter"
|
title: "Extension Window Troubleshooter"
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -151,7 +167,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
description: "",
|
description: "",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
goto("/extension/permission-inspector")
|
goto("/app/extension/permission-inspector")
|
||||||
},
|
},
|
||||||
keywords: ["extension"]
|
keywords: ["extension"]
|
||||||
},
|
},
|
||||||
@ -164,7 +180,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
description: "",
|
description: "",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
goto("/troubleshooters/extension-loading")
|
goto("/app/troubleshooters/extension-loading")
|
||||||
},
|
},
|
||||||
keywords: ["extension", "troubleshooter"]
|
keywords: ["extension", "troubleshooter"]
|
||||||
},
|
},
|
||||||
@ -177,7 +193,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
description: "Create a Quicklink",
|
description: "Create a Quicklink",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
goto("/extension/create-quick-link")
|
goto("/app/extension/create-quick-link")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -188,7 +204,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
},
|
},
|
||||||
description: "Open Settings",
|
description: "Open Settings",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
goto("/settings")
|
goto("/app/settings")
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -248,7 +264,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
},
|
},
|
||||||
description: "Dance",
|
description: "Dance",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
goto("/dance")
|
goto("/app/dance")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -289,7 +305,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
description: "Clipboard History",
|
description: "Clipboard History",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
appState.clearSearchTerm()
|
appState.clearSearchTerm()
|
||||||
goto("/extension/clipboard")
|
goto("/app/extension/clipboard")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -306,7 +322,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const window = new WebviewWindow(`main:pinned-screenshot-${uuidv4()}`, {
|
const window = new WebviewWindow(`main:pinned-screenshot-${uuidv4()}`, {
|
||||||
url: "/extension/pin-screenshot",
|
url: "/app/extension/pin-screenshot",
|
||||||
title: "Pinned Screenshot",
|
title: "Pinned Screenshot",
|
||||||
hiddenTitle: true,
|
hiddenTitle: true,
|
||||||
titleBarStyle: "transparent",
|
titleBarStyle: "transparent",
|
||||||
@ -326,7 +342,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
|
|||||||
},
|
},
|
||||||
description: "MDNS Debugger",
|
description: "MDNS Debugger",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
goto("/troubleshooters/mdns-debugger")
|
goto("/app/troubleshooters/mdns-debugger")
|
||||||
},
|
},
|
||||||
flags: {
|
flags: {
|
||||||
developer: true
|
developer: true
|
||||||
|
@ -24,7 +24,7 @@ export async function onTemplateUiCmdSelect(
|
|||||||
) {
|
) {
|
||||||
await createExtSupportDir(ext.extPath)
|
await createExtSupportDir(ext.extPath)
|
||||||
// console.log("onTemplateUiCmdSelect", ext, cmd, isDev, hmr)
|
// console.log("onTemplateUiCmdSelect", ext, cmd, isDev, hmr)
|
||||||
const url = `/extension/ui-worker?extPath=${encodeURIComponent(ext.extPath)}&cmdName=${encodeURIComponent(cmd.name)}`
|
const url = `/app/extension/ui-worker?extPath=${encodeURIComponent(ext.extPath)}&cmdName=${encodeURIComponent(cmd.name)}`
|
||||||
if (cmd.window) {
|
if (cmd.window) {
|
||||||
const winLabel = await winExtMap.registerExtensionWithWindow({ extPath: ext.extPath })
|
const winLabel = await winExtMap.registerExtensionWithWindow({ extPath: ext.extPath })
|
||||||
const window = launchNewExtWindow(winLabel, url, cmd.window)
|
const window = launchNewExtWindow(winLabel, url, cmd.window)
|
||||||
@ -52,7 +52,7 @@ export async function onCustomUiCmdSelect(
|
|||||||
} else {
|
} else {
|
||||||
url = decodeURIComponent(convertFileSrc(`${trimSlash(cmd.main)}`, "ext"))
|
url = decodeURIComponent(convertFileSrc(`${trimSlash(cmd.main)}`, "ext"))
|
||||||
}
|
}
|
||||||
let url2 = `/extension/ui-iframe?url=${encodeURIComponent(url)}&extPath=${encodeURIComponent(ext.extPath)}`
|
let url2 = `/app/extension/ui-iframe?url=${encodeURIComponent(url)}&extPath=${encodeURIComponent(ext.extPath)}`
|
||||||
if (cmd.window) {
|
if (cmd.window) {
|
||||||
const winLabel = await winExtMap.registerExtensionWithWindow({
|
const winLabel = await winExtMap.registerExtensionWithWindow({
|
||||||
extPath: ext.extPath,
|
extPath: ext.extPath,
|
||||||
@ -61,7 +61,7 @@ export async function onCustomUiCmdSelect(
|
|||||||
if (platform() === "windows" && !useDevMain) {
|
if (platform() === "windows" && !useDevMain) {
|
||||||
const addr = await spawnExtensionFileServer(winLabel)
|
const addr = await spawnExtensionFileServer(winLabel)
|
||||||
const newUrl = `http://${addr}`
|
const newUrl = `http://${addr}`
|
||||||
url2 = `/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
|
url2 = `/app/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
|
||||||
}
|
}
|
||||||
const window = launchNewExtWindow(winLabel, url2, cmd.window)
|
const window = launchNewExtWindow(winLabel, url2, cmd.window)
|
||||||
window.onCloseRequested(async (event) => {
|
window.onCloseRequested(async (event) => {
|
||||||
@ -78,7 +78,7 @@ export async function onCustomUiCmdSelect(
|
|||||||
const addr = await spawnExtensionFileServer(winLabel) // addr has format "127.0.0.1:<port>"
|
const addr = await spawnExtensionFileServer(winLabel) // addr has format "127.0.0.1:<port>"
|
||||||
console.log("Extension file server address: ", addr)
|
console.log("Extension file server address: ", addr)
|
||||||
const newUrl = `http://${addr}`
|
const newUrl = `http://${addr}`
|
||||||
url2 = `/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
|
url2 = `/app/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
|
||||||
}
|
}
|
||||||
goto(url2)
|
goto(url2)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { cn } from "@/utils"
|
||||||
import { GridAnimation } from "@kksh/ui"
|
import { GridAnimation } from "@kksh/ui"
|
||||||
import { decompressFrame, decompressString, deserializeFrame } from "@kksh/utils"
|
import { decompressFrame, decompressString, deserializeFrame } from "@kksh/utils"
|
||||||
import compressedDance from "$lib/../data/dance.bin?raw"
|
import compressedDance from "$lib/../data/dance.bin?raw"
|
||||||
@ -7,11 +8,14 @@
|
|||||||
const { fps, frames: rawFrames }: { fps: number; frames: string[] } = rawData
|
const { fps, frames: rawFrames }: { fps: number; frames: string[] } = rawData
|
||||||
const decodedFrames = rawFrames.map((frame) => deserializeFrame(decompressFrame(frame)))
|
const decodedFrames = rawFrames.map((frame) => deserializeFrame(decompressFrame(frame)))
|
||||||
|
|
||||||
let { scale = 1 } = $props()
|
let { scale = 1, class: className }: { scale?: number; class?: string } = $props()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<GridAnimation
|
<GridAnimation
|
||||||
class="pointer-events-none max-h-full max-w-full select-none invert dark:invert-0"
|
class={cn(
|
||||||
|
"pointer-events-none max-h-full max-w-full select-none invert dark:invert-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
{fps}
|
{fps}
|
||||||
frames={decodedFrames}
|
frames={decodedFrames}
|
||||||
{scale}
|
{scale}
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
async function pickExtFiles() {
|
async function pickExtFiles() {
|
||||||
if (!$appConfig.devExtensionPath) {
|
if (!$appConfig.devExtensionPath) {
|
||||||
toast.warning("Please set the dev extension path in the settings")
|
toast.warning("Please set the dev extension path in the settings")
|
||||||
return goto("/settings/set-dev-ext-path")
|
return goto("/app/settings/set-dev-ext-path")
|
||||||
}
|
}
|
||||||
const selected = await openFileSelector({
|
const selected = await openFileSelector({
|
||||||
directory: false,
|
directory: false,
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
toast.warning(
|
toast.warning(
|
||||||
"Please set the dev extension path in the settings to install tarball extension"
|
"Please set the dev extension path in the settings to install tarball extension"
|
||||||
)
|
)
|
||||||
return goto("/settings/set-dev-ext-path")
|
return goto("/app/settings/set-dev-ext-path")
|
||||||
}
|
}
|
||||||
await extensions
|
await extensions
|
||||||
.installFromNpmPackageName(data.name, $appConfig.devExtensionPath)
|
.installFromNpmPackageName(data.name, $appConfig.devExtensionPath)
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
toast.warning(
|
toast.warning(
|
||||||
"Please set the dev extension path in the settings to install tarball extension"
|
"Please set the dev extension path in the settings to install tarball extension"
|
||||||
)
|
)
|
||||||
return goto("/settings/set-dev-ext-path")
|
return goto("/app/settings/set-dev-ext-path")
|
||||||
}
|
}
|
||||||
await extensions
|
await extensions
|
||||||
.installFromTarballUrl(data.url, $appConfig.devExtensionPath)
|
.installFromTarballUrl(data.url, $appConfig.devExtensionPath)
|
||||||
|
@ -59,7 +59,7 @@ export async function handleKunkunProtocol(parsedUrl: URL) {
|
|||||||
if (parsed.identifier) {
|
if (parsed.identifier) {
|
||||||
goto(`/extension/store/${parsed.identifier}`)
|
goto(`/extension/store/${parsed.identifier}`)
|
||||||
} else {
|
} else {
|
||||||
goto("/extension/store")
|
goto("/app/extension/store")
|
||||||
}
|
}
|
||||||
} else if (href.startsWith(DEEP_LINK_PATH_REFRESH_DEV_EXTENSION)) {
|
} else if (href.startsWith(DEEP_LINK_PATH_REFRESH_DEV_EXTENSION)) {
|
||||||
emitRefreshDevExt()
|
emitRefreshDevExt()
|
||||||
|
@ -76,7 +76,7 @@ export async function globalKeyDownHandler(e: KeyboardEvent) {
|
|||||||
if ((_platform === "macos" && e.metaKey) || (_platform === "windows" && e.ctrlKey)) {
|
if ((_platform === "macos" && e.metaKey) || (_platform === "windows" && e.ctrlKey)) {
|
||||||
if (e.key === ",") {
|
if (e.key === ",") {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
goto("/settings")
|
goto("/app/settings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Toggle Devtools with control + shift + I
|
// Toggle Devtools with control + shift + I
|
||||||
|
@ -7,7 +7,7 @@ export function goBack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function goHome() {
|
export function goHome() {
|
||||||
goto("/")
|
goto("/app/")
|
||||||
}
|
}
|
||||||
|
|
||||||
export function goHomeOrCloseDependingOnWindow() {
|
export function goHomeOrCloseDependingOnWindow() {
|
||||||
|
@ -1,117 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AppContext from "@/components/context/AppContext.svelte"
|
|
||||||
import "../app.css"
|
import "../app.css"
|
||||||
import { appConfig, appState, extensions, quickLinks, winExtMap } from "@/stores"
|
import { ModeWatcher, ThemeWrapper } from "@kksh/svelte5"
|
||||||
import { initDeeplink } from "@/utils/deeplink"
|
|
||||||
import { updateAppHotkey } from "@/utils/hotkey"
|
|
||||||
import { globalKeyDownHandler, goBackOrCloseOnEscape } from "@/utils/key"
|
|
||||||
import { listenToWindowBlur } from "@/utils/tauri-events"
|
|
||||||
import { isInMainWindow } from "@/utils/window"
|
|
||||||
import { listenToKillProcessEvent, listenToRecordExtensionProcessEvent } from "@kksh/api/events"
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
ModeWatcher,
|
|
||||||
themeConfigStore,
|
|
||||||
ThemeWrapper,
|
|
||||||
updateTheme,
|
|
||||||
type ThemeConfig
|
|
||||||
} from "@kksh/svelte5"
|
|
||||||
import { Constants, ViewTransition } from "@kksh/ui"
|
|
||||||
import type { UnlistenFn } from "@tauri-apps/api/event"
|
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
|
||||||
import { attachConsole, error, info } from "@tauri-apps/plugin-log"
|
|
||||||
import { afterNavigate, beforeNavigate } from "$app/navigation"
|
|
||||||
import { gsap } from "gsap"
|
|
||||||
import { Flip } from "gsap/Flip"
|
|
||||||
import { onDestroy, onMount } from "svelte"
|
|
||||||
import { toast, Toaster } from "svelte-sonner"
|
|
||||||
import * as shellx from "tauri-plugin-shellx-api"
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* Gsap Flip Animation */
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
gsap.registerPlugin(Flip)
|
|
||||||
let flipState: Flip.FlipState
|
|
||||||
beforeNavigate(() => {
|
|
||||||
flipState = Flip.getState(
|
|
||||||
`.${Constants.CLASSNAMES.EXT_LOGO}, .${Constants.CLASSNAMES.BACK_BUTTON}`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
afterNavigate(() => {
|
|
||||||
if (!flipState) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Flip.from(flipState, {
|
|
||||||
targets: `.${Constants.CLASSNAMES.EXT_LOGO}, .${Constants.CLASSNAMES.BACK_BUTTON}`,
|
|
||||||
duration: 0.5,
|
|
||||||
absolute: true,
|
|
||||||
scale: true,
|
|
||||||
ease: "ease-out"
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
let { children } = $props()
|
let { children } = $props()
|
||||||
const unlisteners: UnlistenFn[] = []
|
|
||||||
onDestroy(() => {
|
|
||||||
unlisteners.forEach((unlistener) => unlistener())
|
|
||||||
})
|
|
||||||
onMount(async () => {
|
|
||||||
attachConsole().then((unlistener) => unlisteners.push(unlistener))
|
|
||||||
initDeeplink().then((unlistener) => unlisteners.push(unlistener))
|
|
||||||
shellx
|
|
||||||
.fixPathEnv()
|
|
||||||
.then(() => {
|
|
||||||
info("fixed path env")
|
|
||||||
})
|
|
||||||
.catch(error)
|
|
||||||
|
|
||||||
quickLinks.init()
|
|
||||||
appConfig.init()
|
|
||||||
if (isInMainWindow()) {
|
|
||||||
if ($appConfig.triggerHotkey) {
|
|
||||||
updateAppHotkey($appConfig.triggerHotkey)
|
|
||||||
}
|
|
||||||
unlisteners.push(
|
|
||||||
await listenToWindowBlur(() => {
|
|
||||||
const win = getCurrentWebviewWindow()
|
|
||||||
win.isFocused().then((isFocused) => {
|
|
||||||
// this extra is focused check may be needed because blur event got triggered somehow when window show()
|
|
||||||
// for edge case: when settings page is opened and focused, switch to main window, the blur event is triggered for main window
|
|
||||||
if (!isFocused) {
|
|
||||||
if ($appConfig.hideOnBlur) {
|
|
||||||
win.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
)
|
|
||||||
extensions.init()
|
|
||||||
unlisteners.push(
|
|
||||||
await listenToRecordExtensionProcessEvent(async (event) => {
|
|
||||||
console.log("record extension process event", event)
|
|
||||||
winExtMap.registerProcess(event.payload.windowLabel, event.payload.pid)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
unlisteners.push(
|
|
||||||
await listenToKillProcessEvent((event) => {
|
|
||||||
console.log("kill process event", event)
|
|
||||||
winExtMap.unregisterProcess(event.payload.pid)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
getCurrentWebviewWindow().show()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:keydown={globalKeyDownHandler} />
|
|
||||||
<ViewTransition />
|
|
||||||
<ModeWatcher />
|
<ModeWatcher />
|
||||||
<Toaster richColors />
|
<ThemeWrapper>
|
||||||
<AppContext {appConfig} {appState}>
|
{@render children()}
|
||||||
<ThemeWrapper>
|
</ThemeWrapper>
|
||||||
{@render children()}
|
|
||||||
</ThemeWrapper>
|
|
||||||
</AppContext>
|
|
||||||
|
@ -1,13 +1,5 @@
|
|||||||
import { getExtensionsFolder, IS_IN_TAURI } from "@/constants"
|
|
||||||
import { error } from "@tauri-apps/plugin-log"
|
|
||||||
import type { LayoutLoad } from "./$types"
|
|
||||||
|
|
||||||
// Tauri doesn't have a Node.js server to do proper SSR
|
// Tauri doesn't have a Node.js server to do proper SSR
|
||||||
// so we will use adapter-static to prerender the app (SSG)
|
// so we will use adapter-static to prerender the app (SSG)
|
||||||
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
|
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
|
||||||
export const prerender = true
|
export const prerender = true
|
||||||
export const ssr = false
|
export const ssr = false
|
||||||
|
|
||||||
export const load: LayoutLoad = async () => {
|
|
||||||
return { extsInstallDir: IS_IN_TAURI ? await getExtensionsFolder() : "" }
|
|
||||||
}
|
|
||||||
|
@ -1,176 +1 @@
|
|||||||
<!-- This file renders the main command palette, a list of commands -->
|
<h1 class="text-red-500">Root</h1>
|
||||||
<script lang="ts">
|
|
||||||
import { commandLaunchers } from "@/cmds"
|
|
||||||
import { builtinCmds } from "@/cmds/builtin"
|
|
||||||
import { systemCommands } from "@/cmds/system"
|
|
||||||
import { appConfig, appState, devStoreExts, installedStoreExts, quickLinks } from "@/stores"
|
|
||||||
import { cmdQueries } from "@/stores/cmdQuery"
|
|
||||||
import { isKeyboardEventFromInputElement } from "@/utils/dom"
|
|
||||||
import Icon from "@iconify/svelte"
|
|
||||||
import { toggleDevTools } from "@kksh/api/commands"
|
|
||||||
import { Button, Command, DropdownMenu } from "@kksh/svelte5"
|
|
||||||
import {
|
|
||||||
BuiltinCmds,
|
|
||||||
CustomCommandInput,
|
|
||||||
ExtCmdsGroup,
|
|
||||||
GlobalCommandPaletteFooter,
|
|
||||||
QuickLinks,
|
|
||||||
SystemCmds
|
|
||||||
} from "@kksh/ui/main"
|
|
||||||
import type { CmdValue } from "@kksh/ui/types"
|
|
||||||
import { cn, commandScore } from "@kksh/ui/utils"
|
|
||||||
import { convertFileSrc } from "@tauri-apps/api/core"
|
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
|
||||||
import { exit } from "@tauri-apps/plugin-process"
|
|
||||||
import { ArrowBigUpIcon, CircleXIcon, EllipsisVerticalIcon, RefreshCcwIcon } from "lucide-svelte"
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import { hasCommand, whereIsCommand } from "tauri-plugin-shellx-api"
|
|
||||||
|
|
||||||
let inputEle: HTMLInputElement | null = null
|
|
||||||
function onKeyDown(event: KeyboardEvent) {
|
|
||||||
if (event.key === "Escape") {
|
|
||||||
;(event.target as HTMLInputElement).value = ""
|
|
||||||
$appState.searchTerm = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// let imgSrc = convertFileSrc("/?id=15", "cbimg")
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:window
|
|
||||||
on:keydown={(e) => {
|
|
||||||
if (e.key === "/") {
|
|
||||||
if (isKeyboardEventFromInputElement(e)) {
|
|
||||||
e.preventDefault()
|
|
||||||
} else {
|
|
||||||
e.preventDefault()
|
|
||||||
inputEle?.focus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<!-- <pre>{imgSrc}</pre>
|
|
||||||
<img class="border-2 border-red-500 w-64" src={imgSrc} alt="test" /> -->
|
|
||||||
<Command.Root
|
|
||||||
class={cn("h-screen rounded-lg border shadow-md")}
|
|
||||||
bind:value={$appState.highlightedCmd}
|
|
||||||
filter={(value, search, keywords) => {
|
|
||||||
return commandScore(
|
|
||||||
value.startsWith("{") ? (JSON.parse(value) as CmdValue).cmdName : value,
|
|
||||||
search,
|
|
||||||
keywords
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
loop
|
|
||||||
>
|
|
||||||
<CustomCommandInput
|
|
||||||
autofocus
|
|
||||||
bind:ref={inputEle}
|
|
||||||
id="main-command-input"
|
|
||||||
placeholder={$cmdQueries.length === 0 ? "Type a command or search..." : undefined}
|
|
||||||
bind:value={$appState.searchTerm}
|
|
||||||
onkeydown={onKeyDown}
|
|
||||||
>
|
|
||||||
{#snippet rightSlot()}
|
|
||||||
<span
|
|
||||||
class={cn("absolute flex space-x-2")}
|
|
||||||
style={`left: ${$appState.searchTerm.length + 3}ch`}
|
|
||||||
>
|
|
||||||
{#each $cmdQueries as cmdQuery}
|
|
||||||
{@const queryWidth = Math.max(cmdQuery.name.length, cmdQuery.value.length) + 2}
|
|
||||||
<input
|
|
||||||
class="bg-muted rounded-md border border-gray-300 pl-2 font-mono focus:outline-none dark:border-gray-600"
|
|
||||||
type="text"
|
|
||||||
placeholder={cmdQuery.name}
|
|
||||||
style={`width: ${queryWidth}ch`}
|
|
||||||
onkeydown={(evt) => {
|
|
||||||
if (evt.key === "Enter") {
|
|
||||||
evt.preventDefault()
|
|
||||||
evt.stopPropagation()
|
|
||||||
commandLaunchers.onQuickLinkSelect(
|
|
||||||
JSON.parse($appState.highlightedCmd),
|
|
||||||
$cmdQueries
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
bind:value={cmdQuery.value}
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<DropdownMenu.Root>
|
|
||||||
<DropdownMenu.Trigger>
|
|
||||||
<Button variant="outline" size="icon"><EllipsisVerticalIcon /></Button>
|
|
||||||
</DropdownMenu.Trigger>
|
|
||||||
<DropdownMenu.Content class="w-80">
|
|
||||||
<DropdownMenu.Group>
|
|
||||||
<DropdownMenu.Item onclick={() => exit()}>
|
|
||||||
<CircleXIcon class="h-4 w-4 text-red-500" />
|
|
||||||
Quit
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item onclick={() => getCurrentWebviewWindow().hide()}>
|
|
||||||
<CircleXIcon class="h-4 w-4 text-red-500" />
|
|
||||||
Close Window
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Group>
|
|
||||||
<DropdownMenu.Separator />
|
|
||||||
<DropdownMenu.Group>
|
|
||||||
<DropdownMenu.GroupHeading data-tauri-drag-region>Developer</DropdownMenu.GroupHeading>
|
|
||||||
<DropdownMenu.Item onclick={toggleDevTools}>
|
|
||||||
<Icon icon="mingcute:code-fill" class="mr-2 h-5 w-5 text-green-500" />
|
|
||||||
Toggle Devtools
|
|
||||||
<DropdownMenu.Shortcut
|
|
||||||
><span class="flex items-center">⌃+<ArrowBigUpIcon class="h-4 w-4" />+I</span
|
|
||||||
></DropdownMenu.Shortcut
|
|
||||||
>
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item onclick={() => location.reload()}>
|
|
||||||
<RefreshCcwIcon class="mr-2 h-4 w-4 text-green-500" />
|
|
||||||
Reload Window
|
|
||||||
<DropdownMenu.Shortcut
|
|
||||||
><span class="flex items-center">⌃+<ArrowBigUpIcon class="h-4 w-4" />+R</span
|
|
||||||
></DropdownMenu.Shortcut
|
|
||||||
>
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
onclick={() => {
|
|
||||||
appConfig.update((config) => ({ ...config, hmr: !config.hmr }))
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
icon={$appConfig.hmr ? "fontisto:toggle-on" : "fontisto:toggle-off"}
|
|
||||||
class={cn("mr-1 h-5 w-5", { "text-green-500": $appConfig.hmr })}
|
|
||||||
/>
|
|
||||||
Toggle Dev Extension HMR
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Group>
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Root>
|
|
||||||
{/snippet}
|
|
||||||
</CustomCommandInput>
|
|
||||||
<Command.List class="max-h-screen grow">
|
|
||||||
<Command.Empty data-tauri-drag-region>No results found.</Command.Empty>
|
|
||||||
{#if $appConfig.extensionsInstallDir && $devStoreExts.length > 0}
|
|
||||||
<ExtCmdsGroup
|
|
||||||
extensions={$devStoreExts}
|
|
||||||
heading="Dev Extensions"
|
|
||||||
isDev={true}
|
|
||||||
onExtCmdSelect={commandLaunchers.onExtCmdSelect}
|
|
||||||
hmr={$appConfig.hmr}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{#if $appConfig.extensionsInstallDir && $installedStoreExts.length > 0}
|
|
||||||
<ExtCmdsGroup
|
|
||||||
extensions={$installedStoreExts}
|
|
||||||
heading="Extensions"
|
|
||||||
isDev={false}
|
|
||||||
hmr={false}
|
|
||||||
onExtCmdSelect={commandLaunchers.onExtCmdSelect}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
<QuickLinks quickLinks={$quickLinks} />
|
|
||||||
<BuiltinCmds builtinCmds={$builtinCmds} />
|
|
||||||
<SystemCmds {systemCommands} />
|
|
||||||
</Command.List>
|
|
||||||
<GlobalCommandPaletteFooter />
|
|
||||||
</Command.Root>
|
|
||||||
|
107
apps/desktop/src/routes/app/+layout.svelte
Normal file
107
apps/desktop/src/routes/app/+layout.svelte
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import AppContext from "@/components/context/AppContext.svelte"
|
||||||
|
import { appConfig, appState, extensions, quickLinks, winExtMap } from "@/stores"
|
||||||
|
import { initDeeplink } from "@/utils/deeplink"
|
||||||
|
import { updateAppHotkey } from "@/utils/hotkey"
|
||||||
|
import { globalKeyDownHandler, goBackOrCloseOnEscape } from "@/utils/key"
|
||||||
|
import { listenToWindowBlur } from "@/utils/tauri-events"
|
||||||
|
import { isInMainWindow } from "@/utils/window"
|
||||||
|
import { listenToKillProcessEvent, listenToRecordExtensionProcessEvent } from "@kksh/api/events"
|
||||||
|
import { Constants, ViewTransition } from "@kksh/ui"
|
||||||
|
import type { UnlistenFn } from "@tauri-apps/api/event"
|
||||||
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
||||||
|
import { attachConsole, error, info } from "@tauri-apps/plugin-log"
|
||||||
|
import { afterNavigate, beforeNavigate } from "$app/navigation"
|
||||||
|
import { gsap } from "gsap"
|
||||||
|
import { Flip } from "gsap/Flip"
|
||||||
|
import { onDestroy, onMount } from "svelte"
|
||||||
|
import { toast, Toaster } from "svelte-sonner"
|
||||||
|
import * as shellx from "tauri-plugin-shellx-api"
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Gsap Flip Animation */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
gsap.registerPlugin(Flip)
|
||||||
|
let flipState: Flip.FlipState
|
||||||
|
beforeNavigate(() => {
|
||||||
|
flipState = Flip.getState(
|
||||||
|
`.${Constants.CLASSNAMES.EXT_LOGO}, .${Constants.CLASSNAMES.BACK_BUTTON}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterNavigate(() => {
|
||||||
|
if (!flipState) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Flip.from(flipState, {
|
||||||
|
targets: `.${Constants.CLASSNAMES.EXT_LOGO}, .${Constants.CLASSNAMES.BACK_BUTTON}`,
|
||||||
|
duration: 0.5,
|
||||||
|
absolute: true,
|
||||||
|
scale: true,
|
||||||
|
ease: "ease-out"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let { children } = $props()
|
||||||
|
const unlisteners: UnlistenFn[] = []
|
||||||
|
onDestroy(() => {
|
||||||
|
unlisteners.forEach((unlistener) => unlistener())
|
||||||
|
})
|
||||||
|
onMount(async () => {
|
||||||
|
console.log("root layout onMount")
|
||||||
|
|
||||||
|
attachConsole().then((unlistener) => unlisteners.push(unlistener))
|
||||||
|
initDeeplink().then((unlistener) => unlisteners.push(unlistener))
|
||||||
|
shellx
|
||||||
|
.fixPathEnv()
|
||||||
|
.then(() => {
|
||||||
|
info("fixed path env")
|
||||||
|
})
|
||||||
|
.catch(error)
|
||||||
|
|
||||||
|
quickLinks.init()
|
||||||
|
appConfig.init()
|
||||||
|
if (isInMainWindow()) {
|
||||||
|
if ($appConfig.triggerHotkey) {
|
||||||
|
updateAppHotkey($appConfig.triggerHotkey)
|
||||||
|
}
|
||||||
|
unlisteners.push(
|
||||||
|
await listenToWindowBlur(() => {
|
||||||
|
const win = getCurrentWebviewWindow()
|
||||||
|
win.isFocused().then((isFocused) => {
|
||||||
|
// this extra is focused check may be needed because blur event got triggered somehow when window show()
|
||||||
|
// for edge case: when settings page is opened and focused, switch to main window, the blur event is triggered for main window
|
||||||
|
if (!isFocused) {
|
||||||
|
if ($appConfig.hideOnBlur) {
|
||||||
|
win.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
extensions.init()
|
||||||
|
unlisteners.push(
|
||||||
|
await listenToRecordExtensionProcessEvent(async (event) => {
|
||||||
|
console.log("record extension process event", event)
|
||||||
|
winExtMap.registerProcess(event.payload.windowLabel, event.payload.pid)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
unlisteners.push(
|
||||||
|
await listenToKillProcessEvent((event) => {
|
||||||
|
console.log("kill process event", event)
|
||||||
|
winExtMap.unregisterProcess(event.payload.pid)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
getCurrentWebviewWindow().show()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:keydown={globalKeyDownHandler} />
|
||||||
|
<ViewTransition />
|
||||||
|
<Toaster richColors />
|
||||||
|
<AppContext {appConfig} {appState}>
|
||||||
|
{@render children()}
|
||||||
|
</AppContext>
|
7
apps/desktop/src/routes/app/+layout.ts
Normal file
7
apps/desktop/src/routes/app/+layout.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { getExtensionsFolder, IS_IN_TAURI } from "@/constants"
|
||||||
|
import { error } from "@tauri-apps/plugin-log"
|
||||||
|
import type { LayoutLoad } from "./$types"
|
||||||
|
|
||||||
|
export const load: LayoutLoad = async () => {
|
||||||
|
return { extsInstallDir: IS_IN_TAURI ? await getExtensionsFolder() : "" }
|
||||||
|
}
|
183
apps/desktop/src/routes/app/+page.svelte
Normal file
183
apps/desktop/src/routes/app/+page.svelte
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
<!-- This file renders the main command palette, a list of commands -->
|
||||||
|
<script lang="ts">
|
||||||
|
import { commandLaunchers } from "@/cmds"
|
||||||
|
import { builtinCmds } from "@/cmds/builtin"
|
||||||
|
import { systemCommands } from "@/cmds/system"
|
||||||
|
import { appConfig, appState, devStoreExts, installedStoreExts, quickLinks } from "@/stores"
|
||||||
|
import { cmdQueries } from "@/stores/cmdQuery"
|
||||||
|
import { isKeyboardEventFromInputElement } from "@/utils/dom"
|
||||||
|
import Icon from "@iconify/svelte"
|
||||||
|
import { toggleDevTools } from "@kksh/api/commands"
|
||||||
|
import { Button, Command, DropdownMenu } from "@kksh/svelte5"
|
||||||
|
import {
|
||||||
|
BuiltinCmds,
|
||||||
|
CustomCommandInput,
|
||||||
|
ExtCmdsGroup,
|
||||||
|
GlobalCommandPaletteFooter,
|
||||||
|
QuickLinks,
|
||||||
|
SystemCmds
|
||||||
|
} from "@kksh/ui/main"
|
||||||
|
import type { CmdValue } from "@kksh/ui/types"
|
||||||
|
import { cn, commandScore } from "@kksh/ui/utils"
|
||||||
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
|
||||||
|
import { getCurrentWindow, Window } from "@tauri-apps/api/window"
|
||||||
|
import { exit } from "@tauri-apps/plugin-process"
|
||||||
|
import { ArrowBigUpIcon, CircleXIcon, EllipsisVerticalIcon, RefreshCcwIcon } from "lucide-svelte"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import { hasCommand, whereIsCommand } from "tauri-plugin-shellx-api"
|
||||||
|
|
||||||
|
let inputEle: HTMLInputElement | null = $state(null)
|
||||||
|
function onKeyDown(event: KeyboardEvent) {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
;(event.target as HTMLInputElement).value = ""
|
||||||
|
$appState.searchTerm = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
Promise.all([Window.getByLabel("splashscreen"), getCurrentWindow()]).then(
|
||||||
|
([splashscreenWin, mainWin]) => {
|
||||||
|
mainWin.show()
|
||||||
|
if (splashscreenWin) {
|
||||||
|
splashscreenWin.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window
|
||||||
|
on:keydown={(e) => {
|
||||||
|
if (e.key === "/") {
|
||||||
|
if (isKeyboardEventFromInputElement(e)) {
|
||||||
|
e.preventDefault()
|
||||||
|
} else {
|
||||||
|
e.preventDefault()
|
||||||
|
inputEle?.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Command.Root
|
||||||
|
class={cn("h-screen rounded-lg border shadow-md")}
|
||||||
|
bind:value={$appState.highlightedCmd}
|
||||||
|
filter={(value, search, keywords) => {
|
||||||
|
return commandScore(
|
||||||
|
value.startsWith("{") ? (JSON.parse(value) as CmdValue).cmdName : value,
|
||||||
|
search,
|
||||||
|
keywords
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
loop
|
||||||
|
>
|
||||||
|
<CustomCommandInput
|
||||||
|
autofocus
|
||||||
|
bind:ref={inputEle}
|
||||||
|
id="main-command-input"
|
||||||
|
placeholder={$cmdQueries.length === 0 ? "Type a command or search..." : undefined}
|
||||||
|
bind:value={$appState.searchTerm}
|
||||||
|
onkeydown={onKeyDown}
|
||||||
|
>
|
||||||
|
{#snippet rightSlot()}
|
||||||
|
<span
|
||||||
|
class={cn("absolute flex space-x-2")}
|
||||||
|
style={`left: ${$appState.searchTerm.length + 3}ch`}
|
||||||
|
>
|
||||||
|
{#each $cmdQueries as cmdQuery}
|
||||||
|
{@const queryWidth = Math.max(cmdQuery.name.length, cmdQuery.value.length) + 2}
|
||||||
|
<input
|
||||||
|
class="bg-muted rounded-md border border-gray-300 pl-2 font-mono focus:outline-none dark:border-gray-600"
|
||||||
|
type="text"
|
||||||
|
placeholder={cmdQuery.name}
|
||||||
|
style={`width: ${queryWidth}ch`}
|
||||||
|
onkeydown={(evt) => {
|
||||||
|
if (evt.key === "Enter") {
|
||||||
|
evt.preventDefault()
|
||||||
|
evt.stopPropagation()
|
||||||
|
commandLaunchers.onQuickLinkSelect(
|
||||||
|
JSON.parse($appState.highlightedCmd),
|
||||||
|
$cmdQueries
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
bind:value={cmdQuery.value}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger>
|
||||||
|
<Button variant="outline" size="icon"><EllipsisVerticalIcon /></Button>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Content class="w-80">
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
<DropdownMenu.Item onclick={() => exit()}>
|
||||||
|
<CircleXIcon class="h-4 w-4 text-red-500" />
|
||||||
|
Quit
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item onclick={() => getCurrentWebviewWindow().hide()}>
|
||||||
|
<CircleXIcon class="h-4 w-4 text-red-500" />
|
||||||
|
Close Window
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
<DropdownMenu.GroupHeading data-tauri-drag-region>Developer</DropdownMenu.GroupHeading>
|
||||||
|
<DropdownMenu.Item onclick={toggleDevTools}>
|
||||||
|
<Icon icon="mingcute:code-fill" class="mr-2 h-5 w-5 text-green-500" />
|
||||||
|
Toggle Devtools
|
||||||
|
<DropdownMenu.Shortcut
|
||||||
|
><span class="flex items-center">⌃+<ArrowBigUpIcon class="h-4 w-4" />+I</span
|
||||||
|
></DropdownMenu.Shortcut
|
||||||
|
>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item onclick={() => location.reload()}>
|
||||||
|
<RefreshCcwIcon class="mr-2 h-4 w-4 text-green-500" />
|
||||||
|
Reload Window
|
||||||
|
<DropdownMenu.Shortcut
|
||||||
|
><span class="flex items-center">⌃+<ArrowBigUpIcon class="h-4 w-4" />+R</span
|
||||||
|
></DropdownMenu.Shortcut
|
||||||
|
>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
onclick={() => {
|
||||||
|
appConfig.update((config) => ({ ...config, hmr: !config.hmr }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={$appConfig.hmr ? "fontisto:toggle-on" : "fontisto:toggle-off"}
|
||||||
|
class={cn("mr-1 h-5 w-5", { "text-green-500": $appConfig.hmr })}
|
||||||
|
/>
|
||||||
|
Toggle Dev Extension HMR
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
{/snippet}
|
||||||
|
</CustomCommandInput>
|
||||||
|
<Command.List class="max-h-screen grow">
|
||||||
|
<Command.Empty data-tauri-drag-region>No results found.</Command.Empty>
|
||||||
|
{#if $appConfig.extensionsInstallDir && $devStoreExts.length > 0}
|
||||||
|
<ExtCmdsGroup
|
||||||
|
extensions={$devStoreExts}
|
||||||
|
heading="Dev Extensions"
|
||||||
|
isDev={true}
|
||||||
|
onExtCmdSelect={commandLaunchers.onExtCmdSelect}
|
||||||
|
hmr={$appConfig.hmr}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{#if $appConfig.extensionsInstallDir && $installedStoreExts.length > 0}
|
||||||
|
<ExtCmdsGroup
|
||||||
|
extensions={$installedStoreExts}
|
||||||
|
heading="Extensions"
|
||||||
|
isDev={false}
|
||||||
|
hmr={false}
|
||||||
|
onExtCmdSelect={commandLaunchers.onExtCmdSelect}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<QuickLinks quickLinks={$quickLinks} />
|
||||||
|
<BuiltinCmds builtinCmds={$builtinCmds} />
|
||||||
|
<SystemCmds {systemCommands} />
|
||||||
|
</Command.List>
|
||||||
|
<GlobalCommandPaletteFooter />
|
||||||
|
</Command.Root>
|
@ -44,7 +44,7 @@
|
|||||||
function onSignOut() {
|
function onSignOut() {
|
||||||
auth
|
auth
|
||||||
.signOut()
|
.signOut()
|
||||||
.then(() => goto("/auth"))
|
.then(() => goto("/app/auth"))
|
||||||
.catch((err) => toast.error("Failed to sign out", { description: err.message }))
|
.catch((err) => toast.error("Failed to sign out", { description: err.message }))
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -56,7 +56,7 @@
|
|||||||
size="icon"
|
size="icon"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
console.log("go Home")
|
console.log("go Home")
|
||||||
goto("/")
|
goto("/app/")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ArrowLeft class="size-4" />
|
<ArrowLeft class="size-4" />
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
function handleKeyDown(event: KeyboardEvent) {
|
function handleKeyDown(event: KeyboardEvent) {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
goto("/")
|
goto("/app/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -17,7 +17,7 @@
|
|||||||
title="Fail to Load Extension"
|
title="Fail to Load Extension"
|
||||||
class="w-fit max-w-screen-sm border-2 border-red-500"
|
class="w-fit max-w-screen-sm border-2 border-red-500"
|
||||||
message={$page.error?.message ?? "Unknown Error"}
|
message={$page.error?.message ?? "Unknown Error"}
|
||||||
onGoBack={() => goto("/")}
|
onGoBack={() => goto("/app/")}
|
||||||
rawJsonError={JSON.stringify($page, null, 2)}
|
rawJsonError={JSON.stringify($page, null, 2)}
|
||||||
/>
|
/>
|
||||||
</Layouts.Center>
|
</Layouts.Center>
|
@ -136,7 +136,7 @@
|
|||||||
function handleKeydown(e: KeyboardEvent) {
|
function handleKeydown(e: KeyboardEvent) {
|
||||||
if (e.key === "Escape") {
|
if (e.key === "Escape") {
|
||||||
if (!delayedImageDialogOpen) {
|
if (!delayedImageDialogOpen) {
|
||||||
goto("/extension/store")
|
goto("/app/extension/store")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@
|
|||||||
size="icon"
|
size="icon"
|
||||||
class={cn("fixed left-3 top-3", Constants.CLASSNAMES.BACK_BUTTON)}
|
class={cn("fixed left-3 top-3", Constants.CLASSNAMES.BACK_BUTTON)}
|
||||||
data-flip-id={Constants.CLASSNAMES.BACK_BUTTON}
|
data-flip-id={Constants.CLASSNAMES.BACK_BUTTON}
|
||||||
onclick={() => goto("/extension/store")}
|
onclick={() => goto("/app/extension/store")}
|
||||||
>
|
>
|
||||||
<ArrowLeftIcon />
|
<ArrowLeftIcon />
|
||||||
</Button>
|
</Button>
|
@ -51,7 +51,7 @@
|
|||||||
const iframeUiAPI: IUiIframeServer2 = {
|
const iframeUiAPI: IUiIframeServer2 = {
|
||||||
goBack: async () => {
|
goBack: async () => {
|
||||||
if (isInMainWindow()) {
|
if (isInMainWindow()) {
|
||||||
goto("/")
|
goto("/app/")
|
||||||
} else {
|
} else {
|
||||||
appWin.close()
|
appWin.close()
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ export const load: PageLoad = async ({
|
|||||||
if (!_extPath || !_extUrl) {
|
if (!_extPath || !_extUrl) {
|
||||||
toast.error("Invalid extension path or url")
|
toast.error("Invalid extension path or url")
|
||||||
error("Invalid extension path or url")
|
error("Invalid extension path or url")
|
||||||
goto("/")
|
goto("/app/")
|
||||||
}
|
}
|
||||||
const extPath = z.string().parse(_extPath)
|
const extPath = z.string().parse(_extPath)
|
||||||
const extUrl = z.string().parse(_extUrl)
|
const extUrl = z.string().parse(_extUrl)
|
||||||
@ -36,7 +36,7 @@ export const load: PageLoad = async ({
|
|||||||
toast.error("Error loading extension manifest", {
|
toast.error("Error loading extension manifest", {
|
||||||
description: `${err}`
|
description: `${err}`
|
||||||
})
|
})
|
||||||
goto("/")
|
goto("/app/")
|
||||||
}
|
}
|
||||||
const loadedExt = _loadedExt!
|
const loadedExt = _loadedExt!
|
||||||
const extInfoInDB = await db.getUniqueExtensionByPath(loadedExt.extPath)
|
const extInfoInDB = await db.getUniqueExtensionByPath(loadedExt.extPath)
|
||||||
@ -44,7 +44,7 @@ export const load: PageLoad = async ({
|
|||||||
toast.error("Unexpected Error", {
|
toast.error("Unexpected Error", {
|
||||||
description: `Extension ${loadedExt.kunkun.identifier} not found in database. Run Troubleshooter.`
|
description: `Extension ${loadedExt.kunkun.identifier} not found in database. Run Troubleshooter.`
|
||||||
})
|
})
|
||||||
goto("/")
|
goto("/app/")
|
||||||
}
|
}
|
||||||
return { extPath, url: extUrl, loadedExt, extInfoInDB: extInfoInDB! }
|
return { extPath, url: extUrl, loadedExt, extInfoInDB: extInfoInDB! }
|
||||||
}
|
}
|
@ -63,7 +63,7 @@
|
|||||||
|
|
||||||
async function goBack() {
|
async function goBack() {
|
||||||
if (isInMainWindow()) {
|
if (isInMainWindow()) {
|
||||||
goto("/")
|
goto("/app/")
|
||||||
} else {
|
} else {
|
||||||
appWin.close()
|
appWin.close()
|
||||||
}
|
}
|
@ -27,7 +27,7 @@ export const load: PageLoad = async ({ url }) => {
|
|||||||
if (!extPath || !cmdName) {
|
if (!extPath || !cmdName) {
|
||||||
toast.error("Invalid extension path or url")
|
toast.error("Invalid extension path or url")
|
||||||
error("Invalid extension path or url")
|
error("Invalid extension path or url")
|
||||||
goto("/")
|
goto("/app/")
|
||||||
}
|
}
|
||||||
|
|
||||||
let _loadedExt: ExtPackageJsonExtra | undefined
|
let _loadedExt: ExtPackageJsonExtra | undefined
|
||||||
@ -38,7 +38,7 @@ export const load: PageLoad = async ({ url }) => {
|
|||||||
toast.error("Error loading extension manifest", {
|
toast.error("Error loading extension manifest", {
|
||||||
description: `${err}`
|
description: `${err}`
|
||||||
})
|
})
|
||||||
goto("/")
|
goto("/app/")
|
||||||
}
|
}
|
||||||
const loadedExt = _loadedExt!
|
const loadedExt = _loadedExt!
|
||||||
const extInfoInDB = await db.getUniqueExtensionByPath(loadedExt.extPath)
|
const extInfoInDB = await db.getUniqueExtensionByPath(loadedExt.extPath)
|
||||||
@ -46,7 +46,7 @@ export const load: PageLoad = async ({ url }) => {
|
|||||||
toast.error("Unexpected Error", {
|
toast.error("Unexpected Error", {
|
||||||
description: `Extension ${loadedExt.kunkun.identifier} not found in database. Run Troubleshooter.`
|
description: `Extension ${loadedExt.kunkun.identifier} not found in database. Run Troubleshooter.`
|
||||||
})
|
})
|
||||||
goto("/")
|
goto("/app/")
|
||||||
}
|
}
|
||||||
const pkgJsonPath = await join(extPath!, "package.json")
|
const pkgJsonPath = await join(extPath!, "package.json")
|
||||||
if (!(await exists(extPath!))) {
|
if (!(await exists(extPath!))) {
|
@ -14,32 +14,32 @@
|
|||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
title: "General",
|
title: "General",
|
||||||
url: "/settings",
|
url: "/app/settings",
|
||||||
icon: Cog
|
icon: Cog
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Developer",
|
title: "Developer",
|
||||||
url: "/settings/developer",
|
url: "/app/settings/developer",
|
||||||
icon: SquareTerminal
|
icon: SquareTerminal
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Extensions",
|
title: "Extensions",
|
||||||
url: "/settings/extensions",
|
url: "/app/settings/extensions",
|
||||||
icon: Blocks
|
icon: Blocks
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Set Dev Extension",
|
title: "Set Dev Extension",
|
||||||
url: "/settings/set-dev-ext-path",
|
url: "/app/settings/set-dev-ext-path",
|
||||||
icon: Route
|
icon: Route
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Add Dev Extension",
|
title: "Add Dev Extension",
|
||||||
url: "/settings/add-dev-extension",
|
url: "/app/settings/add-dev-extension",
|
||||||
icon: FileCode2
|
icon: FileCode2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "About",
|
title: "About",
|
||||||
url: "/settings/about",
|
url: "/app/settings/about",
|
||||||
icon: Info
|
icon: Info
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -11,17 +11,17 @@
|
|||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
title: "Extension Loading",
|
title: "Extension Loading",
|
||||||
url: "/troubleshooters/extension-loading",
|
url: "/app/troubleshooters/extension-loading",
|
||||||
icon: Loader
|
icon: Loader
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Extension Window",
|
title: "Extension Window",
|
||||||
url: "/troubleshooters/extension-window",
|
url: "/app/troubleshooters/extension-window",
|
||||||
icon: AppWindow
|
icon: AppWindow
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "MDNS Debugger",
|
title: "MDNS Debugger",
|
||||||
url: "/troubleshooters/mdns-debugger",
|
url: "/app/troubleshooters/mdns-debugger",
|
||||||
icon: Network
|
icon: Network
|
||||||
}
|
}
|
||||||
]
|
]
|
32
apps/desktop/src/routes/splashscreen/+page.svelte
Normal file
32
apps/desktop/src/routes/splashscreen/+page.svelte
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Layouts } from "@kksh/ui"
|
||||||
|
import { getCurrentWindow } from "@tauri-apps/api/window"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const mainWin = await getCurrentWindow()
|
||||||
|
mainWin.show()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Layouts.Center class="h-screen w-screen">
|
||||||
|
<div class="animate-zoom-in flex flex-col items-center justify-center gap-2 pb-20">
|
||||||
|
<img src="/favicon.png" alt="Logo" />
|
||||||
|
<h2 class="font-mono text-2xl font-extrabold">Kunkun</h2>
|
||||||
|
</div>
|
||||||
|
</Layouts.Center>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.animate-zoom-in {
|
||||||
|
animation: zoom-in 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes zoom-in {
|
||||||
|
from {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user