Compare commits

..

No commits in common. "develop" and "Kunkun-v0.1.22" have entirely different histories.

262 changed files with 4286 additions and 13123 deletions

View File

@ -11,9 +11,11 @@
"jarvis",
"form-view",
"@kksh/desktop",
"@kksh/supabase",
"@kksh/utils",
"@kksh/extension",
"@kksh/schema",
"@kksh/supabase",
"@kksh/ui"
]
}

View File

@ -15,7 +15,7 @@ body:
placeholder: Bug description
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
@ -40,11 +40,4 @@ body:
label: Contributes
options:
- label: I am willing to submit a PR to fix this issue
- label: I am willing to submit a PR with failing tests
- type: checkboxes
id: searched
attributes:
label: Searched
options:
- label: I have searched for similar issues and found none
- label: I am willing to submit a PR with failing tests

View File

@ -2,7 +2,7 @@ blank_issues_enabled: false
contact_links:
- name: GitHub Discussions
url: https://github.com/kunkunsh/kunkun/discussions
about: Discussions and questions here
about: Please ask and answer questions here.
- name: 💬 Discord
url: https://discord.gg/7dzw3TYeTU
about: Please ask and answer questions here
about: Please ask and answer questions here.

View File

@ -41,9 +41,4 @@ body:
options:
- label: I am willing to submit a PR to implement this feature
- type: checkboxes
id: searched
attributes:
label: Searched
options:
- label: I have searched for similar issues and found none

View File

@ -1,23 +1,20 @@
name: Build Beta
name: Build Beta Package
on:
schedule:
- cron: "22 22 * * *"
pull_request:
branches:
- develop
workflow_dispatch:
inputs:
updater:
description: "Enable updater?"
required: true
type: boolean
default: false
default: true
platform_windows:
description: "windows"
required: true
type: boolean
default: false
default: true
platform_linux:
description: "linux"
required: true
@ -93,7 +90,7 @@ jobs:
id: setting
run: |
matrix=""
if [ "${{ github.event_name }}" == "schedule" ] || [ "${{ github.event_name }}" == "pull_request" ]; then
if [ "${{ github.event_name }}" == "schedule" ]; then
matrix="\"windows-latest\",\"ubuntu-22.04\",\"macos-14\",\"macos-13\""
build_mode=""
build_path="release"
@ -140,6 +137,8 @@ jobs:
RETENTION_DAYS: ${{ needs.preprocess.outputs.retention_days }}
FILE_PREFIX: ${{ needs.preprocess.outputs.file_prefix }}
# BUILD_TIME: ${{ needs.preprocess.outputs.build_time }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
NO_STRIP: true
steps:
- name: Checkout repository
@ -205,15 +204,12 @@ jobs:
run: pnpm prepare
- name: Build Packages
env:
NODE_OPTIONS: --max-old-space-size=4096
run: pnpm build
- name: Build the App
working-directory: apps/desktop
env:
CI: false
NODE_OPTIONS: --max-old-space-size=4096
run: pnpm tauri build ${{ env.BUILD_MODE}} ${{ matrix.os == 'windows-latest' && '-b nsis' || '' }}
- name: Rename macos-aarch64

View File

@ -5,7 +5,6 @@ on:
pull_request:
branches:
- main
- develop
jobs:
build-test:
@ -52,8 +51,6 @@ jobs:
- name: Setup
run: pnpm prepare
- name: Build
env:
NODE_OPTIONS: --max-old-space-size=4096
run: pnpm build
- name: JS Test
if: matrix.os == 'ubuntu-24.04'

View File

@ -15,16 +15,16 @@ jobs:
matrix:
settings:
- platform: "macos-14" # for Arm based macs (M1 and above).
args: "--target aarch64-apple-darwin --verbose --config src-tauri/tauri.conf.publish.json"
args: "--target aarch64-apple-darwin --verbose"
- platform: "macos-13" # for Intel based macs.
args: "--target x86_64-apple-darwin --verbose --config src-tauri/tauri.conf.publish.json"
args: "--target x86_64-apple-darwin --verbose"
# Universal Build no longer supported after adding openssl, which is not cross-compilable.
- platform: "macos-14" # for Both Arm and Intel based macs.
args: "--target universal-apple-darwin --verbose --config src-tauri/tauri.conf.publish.json"
args: "--target universal-apple-darwin --verbose"
- platform: "ubuntu-22.04" # for Tauri v1 you could replace this with ubuntu-20.04.
args: "--verbose --config src-tauri/tauri.conf.publish.json"
args: "--verbose"
- platform: "windows-latest"
args: "--verbose --config src-tauri/tauri.conf.publish.json"
args: "--verbose"
runs-on: ${{ matrix.settings.platform }}
steps:
@ -87,8 +87,6 @@ jobs:
# pnpm --filter=@kksh/ci run ci-env-check
bun packages/ci/scripts/ci-env-check.ts
- name: Build Packages
env:
NODE_OPTIONS: --max-old-space-size=4096
run: pnpm build
- name: Get App Version
if: matrix.settings.platform == 'windows-latest'
@ -98,9 +96,7 @@ jobs:
- uses: tauri-apps/tauri-action@v0
env:
CI: false
KUNKUN_PUBLISH: true
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_OPTIONS: --max-old-space-size=4096
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}

View File

@ -1,121 +0,0 @@
name: "Desktop Test build"
on:
push:
branches:
- "test-build"
tags:
- "v*"
workflow_dispatch:
jobs:
publish-tauri:
permissions:
contents: write
strategy:
fail-fast: false
matrix:
settings:
- platform: "macos-14" # for Arm based macs (M1 and above).
args: "--target aarch64-apple-darwin --verbose --config src-tauri/tauri.conf.publish.json"
- platform: "macos-13" # for Intel based macs.
args: "--target x86_64-apple-darwin --verbose --config src-tauri/tauri.conf.publish.json"
# Universal Build no longer supported after adding openssl, which is not cross-compilable.
- platform: "macos-14" # for Both Arm and Intel based macs.
args: "--target universal-apple-darwin --verbose --config src-tauri/tauri.conf.publish.json"
- platform: "ubuntu-22.04" # for Tauri v1 you could replace this with ubuntu-20.04.
args: "--verbose --config src-tauri/tauri.conf.publish.json"
- platform: "windows-latest"
args: "--verbose --config src-tauri/tauri.conf.publish.json"
runs-on: ${{ matrix.settings.platform }}
steps:
- uses: actions/checkout@v4
with:
submodules: "true"
- name: Install Dependencies (ubuntu only)
if: matrix.settings.platform == 'ubuntu-22.04' # This must match the platform value defined above.
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libxdo-dev
# You can remove the one that doesn't apply to your app to speed up the workflow a bit.
- name: Install protobuf (Mac)
if: startsWith(matrix.settings.platform, 'macos')
run: |
brew install protobuf
brew install openssl
- name: Install Protobuf (Ubuntu)
if: matrix.settings.platform == 'ubuntu-22.04'
run: |
sudo apt install -y protobuf-compiler
- uses: pnpm/action-setup@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: "pnpm" # Set this to npm, yarn or pnpm.
cache-dependency-path: ./pnpm-lock.yaml
- name: Install protoc and openssl for windows
if: matrix.settings.platform == 'windows-latest'
run: |
choco install protoc
choco install openssl
echo OPENSSL_DIR='C:\Program Files\OpenSSL' >> $env:GITHUB_ENV
echo OPENSSL_INCLUDE_DIR='C:\Program Files\OpenSSL\include' >> $env:GITHUB_ENV
echo OPENSSL_LIB_DIR='C:\Program Files\OpenSSL\lib\VC\x64\MDd' >> $env:GITHUB_ENV
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
# Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds.
targets: ${{ matrix.settings.platform == 'macos-14' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- name: Add rust target (macos only)
if: matrix.settings.platform == 'macos-14'
run: |
rustup target add aarch64-apple-darwin
rustup target add x86_64-apple-darwin
- name: Rust Cache
uses: swatinem/rust-cache@v2
with:
workspaces: ". -> target"
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install Dependencies
run: pnpm install
- name: Environment Check
run: |
# pnpm --filter=@kksh/ci run ci-env-check
bun packages/ci/scripts/ci-env-check.ts
- name: Build Packages
env:
NODE_OPTIONS: --max-old-space-size=4096
run: pnpm build
- name: Get App Version
if: matrix.settings.platform == 'windows-latest'
id: appversion
run: |
echo "version=$(node -p "require('./apps/desktop/package.json').version")" >> $env:GITHUB_OUTPUT
- uses: tauri-apps/tauri-action@v0
env:
CI: false
KUNKUN_PUBLISH: true
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_OPTIONS: --max-old-space-size=4096
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
with:
tagName: Kunkun-v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version.
releaseName: "Kunkun v__VERSION__"
releaseBody: "See the assets to download this version and install."
releaseDraft: false
prerelease: false
args: ${{ matrix.settings.args }} ${{ contains(steps.appversion.outputs.version, 'beta') && matrix.settings.platform == 'windows-latest' && '-b nsis' || '' }}
projectPath: "./apps/desktop"

2
.gitmodules vendored
View File

@ -12,4 +12,4 @@
url = https://github.com/kunkunsh/tauri-plugin-user-input.git
[submodule "vendors/tauri-plugin-keyring"]
path = vendors/tauri-plugin-keyring
url = https://github.com/HuakunShen/tauri-plugin-keyring.git
url = https://github.com/HuakunShen/tauri-plugin-keyring.git

View File

@ -1,5 +1,4 @@
.svelte-kit/
target/
vendors/**
vendors
.nuxt/

View File

@ -10,10 +10,5 @@
"titleBar.activeForeground": "#FFFBFC"
},
"svelte.enable-ts-plugin": true,
"deno.enable": false,
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/*.code-search": true
}
"deno.enable": false
}

View File

@ -26,15 +26,13 @@ If you are interested in contributing to the project, please read the following
- [cmake](https://cmake.org/)
- MacOS: `brew install cmake`
- Linux: `sudo apt install -y cmake`
- Other Linux Dependencies
- `sudo apt-get install -y protobuf-compiler libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libxdo-dev`
### Setup
```bash
git clone https://github.com/kunkunsh/kunkun.git --recursive
pnpm install
pnpm build # build submodules
pnpm prepare
```
### Run Desktop App
@ -46,15 +44,6 @@ cd apps/desktop
pnpm tauri dev
```
### Build from Source
If you have problem running the app, consider building from source to see if it works.
```bash
cd apps/desktop
pnpm tauri build
```
## i188n
If you are willing to help with the translation, please use translations in json files in `apps/desktop/messages`.

1074
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -20,8 +20,7 @@ tokio-util = "0.7.12"
mdns-sd = "0.11.1"
tauri-plugin-network = { path = "./vendors/tauri-plugin-network" }
tauri-plugin-keyring = { path = "./vendors/tauri-plugin-keyring" }
tauri-plugin-shellx = { version = "2.0.16" }
tauri-plugin-clipboard = "2.1.11"
tauri-plugin-clipboard = "2.1.8"
mac-security-rs = { path = "./packages/mac-security-rs" }
log = "0.4.22"
strum = "0.26"

118
README.md
View File

@ -1,75 +1,38 @@
![kunkun](https://socialify.git.ci/kunkunsh/kunkun/image?description=1&forks=1&issues=1&logo=https%3A%2F%2Fstorage.huakun.tech%2F2024%2F9%2F12%2F4MjHiKK.png&name=1&owner=1&pattern=Circuit%20Board&pulls=1&stargazers=1&theme=Auto)
# Kunkun
> Kunkun is a cross-platform extensible app launcher like Raycast or Alfred.
> All extensions run in a sandboxed environment by default to ensure security.
> [!WARNING]
> 🚧 Work in Progress 🚧
> This project is still in its early stages.
>
> We know its not perfect yet. The author is pouring heart, soul, and a few sleepless nights into fixing the issues. Your patience means everything.
>
> Got feedback or found a bug? Open an issue—it helps more than you know.
> This project is still in the early stages of development.
> There are known compatibility issues with Linux.
> Author is working on fixing them.
## Demo Video and Instructions
- YouTube: https://youtu.be/HfQb38s8VjY
- Download extension from https://kunkun.sh/download
![GitHub last commit](https://img.shields.io/github/last-commit/kunkunsh/kunkun)
[![YouTube badge][]][YouTube link]
[![](https://dcbadge.limes.pink/api/server/7dzw3TYeTU)](https://discord.gg/7dzw3TYeTU)
- Website: https://kunkun.sh/
- Documentation: https://docs.kunkun.sh/
[YouTube badge]: https://img.shields.io/youtube/channel/subscribers/UC1gJeFbvRcQXDC_C8nKetdA?style=social
[YouTube link]: https://www.youtube.com/@huakun
<table>
<tr>
<th>Demo Video and Instructions</th>
<th>Download</th>
<th>Platforms</th>
</tr>
<tr>
<td>
<ul>
<li><a href="https://youtu.be/HfQb38s8VjY">Introduction Video</a></li>
<li><a href="https://kunkun.sh/">Visit Website</a></li>
<li><a href="https://docs.kunkun.sh/">Documentation</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="https://kunkun.sh/download/">From Website</a></li>
<li><a href="https://github.com/kunkunsh/kunkun/releases">From GitHub Releases</a></li>
<li><a href="https://formulae.brew.sh/cask/kunkun">Via Homebrew</a></li>
</ul>
</td>
<td>
<ul>
<li>MacOS</li>
<li>Linux</li>
<li>Windows</li>
</ul>
</td>
</tr>
</table>
## Download
<table>
<tr>
<th>Extension Request</th>
</tr>
<tr>
<td>
You can <a href="https://github.com/kunkunsh/kunkun/discussions/new?category=extension-requests&body=%3E%20%5B!IMPORTANT%5D%0A%3E%20Upvote%20if%20you%20want%20this">Submit Extension Request</a>
request in the
<a href="https://github.com/kunkunsh/kunkun/discussions/categories/extension-requests?discussions_q=is%3Aopen+sort%3Atop+category%3A%22Extension+Requests%22">Extension Requests discussion</a>
section to gauge interest in your request.
<br/>
If there is significant demand, the extension may be considered for implementation.
</td>
</tr>
- From the Website: https://kunkun.sh/download/
- From GitHub Releases: https://github.com/kunkunsh/kunkun/releases
</table>
## Platforms
<a href="https://star-history.com/#kunkunsh/kunkun&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=kunkunsh/kunkun&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=kunkunsh/kunkun&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=kunkunsh/kunkun&type=Date" />
</picture>
</a>
- [x] MacOS
- [x] Linux
- [x] Windows
## Sample Extensions
@ -149,40 +112,3 @@
##### Clipboard History
![](https://i.imgur.com/uw1hJmG.png)
## Stats
![Alt](https://repobeats.axiom.co/api/embed/7105c01eb031bd6a88897d79c8713aa4251842e9.svg "Repobeats analytics image")
<!-- Copy-paste in your Readme.md file -->
<a href="https://next.ossinsight.io/widgets/official/compose-last-28-days-stats?repo_id=882158748" target="_blank" style="display: block" align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-last-28-days-stats/thumbnail.png?repo_id=882158748&image_size=auto&color_scheme=dark" width="655" height="auto">
<img alt="Performance Stats of kunkunsh/kunkun - Last 28 days" src="https://next.ossinsight.io/widgets/official/compose-last-28-days-stats/thumbnail.png?repo_id=882158748&image_size=auto&color_scheme=light" width="655" height="auto">
</picture>
</a>
<!-- Made with [OSS Insight](https://ossinsight.io/) -->
<!-- Copy-paste in your Readme.md file -->
<a href="https://next.ossinsight.io/widgets/official/compose-org-activity-growth-total?activity=stars&period=past_28_days&owner_id=176965503" target="_blank" style="display: block" align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-activity-growth-total/thumbnail.png?activity=stars&period=past_28_days&owner_id=176965503&image_size=4x7&color_scheme=dark" width="657" height="auto">
<img alt="Stars trends of kunkunsh" src="https://next.ossinsight.io/widgets/official/compose-org-activity-growth-total/thumbnail.png?activity=stars&period=past_28_days&owner_id=176965503&image_size=4x7&color_scheme=light" width="657" height="auto">
</picture>
</a>
<!-- Made with [OSS Insight](https://ossinsight.io/) -->
<!-- Copy-paste in your Readme.md file -->
<a href="https://next.ossinsight.io/widgets/official/compose-org-overview-stars?period=past_28_days&owner_id=176965503" target="_blank" style="display: block" align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-overview-stars/thumbnail.png?period=past_28_days&owner_id=176965503&image_size=2x6&color_scheme=dark" width="561" height="auto">
<img alt="Overview of Stars earned of kunkunsh" src="https://next.ossinsight.io/widgets/official/compose-org-overview-stars/thumbnail.png?period=past_28_days&owner_id=176965503&image_size=2x6&color_scheme=light" width="561" height="auto">
</picture>
</a>
<!-- Made with [OSS Insight](https://ossinsight.io/) -->

View File

@ -1,19 +1,5 @@
# kksh
## 0.1.3
### Patch Changes
- Updated dependencies
- @kksh/api@0.1.5
## 0.1.2
### Patch Changes
- Updated dependencies
- @kksh/api@0.1.4
## 0.1.1
### Patch Changes

View File

@ -1,7 +1,7 @@
{
"name": "kksh",
"module": "dist/cli.js",
"version": "0.1.3",
"version": "0.1.1",
"type": "module",
"bin": {
"kksh": "./dist/cli.js",
@ -31,7 +31,7 @@
"debug": "^4.4.0",
"fs-extra": "^11.2.0",
"inquirer": "^10.1.2",
"valibot": "^1.0.0"
"valibot": "^1.0.0-beta.10"
},
"files": [
"dist"

View File

@ -52,7 +52,7 @@ export function verifySingleProject(projectPath: string): boolean {
logger.info(`name`, pkg.name)
logger.info(`version`, pkg.version)
logger.info(`identifier`, pkg.kunkun.identifier)
if ((pkg.files?.length ?? 0) === 0) {
logger.warn(
`"files" field is empty, it is recommended to include only the necessary files, e.g. dist`

View File

@ -1,19 +1,5 @@
# create-kunkun
## 0.1.49
### Patch Changes
- Updated dependencies
- @kksh/api@0.1.5
## 0.1.48
### Patch Changes
- Updated dependencies
- @kksh/api@0.1.4
## 0.1.45
### Patch Changes

View File

@ -1,7 +1,7 @@
{
"name": "create-kunkun",
"type": "module",
"version": "0.1.49",
"version": "0.1.47",
"bin": {
"create-kunkun": "dist/index.mjs"
},
@ -27,7 +27,7 @@
"commander": "^12.1.0",
"fs-extra": "^11.2.0",
"handlebars": "^4.7.8",
"valibot": "^1.0.0"
"valibot": "^1.0.0-beta.10"
},
"files": [
"dist"

View File

@ -1,13 +0,0 @@
import { IconType } from "@kksh/api/models"
import { getExtensionsLatestPublishByIdentifier } from "@kksh/sdk"
const latestPublish = await getExtensionsLatestPublishByIdentifier({
path: {
identifier: "RAG1"
}
})
console.log(latestPublish)
// latestPublish
// console.log(typeof IconEnum.Iconify)
console.log(IconType.options)

View File

@ -1,91 +0,0 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"app_name": "KunKun",
"secondary_app_name": "KunKun",
"common_edit": "Bearbeiten",
"common_clear": "Löschen",
"common_check": "Prüfen",
"common_install": "Installieren",
"home_command_input_placeholder": "Suchen…",
"home_command_input_dropdown_quit": "Beenden",
"home_command_input_dropdown_developer_title": "Entwickler",
"home_command_input_dropdown_close_window": "Fenster schließen",
"home_command_input_dropdown_toggle_devtools": "Entwicklertools umschalten",
"home_command_input_dropdown_reload_window": "Fenster neu laden",
"home_command_input_dropdown_open_preference": "Einstellungen öffnen",
"home_command_input_dropdown_toggle_dev_extension_hmr": "Entwicklererweiterungen HMR umschalten",
"command_group_heading_dev_ext": "Entwicklererweiterungen",
"command_group_heading_ext": "Erweiterungen",
"command_group_heading_quick_links": "Quick Links",
"settings_menu_settings": "Einstellungen",
"settings_menu_general": "Allgemein",
"settings_menu_app_search_paths": "Verzeichnisse für Programme",
"settings_menu_developer": "Entwickler",
"settings_menu_extensions": "Erweiterungen",
"settings_menu_set_dev_ext": "Dev-Erweiterung festlegen",
"settings_menu_add_dev_ext": "Dev-Erweiterung hinzufügen",
"settings_menu_about": "Über",
"settings_general_launch_at_login": "Beim Systemstart öffnen",
"settings_general_hotkey": "Tastenkombination",
"settings_general_menu_bar_icon": "Menüleiste-Symbol",
"settings_general_hide_on_blur": "Automatisch ausblenden",
"settings_general_extension_auto_upgrade": "Erweiterungen automatisch aktualisieren",
"settings_general_dev_extension_hmr": "Entwicklererweiterungen HMR",
"settings_general_join_beta_updates": "Beta-Updates nutzen",
"settings_general_developer_mode": "Entwickler-Modus",
"settings_general_language": "Sprache",
"settings_general_loading_animation": "Ladeanimation",
"settings_app_search_paths_title": "Zusätzliche Verzeichnisse für die Programm-Suche",
"settings_app_search_paths_add_app_search_path": "Verzeichnis für Programm-Suche hinzufügen",
"settings_app_search_paths_table_col_search_path": "Suchpfad",
"settings_app_search_paths_table_col_depth": "Tiefe",
"settings_app_search_paths_table_col_actions": "Aktionen",
"settings_about_version": "Version",
"settings_about_author": "Autor",
"settings_about_source_code": "Quellcode",
"settings_about_extensions_source_code": "Quellcode für Erweiterungen",
"settings_about_check_for_updates": "Nach Updates suchen",
"settings_set_dev_ext_title": "Verzeichnis der Entwicklererweiterungen",
"settings_set_dev_ext_description": "Hier werden Entwicklererweiterungen installiert.",
"settings_set_dev_ext_enter_path": "Verzeichnis eingeben",
"settings_extensions_title": "Deine Erweiterungen",
"settings_extensions_table_col_name": "Name",
"settings_extensions_table_col_identifier": "Identifikator",
"settings_extensions_table_col_type": "Typ",
"settings_extensions_table_col_version": "Version",
"settings_extensions_table_col_uninstall": "Deinstallieren",
"settings_add_dev_ext_title": "Entwicklererweiterung hinzufügen",
"settings_add_dev_ext_description": "Es gibt vier Möglichkeiten, eine Erweiterung als Entwicklererweiterung zu installieren. Tarball-Archiv, lokales Verzeichnis, URL zu Tarball-Archiv oder NPM-Paketnamen.",
"settings_add_dev_ext_install_from_ext_folders": "Verzeichnis",
"settings_add_dev_ext_install_from_ext_files": "Tarball-Archiv",
"settings_add_dev_ext_drag_and_drop": "Drag and Drop",
"settings_add_dev_ext_drag_and_drop_strike": "Drag and Drop",
"settings_add_dev_ext_drag_and_drop2": "Verzeichnis oder Tarball-Archiv",
"settings_add_dev_ext_install_tarball_from_url": "Tarball-Archiv aus URL installieren",
"troubleshooters_sidebar_title": "Fehlerbehebung",
"troubleshooters_sidebar_extension_loading_title": "Ladevorgang",
"troubleshooters_sidebar_extension_window_title": "Darstellung",
"troubleshooters_sidebar_mdns_debugger_title": "MDNS-Debugger",
"troubleshooters_extension_window_title": "Fehlerbehebung für die Darstellung von Erweiterungen",
"troubleshooters_extension_window_refresh_every_second": "Jede Sekunde neu laden",
"troubleshooters_extension_window_refresh": "Neu laden",
"troubleshooters_extension_window_refreshed": "{count}x neu geladen",
"troubleshooters_extension_loading_title": "Fehlerbehebung für den Ladevorgang von Erweiterungen",
"troubleshooters_extension_loading_table_col_identifier": "Identifikator",
"troubleshooters_extension_loading_table_col_path": "Verzeichnis",
"troubleshooters_extension_loading_table_col_error": "Fehler"
}

View File

@ -24,7 +24,6 @@
"settings_menu_settings": "Settings",
"settings_menu_general": "General",
"settings_menu_app_search_paths": "App Search Paths",
"settings_menu_developer": "Developer",
"settings_menu_extensions": "Extensions",
"settings_menu_set_dev_ext": "Set Dev Extension",
@ -40,13 +39,6 @@
"settings_general_join_beta_updates": "Join Beta Updates",
"settings_general_developer_mode": "Developer Mode",
"settings_general_language": "Language",
"settings_general_loading_animation": "Loading Animation",
"settings_app_search_paths_title": "Extra App Search Paths",
"settings_app_search_paths_add_app_search_path": "Add App Search Path",
"settings_app_search_paths_table_col_search_path": "Search Path",
"settings_app_search_paths_table_col_depth": "Depth",
"settings_app_search_paths_table_col_actions": "Actions",
"settings_about_version": "Version",
"settings_about_author": "Author",
@ -70,7 +62,6 @@
"settings_add_dev_ext_install_from_ext_folders": "Install from Extension Folders",
"settings_add_dev_ext_install_from_ext_files": "Install from Extension Tarball File",
"settings_add_dev_ext_drag_and_drop": "Drag and Drop",
"settings_add_dev_ext_drag_and_drop_strike": "Drag and Drop",
"settings_add_dev_ext_drag_and_drop2": "Extension Folder or Tarball",
"settings_add_dev_ext_install_tarball_from_url": "Install Tarball From URL",

View File

@ -1,84 +0,0 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"app_name": "KunKun",
"secondary_app_name": "KunKun",
"common_edit": "Editar",
"common_clear": "Limpar",
"common_check": "Verificar",
"common_install": "Instalar",
"home_command_input_placeholder": "Digite \"\/\" para buscar...",
"home_command_input_dropdown_quit": "Sair",
"home_command_input_dropdown_developer_title": "Desenvolvedor",
"home_command_input_dropdown_close_window": "Fechar Janela",
"home_command_input_dropdown_toggle_devtools": "Alternar Ferramentas de Desenvolvedor",
"home_command_input_dropdown_reload_window": "Recarregar Janela",
"home_command_input_dropdown_open_preference": "Abrir Preferências",
"home_command_input_dropdown_toggle_dev_extension_hmr": "Alternar HMR de Extensão de Desenvolvedor",
"command_group_heading_dev_ext": "Extensões de Desenvolvedor",
"command_group_heading_ext": "Extensões",
"command_group_heading_quick_links": "Links Rápidos",
"settings_menu_settings": "Configurações",
"settings_menu_general": "Geral",
"settings_menu_developer": "Desenvolvedor",
"settings_menu_extensions": "Extensões",
"settings_menu_set_dev_ext": "Definir Extensão de Desenvolvedor",
"settings_menu_add_dev_ext": "Adicionar Extensão de Desenvolvedor",
"settings_menu_about": "Sobre",
"settings_general_launch_at_login": "Iniciar ao Fazer Login",
"settings_general_hotkey": "Tecla de Atalho",
"settings_general_menu_bar_icon": "Ícone na Barra de Menu",
"settings_general_hide_on_blur": "Ocultar ao Perder Foco",
"settings_general_extension_auto_upgrade": "Atualização Automática de Extensões",
"settings_general_dev_extension_hmr": "HMR de Extensão de Desenvolvedor",
"settings_general_join_beta_updates": "Participar das Atualizações Beta",
"settings_general_developer_mode": "Modo Desenvolvedor",
"settings_general_language": "Idioma",
"settings_general_loading_animation": "Animação de Carregamento",
"settings_about_version": "Versão",
"settings_about_author": "Autor",
"settings_about_source_code": "Código Fonte",
"settings_about_extensions_source_code": "Código Fonte das Extensões",
"settings_about_check_for_updates": "Verificar Atualizações",
"settings_set_dev_ext_title": "Definir Caminho da Extensão de Desenvolvedor",
"settings_set_dev_ext_description": "Aqui é onde suas extensões serão instaladas.",
"settings_set_dev_ext_enter_path": "Digite o Caminho",
"settings_extensions_title": "Suas Extensões",
"settings_extensions_table_col_name": "Nome",
"settings_extensions_table_col_identifier": "Identificador",
"settings_extensions_table_col_type": "Tipo",
"settings_extensions_table_col_version": "Versão",
"settings_extensions_table_col_uninstall": "Desinstalar",
"settings_add_dev_ext_title": "Adicionar Extensão de Desenvolvedor",
"settings_add_dev_ext_description": "Existem 4 opções para instalar uma extensão em modo desenvolvedor. Você pode carregá-la de um arquivo tarball local, pasta local, URL remota de tarball ou nome de pacote npm.",
"settings_add_dev_ext_install_from_ext_folders": "Instalar de Pastas de Extensão",
"settings_add_dev_ext_install_from_ext_files": "Instalar de Arquivo Tarball de Extensão",
"settings_add_dev_ext_drag_and_drop": "Arraste e Solte",
"settings_add_dev_ext_drag_and_drop_strike": "Arraste e Solte",
"settings_add_dev_ext_drag_and_drop2": "Pasta ou Tarball da Extensão",
"settings_add_dev_ext_install_tarball_from_url": "Instalar Tarball da URL",
"troubleshooters_sidebar_title": "Solucionadores de Problemas",
"troubleshooters_sidebar_extension_loading_title": "Carregamento de Extensão",
"troubleshooters_sidebar_extension_window_title": "Janela da Extensão",
"troubleshooters_sidebar_mdns_debugger_title": "Depurador MDNS",
"troubleshooters_extension_window_title": "Solucionador de Problemas da Janela de Extensão",
"troubleshooters_extension_window_refresh_every_second": "Atualizar a Cada Segundo",
"troubleshooters_extension_window_refresh": "Atualizar",
"troubleshooters_extension_window_refreshed": "Atualizado {count} vezes",
"troubleshooters_extension_loading_title": "Solucionador de Problemas do Carregamento de Extensão",
"troubleshooters_extension_loading_table_col_identifier": "Identificador",
"troubleshooters_extension_loading_table_col_path": "Caminho",
"troubleshooters_extension_loading_table_col_error": "Erro"
}

View File

@ -39,7 +39,6 @@
"settings_general_join_beta_updates": "Получать бета-обновления",
"settings_general_developer_mode": "Режим разработчика",
"settings_general_language": "Язык",
"settings_general_loading_animation": "Анимация загрузки",
"settings_about_version": "Версия",
"settings_about_author": "Автор",
@ -62,10 +61,8 @@
"settings_add_dev_ext_description": "В режиме разработчика у вас есть четыре способа установки dev-расширений: из локального tar-архива, из локальной директории, по URL-адресу на tar-архив либо по названию пакета npm.",
"settings_add_dev_ext_install_from_ext_folders": "Установить из директори",
"settings_add_dev_ext_install_from_ext_files": "Установить из tar-архива",
"settings_add_dev_ext_drag_and_drop_strike": "Локальные расширения",
"settings_add_dev_ext_drag_and_drop": "Можете перетащить сюда расширения",
"settings_add_dev_ext_drag_and_drop2": "в виде директории или tar-архива",
"settings_add_dev_ext_drag_and_drop": "Локальные расширения",
"settings_add_dev_ext_drag_and_drop2": "в виде директории или tar-архива могут быть перемещены сюда",
"settings_add_dev_ext_install_tarball_from_url": "Установить tar-архив по URL-адресу",
"troubleshooters_sidebar_title": "Диагностика",

View File

@ -1,84 +0,0 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"app_name": "KunKun",
"secondary_app_name": "KunKun",
"common_edit": "Sửa",
"common_clear": "Xoá",
"common_check": "Kiểm tra",
"common_install": "Cài đặt",
"home_command_input_placeholder": "Gõ \"\/\" để tìm kiếm...",
"home_command_input_dropdown_quit": "Thoát",
"home_command_input_dropdown_developer_title": "Nhà phát triển",
"home_command_input_dropdown_close_window": "Đóng cửa sổ",
"home_command_input_dropdown_toggle_devtools": "Bật\/Tắt DevTools",
"home_command_input_dropdown_reload_window": "Khởi động lại",
"home_command_input_dropdown_open_preference": "Mở cài đặt",
"home_command_input_dropdown_toggle_dev_extension_hmr": "Bật\/tắt HMR cho tiện ích đang phát triển",
"command_group_heading_dev_ext": "Tiện Ích Mở Rộng Đang Phát Triển",
"command_group_heading_ext": "Tiện Ích Mở Rộng",
"command_group_heading_quick_links": "Liên kết nhanh",
"settings_menu_settings": "Cài đặt",
"settings_menu_general": "Chung",
"settings_menu_developer": "Nhà phát triển",
"settings_menu_extensions": "Tiện ích mở rộng",
"settings_menu_set_dev_ext": "Đường dẫn tiện ích",
"settings_menu_add_dev_ext": "Thêm tiện ích",
"settings_menu_about": "Thông tin",
"settings_general_launch_at_login": "Khởi động khi đăng nhập",
"settings_general_hotkey": "Phím tắt",
"settings_general_menu_bar_icon": "Biểu tượng thanh menu",
"settings_general_hide_on_blur": "Ẩn khi chuyển sang ứng dụng khác",
"settings_general_extension_auto_upgrade": "Tự động nâng cấp tiện ích mở rộng",
"settings_general_dev_extension_hmr": "HMR cho tiện ích mở rộng đang phát triển",
"settings_general_join_beta_updates": "Cài đặt cập nhật thử nghiệm (beta)",
"settings_general_developer_mode": "Chế độ nhà phát triển",
"settings_general_language": "Ngôn ngữ",
"settings_general_loading_animation": "Hình ảnh tải",
"settings_about_version": "Phiên bản",
"settings_about_author": "Tác giả",
"settings_about_source_code": "Mã nguồn",
"settings_about_extensions_source_code": "Mã nguồn tiện ích mở rộng",
"settings_about_check_for_updates": "Kiểm tra cập nhật",
"settings_set_dev_ext_title": "Đặt đường dẫn tiện ích mở rộng đang phát triển",
"settings_set_dev_ext_description": "Đây là đường dẫn cài đặt các tiện ích mở rộng của bạn.",
"settings_set_dev_ext_enter_path": "Nhập đường dẫn",
"settings_extensions_title": "Tiện ích mở rộng của bạn",
"settings_extensions_table_col_name": "Tên",
"settings_extensions_table_col_identifier": "Định danh",
"settings_extensions_table_col_type": "Loại",
"settings_extensions_table_col_version": "Phiên bản",
"settings_extensions_table_col_uninstall": "Gỡ cài đặt",
"settings_add_dev_ext_title": "Thêm tiện ích đang phát triển",
"settings_add_dev_ext_description": "Có 4 cách để cài đặt một tiện ích mở rộng trong chế độ nhà phát triển. Nạp nó từ file tarball, thư mục từ máy tính, file tarball từ liên kết ngoài, hoặc tên gói npm.",
"settings_add_dev_ext_install_from_ext_folders": "Cài Đặt Từ Thư Mục Tiện Ích",
"settings_add_dev_ext_install_from_ext_files": "Cài Đặt Từ File Tarball",
"settings_add_dev_ext_drag_and_drop": "Kéo và Thả",
"settings_add_dev_ext_drag_and_drop_strike": "Kéo và Thả",
"settings_add_dev_ext_drag_and_drop2": "Thư Mục Tiện Ích hoặc File Tarball",
"settings_add_dev_ext_install_tarball_from_url": "Cài Đặt File Tarball Từ Liên kết",
"troubleshooters_sidebar_title": "Khắc Phục Sự Cố",
"troubleshooters_sidebar_extension_loading_title": "Tải Tiện ích Mở Rộng",
"troubleshooters_sidebar_extension_window_title": "Cửa sổ Tiện ích Mở Rộng",
"troubleshooters_sidebar_mdns_debugger_title": "Trình Gỡ Lỗi MDNS",
"troubleshooters_extension_window_title": "Khắc Phục Sự Cố Cửa sổ Tiện ích Mở Rộng",
"troubleshooters_extension_window_refresh_every_second": "Làm mới mỗi giây",
"troubleshooters_extension_window_refresh": "Làm mới",
"troubleshooters_extension_window_refreshed": "Đã làm mới {count} lần",
"troubleshooters_extension_loading_title": "Khắc Phục Sự Cố Tải Tiện ích Mở Rộng",
"troubleshooters_extension_loading_table_col_identifier": "Định danh",
"troubleshooters_extension_loading_table_col_path": "Đường dẫn",
"troubleshooters_extension_loading_table_col_error": "Lỗi"
}

View File

@ -24,7 +24,6 @@
"settings_menu_settings": "设置",
"settings_menu_general": "通用",
"settings_menu_app_search_paths": "应用搜索路径",
"settings_menu_developer": "开发者",
"settings_menu_extensions": "插件",
"settings_menu_set_dev_ext": "设置开发插件",
@ -40,13 +39,6 @@
"settings_general_join_beta_updates": "加入 Beta 更新",
"settings_general_developer_mode": "开发者模式",
"settings_general_language": "语言",
"settings_general_loading_animation": "加载动画",
"settings_app_search_paths_title": "额外应用搜索路径",
"settings_app_search_paths_add_app_search_path": "添加应用搜索路径",
"settings_app_search_paths_table_col_search_path": "搜索路径",
"settings_app_search_paths_table_col_depth": "深度",
"settings_app_search_paths_table_col_actions": "操作",
"settings_about_version": "版本",
"settings_about_author": "作者",
@ -70,7 +62,6 @@
"settings_add_dev_ext_install_from_ext_folders": "从插件文件夹安装",
"settings_add_dev_ext_install_from_ext_files": "从插件 tarball 文件安装",
"settings_add_dev_ext_drag_and_drop": "拖放",
"settings_add_dev_ext_drag_and_drop_strike": "拖放",
"settings_add_dev_ext_drag_and_drop2": "插件文件夹或 tarball",
"settings_add_dev_ext_install_tarball_from_url": "从 tarball URL 安装",

View File

@ -1,6 +1,6 @@
{
"name": "@kksh/desktop",
"version": "0.1.37",
"version": "0.1.22",
"description": "",
"type": "module",
"scripts": {
@ -16,55 +16,46 @@
"license": "MIT",
"dependencies": {
"@formkit/auto-animate": "^0.8.2",
"@inlang/paraglide-sveltekit": "0.16.0",
"@kksh/drizzle": "workspace:*",
"@inlang/paraglide-sveltekit": "0.15.5",
"@kksh/extension": "workspace:*",
"@kksh/supabase": "workspace:*",
"@kksh/svelte5": "^0.1.15",
"@kksh/ui": "workspace:*",
"@kksh/utils": "workspace:*",
"@std/semver": "npm:@jsr/std__semver@^1.0.4",
"@supabase/supabase-js": "^2.49.1",
"@tanstack/table-core": "^8.21.2",
"@tauri-apps/api": "^2.3.0",
"@tauri-apps/plugin-autostart": "^2.2.0",
"@std/semver": "npm:@jsr/std__semver@^1.0.3",
"@supabase/supabase-js": "^2.48.0",
"@tanstack/table-core": "^8.20.5",
"@tauri-apps/api": "^2.1.1",
"@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-sql": "^2.2.0",
"@tauri-apps/plugin-stronghold": "^2.2.0",
"@tauri-store/svelte": "^2.1.1",
"dompurify": "^3.2.4",
"drizzle-orm": "^0.41.0",
"eslint": "^9.21.0",
"fuse.js": "^7.1.0",
"gsap": "^3.12.7",
"kkrpc": "^0.2.2",
"dompurify": "^3.2.3",
"gsap": "^3.12.5",
"kkrpc": "^0.1.1",
"lz-string": "^1.5.0",
"pretty-bytes": "^6.1.1",
"semver": "^7.7.1",
"svelte-inspect-value": "^0.5.0",
"semver": "^7.6.3",
"svelte-sonner": "^0.3.28",
"sveltekit-superforms": "^2.23.1",
"sveltekit-superforms": "^2.22.1",
"tauri-plugin-clipboard-api": "^2.1.11",
"tauri-plugin-shellx-api": "^2.0.16",
"tauri-plugin-svelte": "1.2.1",
"tauri-plugin-user-input-api": "workspace:*",
"uuid": "^11.1.0"
"uuid": "^11.0.3"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@eslint/js": "^9.19.0",
"@inlang/paraglide-js": "1.11.8",
"@kksh/types": "workspace:*",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.17.3",
"@sveltejs/kit": "^2.17.1",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/typography": "^0.5.16",
"@tauri-apps/cli": "^2.3.1",
"@tauri-apps/cli": "^2.2.7",
"@types/bun": "latest",
"@types/semver": "^7.5.8",
"@typescript-eslint/eslint-plugin": "^8.25.0",
"@typescript-eslint/parser": "^8.25.0",
"@typescript-eslint/eslint-plugin": "^8.23.0",
"@typescript-eslint/parser": "^8.23.0",
"autoprefixer": "^10.4.20",
"bits-ui": "1.0.0-next.86",
"clsx": "^2.1.1",
@ -72,15 +63,15 @@
"eslint-plugin-svelte": "^2.46.1",
"globals": "^15.14.0",
"lucide-svelte": "^0.474.0",
"prettier": "^3.5.2",
"prettier": "^3.4.2",
"svelte-radix": "^2.0.1",
"tailwind-merge": "^2.6.0",
"tailwind-variants": "^0.3.1",
"tailwind-merge": "^2.5.5",
"tailwind-variants": "^0.3.0",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
"tslib": "^2.8.1",
"typescript": "^5.6.3",
"typescript-eslint": "^8.25.0",
"vite": "^6.2.0"
"typescript-eslint": "^8.20.0",
"vite": "^6.0.3"
}
}

View File

@ -1,7 +1,7 @@
{
"$schema": "https://inlang.com/schema/project-settings",
"sourceLanguageTag": "en",
"languageTags": ["en", "zh", "ru", "pt", "vi", "de"],
"languageTags": ["en", "zh", "ru"],
"modules": [
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js",

View File

@ -14,10 +14,10 @@ name = "kunkun_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2.0.5", features = [] }
tauri-build = { version = "2.0.3", features = [] }
[dependencies]
tauri = { version = "2.2.5", features = [
tauri = { version = "2.1.1", features = [
"macos-private-api",
"image-png",
"image-ico",
@ -34,12 +34,12 @@ chrono = { workspace = true }
log = { workspace = true }
urlencoding = "2.1.3"
tauri-plugin-process = "2.2.0"
tauri-plugin-shellx = { workspace = true }
tauri-plugin-fs = { version = "2.2.0", features = ["watch"] }
tauri-plugin-shellx = "2.0.12"
tauri-plugin-fs = "2.2.0"
tauri-plugin-dialog = "2.2.0"
tauri-plugin-notification = "2.2.1"
tauri-plugin-notification = "2.2.0"
tauri-plugin-os = "2.2.0"
tauri-plugin-http = "2.3.0"
tauri-plugin-http = "2.2.0"
tauri-plugin-upload = { workspace = true }
# tauri-plugin-upload = "2.2.1"
tauri-plugin-jarvis = { workspace = true }
@ -50,26 +50,23 @@ tauri-plugin-user-input = { workspace = true }
tauri-plugin-clipboard = { workspace = true }
tauri-plugin-store = "2.2.0"
tauri-plugin-deep-link = "2.2.0"
tauri-plugin-log = { version = "2.2.1", features = ["colored"] }
tauri-plugin-log = { version = "2.2.0", features = ["colored"] }
crypto = { workspace = true }
zip = "2.2.2"
uuid = "1.14.0"
uuid = "1.11.0"
# tauri-plugin-devtools = "2.0.0"
obfstr = { workspace = true }
base64 = { workspace = true }
tauri-plugin-stronghold = "2.2.0"
tauri-plugin-sql = "2"
[target."cfg(target_os = \"macos\")".dependencies]
cocoa = "0.24.1"
mac-security-rs = { workspace = true }
objc = "0.2.7"
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
tauri-plugin-autostart = "2"
tauri-plugin-cli = "2"
tauri-plugin-global-shortcut = "2.0.1"
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
tauri-plugin-updater = "2.0.2"
tauri-plugin-svelte = "2.1.1"

View File

@ -24,7 +24,6 @@
"core:event:default",
"core:window:default",
"core:window:allow-set-size",
"core:window:allow-set-enabled",
"core:window:allow-start-dragging",
"core:window:allow-set-focus",
"core:window:allow-toggle-maximize",
@ -40,7 +39,6 @@
"core:webview:allow-create-webview",
"core:webview:allow-create-webview-window",
"core:app:default",
"core:app:allow-app-hide",
"core:resources:default",
"core:menu:default",
"core:tray:default",
@ -61,7 +59,6 @@
"shellx:allow-execute",
"shellx:allow-open",
"shellx:allow-kill",
"shellx:allow-kill-pid",
"shellx:allow-spawn",
"shellx:allow-stdin-write",
"shellx:allow-fix-path-env",
@ -114,7 +111,28 @@
"identifier": "fs:scope",
"allow": [
{
"path": "**/*"
"path": "$DESKTOP"
},
{
"path": "$DESKTOP/**"
},
{
"path": "$DOWNLOAD"
},
{
"path": "$DOWNLOAD/**"
},
{
"path": "$DOCUMENT"
},
{
"path": "$DOCUMENT/**"
},
{
"path": "$TEMP/**"
},
{
"path": "$TEMP"
}
]
},
@ -146,9 +164,6 @@
}
]
},
"deep-link:default",
"autostart:allow-enable",
"autostart:allow-disable",
"autostart:allow-is-enabled"
"deep-link:default"
]
}

View File

@ -1,5 +0,0 @@
{
"identifier": "svelte",
"windows": ["*"],
"permissions": ["svelte:default", "core:event:default"]
}

View File

@ -7,10 +7,8 @@ use log;
#[cfg(target_os = "macos")]
use tauri::ActivationPolicy;
use tauri::Manager;
use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_deep_link::DeepLinkExt;
use tauri_plugin_jarvis::{
constants::KUNKUN_PUBLISH,
db::JarvisDB,
server::Protocol,
utils::{
@ -27,7 +25,7 @@ use utils::server::tauri_file_server;
pub fn run() {
let context = tauri::generate_context!();
let mut builder = tauri::Builder::default();
// let app_data_path = tauri::path::PathResolver::app_data_dir().unwrap();
// let db_key = if cfg!(debug_assertions) {
// None
// } else {
@ -65,29 +63,14 @@ pub fn run() {
// .build(),
// );
// }
if KUNKUN_PUBLISH == "true" {
// only include updater with KUNKUN_PUBLISH = true, this is used to avoid using updater in beta build CI, which requires secrets for the updater endpoint if updater is included
println!("KUNKUN_PUBLISH: {}", KUNKUN_PUBLISH);
builder = builder.plugin(tauri_plugin_updater::Builder::new().build());
}
let shell_unlocked = true;
builder = builder
.plugin(tauri_plugin_single_instance::init(|app, args, _cwd| {
let window = app.get_webview_window("main").expect("no main window");
// if toggle is passed, we want to show/hide the main window
if args.get(1).map_or(false, |arg| arg == "toggle") {
if window.is_visible().unwrap_or(false) {
log::info!("hiding main window");
window.hide().unwrap();
} else {
log::info!("showing main window");
window.show().unwrap();
window.set_focus().unwrap();
};
} else {
let _ = window.set_focus();
}
.plugin(tauri_plugin_single_instance::init(|app, args, cwd| {
let _ = app
.get_webview_window("main")
.expect("no main window")
.set_focus();
}))
.plugin(
tauri_plugin_log::Builder::new()
@ -108,22 +91,13 @@ pub fn run() {
.build(),
)
.plugin(tauri_plugin_cli::init())
.plugin(
tauri_plugin_sql::Builder::default()
// .add_migrations("sqlite:mydatabase.db", migrations)
.build(),
)
.plugin(tauri_plugin_user_input::init())
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_svelte::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_autostart::init(
MacosLauncher::LaunchAgent,
Some(vec![]),
))
.plugin(tauri_plugin_upload::init())
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_store::Builder::default().build())
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
.plugin(tauri_plugin_dialog::init())
@ -137,7 +111,7 @@ pub fn run() {
.plugin(tauri_plugin_network::init())
.plugin(tauri_plugin_system_info::init())
.invoke_handler(tauri::generate_handler![
commands::keyring::get_stronghold_key,
commands::keyring::get_stronghold_key
]);
let app = builder
@ -149,7 +123,7 @@ pub fn run() {
})
.register_uri_scheme_protocol("ext", |app, request| {
let app_handle = app.app_handle();
// app_handle.
let win_label = app.webview_label();
let jarvis_state = app_handle.state::<tauri_plugin_jarvis::JarvisState>();
let window_ext_map = jarvis_state.window_label_ext_map.lock().unwrap();
@ -236,8 +210,7 @@ pub fn run() {
}
// setup::deeplink::setup_deeplink(app);
// #[cfg(all(target_os = "macos", debug_assertions))]
#[cfg(target_os = "macos")]
app.set_activation_policy(ActivationPolicy::Accessory);
// app.set_activation_policy(ActivationPolicy::Accessory);
// let mut store = StoreBuilder::new("appConfig.bin").build(app.handle().clone());
// let store = app.handle().store_builder("appConfig.json").build()?;

View File

@ -5,7 +5,7 @@
"identifier": "sh.kunkun.desktop",
"build": {
"beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:1566",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "pnpm build",
"frontendDist": "../build"
},
@ -20,24 +20,19 @@
"url": "/app",
"title": "Kunkun",
"width": 800,
"label": "main",
"visible": false,
"height": 600,
"decorations": true,
"center": true
"decorations": true
},
{
"url": "/splashscreen",
"visible": true,
"label": "splashscreen",
"center": true
"label": "splashscreen"
}
]
},
"bundle": {
"macOS": {
"minimumSystemVersion": "10.15"
},
"createUpdaterArtifacts": true,
"fileAssociations": [
{
"ext": ["kunkun"],
@ -60,6 +55,10 @@
"fs": {
"requireLiteralLeadingDot": false
},
"updater": {
"endpoints": ["https://updater.kunkun.sh"],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDc1NENCRjZFM0JBOEQ0ODMKUldTRDFLZzdicjlNZFhHS0ZKYk13WkdZUTFUM01LNjkvVW5Bb2x1SnB1R0crbFRuMnlRSlJ0STgK"
},
"deep-link": {
"desktop": {
"schemes": ["kunkun"]

View File

@ -1,11 +0,0 @@
{
"bundle": {
"createUpdaterArtifacts": true
},
"plugins": {
"updater": {
"endpoints": ["https://updater.kunkun.sh"],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDc1NENCRjZFM0JBOEQ0ODMKUldTRDFLZzdicjlNZFhHS0ZKYk13WkdZUTFUM01LNjkvVW5Bb2x1SnB1R0crbFRuMnlRSlJ0STgK"
}
}
}

View File

@ -11,7 +11,6 @@ import { WebviewWindow } from "@tauri-apps/api/webviewWindow"
import { exit } from "@tauri-apps/plugin-process"
import { dev } from "$app/environment"
import { goto } from "$app/navigation"
import Fuse from "fuse.js"
import { toast } from "svelte-sonner"
import { derived } from "svelte/store"
import * as clipboard from "tauri-plugin-clipboard-api"
@ -242,23 +241,6 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
},
keywords: ["extension", "troubleshooter"]
},
{
name: "ORM Troubleshooter",
icon: {
type: IconEnum.Iconify,
value: "material-symbols:database"
},
description: "",
flags: {
developer: true,
dev: true
},
function: async () => {
appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/troubleshooters/orm"))
},
keywords: ["extension", "troubleshooter", "database", "orm"]
},
{
name: "Create Quicklink",
icon: {
@ -428,7 +410,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
visible: false
})
setTimeout(() => {
window.show().then(() => window.setFocus())
window.show()
}, 2_000)
}
},
@ -493,19 +475,11 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
}
].map((cmd) => ({ ...cmd, id: uuidv4() }))
export const fuse = new Fuse<BuiltinCmd>(rawBuiltinCmds, {
includeScore: true,
threshold: 0.2,
keys: ["name"]
})
export const builtinCmds = derived([appConfig, appState], ([$appConfig, $appState]) => {
return $appState.searchTerm
? fuse
.search($appState.searchTerm)
.map((result) => result.item)
.filter(
(cmd) => (!cmd.flags?.developer || $appConfig.developerMode) && (!cmd.flags?.dev || dev)
)
: rawBuiltinCmds
return rawBuiltinCmds.filter((cmd) => {
const passDeveloper = cmd.flags?.developer ? $appConfig.developerMode : true
const passDev = cmd.flags?.dev ? dev : true
return passDeveloper && passDev
})
// .filter((cmd) => commandScore(cmd.name, $appState.searchTerm, cmd.keywords) > 0.5)
})

View File

@ -2,23 +2,17 @@ import { i18n } from "@/i18n"
import { appState } from "@/stores"
import { winExtMap } from "@/stores/winExtMap"
import { helperAPI } from "@/utils/helper"
import { paste } from "@/utils/hotkey"
import { decideKkrpcSerialization } from "@/utils/kkrpc"
import { sleep } from "@/utils/time"
import { trimSlash } from "@/utils/url"
import { constructExtensionSupportDir } from "@kksh/api"
import { spawnExtensionFileServer } from "@kksh/api/commands"
import { db, spawnExtensionFileServer } from "@kksh/api/commands"
import type { HeadlessCommand } from "@kksh/api/headless"
import { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models"
import { constructJarvisServerAPIWithPermissions, type IApp } from "@kksh/api/ui"
import { db } from "@kksh/drizzle"
import { launchNewExtWindow, loadExtensionManifestFromDisk } from "@kksh/extension"
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
import { convertFileSrc } from "@tauri-apps/api/core"
import * as path from "@tauri-apps/api/path"
import { getCurrentWindow } from "@tauri-apps/api/window"
import * as fs from "@tauri-apps/plugin-fs"
import { info } from "@tauri-apps/plugin-log"
import { platform } from "@tauri-apps/plugin-os"
import { goto } from "$app/navigation"
import { RPCChannel, WorkerParentIO } from "kkrpc/browser"
@ -26,12 +20,10 @@ import * as v from "valibot"
export const KunkunIframeExtParams = v.object({
url: v.string(),
cmdName: v.optional(v.string()),
extPath: v.string()
})
export type KunkunIframeExtParams = v.InferOutput<typeof KunkunIframeExtParams>
export const KunkunTemplateExtParams = v.object({
url: v.optional(v.string()),
extPath: v.string(),
cmdName: v.string()
})
@ -44,10 +36,10 @@ export async function createExtSupportDir(extPath: string) {
}
}
function setTemplateExtParams(extPath: string, cmdName: string, url?: string) {
function setTemplateExtParams(extPath: string, cmdName: string) {
localStorage.setItem(
"kunkun-template-ext-params",
JSON.stringify({ extPath, cmdName, url } satisfies KunkunTemplateExtParams)
JSON.stringify({ extPath, cmdName } satisfies KunkunTemplateExtParams)
)
}
@ -58,15 +50,13 @@ export async function onTemplateUiCmdSelect(
) {
await createExtSupportDir(ext.extPath)
const url = `/app/extension/ui-worker?extPath=${encodeURIComponent(ext.extPath)}&cmdName=${encodeURIComponent(cmd.name)}`
setTemplateExtParams(ext.extPath, cmd.name, url)
setTemplateExtParams(ext.extPath, cmd.name)
if (cmd.window) {
const winLabel = await winExtMap.registerExtensionWithWindow({ extPath: ext.extPath })
const paramsStr = JSON.stringify({
url,
extPath: ext.extPath,
cmdName: cmd.name
} satisfies KunkunIframeExtParams)
localStorage.setItem("kunkun-template-ext-params", paramsStr)
localStorage.setItem(
"kunkun-template-ext-params",
JSON.stringify({ url, extPath: ext.extPath } satisfies KunkunIframeExtParams)
)
const window = launchNewExtWindow(winLabel, url, cmd.window)
window.onCloseRequested(async (event) => {
await winExtMap.unregisterExtensionFromWindow(winLabel)
@ -88,7 +78,6 @@ export async function onHeadlessCmdSelect(
const loadedExt = await loadExtensionManifestFromDisk(
await path.join(ext.extPath, "package.json")
)
const scriptPath = await path.join(loadedExt.extPath, cmd.main)
const workerScript = await fs.readTextFile(scriptPath)
const blob = new Blob([workerScript], { type: "application/javascript" })
@ -100,21 +89,7 @@ export async function onHeadlessCmdSelect(
}
const serverAPI: IKunkunFullServerAPI = constructJarvisServerAPIWithPermissions(
loadedExt.kunkun.permissions,
loadedExt.extPath,
{
recordSpawnedProcess: async (pid: number) => {
console.log("recordSpawnedProcess pid", pid)
},
getSpawnedProcesses: async () => {
console.log("getSpawnedProcesses")
return []
},
paste: async () => {
await getCurrentWindow().hide()
await sleep(200)
return paste()
}
}
loadedExt.extPath
)
const serverAPI2 = {
...serverAPI,
@ -128,15 +103,8 @@ export async function onHeadlessCmdSelect(
} satisfies IApp
}
const io = new WorkerParentIO(worker)
const kkrpcSerialization = decideKkrpcSerialization(loadedExt)
info(
`Establishing kkrpc connection for ${loadedExt.kunkun.identifier} with serialization: ${kkrpcSerialization}`
)
const rpc = new RPCChannel<typeof serverAPI2, HeadlessCommand>(io, {
expose: serverAPI2,
serialization: {
version: kkrpcSerialization
}
expose: serverAPI2
})
const workerAPI = rpc.getAPI()
await workerAPI.load()
@ -195,8 +163,7 @@ export async function onCustomUiCmdSelect(
extPath: ext.extPath,
dist: cmd.dist
})
const _platform = platform()
if ((_platform === "windows" || _platform === "linux") && !useDevMain) {
if (platform() === "windows" && !useDevMain) {
const addr = await spawnExtensionFileServer(winLabel) // addr has format "127.0.0.1:<port>"
console.log("Extension file server address: ", addr)
const newUrl = `http://${addr}`

View File

@ -12,7 +12,7 @@ import { onQuickLinkSelect } from "./quick-links"
const onExtCmdSelect: OnExtCmdSelect = (
ext: ExtPackageJsonExtra,
cmd: CustomUiCmd | TemplateUiCmd | HeadlessCmd,
cmd: CustomUiCmd | TemplateUiCmd,
{ isDev, hmr }: { isDev: boolean; hmr: boolean }
) => {
switch (cmd.type) {

View File

@ -1,23 +1,10 @@
import { getSystemCommands } from "@kksh/api/commands"
import type { SysCommand } from "@kksh/api/models"
import { commandScore } from "@kksh/ui/utils"
import Fuse from "fuse.js"
import { derived, readable } from "svelte/store"
import { appState } from "../stores/appState"
export const systemCommands = getSystemCommands()
export const fuse = new Fuse<SysCommand>(systemCommands, {
includeScore: true,
threshold: 0.2,
keys: ["name"]
})
export const systemCommandsFiltered = derived(appState, ($appState) => {
return $appState.searchTerm
? fuse.search($appState.searchTerm).map((result) => result.item)
: systemCommands
})
export const systemCommands = readable(getSystemCommands())
// export const systemCommandsFiltered = derived(
// [systemCommands, appState],

View File

@ -1,37 +0,0 @@
<script lang="ts">
import { appConfig, appState } from "@/stores"
import { cn } from "@/utils"
import { Button } from "@kksh/svelte5"
import { BorderBeam, Constants, Layouts, TauriLink } from "@kksh/ui"
import { goto } from "$app/navigation"
import { ArrowLeftIcon, LoaderCircleIcon } from "lucide-svelte"
import Dance from "../dance/dance.svelte"
let { class: className }: { class?: string } = $props()
function goHome() {
appState.setFullScreenLoading(false)
goto("/app")
}
</script>
<Layouts.Center class={cn("flex h-screen flex-col items-center justify-center", className)}>
<Button
variant="outline"
size="icon"
onclick={goHome}
class={cn(Constants.CLASSNAMES.BACK_BUTTON, "absolute left-4 top-4")}
data-flip-id={Constants.CLASSNAMES.BACK_BUTTON}
>
<ArrowLeftIcon class="size-4" />
</Button>
{#if $appConfig.loadingAnimation === "kunkun-dancing"}
<!-- <DanceTransition delay={300} autoHide={false} show={!uiControl.iframeLoaded} /> -->
<Dance class="absolute z-50 h-screen opacity-20" />
{:else}
<!-- <LoadingAnimation delay={300} autoHide={false} show={!uiControl.iframeLoaded} /> -->
<LoaderCircleIcon class="h-24 w-24 animate-spin" />
<span class="font-mono">Loading</span>
{/if}
<BorderBeam size={150} duration={12} />
</Layouts.Center>

View File

@ -75,7 +75,7 @@
<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}>
<Button class="" size="sm" variant="secondary" onclick={autoInstall} disabled={!autoInstallable}>
Auto Install
</Button> -->
</Button>
</div>

View File

@ -1,8 +1,8 @@
<script lang="ts">
import { setAppConfigContext } from "@/context"
import { setAppStateContext } from "@/context/appState"
import type { AppConfigState, AppState } from "@kksh/types"
import { type Snippet } from "svelte"
import type { AppConfig, AppState } from "@kksh/types"
import type { Snippet } from "svelte"
import type { Writable } from "svelte/store"
const {
@ -10,7 +10,7 @@
appState,
children
}: {
appConfig: Writable<AppConfigState>
appConfig: Writable<AppConfig>
appState: Writable<AppState>
children: Snippet<[]>
} = $props()

View File

@ -1,53 +1,40 @@
<script lang="ts">
import { appState } from "@/stores"
import { IconEnum, type AppInfo } from "@kksh/api/models"
import { Command } from "@kksh/svelte5"
import { IconMultiplexer } from "@kksh/ui"
import { DraggableCommandGroup } from "@kksh/ui/custom"
import { convertFileSrc } from "@tauri-apps/api/core"
import { getCurrentWindow } from "@tauri-apps/api/window"
import * as os from "@tauri-apps/plugin-os"
import { toast } from "svelte-sonner"
import { executeBashScript, open } from "tauri-plugin-shellx-api"
import { open } from "tauri-plugin-shellx-api"
const platform = os.platform()
let { apps }: { apps: AppInfo[] } = $props()
</script>
<DraggableCommandGroup heading="Apps">
{#each apps.filter((app) => app.name) as app, idx}
{@const iconPath = platform === "windows" ? (app.icon_path ?? app.app_path_exe) : app.icon_path}
{#each apps as app}
<Command.Item
class="flex justify-between"
onSelect={async () => {
onSelect={() => {
if (platform === "windows") {
if (app.app_path_exe) {
open(app.app_path_exe)
} else {
toast.error("No executable path found for this app")
}
} else if (platform === "macos") {
open(app.app_desktop_path)
} else if (platform === "linux") {
if (app.app_path_exe) {
executeBashScript(app.app_path_exe)
} else {
toast.error("No executable path found for this app")
}
} else {
toast.error("Unsupported platform")
open(app.app_desktop_path)
}
await getCurrentWindow().hide()
appState.clearSearchTerm()
}}
value={`app:${idx}:${app.app_desktop_path}`}
value={app.name}
>
<span class="flex gap-2">
<IconMultiplexer
icon={iconPath
icon={app.icon_path
? {
type: IconEnum.RemoteUrl,
value: convertFileSrc(iconPath, "appicon")
value: convertFileSrc(app.icon_path, "appicon")
}
: {
type: IconEnum.Iconify,
@ -56,7 +43,6 @@
class="!h-5 !w-5 shrink-0"
/>
<span>{app.name}</span>
<!-- <span>{app.app_path_exe}</span> -->
</span>
</Command.Item>
{/each}

View File

@ -11,46 +11,21 @@
} from "@/paraglide/runtime"
import { appConfig } from "@/stores"
import { Select, Switch } from "@kksh/svelte5"
import type { LoadingAnimation } from "@kksh/types"
import * as autoStart from "@tauri-apps/plugin-autostart"
import { onMount } from "svelte"
import { toast } from "svelte-sonner"
const languages = availableLanguageTags.map((lang) => ({
value: lang,
label: LanguageMap[lang as keyof typeof LanguageMap] ?? lang
label: LanguageMap[lang] ?? lang
}))
let loadingAnimation = $state<LoadingAnimation>("spinning-circle")
const loadingAnimations = ["spinning-circle", "kunkun-dancing"] as const
let launchAtLogin = $state(false)
let language = $state(languageTag())
onMount(() => {
autoStart.isEnabled().then((enabled) => {
launchAtLogin = enabled
})
loadingAnimation = $appConfig.loadingAnimation
})
const triggerContent = $derived(languages.find((f) => f.value === language)?.label ?? "Language")
let value = $state(languageTag())
const triggerContent = $derived(languages.find((f) => f.value === value)?.label ?? "Language")
</script>
<ul class="rounded-lg border">
<li>
<span>{m.settings_general_launch_at_login()}</span>
<Switch
bind:checked={launchAtLogin}
onCheckedChange={(checked) => {
const action = checked ? autoStart.enable : autoStart.disable
action()
.then(() => {
toast.success(checked ? "Enabled" : "Disabled")
})
.catch((err) => {
toast.error(checked ? "Failed to enable" : "Failed to disable", {
description: err.message
})
})
}}
/>
<Switch bind:checked={$appConfig.launchAtLogin} />
</li>
<li class="">
<span>{m.settings_general_hotkey()}</span>
@ -84,7 +59,7 @@
<li>
<span>{m.settings_general_language()}</span>
<Select.Root type="single" name="language" bind:value={language}>
<Select.Root type="single" name="language" bind:value>
<Select.Trigger class="w-fit">
{triggerContent}
</Select.Trigger>
@ -105,31 +80,6 @@
</Select.Content>
</Select.Root>
</li>
<li>
<span>{m.settings_general_loading_animation()}</span>
<Select.Root type="single" name="loadingAnimation" bind:value={loadingAnimation}>
<Select.Trigger class="w-fit">
{loadingAnimation}
</Select.Trigger>
<Select.Content>
<Select.Group>
<Select.GroupHeading>Loading Animation</Select.GroupHeading>
{#each loadingAnimations as anim}
<Select.Item
onclick={() => {
appConfig.setLoadingAnimation(anim)
}}
value={anim}
label={anim}
>
{anim}
</Select.Item>
{/each}
</Select.Group>
</Select.Content>
</Select.Root>
</li>
</ul>
<style scoped>

View File

@ -51,18 +51,18 @@
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">
<p class="font-mono text-sm">
If you are unsure, you can use <strong class="text-lg">Auto Install</strong>.
</p> -->
</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"}
{#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}
{#if denoPath}
<div class="flex items-center gap-2">
<span></span>

View File

@ -3,7 +3,7 @@
import DevExtPathForm from "@/components/standalone/settings/DevExtPathForm.svelte"
import { i18n } from "@/i18n"
import * as m from "@/paraglide/messages"
import { appConfig, appState, extensions } from "@/stores"
import { appConfig, extensions } from "@/stores"
import { goBackOnEscape } from "@/utils/key"
import { goBack } from "@/utils/route"
import { IconEnum } from "@kksh/api/models"
@ -65,12 +65,10 @@
}
async function pickExtFolders() {
appState.setLockHideOnBlur(true)
const selected = await openFileSelector({
directory: true,
multiple: true // allow install multiple extensions at once
})
appState.setLockHideOnBlur(false)
if (!selected) {
return toast.warning("No File Selected")
}
@ -93,7 +91,6 @@
toast.warning("Please set the dev extension path in the settings")
return goto(i18n.resolveRoute("/app/settings/set-dev-ext-path"))
}
appState.setLockHideOnBlur(true)
const selected = await openFileSelector({
directory: false,
multiple: true, // allow install multiple extensions at once
@ -104,7 +101,6 @@
}
]
})
appState.setLockHideOnBlur(false)
if (!selected) {
return toast.warning("No File Selected")
}
@ -115,18 +111,15 @@
</script>
<div class="my-3 flex justify-center gap-3">
<Button size="sm" onclick={pickExtFolders}>
{m.settings_add_dev_ext_install_from_ext_folders()}
</Button>
<Button size="sm" onclick={pickExtFiles}>
{m.settings_add_dev_ext_install_from_ext_files()}
</Button>
<Button size="sm" onclick={pickExtFolders}
>{m.settings_add_dev_ext_install_from_ext_folders()}</Button
>
<Button size="sm" onclick={pickExtFiles}>{m.settings_add_dev_ext_install_from_ext_files()}</Button
>
</div>
<StrikeSeparator class="my-1">
<h3 class="text-muted-foreground font-mono text-sm">
{m.settings_add_dev_ext_drag_and_drop_strike()}
</h3>
<h3 class="text-muted-foreground font-mono text-sm">{m.settings_add_dev_ext_drag_and_drop()}</h3>
</StrikeSeparator>
<Layouts.Center>
@ -157,12 +150,12 @@
icon={{ value: "mdi:folder-cog-outline", type: IconEnum.Iconify }}
class="h-10 w-10"
/>
<small class="select-none font-mono text-xs">
{m.settings_add_dev_ext_drag_and_drop()}
</small>
<small class="select-none font-mono text-xs">
{m.settings_add_dev_ext_drag_and_drop2()}
</small>
<small class="select-none font-mono text-xs"
>{m.settings_add_dev_ext_drag_and_drop()}</small
>
<small class="select-none font-mono text-xs"
>{m.settings_add_dev_ext_drag_and_drop2()}</small
>
</div>
</button>
</Card.Root>

View File

@ -23,7 +23,5 @@ export const IS_IN_TAURI =
export const LanguageMap = {
en: "English",
zh: "中文",
ru: "Русский",
pt: "Português",
vi: "Tiếng Việt"
ru: "Русский"
}

View File

@ -3,16 +3,16 @@
* It's designed to allow all components to access a shared state.
* With context, we can avoid prop drilling, and avoid using stores which makes components hard to encapsulate.
*/
import type { AppConfigState } from "@kksh/types"
import type { AppConfig } from "@/types/appConfig"
import { getContext, setContext } from "svelte"
import type { Writable } from "svelte/store"
export const APP_CONFIG_CONTEXT_KEY = Symbol("appConfig")
export function getAppConfigContext(): Writable<AppConfigState> {
export function getAppConfigContext(): Writable<AppConfig> {
return getContext(APP_CONFIG_CONTEXT_KEY)
}
export function setAppConfigContext(appConfig: Writable<AppConfigState>) {
export function setAppConfigContext(appConfig: Writable<AppConfig>) {
setContext(APP_CONFIG_CONTEXT_KEY, appConfig)
}

View File

@ -1,4 +1,4 @@
import type { AppState } from "@kksh/types"
import type { AppState } from "@/types/appState"
import { getContext, setContext } from "svelte"
import type { Writable } from "svelte/store"

View File

@ -1,60 +0,0 @@
// /* eslint-disable @typescript-eslint/no-explicit-any */
// import { db as dbCmd } from "@kksh/api/commands"
// import * as schema from "@kksh/drizzle/schema"
// import { error } from "@tauri-apps/plugin-log"
// import { drizzle } from "drizzle-orm/sqlite-proxy"
// /**
// * Loads the sqlite database via the Tauri Proxy.
// */
// // export const sqlite = await Database.load("sqlite:test.db");
// /**
// * The drizzle database instance.
// */
// export const db = drizzle<typeof schema>(
// async (sql, params, method) => {
// let rows: any = []
// let results = []
// console.log({
// sql,
// params,
// method
// })
// console.log(sql)
// // If the query is a SELECT, use the select method
// if (isSelectQuery(sql)) {
// rows = await dbCmd.select(sql, params).catch((e) => {
// error("SQL Error:", e)
// return []
// })
// } else {
// // Otherwise, use the execute method
// rows = await dbCmd.execute(sql, params).catch((e) => {
// error("SQL Error:", e)
// return []
// })
// return { rows: [] }
// }
// rows = rows.map((row: any) => {
// return Object.values(row)
// })
// // If the method is "all", return all rows
// results = method === "all" ? rows : rows[0]
// return { rows: results }
// },
// // Pass the schema to the drizzle instance
// { schema: schema, logger: true }
// )
// /**
// * Checks if the given SQL query is a SELECT query.
// * @param sql The SQL query to check.
// * @returns True if the query is a SELECT query, false otherwise.
// */
// function isSelectQuery(sql: string): boolean {
// const selectRegex = /^\s*SELECT\b/i
// return selectRegex.test(sql)
// }

View File

@ -1,16 +1,15 @@
import { getExtensionsFolder } from "@/constants"
import type { SearchPath } from "@kksh/api/models"
import { createTauriSyncStore, type WithSyncStore } from "@/utils/sync-store"
import { updateTheme, type ThemeConfig } from "@kksh/svelte5"
import { LoadingAnimation, PersistedAppConfig, type AppConfigState } from "@kksh/types"
import { debug, error, info } from "@tauri-apps/plugin-log"
import { PersistedAppConfig, type AppConfig } from "@kksh/types"
import { debug, error } from "@tauri-apps/plugin-log"
import * as os from "@tauri-apps/plugin-os"
import { load } from "@tauri-apps/plugin-store"
import { Store } from "@tauri-store/svelte"
import { toast } from "svelte-sonner"
import { get, writable } from "svelte/store"
import * as v from "valibot"
import { setLanguageTag } from "../paraglide/runtime"
export const defaultAppConfig: AppConfigState = {
export const defaultAppConfig: AppConfig = {
isInitialized: false,
platform: "macos",
language: "en",
@ -20,6 +19,7 @@ export const defaultAppConfig: AppConfigState = {
lightMode: "auto"
},
triggerHotkey: null,
launchAtLogin: true,
showInTray: true,
devExtensionPath: null,
extensionsInstallDir: undefined,
@ -28,82 +28,72 @@ export const defaultAppConfig: AppConfigState = {
extensionAutoUpgrade: true,
joinBetaProgram: false,
onBoarded: false,
developerMode: false,
appSearchPaths: [],
loadingAnimation: "kunkun-dancing"
developerMode: false
}
export const appConfigLoaded = writable(false)
interface AppConfigAPI {
init: () => Promise<void>
get: () => AppConfigState
get: () => AppConfig
setTheme: (theme: ThemeConfig) => void
setDevExtensionPath: (devExtensionPath: string | null) => void
setTriggerHotkey: (triggerHotkey: string[]) => void
setOnBoarded: (onBoarded: boolean) => void
setLanguage: (language: string) => void
addAppSearchPath: (appSearchPath: SearchPath) => void
removeAppSearchPath: (appSearchPath: SearchPath) => void
}
class AppConfigStore extends Store<AppConfigState> implements AppConfigAPI {
constructor() {
super("app-config", defaultAppConfig, {
saveOnChange: true
})
this.start().catch((err) => {
error("Failed to start app config store", err)
toast.error("Failed to start app config store", { description: err.message })
})
}
async init() {
function createAppConfig(): WithSyncStore<AppConfig & { language: string }> & AppConfigAPI {
const store = createTauriSyncStore("app-config", defaultAppConfig)
async function init() {
debug("Initializing app config")
const extensionsInstallDir = await getExtensionsFolder()
this.update((config) => ({
...config,
isInitialized: true,
platform: os.platform(),
extensionsInstallDir
}))
const persistStore = await load("kk-config.json", { autoSave: true })
const loadedConfig = await persistStore.get("config")
const parseRes = v.safeParse(PersistedAppConfig, loadedConfig)
if (parseRes.success) {
console.log("Parse Persisted App Config Success", parseRes.output)
const extensionsInstallDir = await getExtensionsFolder()
store.update((config) => ({
...config,
...parseRes.output,
isInitialized: true,
extensionsInstallDir,
platform: os.platform()
}))
} else {
error("Failed to parse app config, going to remove it and reinitialize")
console.error(v.flatten<typeof PersistedAppConfig>(parseRes.issues))
await persistStore.clear()
await persistStore.set("config", v.parse(PersistedAppConfig, defaultAppConfig))
}
appConfigLoaded.set(true)
store.subscribe(async (config) => {
console.log("Saving app config", config)
await persistStore.set("config", config)
updateTheme(config.theme)
})
}
get() {
return get(this)
}
setTheme(theme: ThemeConfig) {
this.update((config) => ({ ...config, theme }))
}
setDevExtensionPath(devExtensionPath: string | null) {
info(`setDevExtensionPath ${devExtensionPath}`)
this.update((config) => ({ ...config, devExtensionPath }))
}
setTriggerHotkey(triggerHotkey: string[]) {
this.update((config) => ({ ...config, triggerHotkey }))
}
setOnBoarded(onBoarded: boolean) {
this.update((config) => ({ ...config, onBoarded }))
}
setLanguage(language: string) {
this.update((config) => ({ ...config, language }))
}
addAppSearchPath(appSearchPath: SearchPath) {
this.update((config) => ({
...config,
appSearchPaths: [...config.appSearchPaths, appSearchPath]
}))
}
removeAppSearchPath(appSearchPath: SearchPath) {
this.update((config) => ({
...config,
appSearchPaths: config.appSearchPaths.filter((path) => path.path !== appSearchPath.path)
}))
}
setLoadingAnimation(loadingAnimation: LoadingAnimation) {
this.update((config) => ({ ...config, loadingAnimation }))
return {
...store,
get: () => get(store),
setTheme: (theme: ThemeConfig) => store.update((config) => ({ ...config, theme })),
setDevExtensionPath: (devExtensionPath: string | null) => {
console.log("setDevExtensionPath", devExtensionPath)
store.update((config) => ({ ...config, devExtensionPath }))
},
setTriggerHotkey: (triggerHotkey: string[]) => {
store.update((config) => ({ ...config, triggerHotkey }))
},
setOnBoarded: (onBoarded: boolean) => {
store.update((config) => ({ ...config, onBoarded }))
},
setLanguage: (language: string) => {
store.update((config) => ({ ...config, language }))
},
init
}
}
// export const appConfig = createAppConfig()
export const appConfig = new AppConfigStore()
export const appConfig = createAppConfig()

View File

@ -7,9 +7,7 @@ export const defaultAppState: AppState = {
highlightedCmd: "",
loadingBar: false,
defaultAction: "",
actionPanel: undefined,
lockHideOnBlur: false, // when dialog is open, we don't hide the app, we lock the hide on blur and unlock when dialog is closed
fullScreenLoading: false
actionPanel: undefined
}
interface AppStateAPI {
@ -18,8 +16,6 @@ interface AppStateAPI {
setLoadingBar: (loadingBar: boolean) => void
setDefaultAction: (defaultAction: string | null) => void
setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => void
setLockHideOnBlur: (lockHideOnBlur: boolean) => void
setFullScreenLoading: (fullScreenLoading: boolean) => void
}
function createAppState(): Writable<AppState> & AppStateAPI {
@ -39,12 +35,6 @@ function createAppState(): Writable<AppState> & AppStateAPI {
},
setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => {
store.update((state) => ({ ...state, actionPanel }))
},
setLockHideOnBlur: (lockHideOnBlur: boolean) => {
store.update((state) => ({ ...state, lockHideOnBlur }))
},
setFullScreenLoading: (fullScreenLoading: boolean) => {
store.update((state) => ({ ...state, fullScreenLoading }))
}
}
}

View File

@ -2,17 +2,9 @@ import { getAllApps, refreshApplicationsList } from "@kksh/api/commands"
import { AppInfo } from "@kksh/api/models"
import { commandScore } from "@kksh/ui/utils"
import * as fs from "@tauri-apps/plugin-fs"
import { platform } from "@tauri-apps/plugin-os"
import Fuse from "fuse.js"
import { derived, get, writable } from "svelte/store"
import { appState } from "./appState"
const fuse = new Fuse<AppInfo>([], {
includeScore: true,
threshold: 0.2,
keys: ["name"]
})
export function createAppsLoaderStore() {
const store = writable<AppInfo[]>([])
@ -21,9 +13,10 @@ export function createAppsLoaderStore() {
get: () => get(store),
init: async () => {
await refreshApplicationsList()
let apps = await getAllApps()
if (platform() === "macos") {
apps = apps.filter((app) => {
const apps = await getAllApps()
// fs.writeTextFile("/Users/hk/Desktop/apps.json", JSON.stringify(apps))
store.set(
apps.filter((app) => {
return (
!app.app_desktop_path.includes("Parallels") &&
!app.app_desktop_path.startsWith("/Library/Application Support") &&
@ -31,18 +24,25 @@ export function createAppsLoaderStore() {
!app.app_desktop_path.startsWith("/System/Library/PrivateFrameworks")
)
})
}
// console.log("filteredApps", apps)
// fs.writeTextFile("/Users/hk/Desktop/apps.json", JSON.stringify(apps))
store.set(apps)
fuse.setCollection(apps)
)
}
}
}
export const appsLoader = createAppsLoaderStore()
export const appsFiltered = derived([appsLoader, appState], ([$apps, $appState]) => {
return $appState.searchTerm.length > 0
? fuse.search($appState.searchTerm).map((result) => result.item)
: $apps.slice(0, 20)
})
// export const appsFiltered = derived([appsLoader, appState], ([$apps, $appState]) => {
// return []
// return $apps.filter((app) => {
// if ($appState.searchTerm.length === 0) {
// return false
// }
// return (
// commandScore(
// app.name,
// $appState.searchTerm
// // []
// ) > 0.8
// )
// })
// })

View File

@ -1,9 +1,11 @@
import type { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models"
import { db } from "@kksh/drizzle"
import { getExtensionsFolder } from "@/constants"
import { db } from "@kksh/api/commands"
import type { ExtPackageJson, ExtPackageJsonExtra } from "@kksh/api/models"
import * as extAPI from "@kksh/extension"
import { commandScore } from "@kksh/ui/utils"
import * as path from "@tauri-apps/api/path"
import Fuse from "fuse.js"
import { derived, get, writable, type Writable } from "svelte/store"
import * as fs from "@tauri-apps/plugin-fs"
import { derived, get, writable, type Readable, type Writable } from "svelte/store"
import { appConfig } from "./appConfig"
import { appState } from "./appState"
@ -27,7 +29,6 @@ function createExtensionsStore(): Writable<ExtPackageJsonExtra[]> & {
tarballUrl: string,
extras?: { overwritePackageJson?: string }
) => Promise<ExtPackageJsonExtra>
reloadExtension: (extPath: string) => Promise<void>
} {
const store = writable<ExtPackageJsonExtra[]>([])
@ -41,22 +42,6 @@ function createExtensionsStore(): Writable<ExtPackageJsonExtra[]> & {
})
}
// if dev extension's package.json is changed, use this function to reload commands
async function reloadExtension(extPath: string) {
const ext = get(extensions).find((ext) => ext.extPath === extPath)
if (ext) {
const pkgJsonPath = await path.join(extPath, "package.json")
const ext = await extAPI.loadExtensionManifestFromDisk(pkgJsonPath)
// replace the old extension with the new one
store.update((exts) => {
// filter out the old extension
return [...exts.filter((e) => e.extPath !== extPath), ext]
})
} else {
console.warn(`reloadExtension: Extension ${extPath} not found`)
}
}
/**
* Get all extensions installed from the store (non-dev extensions)
*/
@ -210,7 +195,6 @@ function createExtensionsStore(): Writable<ExtPackageJsonExtra[]> & {
return {
...store,
init,
reloadExtension,
getExtensionsFromStore,
findStoreExtensionByIdentifier,
registerNewExtensionByPath,
@ -225,62 +209,44 @@ function createExtensionsStore(): Writable<ExtPackageJsonExtra[]> & {
}
export const extensions = createExtensionsStore()
export const installedStoreExts = derived(extensions, ($extensions) => {
const extContainerPath = get(appConfig).extensionsInstallDir
if (!extContainerPath) return []
return $extensions.filter((ext) => !extAPI.isExtPathInDev(extContainerPath, ext.extPath))
})
export const devStoreExts = derived(extensions, ($extensions) => {
const extContainerPath = get(appConfig).extensionsInstallDir
if (!extContainerPath) return []
return $extensions.filter((ext) => extAPI.isExtPathInDev(extContainerPath, ext.extPath))
})
export type StoreExtCmd = (CustomUiCmd | TemplateUiCmd | HeadlessCmd) & {
ext: ExtPackageJsonExtra
}
export const installedStoreExts: Readable<ExtPackageJsonExtra[]> = derived(
extensions,
($extensionsStore) => {
const extContainerPath = get(appConfig).extensionsInstallDir
if (!extContainerPath) return []
return $extensionsStore.filter((ext) => !extAPI.isExtPathInDev(extContainerPath, ext.extPath))
}
)
export const devStoreExts: Readable<ExtPackageJsonExtra[]> = derived(
extensions,
($extensionsStore) => {
const extContainerPath = get(appConfig).extensionsInstallDir
if (!extContainerPath) return []
return $extensionsStore.filter((ext) => extAPI.isExtPathInDev(extContainerPath, ext.extPath))
}
)
export const cmdsFuse = new Fuse<StoreExtCmd>([], {
includeScore: true,
threshold: 0.2,
keys: ["name"]
})
export const devCmdsFuse = new Fuse<StoreExtCmd>([], {
includeScore: true,
threshold: 0.2,
keys: ["name"]
})
// export const installedStoreExtsFiltered = derived(
// [installedStoreExts, appState],
// ([$installedStoreExts, $appState]) => {
// return $installedStoreExts.filter(
// (ext) => commandScore(ext.kunkun.name, $appState.searchTerm) > 0.5
// )
// }
// )
export const storeExtCmds = derived(installedStoreExts, ($exts) => {
const cmds = $exts.flatMap((ext) => {
return [
...(ext.kunkun.customUiCmds ?? []),
...(ext.kunkun.templateUiCmds ?? []),
...(ext.kunkun.headlessCmds ?? [])
].map((cmd) => ({ ...cmd, ext }))
})
cmdsFuse.setCollection(cmds)
return cmds
})
export const devStoreExtCmds = derived(devStoreExts, ($exts) => {
const cmds = $exts.flatMap((ext) => {
return [
...(ext.kunkun.customUiCmds ?? []),
...(ext.kunkun.templateUiCmds ?? []),
...(ext.kunkun.headlessCmds ?? [])
].map((cmd) => ({ ...cmd, ext }))
})
devCmdsFuse.setCollection(cmds)
return cmds
})
export const storeSearchExtCmds = derived([storeExtCmds, appState], ([$extCmds, $appState]) => {
return $appState.searchTerm
? cmdsFuse.search($appState.searchTerm).map((result) => result.item)
: $extCmds
})
export const devSearchExtCmds = derived([devStoreExtCmds, appState], ([$extCmds, $appState]) => {
return $appState.searchTerm
? devCmdsFuse.search($appState.searchTerm).map((result) => result.item)
: $extCmds
})
// export const devStoreExtsFiltered = derived(
// [devStoreExts, appState],
// ([$devStoreExts, $appState]) => {
// return $devStoreExts.filter((ext) => {
// console.log(
// "commandScore",
// ext.kunkun.name,
// $appState.searchTerm,
// commandScore(ext.kunkun.name, $appState.searchTerm)
// )
// return commandScore(ext.kunkun.name, $appState.searchTerm) > 0.1
// })
// }
// )

View File

@ -2,16 +2,9 @@ import type { Icon } from "@kksh/api/models"
import { createQuickLinkCommand, getAllQuickLinkCommands } from "@kksh/extension/db"
import type { QuickLink } from "@kksh/ui/types"
import { commandScore } from "@kksh/ui/utils"
import Fuse from "fuse.js"
import { derived, get, writable, type Writable } from "svelte/store"
import { appState } from "./appState"
const fuse = new Fuse<QuickLink>([], {
includeScore: true,
threshold: 0.2,
keys: ["name"]
})
export interface QuickLinkAPI {
get: () => QuickLink[]
init: () => Promise<void>
@ -28,9 +21,7 @@ function createQuickLinksStore(): Writable<QuickLink[]> & QuickLinkAPI {
async function refresh() {
const cmds = await getAllQuickLinkCommands()
const items = cmds.map((cmd) => ({ link: cmd.data.link, name: cmd.name, icon: cmd.data.icon }))
store.set(items)
fuse.setCollection(items)
store.set(cmds.map((cmd) => ({ link: cmd.data.link, name: cmd.name, icon: cmd.data.icon })))
}
async function createQuickLink(name: string, link: string, icon: Icon) {
@ -48,11 +39,7 @@ function createQuickLinksStore(): Writable<QuickLink[]> & QuickLinkAPI {
}
export const quickLinks = createQuickLinksStore()
export const quickLinksFiltered = derived([quickLinks, appState], ([$quickLinks, $appState]) => {
return $appState.searchTerm
? fuse.search($appState.searchTerm).map((result) => result.item)
: $quickLinks
})
// export const quickLinksFiltered = derived([quickLinks, appState], ([$quicklinks, $appState]) => {
// return $quicklinks.filter((lnk) => {
// if ($appState.searchTerm.length === 0) {

View File

@ -104,7 +104,6 @@ function createWinExtMapStore(): Writable<WinExtMap> & API {
}
},
registerProcess: async (windowLabel: string, pid: number) => {
console.log("registerProcess", windowLabel, pid)
const winExtMap = get(store)
await registerExtensionSpawnedProcess(windowLabel, pid)
if (!winExtMap[windowLabel]) {
@ -117,7 +116,6 @@ function createWinExtMapStore(): Writable<WinExtMap> & API {
const winExtMap = get(store)
const found = Object.entries(winExtMap).find(([windowLabel, ext]) => ext.pids.includes(pid))
if (!found) {
warn(`Process ${pid} does not have an extension registered, thus will not be killed`)
return
}
const [windowLabel, ext] = found

View File

@ -1,13 +1,19 @@
import { SupabaseAPI } from "@kksh/supabase/api"
import type { Database } from "@kksh/supabase/types"
import * as sb from "@supabase/supabase-js"
import { SUPABASE_ANON_KEY, SUPABASE_URL } from "./constants"
// export const supabase = createSB(SUPABASE_URL, SUPABASE_ANON_KEY)
export const supabase: sb.SupabaseClient = sb.createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
auth: {
flowType: "pkce"
export const supabase: sb.SupabaseClient<Database> = sb.createClient<Database>(
SUPABASE_URL,
SUPABASE_ANON_KEY,
{
auth: {
flowType: "pkce"
}
}
})
)
export const storage = supabase.storage
export const supabaseExtensionsStorage = supabase.storage.from("extensions")
// export const supabaseAPI = new SupabaseAPI(supabase)
export const supabaseAPI = new SupabaseAPI(supabase)

View File

@ -1,12 +0,0 @@
import * as v from "valibot"
export const WatchEvent = v.object({
type: v.object({
modify: v.object({
kind: v.union([v.literal("data"), v.literal("metadata")]),
mode: v.union([v.literal("any"), v.literal("content")])
})
}),
paths: v.array(v.string())
})
export type WatchEvent = v.InferOutput<typeof WatchEvent>

View File

@ -1,44 +0,0 @@
import { proxyDB, schema } from "@kksh/drizzle"
import { getExtClipboard } from "@kksh/drizzle/api"
import { error, info } from "@tauri-apps/plugin-log"
import * as orm from "drizzle-orm"
/**
* For now, simply delete all clipboard data older than 10 days
*/
export async function cleanClipboard() {
const nDays = 10
const clipboardExt = await getExtClipboard()
const nDaysAgo = new Date()
nDaysAgo.setDate(nDaysAgo.getDate() - nDays)
try {
// Select data older than 10 days to check what will be deleted
const oldClipboardData = await proxyDB
.select({ count: orm.count() })
.from(schema.extensionData)
.where(
orm.and(
orm.eq(schema.extensionData.extId, clipboardExt.extId),
orm.lt(schema.extensionData.createdAt, nDaysAgo.toISOString())
)
)
const nLinesToDelete = oldClipboardData.at(0)?.count ?? 0
console.info(`Found ${nLinesToDelete} clipboard entries older than ${nDays} days to clean up`)
info(`Found ${nLinesToDelete} clipboard entries older than ${nDays} days to clean up`)
// Now delete the old data
const deleted = await proxyDB
.delete(schema.extensionData)
.where(
orm.and(
orm.eq(schema.extensionData.extId, clipboardExt.extId),
orm.lt(schema.extensionData.createdAt, nDaysAgo.toISOString())
)
)
console.log("deleted", deleted)
} catch (e) {
error(`Error during clipboard cleanup: ${e}`)
}
}

View File

@ -1,13 +0,0 @@
import { proxyDB } from "@kksh/drizzle"
import { error, info } from "@tauri-apps/plugin-log"
import { sql } from "drizzle-orm"
export async function vacuumSqlite() {
const statement = sql`VACUUM`
try {
await proxyDB.run(statement)
info("Vacuumed sqlite")
} catch (error) {
console.error(`Failed to vacuum sqlite: ${error}`)
}
}

View File

@ -1,11 +1,7 @@
import { app } from "@tauri-apps/api"
import { getAllWindows, getCurrentWindow, type Window } from "@tauri-apps/api/window"
import { getAllWindows } from "@tauri-apps/api/window"
import { isRegistered, register, unregister } from "@tauri-apps/plugin-global-shortcut"
import { debug, info, warn } from "@tauri-apps/plugin-log"
import * as os from "@tauri-apps/plugin-os"
import * as userInput from "tauri-plugin-user-input-api"
import { sendNotificationWithPermission } from "./notification"
import { sleep } from "./time"
/**
* Tauri global shortcut doesn't accept 'Meta' Key. This function maps browser detected keys to Tauri-accepted keys.
@ -18,11 +14,6 @@ export function mapKeyToTauriKey(key: string): string {
return key
}
/**
* Registers a global hotkey for the application. If the hotkey is already registered, it will be unregistered first.
* When the hotkey is pressed, it toggles the visibility and focus of the main window.
* @param hotkeyStr - The hotkey string to register.
*/
export async function registerAppHotkey(hotkeyStr: string) {
if (await isRegistered(hotkeyStr)) {
warn(`Hotkey (${hotkeyStr}) already registered`)
@ -48,17 +39,13 @@ export async function registerAppHotkey(hotkeyStr: string) {
mainWin.setFocus()
}
} else {
mainWin.show().then(() => mainWin.setFocus())
mainWin.show()
mainWin.setFocus()
}
}
})
}
/**
* Updates the application's global hotkey. If an old hotkey is provided, it will be unregistered first.
* @param newHotkey - The new hotkey combination to register.
* @param oldHotkey - The old hotkey combination to unregister, if any.
*/
export async function updateAppHotkey(newHotkey: string[], oldHotkey?: string[] | null) {
if (oldHotkey) {
const hotkeyStr = oldHotkey.map(mapKeyToTauriKey).join("+")
@ -69,44 +56,3 @@ export async function updateAppHotkey(newHotkey: string[], oldHotkey?: string[]
const hotkeyStr = newHotkey.map(mapKeyToTauriKey).join("+")
return registerAppHotkey(hotkeyStr)
}
/**
* Simulates a key combination press and release.
* @param keys - The array of keys to press and release.
*/
export async function applyKeyComb(keys: userInput.Key[]) {
// await Promise.all(keys.map((key) => userInput.key("KeyPress", key)))
for (const key of keys) {
await userInput.key("KeyPress", key)
await sleep(100)
}
await sleep(150)
for (const key of keys) {
await userInput.key("KeyRelease", key)
await sleep(100)
}
}
/**
* Simulates a paste operation based on the operating system.
* On macOS, it uses Command+V. On Windows and Linux, it uses Shift+Insert.
*/
export async function paste() {
const _platform = os.platform()
if (_platform === "macos") {
return applyKeyComb(["MetaLeft", "KeyV"])
} else if (_platform === "windows" || _platform === "linux") {
return applyKeyComb(["ShiftLeft", "Insert"])
} else {
console.error("Unsupported platform: " + _platform)
}
}
export async function hideAndPaste(win?: Window) {
return app
.hide()
.then(() => sleep(60))
.then(() => (win ?? getCurrentWindow()).hide())
.then(() => sleep(60))
.then(() => paste())
}

View File

@ -1,32 +1,18 @@
import { appConfig, extensions } from "@/stores"
import { appConfig } from "@/stores"
import { getCurrentWindow } from "@tauri-apps/api/window"
import { error, info } from "@tauri-apps/plugin-log"
import { info } from "@tauri-apps/plugin-log"
import { dev } from "$app/environment"
import { cleanClipboard } from "./clipboard"
import { vacuumSqlite } from "./db"
import { mapKeyToTauriKey, registerAppHotkey } from "./hotkey"
import { listenToReloadOneExtension } from "./tauri-events"
/**
* Initialize the app
*/
export async function init() {
export function init() {
const window = getCurrentWindow()
if (window.label === "main") {
initMainWindow()
listenToReloadOneExtension(({ payload: { extPath } }) => {
info(`listenToReloadOneExtension in main window: ${extPath}`)
extensions.reloadExtension(extPath)
})
}
await cleanClipboard()
.then(() => {
info("Cleaned clipboard")
})
.catch((e) => {
error(`Failed to clean clipboard: ${e}`)
})
vacuumSqlite()
if (!dev) {
// document.addEventListener("contextmenu", function (event) {
// event.preventDefault()

View File

@ -76,10 +76,7 @@ export function goHomeOrCloseOnEscapeWithInput(e: KeyboardEvent) {
export async function globalKeyDownHandler(e: KeyboardEvent) {
keys.keydown(e.key)
const _platform = platform()
if (
(_platform === "macos" && e.metaKey) ||
((_platform === "windows" || _platform === "linux") && e.ctrlKey)
) {
if ((_platform === "macos" && e.metaKey) || (_platform === "windows" && e.ctrlKey)) {
if (e.key === ",") {
e.preventDefault()
goto(i18n.resolveRoute("/app/settings"))
@ -97,7 +94,7 @@ export async function globalKeyDownHandler(e: KeyboardEvent) {
await appWin.hide()
location.reload()
setTimeout(() => {
appWin.show().then(() => appWin.setFocus())
appWin.show()
}, 1_000)
}
}

View File

@ -1,19 +0,0 @@
import { parseAPIVersion } from "@kksh/extension/load"
import type { ExtPackageJsonExtra } from "@kunkunapi/src/models/manifest"
import semver from "semver"
/**
* Decide the serialization method for kkrpc based on the api version
* apiVersion is populated in loadExtensionManifestFromDisk, but we parse it again
* @param apiVersion - The version of the @kksh/api
* @returns "superjson" or "json"
*/
export function decideKkrpcSerialization(ext: ExtPackageJsonExtra): "superjson" | "json" {
const apiVersion = parseAPIVersion(ext.dependencies || {})
if (apiVersion && semver.lte(apiVersion, "0.1.5")) {
// 0.1.6 is the first version that supports superjson and default to use superjson
return "json"
}
// fallback default to use superjson, some extensions might not install @kksh/api
return "superjson"
}

View File

@ -16,8 +16,6 @@ export const FileDragOver = "tauri://drag-over"
export const NewClipboardItemAddedEvent = "new_clipboard_item_added"
export const RefreshConfigEvent = "kunkun://refresh-config"
export const RefreshExtEvent = "kunkun://refresh-extensions"
export const ReloadOneExtensionEvent = "kunkun://reload-one-extension"
export function listenToFileDrop(cb: EventCallback<{ paths: string[] }>) {
return listen<{ paths: string[] }>(FileDragDrop, cb)
}
@ -57,11 +55,3 @@ export function emitRefreshDevExt() {
export function listenToRefreshDevExt(cb: EventCallback<null>) {
return listen(DEEP_LINK_PATH_REFRESH_DEV_EXTENSION, cb)
}
export function emitReloadOneExtension(extPath: string) {
return emitTo("main", ReloadOneExtensionEvent, { extPath })
}
export function listenToReloadOneExtension(cb: EventCallback<{ extPath: string }>) {
return listen(ReloadOneExtensionEvent, cb)
}

View File

@ -1,7 +1,8 @@
import { extensions } from "@/stores"
import { supabaseAPI } from "@/supabase"
import { isCompatible } from "@kksh/api"
import type { ExtPackageJsonExtra } from "@kksh/api/models"
import { getExtensionsLatestPublishByIdentifier } from "@kksh/sdk"
import { greaterThan } from "@std/semver"
import { relaunch } from "@tauri-apps/plugin-process"
import { check } from "@tauri-apps/plugin-updater"
import { gt } from "semver"
@ -31,22 +32,11 @@ export async function checkSingleExtensionUpdate(
installedExt: ExtPackageJsonExtra,
autoupgrade: boolean
) {
const {
data: sbExt,
error,
response
} = await getExtensionsLatestPublishByIdentifier({
path: {
identifier: "RAG"
}
})
// const { data: sbExt, error } = await supabaseAPI.getLatestExtPublish(
// installedExt.kunkun.identifier
// )
const { data: sbExt, error } = await supabaseAPI.getLatestExtPublish(
installedExt.kunkun.identifier
)
if (error) {
return toast.error(
`Failed to check update for ${installedExt.kunkun.identifier}: ${error} (${response.status})`
)
return toast.error(`Failed to check update for ${installedExt.kunkun.identifier}: ${error}`)
}
if (!sbExt) {
@ -59,7 +49,10 @@ export async function checkSingleExtensionUpdate(
) {
if (autoupgrade) {
await extensions
.upgradeStoreExtension(sbExt.identifier, sbExt.tarball_path)
.upgradeStoreExtension(
sbExt.identifier,
supabaseAPI.translateExtensionFilePathToUrl(sbExt.tarball_path)
)
.then(() => {
toast.success(`${sbExt.name} upgraded`, {
description: `From ${installedExt.version} to ${sbExt.version}`

View File

@ -12,7 +12,6 @@
<svelte:window on:keydown={handleKeyDown} />
<div class="fixed h-12 w-full" data-tauri-drag-region></div>
<Layouts.Center class="min-h-screen py-5">
<Error.RawErrorJSONPreset
title="Error"

View File

@ -2,8 +2,6 @@
import { ParaglideJS } from "@inlang/paraglide-sveltekit"
import { i18n } from "$lib/i18n"
import "../app.css"
import FullScreenLoading from "@/components/common/FullScreenLoading.svelte"
import { appState } from "@/stores/appState"
import { ModeWatcher, ThemeWrapper } from "@kksh/svelte5"
import { Toaster } from "svelte-sonner"
@ -14,9 +12,6 @@
<ModeWatcher />
<Toaster richColors closeButton />
<ThemeWrapper>
{#if $appState.fullScreenLoading}
<FullScreenLoading class="bg-background absolute inset-0 z-50" />
{/if}
{@render children()}
</ThemeWrapper>
</ParaglideJS>

View File

@ -1,15 +1,5 @@
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
import { browser } from "$app/environment"
// Tauri doesn't have a Node.js server to do proper SSR
// so we will use adapter-static to prerender the app (SSG)
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
export const prerender = true
export const ssr = false
export const load = () => {
if (browser) {
const win = getCurrentWebviewWindow()
return { win }
}
}

View File

@ -14,7 +14,7 @@
import { Constants, ViewTransition } from "@kksh/ui"
import type { UnlistenFn } from "@tauri-apps/api/event"
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
import { attachConsole, debug, error, info } from "@tauri-apps/plugin-log"
import { attachConsole, error, info } from "@tauri-apps/plugin-log"
import { afterNavigate, beforeNavigate } from "$app/navigation"
import { gsap } from "gsap"
import { Flip } from "gsap/Flip"
@ -46,14 +46,15 @@
})
})
let { children, data } = $props()
let { children } = $props()
const unlisteners: UnlistenFn[] = []
onDestroy(() => {
unlisteners.forEach((unlistener) => unlistener())
})
onMount(async () => {
await attachConsole().then((unlistener) => unlisteners.push(unlistener))
console.log("root layout onMount")
attachConsole().then((unlistener) => unlisteners.push(unlistener))
initDeeplink().then((unlistener) => unlisteners.push(unlistener))
shellx
.fixPathEnv()
@ -80,7 +81,7 @@
// 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 && !$appState.lockHideOnBlur) {
if ($appConfig.hideOnBlur) {
win.hide()
}
}
@ -90,18 +91,18 @@
extensions.init()
unlisteners.push(
await listenToRecordExtensionProcessEvent(async (event) => {
debug(`record extension process event ${event.payload.pid}`)
console.log("record extension process event", event)
winExtMap.registerProcess(event.payload.windowLabel, event.payload.pid)
})
)
unlisteners.push(
await listenToKillProcessEvent((event) => {
debug(`kill process event ${event.payload.pid}`)
console.log("kill process event", event)
winExtMap.unregisterProcess(event.payload.pid)
})
)
}
data.win?.show().then(() => data.win?.setFocus())
getCurrentWebviewWindow().show()
})
</script>

View File

@ -1,11 +1,7 @@
import { getExtensionsFolder, IS_IN_TAURI } from "@/constants"
import * as path from "@tauri-apps/api/path"
import { error } from "@tauri-apps/plugin-log"
import { setStoreCollectionPath } from "@tauri-store/svelte"
import type { LayoutLoad } from "./$types"
export const load: LayoutLoad = async () => {
const appDataPath = await path.appDataDir()
await setStoreCollectionPath(await path.join(appDataPath, "kk-config"))
return { extsInstallDir: IS_IN_TAURI ? await getExtensionsFolder() : "", appDataPath }
return { extsInstallDir: IS_IN_TAURI ? await getExtensionsFolder() : "" }
}

View File

@ -2,20 +2,22 @@
<script lang="ts">
import { commandLaunchers } from "@/cmds"
import { builtinCmds } from "@/cmds/builtin"
import { systemCommands, systemCommandsFiltered } from "@/cmds/system"
import { systemCommands } from "@/cmds/system"
import AppsCmds from "@/components/main/AppsCmds.svelte"
import { i18n } from "@/i18n"
import * as m from "@/paraglide/messages"
import {
appConfig,
appConfigLoaded,
appsFiltered,
// appsFiltered,
appsLoader,
appState,
devSearchExtCmds,
devStoreExtCmds,
quickLinksFiltered,
storeExtCmds,
storeSearchExtCmds
devStoreExts,
// devStoreExtsFiltered,
// installedStoreExtsFiltered,
installedStoreExts,
quickLinks
// quickLinksFiltered
} from "@/stores"
import { cmdQueries } from "@/stores/cmdQuery"
import { isKeyboardEventFromInputElement } from "@/utils/dom"
@ -25,28 +27,19 @@
import {
BuiltinCmds,
CustomCommandInput,
ExtCmds,
ExtCmdsGroup,
GlobalCommandPaletteFooter,
QuickLinks,
SystemCmds
} from "@kksh/ui/main"
import { cn } from "@kksh/ui/utils"
import { Ext } from "@kunkunapi/src/models/extension"
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
import { getCurrentWindow, Window } from "@tauri-apps/api/window"
import { platform } from "@tauri-apps/plugin-os"
import { exit } from "@tauri-apps/plugin-process"
import { goto } from "$app/navigation"
import {
ArrowBigUpIcon,
CircleXIcon,
EllipsisVerticalIcon,
RefreshCcwIcon,
SettingsIcon
} from "lucide-svelte"
import { ArrowBigUpIcon, CircleXIcon, EllipsisVerticalIcon, RefreshCcwIcon } from "lucide-svelte"
import { onMount } from "svelte"
import { Inspect } from "svelte-inspect-value"
import * as v from "valibot"
const win = getCurrentWindow()
let inputEle: HTMLInputElement | null = $state(null)
@ -66,16 +59,12 @@
if (splashscreenWin) {
splashscreenWin.close()
}
win.show().then(() => win.setFocus())
win.show()
})
win.onFocusChanged(({ payload: focused }) => {
if (focused) {
win
.show()
.then(() => win.setFocus())
.finally(() => {
inputEle?.focus()
})
win.show()
inputEle?.focus()
}
})
inputEle?.focus()
@ -83,7 +72,6 @@
// 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) {
console.log("appConfig.get().onBoarded", appConfig.get().onBoarded)
if (!appConfig.get().onBoarded) {
setTimeout(() => {
goto(i18n.resolveRoute("/app/help/onboarding"))
@ -106,21 +94,10 @@
}
}}
/>
<!--
<Inspect name="devStoreExts" value={$devStoreExts} />
<Inspect name="extensions" value={$extensions} />
<Inspect name="installedStoreExts" value={$installedStoreExts} />
<Inspect name="storeSearchExtCmds" value={$storeSearchExtCmds} />
<Inspect name="devSearchExtCmds" value={$devSearchExtCmds} />
<Inspect name="storeExtCmds" value={$storeExtCmds} />
<Inspect name="devStoreExtCmds" value={$devStoreExtCmds} />
<Inspect name="$appState.searchTerm" value={$appState.searchTerm} />
-->
<Command.Root
class={cn("h-screen rounded-lg shadow-md")}
class={cn("h-screen rounded-lg border shadow-md")}
bind:value={$appState.highlightedCmd}
shouldFilter={false}
shouldFilter={true}
loop
>
<CustomCommandInput
@ -162,7 +139,7 @@
<DropdownMenu.Trigger>
<Button variant="outline" size="icon"><EllipsisVerticalIcon /></Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content class="w-fit min-w-80">
<DropdownMenu.Content class="w-80">
<DropdownMenu.Group>
<DropdownMenu.Item onclick={() => exit()}>
<CircleXIcon class="h-4 w-4 text-red-500" />
@ -192,8 +169,8 @@
<span class="flex items-center">⌃+<ArrowBigUpIcon class="h-4 w-4" />+R </span>
</DropdownMenu.Shortcut>
</DropdownMenu.Item>
<DropdownMenu.Item onclick={() => goto(i18n.resolveRoute("/app/settings"))}>
<SettingsIcon class="mr-2 h-4 w-4 text-green-500" />
<DropdownMenu.Item onclick={() => location.reload()}>
<RefreshCcwIcon class="mr-2 h-4 w-4 text-green-500" />
{m.home_command_input_dropdown_open_preference()}
<DropdownMenu.Shortcut>
{#if platform() === "macos"}
@ -221,37 +198,45 @@
</CustomCommandInput>
<Command.List class="max-h-screen grow">
<Command.Empty data-tauri-drag-region>No results found.</Command.Empty>
{#if $devSearchExtCmds.length > 0}
<ExtCmds
{#if $appConfig.extensionsInstallDir && $devStoreExts.length > 0}
<ExtCmdsGroup
extensions={$devStoreExts}
heading={m.command_group_heading_dev_ext()}
extCmds={$devSearchExtCmds}
hmr={$appConfig.hmr}
isDev={true}
onExtCmdSelect={commandLaunchers.onExtCmdSelect}
hmr={$appConfig.hmr}
/>
{/if}
{#if $appConfig.extensionsInstallDir && $installedStoreExts.length > 0}
<ExtCmdsGroup
extensions={$installedStoreExts}
heading={m.command_group_heading_ext()}
isDev={false}
hmr={false}
onExtCmdSelect={commandLaunchers.onExtCmdSelect}
/>
{/if}
{#if $storeSearchExtCmds.length > 0}
<ExtCmds
heading={m.command_group_heading_ext()}
extCmds={$storeSearchExtCmds}
hmr={false}
isDev={false}
onExtCmdSelect={commandLaunchers.onExtCmdSelect}
/>
<QuickLinks quickLinks={$quickLinks} />
<BuiltinCmds builtinCmds={$builtinCmds} />
<SystemCmds systemCommands={$systemCommands} />
<AppsCmds apps={$appsLoader} />
<!-- <AppsCmds apps={$appsFiltered} /> -->
<!-- {#if $quickLinksFiltered.length > 0}
<QuickLinks quickLinks={$quickLinksFiltered} />
{/if}
{#if $appsFiltered.length > 0}
<AppsCmds apps={$appsFiltered} />
{/if}
{#if $builtinCmds.length > 0}
<BuiltinCmds builtinCmds={$builtinCmds} />
{/if}
{#if $systemCommandsFiltered.length > 0}
<SystemCmds systemCommands={$systemCommandsFiltered} />
{/if}
{#if $appsFiltered.length > 0}
<AppsCmds apps={$appsFiltered} />
{/if}
{#if $quickLinksFiltered.length > 0}
<QuickLinks quickLinks={$quickLinksFiltered} />
{/if}
{/if} -->
<!-- <AppsCmds apps={$appsLoader} /> -->
<!-- <AppsCmds apps={$appsFiltered} /> -->
</Command.List>
<GlobalCommandPaletteFooter />
</Command.Root>

View File

@ -1,32 +1,24 @@
<script lang="ts">
import { hideAndPaste } from "@/utils/hotkey"
import { goHome } from "@/utils/route"
import { listenToNewClipboardItem, listenToWindowFocus } from "@/utils/tauri-events"
import { goBack, goHome } from "@/utils/route"
import { listenToNewClipboardItem } from "@/utils/tauri-events"
import Icon from "@iconify/svelte"
import { ClipboardContentType } from "@kksh/api/commands"
import { ClipboardContentType, db } from "@kksh/api/commands"
import { SearchModeEnum, SQLSortOrderEnum, type ExtData } from "@kksh/api/models"
import { db } from "@kksh/drizzle"
import { Button, Command, Resizable } from "@kksh/svelte5"
import { Constants } from "@kksh/ui"
import { CustomCommandInput, GlobalCommandPaletteFooter } from "@kksh/ui/main"
import type { UnlistenFn } from "@tauri-apps/api/event"
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
import { platform } from "@tauri-apps/plugin-os"
import { ArrowLeft, FileQuestionIcon, ImageIcon, LetterTextIcon } from "lucide-svelte"
import { onDestroy, onMount, type Snippet } from "svelte"
import { toast } from "svelte-sonner"
import clipboard from "tauri-plugin-clipboard-api"
import ContentPreview from "./content-preview.svelte"
const _platform = platform()
let inputEle = $state<HTMLInputElement | null>(null)
const curWin = getCurrentWebviewWindow()
let searchTerm = $state("")
let clipboardHistoryList = $state<ExtData[]>([])
let highlightedItemValue = $state<string>("")
let highlighted = $state<ExtData | null>(null)
let unlistenClipboard = $state<UnlistenFn | null>(null)
let unlistenFocusEvt = $state<UnlistenFn | null>(null)
let isScrolling = $state(false)
let page = $state(1)
@ -75,19 +67,10 @@
}).then((unlisten) => {
unlistenClipboard = unlisten
})
listenToWindowFocus(async () => {
if (inputEle) {
inputEle.focus()
}
}).then((unlisten) => {
unlistenFocusEvt = unlisten
})
})
onDestroy(() => {
unlistenClipboard?.()
unlistenFocusEvt?.()
})
$effect(() => {
@ -172,41 +155,29 @@
}
}
function writeToClipboard(data: ExtData) {
if (!data.data) {
toast.warning("No data found")
return Promise.reject(new Error("No data found"))
}
const dataType = data?.dataType as ClipboardContentType
switch (dataType) {
case "Text":
return clipboard.writeText(data.data)
case "Image":
return clipboard.writeImageBase64(data.data)
case "Html":
return clipboard.writeHtmlAndText(data.data, data.searchText ?? data.data)
case "Rtf":
return clipboard.writeRtf(data.data)
default:
return Promise.reject(new Error("Unsupported data type: " + dataType))
}
}
function onItemSelected(dataId: number) {
// fetch data from db
db.getExtensionDataById(dataId)
.then((data) => {
console.log("data", data)
if (!data) {
toast.warning("No data found")
return Promise.reject(new Error("No data found"))
if (!data || !data.data) {
return
}
const dataType = data?.dataType as ClipboardContentType
switch (dataType) {
case "Text":
return clipboard.writeText(data.data)
case "Image":
return clipboard.writeImageBase64(data.data)
case "Html":
return clipboard.writeHtml(data.data)
case "Rtf":
return clipboard.writeRtf(data.data)
default:
return Promise.reject(new Error("Unsupported data type: " + dataType))
}
return writeToClipboard(data).then(async () => {
return hideAndPaste(curWin)
})
})
.then(() => toast.success("Copied to clipboard"))
.catch((err) => {
console.error(err)
toast.error("Failed to fetch data from db", {
description: err.message
})
@ -248,7 +219,6 @@
autofocus
placeholder="Type a command or search..."
leftSlot={leftSlot as Snippet}
bind:ref={inputEle}
bind:value={searchTerm}
/>
<Resizable.PaneGroup direction="horizontal" class="w-full rounded-lg">

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { cn } from "@/utils"
import { db } from "@kksh/api/commands"
import type { ExtData } from "@kksh/api/models"
import { db } from "@kksh/drizzle"
import { Resizable, Separator } from "@kksh/svelte5"
import { convertFileSrc } from "@tauri-apps/api/core"
import DOMPurify from "dompurify"

View File

@ -8,8 +8,6 @@
import * as userInput from "tauri-plugin-user-input-api"
import { type InputEvent } from "tauri-plugin-user-input-api"
let { data } = $props()
const SymbolMap = {
Alt: "⎇",
AltGr: "⌥",
@ -99,7 +97,10 @@
}
$effect(() => {
data.win?.show().then(() => data.win?.setFocus())
const win = getCurrentWebviewWindow()
if (win) {
win.show()
}
userInput.setEventTypes([userInput.EventTypeEnum.KeyPress, userInput.EventTypeEnum.KeyRelease])
userInput.startListening((evt: InputEvent) => {

View File

@ -8,24 +8,23 @@
import * as clipboard from "tauri-plugin-clipboard-api"
let image = $state<string | null>(null)
let { data } = $props()
const appWin = getCurrentWebviewWindow()
let originalSize = $state<{ width: number; height: number } | null>(null)
let originalScaleFactor = $state<number | null>(null)
let scale = $state<number>(1)
let currentSize = $derived(
originalSize ? { width: originalSize.width * scale, height: originalSize.height * scale } : null
)
const win = getCurrentWebviewWindow()
$effect(() => {
if (currentSize) {
win.setSize(new LogicalSize(currentSize.width, currentSize.height))
appWin.setSize(new LogicalSize(currentSize.width, currentSize.height))
}
})
async function getWindowSize() {
const size = await win.outerSize()
const scaleFactor = originalScaleFactor ?? (await win.scaleFactor())
const size = await appWin.outerSize()
const scaleFactor = originalScaleFactor ?? (await appWin.scaleFactor())
const logicalSize = size.toLogical(scaleFactor)
return { logicalSize, scaleFactor }
}
@ -37,7 +36,7 @@
image = b64
})
.finally(() => {
data.win?.show().then(() => data.win?.setFocus())
appWin.show()
})
const { logicalSize, scaleFactor } = await getWindowSize()
originalSize = { width: logicalSize.width, height: logicalSize.height }
@ -68,13 +67,13 @@
<svelte:window
on:keydown={(e) => {
if (e.key === "Escape") {
win.close()
appWin.close()
}
}}
/>
<Button size="icon" variant="ghost" class="fixed left-2 top-2" onclick={() => win.close()}>
<CircleX />
</Button>
<Button size="icon" variant="ghost" class="fixed left-2 top-2" onclick={() => appWin.close()}
><CircleX /></Button
>
<main class="z-50 h-screen w-screen overflow-hidden" data-tauri-drag-region>
{#if image}
<img

View File

@ -1,32 +1,24 @@
<script lang="ts">
import { getExtensionsFolder } from "@/constants"
import { appState, extensions } from "@/stores"
import { keys } from "@/stores/keys"
import { supabaseAPI } from "@/supabase"
import { goBackOnEscapeClearSearchTerm, goHomeOnEscapeClearSearchTerm } from "@/utils/key"
import { goBack, goHome } from "@/utils/route"
import { Action as ActionSchema, ExtensionStoreListItem, ExtPublish } from "@kksh/api/models"
import { Action } from "@kksh/api/ui"
import {
getExtensionsLatestPublishByIdentifier,
postExtensionsIncrementDownloads
} from "@kksh/sdk"
import { SBExt } from "@kksh/supabase/models"
import type { ExtPublishMetadata } from "@kksh/supabase/models"
import { type Tables } from "@kksh/supabase/types"
import { Button, Command } from "@kksh/svelte5"
import { Constants } from "@kksh/ui"
import { ExtListItem } from "@kksh/ui/extension"
import { CustomCommandInput, GlobalCommandPaletteFooter } from "@kksh/ui/main"
import { platform } from "@tauri-apps/plugin-os"
import { goto } from "$app/navigation"
import { ArrowLeft } from "lucide-svelte"
import { onMount, type Snippet } from "svelte"
import type { Snippet } from "svelte"
import { toast } from "svelte-sonner"
import type { Action as SvelteAction } from "svelte/action"
import { getInstallExtras } from "./[identifier]/helper.js"
let { data } = $props()
const { storeExtList, installedExtsMap, upgradableExpsMap } = data
const _platform = platform()
let actionPanelOpen = $state(false)
let listviewInputRef = $state<HTMLInputElement | null>(null)
let highlightedCmdValue = $state("")
const { storeExtList, installedStoreExts, installedExtsMap, upgradableExpsMap } = data
// function isUpgradeable(item: DbExtItem): boolean {
// if (!item.version) return true // latest extensions always have version, this check should be removed later
@ -38,133 +30,56 @@
// )
// }
onMount(() => {
// setTimeout(() => {
// console.log("focus", listviewInputRef)
// listviewInputRef?.focus()
// }, 3_000)
})
const inputAction: SvelteAction = (node) => {
// the node has been mounted in the DOM
$effect(() => {
// setup goes here
console.log("inputAction", node)
listviewInputRef?.focus()
return () => {
// teardown goes here
}
})
}
$effect(() => {
function docKeyDown(e: KeyboardEvent) {
if (e.key === "/") {
listviewInputRef?.focus()
}
}
document.addEventListener("keydown", docKeyDown)
return () => {
document.removeEventListener("keydown", docKeyDown)
}
})
function onExtItemSelected(ext: ExtensionStoreListItem) {
function onExtItemSelected(ext: SBExt) {
goto(`./store/${ext.identifier}`)
}
async function onExtItemUpgrade(ext: ExtensionStoreListItem) {
const { data, error, response } = await getExtensionsLatestPublishByIdentifier({
path: {
identifier: ext.identifier
}
})
if (error)
async function onExtItemUpgrade(ext: SBExt) {
const res = await supabaseAPI.getLatestExtPublish(ext.identifier)
if (res.error)
return toast.error("Fail to get latest extension", {
description: error.error
description: res.error.message
})
const installExtras = await getInstallExtras(data?.metadata)
const tarballUrl = res.data.tarball_path.startsWith("http")
? res.data.tarball_path
: supabaseAPI.translateExtensionFilePathToUrl(res.data.tarball_path)
const installExtras = await getInstallExtras(
res.data as Tables<"ext_publish"> & { metadata: ExtPublishMetadata }
)
return extensions
.upgradeStoreExtension(ext.identifier, data.tarball_path, installExtras)
.upgradeStoreExtension(ext.identifier, tarballUrl, installExtras)
.then((newExt) => {
toast.success(`${ext.name} Upgraded to ${newExt.version}`)
})
}
async function onExtItemInstall(ext: ExtensionStoreListItem) {
const { data, error, response } = await getExtensionsLatestPublishByIdentifier({
path: {
identifier: ext.identifier
}
})
if (error)
async function onExtItemInstall(ext: SBExt) {
const res = await supabaseAPI.getLatestExtPublish(ext.identifier)
if (res.error)
return toast.error("Fail to get latest extension", {
description: error.error
description: res.error.message
})
const installExtras = await getInstallExtras(data?.metadata)
const tarballUrl = res.data.tarball_path.startsWith("http")
? res.data.tarball_path
: supabaseAPI.translateExtensionFilePathToUrl(res.data.tarball_path)
const installExtras = await getInstallExtras(
res.data as Tables<"ext_publish"> & { metadata: ExtPublishMetadata }
)
const installDir = await getExtensionsFolder()
return extensions
.installFromTarballUrl(data.tarball_path, installDir, installExtras)
.installFromTarballUrl(tarballUrl, installDir, installExtras)
.then(() => toast.success(`Plugin ${ext.name} Installed`))
.then(() =>
postExtensionsIncrementDownloads({
body: {
identifier: ext.identifier,
version: ext.version
}
supabaseAPI.incrementDownloads({
identifier: ext.identifier,
version: ext.version
})
.then(({ error }) => {
if (error) {
console.error(error)
}
})
.catch(console.error)
)
}
function onActionPanelBlur() {
setTimeout(() => {
listviewInputRef?.focus()
}, 300)
}
$effect(() => {
void $keys
const keySet = keys.getSet()
if (keySet.size === 2) {
if (keySet.has(_platform === "macos" ? "Meta" : "Control") && keySet.has("k")) {
setTimeout(() => {
actionPanelOpen = !actionPanelOpen
if (!actionPanelOpen) {
onActionPanelBlur()
listviewInputRef?.focus()
}
}, 100)
}
}
})
function onkeydown(e: KeyboardEvent) {
if (e.key === "Escape") {
if (document.activeElement === listviewInputRef) {
goHome()
}
}
}
let highlightedCmd = $derived.by(() => {
void highlightedCmdValue
const ext = storeExtList.find((ext) => ext.identifier === highlightedCmdValue)
if (ext) {
return ext
}
return null
})
</script>
<svelte:window {onkeydown} />
<svelte:window on:keydown={goHomeOnEscapeClearSearchTerm} />
{#snippet leftSlot()}
<Button
variant="outline"
@ -176,27 +91,12 @@
<ArrowLeft class="size-4" />
</Button>
{/snippet}
<Command.Root class="h-screen rounded-lg border shadow-md" loop bind:value={highlightedCmdValue}>
<Command.Root class="h-screen rounded-lg border shadow-md" loop>
<CustomCommandInput
bind:ref={listviewInputRef}
autofocus
placeholder="Type / to focus"
placeholder="Type a command or search..."
leftSlot={leftSlot as Snippet}
bind:value={$appState.searchTerm}
onkeydown={(e) => {
if (e.key === "Enter") {
const modifier = _platform === "macos" ? e.metaKey : e.ctrlKey
if (modifier) {
if (highlightedCmd) {
onExtItemInstall(highlightedCmd)
}
} else {
if (highlightedCmd) {
onExtItemSelected(highlightedCmd)
}
}
}
}}
/>
<Command.List class="max-h-screen grow">
<Command.Empty>No results found.</Command.Empty>
@ -205,40 +105,11 @@
{ext}
installedVersion={$installedExtsMap[ext.identifier]}
isUpgradable={!!$upgradableExpsMap[ext.identifier]}
onSelect={() => {
onExtItemSelected(ext)
}}
onSelect={() => onExtItemSelected(ext)}
onUpgrade={() => onExtItemUpgrade(ext)}
onInstall={() => onExtItemInstall(ext)}
/>
{/each}
</Command.List>
<GlobalCommandPaletteFooter
defaultAction="Show Details"
bind:actionPanelOpen
{onActionPanelBlur}
actionPanel={new Action.ActionPanel({
title: "Actions",
items: [
new Action.Action({
title: `Install (${_platform === "macos" ? "⌘" : "Ctrl"} + ⏎)`,
value: "install"
})
]
})}
onActionSelected={(value) => {
if (value === "install") {
console.log("install")
if (highlightedCmd) {
onExtItemInstall(highlightedCmd)
}
}
}}
onDefaultActionSelected={() => {
console.log("default install")
if (highlightedCmd) {
onExtItemInstall(highlightedCmd)
}
}}
/>
<GlobalCommandPaletteFooter />
</Command.Root>

View File

@ -1,47 +1,43 @@
import { appConfig, appState, extensions, installedStoreExts } from "@/stores"
import { goHome } from "@/utils/route"
// import { supabaseAPI } from "@/supabase"
import type { ExtensionStoreListItem, ExtPackageJsonExtra } from "@kksh/api/models"
import { appConfig, extensions, installedStoreExts } from "@/stores"
import { supabaseAPI } from "@/supabase"
import type { ExtPackageJsonExtra } from "@kksh/api/models"
import { isExtPathInDev, isUpgradable } from "@kksh/extension"
import { getExtensionsStoreList } from "@kksh/sdk"
import { toast } from "svelte-sonner"
import { SBExt } from "@kksh/supabase/models"
import { error } from "@sveltejs/kit"
import { derived, get, type Readable } from "svelte/store"
import type { PageLoad } from "./$types"
export const load: PageLoad = (): Promise<{
storeExtList: ExtensionStoreListItem[]
export const load: PageLoad = async (): Promise<{
storeExtList: SBExt[]
installedStoreExts: Readable<ExtPackageJsonExtra[]>
installedExtsMap: Readable<Record<string, string>>
upgradableExpsMap: Readable<Record<string, boolean>>
}> => {
appState.setFullScreenLoading(true)
return getExtensionsStoreList()
.then(({ data: storeExtList, error, response }) => {
storeExtList = storeExtList ?? []
if (error) {
toast.error(`Failed to load extension store: ${error} (${response.status})`)
goHome()
}
const storeExtsMap = Object.fromEntries(storeExtList.map((ext) => [ext.identifier, ext]))
const installedExtsMap = derived(installedStoreExts, ($exts) =>
Object.fromEntries($exts.map((ext) => [ext.kunkun.identifier, ext.version]))
)
const upgradableExpsMap = derived(installedStoreExts, ($exts) =>
Object.fromEntries(
$exts.map((ext) => {
const dbExt: ExtensionStoreListItem | undefined = storeExtsMap[ext.kunkun.identifier]
return [ext.kunkun.identifier, dbExt ? isUpgradable(dbExt, ext.version) : false]
})
)
)
return {
storeExtList,
installedStoreExts,
installedExtsMap,
upgradableExpsMap
}
})
.finally(() => {
appState.setFullScreenLoading(false)
})
const storeExtList = await supabaseAPI.getExtList()
// map identifier to extItem
const storeExtsMap = Object.fromEntries(storeExtList.map((ext) => [ext.identifier, ext]))
const _appConfig = get(appConfig)
// const installedStoreExts = derived(extensions, ($extensions) => {
// if (!_appConfig.extensionPath) return []
// return $extensions.filter((ext) => !isExtPathInDev(_appConfig.extensionPath!, ext.extPath))
// })
// map installed extension identifier to version
const installedExtsMap = derived(installedStoreExts, ($exts) =>
Object.fromEntries($exts.map((ext) => [ext.kunkun.identifier, ext.version]))
)
const upgradableExpsMap = derived(installedStoreExts, ($exts) =>
Object.fromEntries(
$exts.map((ext) => {
const dbExt: SBExt | undefined = storeExtsMap[ext.kunkun.identifier]
return [ext.kunkun.identifier, dbExt ? isUpgradable(dbExt, ext.version) : false]
})
)
)
return {
storeExtList,
installedStoreExts,
installedExtsMap,
upgradableExpsMap
}
}

View File

@ -2,8 +2,11 @@
import { getExtensionsFolder } from "@/constants.js"
import { i18n } from "@/i18n.js"
import { extensions, installedStoreExts } from "@/stores/extensions.js"
import { DBExtension, ExtensionStoreListItem, ExtPackageJson, ExtPublish } from "@kksh/api/models"
import { postExtensionsIncrementDownloads } from "@kksh/sdk"
import { supabaseAPI } from "@/supabase"
import { goBack } from "@/utils/route.js"
import { ExtPackageJson } from "@kksh/api/models"
import { ExtPublishMetadata } from "@kksh/supabase/models"
import type { Tables } from "@kksh/supabase/types"
import { Button } from "@kksh/svelte5"
import { cn } from "@kksh/svelte5/utils"
import { Constants } from "@kksh/ui"
@ -19,8 +22,10 @@
import { getInstallExtras } from "./helper"
const { data } = $props()
const extPublish = $derived(data.extPublish)
const ext = $derived(data.ext)
const extPublish: Tables<"ext_publish"> & { metadata: ExtPublishMetadata } = $derived(
data.extPublish
)
const ext: Tables<"extensions"> = $derived(data.ext)
const manifest = $derived(data.manifest)
const installedExt = storeDerived(installedStoreExts, ($e) => {
return $e.find((e) => e.kunkun.identifier === extPublish.identifier)
@ -72,29 +77,26 @@
}, 500)
})
const demoImages = $derived(extPublish.demo_images)
const demoImages = $derived(
extPublish.demo_images.map((src) => supabaseAPI.translateExtensionFilePathToUrl(src))
)
async function onInstallSelected() {
loading.install = true
const installExtras = await getInstallExtras(extPublish.metadata)
const tarballUrl = extPublish.tarball_path.startsWith("http")
? extPublish.tarball_path
: supabaseAPI.translateExtensionFilePathToUrl(extPublish.tarball_path)
const installExtras = await getInstallExtras(extPublish)
const installDir = await getExtensionsFolder()
return extensions
.installFromTarballUrl(extPublish.tarball_path, installDir, installExtras)
.installFromTarballUrl(tarballUrl, installDir, installExtras)
.then(() => toast.success(`Plugin ${extPublish.name} Installed`))
.then((loadedExt) => {
info(`Successfully installed ${extPublish.name}`)
postExtensionsIncrementDownloads({
body: {
identifier: extPublish.identifier,
version: extPublish.version
}
supabaseAPI.incrementDownloads({
identifier: extPublish.identifier,
version: extPublish.version
})
.then(({ error }) => {
if (error) {
console.error(error)
}
})
.catch(console.error)
showBtn.install = false
showBtn.uninstall = true
})
@ -109,8 +111,9 @@
function onUpgradeSelected() {
loading.upgrade = true
const tarballUrl = supabaseAPI.translateExtensionFilePathToUrl(extPublish.tarball_path)
return extensions
.upgradeStoreExtension(extPublish.identifier, extPublish.tarball_path)
.upgradeStoreExtension(extPublish.identifier, tarballUrl)
.then((newExt) => {
toast.success(
`${extPublish.name} Upgraded from ${$installedExt?.version} to ${newExt.version}`

View File

@ -1,57 +1,53 @@
import { appState } from "@/stores"
import { DBExtension, ExtPublish, KunkunExtManifest } from "@kksh/api/models"
import { getExtensionsByIdentifier, getExtensionsLatestPublishByIdentifier } from "@kksh/sdk"
import { extensions } from "@/stores"
import { supabaseAPI } from "@/supabase"
import { KunkunExtManifest, type ExtPackageJsonExtra } from "@kksh/api/models"
import { ExtPublishMetadata } from "@kksh/supabase/models"
import type { Tables } from "@kksh/supabase/types"
import { error } from "@sveltejs/kit"
import { toast } from "svelte-sonner"
import * as v from "valibot"
import type { PageLoad } from "./$types"
export const load: PageLoad = ({
export const load: PageLoad = async ({
params
}): Promise<{
extPublish: ExtPublish
ext: DBExtension
extPublish: Tables<"ext_publish"> & { metadata: ExtPublishMetadata }
ext: Tables<"extensions">
manifest: KunkunExtManifest
params: {
identifier: string
}
}> => {
appState.setFullScreenLoading(true)
return getExtensionsLatestPublishByIdentifier({
path: {
identifier: params.identifier
}
})
.then(async ({ data: extPublish, error: err, response }) => {
if (err || !extPublish) {
return error(response.status, {
message: "Failed to get extension publish"
})
}
const {
data: ext,
error: extError,
response: extRes
} = await getExtensionsByIdentifier({
path: {
identifier: params.identifier
}
})
if (extError || !ext) {
console.error(extError)
return error(extRes.status, {
message: extError.error || "Failed to get extension"
})
}
return {
extPublish: v.parse(ExtPublish, extPublish),
ext,
manifest: v.parse(KunkunExtManifest, extPublish.manifest),
params
}
const { error: dbError, data: extPublish } = await supabaseAPI.getLatestExtPublish(
params.identifier
)
const metadataParse = v.safeParse(ExtPublishMetadata, extPublish?.metadata ?? {})
if (dbError) {
return error(400, {
message: dbError.message
})
.finally(() => {
appState.setFullScreenLoading(false)
}
const metadata = metadataParse.success ? metadataParse.output : {}
const parseManifest = v.safeParse(KunkunExtManifest, extPublish.manifest)
if (!parseManifest.success) {
const errMsg = "Invalid extension manifest, you may need to upgrade your app."
toast.error(errMsg)
throw error(400, errMsg)
}
const { data: ext, error: extError } = await supabaseAPI.getExtension(params.identifier)
if (extError) {
return error(400, {
message: extError.message
})
}
return {
extPublish: { ...extPublish, metadata },
ext,
params,
manifest: parseManifest.output
}
}
export const csr = true

View File

@ -1,15 +1,15 @@
import type { ExtPublishMetadata } from "@kunkunapi/src/models"
import type { ExtPublishMetadata } from "@kksh/supabase/models"
import type { Tables } from "@kksh/supabase/types"
export async function getInstallExtras(extMetadata?: {
sourceType?: string
source?: string
}): Promise<{ overwritePackageJson?: string }> {
export async function getInstallExtras(
ext: Tables<"ext_publish"> & { metadata?: ExtPublishMetadata }
): Promise<{ overwritePackageJson?: string }> {
const extras: { overwritePackageJson?: string } = {}
if (extMetadata?.sourceType) {
if (extMetadata?.sourceType === "jsr") {
if (extMetadata?.source) {
if (ext.metadata?.sourceType) {
if (ext.metadata?.sourceType === "jsr") {
if (ext.metadata?.source) {
try {
const res = await fetch(`${extMetadata.source}/package.json`)
const res = await fetch(`${ext.metadata.source}/package.json`)
const pkgJsonContent = await res.text()
extras.overwritePackageJson = pkgJsonContent
} catch (error) {

View File

@ -1,15 +1,13 @@
<script lang="ts">
import DanceTransition from "@/components/dance/dance-transition.svelte"
import { i18n } from "@/i18n"
import { appConfig, appState, winExtMap } from "@/stores"
import { appConfig, winExtMap } from "@/stores"
import { helperAPI } from "@/utils/helper"
import { paste } from "@/utils/hotkey"
import { goBackOnEscape } from "@/utils/key"
import { decideKkrpcSerialization } from "@/utils/kkrpc"
import { goHome } from "@/utils/route"
import { positionToCssStyleString, positionToTailwindClasses } from "@/utils/style"
import { sleep } from "@/utils/time"
import { isInMainWindow } from "@/utils/window"
import { db } from "@kksh/api/commands"
import { CustomPosition, ThemeColor, type Position } from "@kksh/api/models"
import {
constructJarvisServerAPIWithPermissions,
@ -18,17 +16,10 @@
type IUiCustom
} from "@kksh/api/ui"
import { toast, type IUiCustomServer1, type IUiCustomServer2 } from "@kksh/api/ui/custom"
import { db } from "@kksh/drizzle"
import { Button } from "@kksh/svelte5"
import { cn } from "@kksh/ui/utils"
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
import {
RECORD_EXTENSION_PROCESS_EVENT,
type IRecordExtensionProcessEvent
} from "@kunkunapi/src/events"
import { emitTo } from "@tauri-apps/api/event"
import { getCurrentWindow } from "@tauri-apps/api/window"
import { info } from "@tauri-apps/plugin-log"
import { goto } from "$app/navigation"
import { IframeParentIO, RPCChannel } from "kkrpc/browser"
import { ArrowLeftIcon, MoveIcon, RefreshCcwIcon, XIcon } from "lucide-svelte"
@ -37,7 +28,7 @@
let { data }: { data: PageData } = $props()
const { loadedExt, url, extPath, extInfoInDB } = data
let extSpawnedProcesses = $state<number[]>([])
const appWin = getCurrentWindow()
let iframeRef: HTMLIFrameElement
let uiControl = $state<{
iframeLoaded: boolean
@ -64,7 +55,7 @@
if (isInMainWindow()) {
goto(i18n.resolveRoute("/app/"))
} else {
data.win?.close()
appWin.close()
}
},
hideBackButton: async () => {
@ -116,25 +107,7 @@
const serverAPI: IKunkunFullServerAPI = constructJarvisServerAPIWithPermissions(
loadedExt.kunkun.permissions,
loadedExt.extPath,
{
recordSpawnedProcess: async (pid: number) => {
extSpawnedProcesses = [...extSpawnedProcesses, pid]
// winExtMap.registerProcess(appWin.label, pid)
const curWin = await getCurrentWindow()
await emitTo("main", RECORD_EXTENSION_PROCESS_EVENT, {
windowLabel: curWin.label,
pid
} satisfies IRecordExtensionProcessEvent)
// TODO: record process in a store
},
getSpawnedProcesses: () => Promise.resolve(extSpawnedProcesses),
paste: async () => {
await data.win?.hide()
await sleep(200)
return paste()
}
}
loadedExt.extPath
)
const serverAPI2 = {
...serverAPI,
@ -154,7 +127,7 @@
if (isInMainWindow()) {
goHome()
} else {
data.win?.close()
appWin.close()
}
}
@ -162,27 +135,16 @@
setTimeout(() => {
iframeRef.focus()
uiControl.iframeLoaded = true
appState.setFullScreenLoading(false)
}, 300)
}
onMount(() => {
appState.setFullScreenLoading(true)
setTimeout(() => {
data.win?.setFocus()
appWin.show()
}, 200)
if (iframeRef?.contentWindow) {
const io = new IframeParentIO(iframeRef.contentWindow)
const kkrpcSerialization = decideKkrpcSerialization(loadedExt)
info(
`Establishing kkrpc connection for ${loadedExt.kunkun.identifier} with serialization: ${kkrpcSerialization}`
)
const rpc = new RPCChannel(io, {
expose: serverAPI2,
serialization: {
version: kkrpcSerialization
}
})
const rpc = new RPCChannel(io, { expose: serverAPI2 })
} else {
toast.warning("iframeRef.contentWindow not available")
}
@ -195,7 +157,7 @@
})
onDestroy(() => {
winExtMap.unregisterExtensionFromWindow(data.win?.label ?? "")
winExtMap.unregisterExtensionFromWindow(appWin.label)
})
</script>
@ -208,7 +170,7 @@
onclick={onBackBtnClicked}
style={`${positionToCssStyleString(uiControl.backBtnPosition)}`}
>
{#if data.win?.label === "main"}
{#if appWin.label === "main"}
<ArrowLeftIcon class="w-4" />
{:else}
<XIcon class="w-4" />
@ -239,6 +201,7 @@
{/if}
<main class="h-screen">
<DanceTransition delay={300} autoHide={false} show={!uiControl.iframeLoaded} />
<iframe
bind:this={iframeRef}
class={cn("h-full", {

View File

@ -1,7 +1,7 @@
import { KunkunIframeExtParams } from "@/cmds/ext"
import { i18n } from "@/i18n"
import { db, unregisterExtensionWindow } from "@kksh/api/commands"
import type { Ext as ExtInfoInDB, ExtPackageJsonExtra } from "@kksh/api/models"
import { db } from "@kksh/drizzle"
import { loadExtensionManifestFromDisk } from "@kksh/extension"
import { error as svError } from "@sveltejs/kit"
import { join } from "@tauri-apps/api/path"

View File

@ -4,15 +4,9 @@
import { keys } from "@/stores/keys"
import { winExtMap } from "@/stores/winExtMap.js"
import { helperAPI } from "@/utils/helper.js"
import { paste } from "@/utils/hotkey"
import { decideKkrpcSerialization } from "@/utils/kkrpc.js"
import {
emitReloadOneExtension,
listenToFileDrop,
listenToRefreshDevExt
} from "@/utils/tauri-events.js"
import { sleep } from "@/utils/time.js"
import { listenToFileDrop, listenToRefreshDevExt } from "@/utils/tauri-events.js"
import { isInMainWindow } from "@/utils/window.js"
import { db } from "@kksh/api/commands"
import {
constructJarvisServerAPIWithPermissions,
type IApp,
@ -28,29 +22,18 @@
type IComponent,
type TemplateUiCommand
} from "@kksh/api/ui/template"
import { db } from "@kksh/drizzle"
import { Button, Form } from "@kksh/svelte5"
import { LoadingBar } from "@kksh/ui"
import { Templates } from "@kksh/ui/extension"
import { GlobalCommandPaletteFooter } from "@kksh/ui/main"
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
import {
RECORD_EXTENSION_PROCESS_EVENT,
type IRecordExtensionProcessEvent
} from "@kunkunapi/src/events.js"
import { Channel, invoke } from "@tauri-apps/api/core"
import { emitTo, type UnlistenFn } from "@tauri-apps/api/event"
import type { UnlistenFn } from "@tauri-apps/api/event"
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"
import { getCurrentWindow } from "@tauri-apps/api/window"
import * as fs from "@tauri-apps/plugin-fs"
import { readTextFile } from "@tauri-apps/plugin-fs"
import { debug, info } from "@tauri-apps/plugin-log"
import { debug } from "@tauri-apps/plugin-log"
import { platform } from "@tauri-apps/plugin-os"
import { goto } from "$app/navigation"
import { RPCChannel, WorkerParentIO } from "kkrpc/browser"
import { onDestroy, onMount, tick } from "svelte"
import Inspect from "svelte-inspect-value"
import { type CommandEvent } from "tauri-plugin-shellx-api"
import * as v from "valibot"
const { data } = $props()
@ -61,22 +44,20 @@
let unlistenRefreshWorkerExt: UnlistenFn | undefined
let unlistenFileDrop: UnlistenFn | undefined
let worker: Worker | undefined
let listViewContent = $state<ListSchema.List | null>(null)
let formViewContent = $state<FormSchema.Form | null>(null)
let markdownViewContent = $state<MarkdownSchema | null>(null)
let listViewContent = $state<ListSchema.List>()
let formViewContent = $state<FormSchema.Form>()
let markdownViewContent = $state<MarkdownSchema>()
let extensionLoadingBar = $state(false) // whether extension called showLoadingBar
let pbar = $state<number | null>(null)
let loading = $state(false)
let searchTerm = $state("")
let searchBarPlaceholder = $state("")
let extSpawnedProcesses = $state<number[]>([])
const appWin = getCurrentWebviewWindow()
const loadingBar = $derived($appState.loadingBar || extensionLoadingBar)
let loaded = $state(false)
let listview: Templates.ListView | undefined = $state(undefined)
const _platform = platform()
let unlistenPkgJsonWatch: UnlistenFn | undefined
let curViewNodeName = $state<NodeNameEnum | FormNodeNameEnum | null>(null)
async function goBack() {
if (isInMainWindow()) {
goto(i18n.resolveRoute("/app/"))
@ -85,28 +66,23 @@
}
}
async function clearViewContent(keep?: "list" | "form" | "markdown") {
function clearViewContent(keep?: "list" | "form" | "markdown") {
if (keep !== "list") {
listViewContent = null
listViewContent = undefined
}
if (keep !== "form") {
formViewContent = null
formViewContent = undefined
}
if (keep !== "markdown") {
markdownViewContent = null
markdownViewContent = undefined
}
await tick()
// await sleep(3000)
}
const extUiAPI: IUiTemplate = {
async render(_view: IComponent<ListSchema.List | FormSchema.Form | MarkdownSchema>) {
// console.log("render nodeName", _view.nodeName)
// console.log("render", _view)
curViewNodeName = _view.nodeName
if (_view.nodeName === NodeNameEnum.List) {
await clearViewContent("list")
const parsedListViewRes = v.safeParse(ListSchema.List, _view)
async render(view: IComponent<ListSchema.List | FormSchema.Form | MarkdownSchema>) {
if (view.nodeName === NodeNameEnum.List) {
clearViewContent("list")
const parsedListViewRes = v.safeParse(ListSchema.List, view)
if (!parsedListViewRes.success) {
toast.error("Invalid List View", {
description: "See console for details"
@ -186,22 +162,20 @@
// } else {
// listViewContent = parsedListView
// }
} else if (_view.nodeName === FormNodeNameEnum.Form) {
listViewContent = null
// await clearViewContent("form")
// await tick()
const parsedForm = v.parse(FormSchema.Form, _view)
} else if (view.nodeName === FormNodeNameEnum.Form) {
listViewContent = undefined
clearViewContent("form")
const parsedForm = v.parse(FormSchema.Form, view)
formViewContent = parsedForm
// TODO: convert form to zod schema
// const zodSchema = convertFormToZod(parsedForm)
// formViewZodSchema = zodSchema
// formFieldConfig = buildFieldConfig(parsedForm)
} else if (_view.nodeName === NodeNameEnum.Markdown) {
await clearViewContent("markdown")
await tick()
markdownViewContent = v.parse(MarkdownSchema, _view)
} else if (view.nodeName === NodeNameEnum.Markdown) {
clearViewContent("markdown")
markdownViewContent = v.parse(MarkdownSchema, view)
} else {
toast.error(`Unsupported view type: ${_view.nodeName}`)
toast.error(`Unsupported view type: ${view.nodeName}`)
}
},
async showLoadingBar(loading: boolean) {
@ -218,6 +192,7 @@
searchTerm = term
},
async setSearchBarPlaceholder(placeholder: string) {
console.log("setSearchBarPlaceholder", placeholder)
searchBarPlaceholder = placeholder
},
async goBack() {
@ -239,25 +214,7 @@
worker = new Worker(blobURL)
const serverAPI: IKunkunFullServerAPI = constructJarvisServerAPIWithPermissions(
loadedExt.kunkun.permissions,
loadedExt.extPath,
{
recordSpawnedProcess: async (pid: number) => {
extSpawnedProcesses = [...extSpawnedProcesses, pid]
// winExtMap.registerProcess(appWin.label, pid)
const curWin = await getCurrentWindow()
await emitTo("main", RECORD_EXTENSION_PROCESS_EVENT, {
windowLabel: curWin.label,
pid
} satisfies IRecordExtensionProcessEvent)
// TODO: record process in a store
},
getSpawnedProcesses: () => Promise.resolve(extSpawnedProcesses),
paste: async () => {
await appWin.hide()
await sleep(200)
return paste()
}
}
loadedExt.extPath
)
const serverAPI2 = {
...serverAPI,
@ -270,16 +227,10 @@
language: () => Promise.resolve("en")
} satisfies IApp
}
const io = new WorkerParentIO(worker)
const kkrpcSerialization = decideKkrpcSerialization(loadedExt)
info(
`Establishing kkrpc connection for ${loadedExt.kunkun.identifier} with serialization: ${kkrpcSerialization}`
)
const rpc = new RPCChannel<typeof serverAPI2, TemplateUiCommand>(io, {
expose: serverAPI2,
serialization: {
version: kkrpcSerialization
}
expose: serverAPI2
})
workerAPI = rpc.getAPI()
await workerAPI.load()
@ -291,26 +242,10 @@
worker?.terminate()
}
})
// function onPkgJsonChange(evt: fs.WatchEvent) {
// const parsed = v.safeParse(WatchEvent, evt)
// if (parsed.success) {
// if (
// parsed.output.type.modify.kind === "data" &&
// parsed.output.type.modify.mode === "content" &&
// parsed.output.paths.includes(data.pkgJsonPath)
// ) {
// console.log("pkgJson changed", parsed.output.paths)
// // emit event to reload extension commands
// emitReloadOneExtension(loadedExt.extPath)
// }
// }
// }
onMount(async () => {
setTimeout(() => {
appState.setLoadingBar(true)
appWin.show().then(() => appWin.setFocus())
appWin.show()
}, 100)
unlistenRefreshWorkerExt = await listenToRefreshDevExt(() => {
debug("Refreshing Worker Extension")
@ -326,20 +261,14 @@
appState.setLoadingBar(false)
loaded = true
}, 500)
// fs.watch(data.pkgJsonPath, onPkgJsonChange).then((unlisten) => {
// unlistenPkgJsonWatch = unlisten
// })
})
onDestroy(() => {
unlistenRefreshWorkerExt?.()
unlistenFileDrop?.()
unlistenPkgJsonWatch?.()
winExtMap.unregisterExtensionFromWindow(appWin.label)
extensionLoadingBar = false
appState.setActionPanel(undefined)
appState.setDefaultAction(null)
appState.setActionPanel(undefined)
})
$effect(() => {
@ -352,6 +281,7 @@
) {
// actionPanelOpen = true
setTimeout(() => {
console.log("toggle action panel")
actionPanelOpen = !actionPanelOpen
if (!actionPanelOpen) {
onActionPanelBlur()
@ -368,6 +298,8 @@
function onkeydown(e: KeyboardEvent) {
if (e.key === "Escape") {
console.log(document.activeElement)
console.log(document.activeElement?.nodeName)
if (document.activeElement?.nodeName === "INPUT") {
console.log("input")
}
@ -379,8 +311,7 @@
{#if loadingBar}
<LoadingBar class="fixed left-0 top-0 w-full" color="white" />
{/if}
{#if curViewNodeName === NodeNameEnum.List && listViewContent}
{#if loaded && listViewContent !== undefined}
<Templates.ListView
bind:inputRef={listviewInputRef}
bind:searchTerm
@ -402,18 +333,26 @@
onSearchTermChange={(searchTerm: string) => {
workerAPI?.onSearchTermChange(searchTerm)
}}
onHighlightedItemChanged={(item: ListSchema.Item) => {
if (item.defaultAction) {
appState.setDefaultAction(item.defaultAction)
} else if (listViewContent?.defaultAction) {
appState.setDefaultAction(listViewContent.defaultAction)
onHighlightedItemChanged={(value: string) => {
// workerAPI?.onHighlightedListItemChanged(value)
// if (listViewContent?.defaultAction) {
// appState.setDefaultAction(listViewContent.defaultAction)
// }
// if (listViewContent?.actions) {
// appState.setActionPanel(listViewContent.actions)
// }
try {
const parsedItem = v.parse(ListSchema.Item, JSON.parse(value))
if (parsedItem.defaultAction) {
appState.setDefaultAction(parsedItem.defaultAction)
}
if (parsedItem.actions) {
appState.setActionPanel(parsedItem.actions)
}
workerAPI?.onHighlightedListItemChanged(parsedItem.value)
} catch (error) {
console.error(error)
}
if (item.actions) {
appState.setActionPanel(item.actions)
} else if (listViewContent?.actions) {
appState.setActionPanel(listViewContent.actions)
}
workerAPI?.onHighlightedListItemChanged(item.value)
}}
>
{#snippet footer()}
@ -431,18 +370,16 @@
/>
{/snippet}
</Templates.ListView>
{/if}
{#if curViewNodeName === FormNodeNameEnum.Form && formViewContent}
{:else if loaded && formViewContent !== undefined}
<Templates.FormView
{formViewContent}
{pbar}
onGoBack={goBack}
onSubmit={(formData: Record<string, string | number | boolean>) => {
console.log("Submit formData", formData)
console.log("formData", formData)
workerAPI?.onFormSubmit(formData)
}}
/>
{/if}
{#if curViewNodeName === NodeNameEnum.Markdown && markdownViewContent}
{:else if loaded && markdownViewContent !== undefined}
<Templates.MarkdownView {markdownViewContent} onGoBack={goBack} />
{/if}

View File

@ -1,11 +1,10 @@
import { KunkunTemplateExtParams } from "@/cmds/ext"
import { i18n } from "@/i18n"
import type { ExtPackageJsonExtra } from "@kksh/api/models"
import { db } from "@kksh/drizzle"
import { db, unregisterExtensionWindow } from "@kksh/api/commands"
import type { Ext as ExtInfoInDB, ExtPackageJsonExtra } from "@kksh/api/models"
import { loadExtensionManifestFromDisk } from "@kksh/extension"
import { error as sbError, error as svError } from "@sveltejs/kit"
import { join } from "@tauri-apps/api/path"
import { getCurrentWindow } from "@tauri-apps/api/window"
import { exists, readTextFile } from "@tauri-apps/plugin-fs"
import { error } from "@tauri-apps/plugin-log"
import { goto } from "$app/navigation"
@ -15,16 +14,15 @@ import type { PageLoad } from "./$types"
export const load: PageLoad = async ({ url }) => {
// both query parameter must exist
const rawKunkunTemplateExtParams = localStorage.getItem("kunkun-template-ext-params")
if (!rawKunkunTemplateExtParams) {
toast.error("Invalid extension path or url")
return svError(404, "Invalid extension path or url")
}
const json = JSON.parse(rawKunkunTemplateExtParams)
const parsed = v.safeParse(KunkunTemplateExtParams, json)
const parsed = v.safeParse(KunkunTemplateExtParams, JSON.parse(rawKunkunTemplateExtParams))
if (!parsed.success) {
getCurrentWindow().show()
console.error(v.flatten<typeof KunkunTemplateExtParams>(parsed.issues))
toast.error("Fail to parse extension params from local storage", {
description: `${v.flatten<typeof KunkunTemplateExtParams>(parsed.issues)}`
})

View File

@ -8,6 +8,7 @@
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"
@ -25,22 +26,18 @@
}
$effect(() => {
if (step > Step.GeneralSettings) {
if (step === Step.DenoInstall) {
if (denoPath) {
step++
}
} else if (step === Step.FFmpegInstall) {
if (ffmpegPath) {
step++
}
} else if (step > Step.FFmpegInstall) {
appConfig.setOnBoarded(true)
goto(i18n.resolveRoute("/app"))
}
// if (step === Step.DenoInstall) {
// if (denoPath) {
// step++
// }
// } else if (step === Step.FFmpegInstall) {
// if (ffmpegPath) {
// step++
// }
// } else if (step > Step.FFmpegInstall) {
// appConfig.setOnBoarded(true)
// goto(i18n.resolveRoute("/app"))
// }
})
</script>

View File

@ -1,137 +0,0 @@
<script lang="ts">
import * as m from "@/paraglide/messages"
import { appsLoader } from "@/stores"
import { SearchPath } from "@kksh/api/models"
import { Button, Input, Table } from "@kksh/svelte5"
import { Form } from "@kksh/ui"
import * as dialog from "@tauri-apps/plugin-dialog"
import * as fs from "@tauri-apps/plugin-fs"
import { appConfig } from "$lib/stores/appConfig"
import { Inspect } from "svelte-inspect-value"
import { toast } from "svelte-sonner"
import SuperDebug, { defaults, superForm } from "sveltekit-superforms"
import { valibot, valibotClient } from "sveltekit-superforms/adapters"
import { open } from "tauri-plugin-shellx-api"
import * as v from "valibot"
export const SearchPathFormSchema = v.object({
path: v.pipe(v.string(), v.minLength(1)),
depth: v.optional(v.number(), 1)
})
const form = superForm(defaults(valibot(SearchPathFormSchema)), {
validators: valibotClient(SearchPathFormSchema),
SPA: true,
async onUpdate({ form, cancel }) {
if (!form.valid) {
return
}
const { path, depth } = form.data
if (!(await fs.exists(path))) {
return toast.error("Path does not exist")
}
appConfig.addAppSearchPath({ path, depth })
toast.success("Search Path Added")
appsLoader.init()
cancel()
}
})
const { form: formData, enhance, errors } = form
async function pickSearchPath() {
const result = await dialog.open({
directory: true
})
if (result) {
$formData.path = result
}
}
</script>
<main class="container flex flex-col space-y-2">
<h1 class="text-2xl font-bold">{m.settings_app_search_paths_title()}</h1>
{#if $appConfig.developerMode}
<Inspect name="Extra App Search Paths" value={$appConfig.appSearchPaths} />
{/if}
<form method="POST" use:enhance>
<Form.Field {form} name="path">
<Form.Control>
{#snippet children({ props })}
<Form.Label>{m.settings_app_search_paths_table_col_search_path()}</Form.Label>
<div class="flex items-center gap-1">
<Input
{...props}
disabled
bind:value={$formData.path}
placeholder={m.settings_app_search_paths_table_col_search_path()}
/>
<Form.Button class="my-1" onclick={pickSearchPath}>Pick</Form.Button>
</div>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field {form} name="depth">
<Form.Control>
{#snippet children({ props })}
<Form.Label>{m.settings_app_search_paths_table_col_depth()}</Form.Label>
<Input
{...props}
type="number"
bind:value={$formData.depth}
placeholder={m.settings_app_search_paths_table_col_depth()}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Button class="w-full" type="submit">
{m.settings_app_search_paths_add_app_search_path()}
</Button>
</form>
{#if $appConfig.developerMode}
<SuperDebug data={$formData} />
{/if}
<Table.Root>
<Table.Header>
<Table.Row>
<Table.Head>{m.settings_app_search_paths_table_col_search_path()}</Table.Head>
<Table.Head class="text-center">
{m.settings_app_search_paths_table_col_depth()}
</Table.Head>
<Table.Head class="text-center">
{m.settings_app_search_paths_table_col_actions()}
</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{#each $appConfig.appSearchPaths as appSearchPath, i (i)}
<Table.Row>
<Table.Cell
class="cursor-pointer font-medium"
onclick={() => {
open(appSearchPath.path)
}}
>
<code>{appSearchPath.path}</code>
</Table.Cell>
<Table.Cell class="text-center">{appSearchPath.depth}</Table.Cell>
<Table.Cell class="text-center">
<Button
variant="destructive"
onclick={() => {
appConfig.removeAppSearchPath(appSearchPath)
toast.error("Search Path Removed")
appsLoader.init()
}}
>
Remove
</Button>
</Table.Cell>
</Table.Row>
{/each}
</Table.Body>
</Table.Root>
</main>

View File

@ -4,7 +4,7 @@
import { goHome } from "@/utils/route"
import { Button, Sidebar } from "@kksh/svelte5"
import { Constants } from "@kksh/ui"
import { ArrowLeftIcon, FolderSearch } from "lucide-svelte"
import { ArrowLeftIcon } from "lucide-svelte"
import Blocks from "lucide-svelte/icons/blocks"
import Cog from "lucide-svelte/icons/cog"
import FileCode2 from "lucide-svelte/icons/file-code-2"
@ -19,15 +19,10 @@
icon: Cog
},
{
title: m.settings_menu_app_search_paths(),
url: i18n.resolveRoute("/app/settings/app-search-paths"),
icon: FolderSearch
title: m.settings_menu_developer(),
url: i18n.resolveRoute("/app/settings/developer"),
icon: SquareTerminal
},
// {
// title: m.settings_menu_developer(),
// url: i18n.resolveRoute("/app/settings/developer"),
// icon: SquareTerminal
// },
{
title: m.settings_menu_extensions(),
url: i18n.resolveRoute("/app/settings/extensions"),

View File

@ -1,6 +1,6 @@
<script lang="ts">
import * as m from "@/paraglide/messages"
import { db } from "@kksh/drizzle"
import { db } from "@kksh/api/commands"
import { loadExtensionManifestFromDisk } from "@kksh/extension"
import { Button, Dialog, Table } from "@kksh/svelte5"
import { join } from "@tauri-apps/api/path"

View File

@ -1,124 +0,0 @@
<script lang="ts">
import {
getAllCmds,
getAllExtensions,
getExtensionDataById,
getUniqueExtensionByIdentifier,
getUniqueExtensionByPath,
searchExtensionData,
updateCmdByID
} from "@kksh/drizzle/api"
import * as schema from "@kksh/drizzle/schema"
import { Button, Input } from "@kksh/svelte5"
import { CmdTypeEnum, Ext } from "@kunkunapi/src/models/extension"
import { SearchModeEnum, SQLSortOrderEnum } from "@kunkunapi/src/models/sql"
// import * as orm from "drizzle-orm"
import { Inspect } from "svelte-inspect-value"
import { toast } from "svelte-sonner"
import * as v from "valibot"
let searchText = $state("")
/* eslint-disable */
let data: any = $state(null)
let inspectTitle = $state("")
</script>
<main class="container space-y-2">
<Button
onclick={async () => {
getAllCmds()
.then((cmds) => {
console.log(cmds)
data = cmds
inspectTitle = "All Commands"
})
.catch((e) => {
console.error(e)
toast.error("Failed to get all commands", {
description: "See console for more details"
})
})
}}
>
Get All Commands
</Button>
<Button
onclick={() => {
getAllExtensions()
.then((exts) => {
data = exts
inspectTitle = "All Extensions"
})
.catch((e) => {
console.error(e)
toast.error("Failed to get all extensions", {
description: "See console for more details"
})
})
}}
>
Get All Extensions
</Button>
<Button
onclick={async () => {
// get all extensions with path not null
const exts = await getAllExtensions()
for (const ext of exts) {
if (ext.path === null) continue
const _ext = await getUniqueExtensionByIdentifier(ext.identifier)
console.log(_ext)
if (ext.path) {
const __ext = await getUniqueExtensionByPath(ext.path)
console.log(__ext)
}
}
// data = exts
}}
>
Get Unique Extension By Identifier and Path
</Button>
<!-- <Button
onclick={async () => {
updateCmdByID({
cmdId: 1,
name: "google",
cmdType: CmdTypeEnum.QuickLink,
data: `{"link":"https://google.com/search?query={argument}","icon":{"type":"remote-url","value":"https://google.com/favicon.ico","invert":false}}`,
enabled: true
})
}}
>
Update Command By ID
</Button> -->
<Button
onclick={async () => {
const _data = await getExtensionDataById(1, ["search_text", "data"])
data = _data
inspectTitle = "Extension Data"
}}
>
Get Extension Data By ID
</Button>
<form
class="flex gap-1"
onsubmit={async (e) => {
e.preventDefault()
const _data = await searchExtensionData({
extId: 1,
searchMode: SearchModeEnum.FTS,
searchText: searchText,
orderByCreatedAt: SQLSortOrderEnum.Desc,
limit: 10,
fields: ["search_text", "data"]
})
console.log(_data)
data = _data
inspectTitle = "Search Results"
}}
>
<Input class="" bind:value={searchText} placeholder="Search Text" />
<Button class="" type="submit">Search Extension Data</Button>
</form>
<Inspect name={inspectTitle} value={data} expandLevel={2} />
</main>

View File

@ -6,7 +6,6 @@
import { Constants } from "@kksh/ui"
import { ArrowLeftIcon } from "lucide-svelte"
import AppWindow from "lucide-svelte/icons/app-window"
import DB from "lucide-svelte/icons/database"
import Loader from "lucide-svelte/icons/loader"
import Network from "lucide-svelte/icons/network"
@ -26,11 +25,6 @@
title: m.troubleshooters_sidebar_mdns_debugger_title(),
url: i18n.resolveRoute("/app/troubleshooters/mdns-debugger"),
icon: Network
},
{
title: "ORM",
url: i18n.resolveRoute("/app/troubleshooters/orm"),
icon: DB
}
]
let currentItem = $state(items.find((item) => window.location.pathname === item.url))

View File

@ -1,11 +1,11 @@
<script lang="ts">
import { Layouts } from "@kksh/ui"
import { getCurrentWindow } from "@tauri-apps/api/window"
import { onMount } from "svelte"
let { data } = $props()
onMount(() => {
data.win?.show().then(() => data.win?.setFocus())
onMount(async () => {
const mainWin = await getCurrentWindow()
mainWin.show()
})
</script>

View File

@ -86,18 +86,12 @@ const config: Config = {
"caret-blink": {
"0%,70%,100%": { opacity: "1" },
"20%,50%": { opacity: "0" }
},
"border-beam": {
"100%": {
"offset-distance": "100%"
}
}
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"caret-blink": "caret-blink 1.25s ease-out infinite",
"border-beam": "border-beam calc(var(--duration)*1s) infinite linear"
"caret-blink": "caret-blink 1.25s ease-out infinite"
}
}
},

View File

@ -9,8 +9,7 @@
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true
"moduleResolution": "bundler"
},
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files

View File

@ -14,7 +14,7 @@ export default defineConfig(async () => ({
clearScreen: false,
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1566,
port: 1420,
strictPort: true,
host: host || false,
hmr: host

2485
deno.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -11,47 +11,46 @@
"format": "prettier --write \"**/*.{ts,tsx,md,svelte}\""
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
"@ianvs/prettier-plugin-sort-imports": "^4.4.0",
"@kksh/api": "workspace:*",
"prettier": "^3.5.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"svelte": "^5.20.5",
"svelte-check": "^4.1.4",
"turbo": "^2.4.4",
"typescript": "^5.0.0",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.9",
"svelte": "^5.16.6",
"svelte-check": "^4.1.1",
"turbo": "^2.3.4",
"typescript": "5.7.2",
"verify-package-export": "^0.0.3"
},
"packageManager": "pnpm@10.7.0",
"packageManager": "pnpm@9.15.4",
"engines": {
"node": ">=22"
},
"dependencies": {
"@changesets/cli": "^2.28.1",
"@hey-api/client-fetch": "^0.8.3",
"@changesets/cli": "^2.27.11",
"@iconify/svelte": "^4.2.0",
"@kksh/sdk": "^0.0.3",
"@supabase/supabase-js": "^2.49.1",
"@tauri-apps/api": "^2.3.0",
"@tauri-apps/cli": "^2.3.1",
"@supabase/supabase-js": "^2.48.0",
"@tauri-apps/api": "^2.2.0",
"@tauri-apps/cli": "^2.2.2",
"@tauri-apps/plugin-deep-link": "^2.2.0",
"@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.2.0",
"@tauri-apps/plugin-global-shortcut": "^2.2.0",
"@tauri-apps/plugin-http": "^2.3.0",
"@tauri-apps/plugin-log": "^2.2.3",
"@tauri-apps/plugin-notification": "^2.2.1",
"@tauri-apps/plugin-http": "^2.2.0",
"@tauri-apps/plugin-log": "^2.2.0",
"@tauri-apps/plugin-notification": "^2.2.0",
"@tauri-apps/plugin-os": "^2.2.0",
"@tauri-apps/plugin-process": "2.2.0",
"@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-store": "^2.2.0",
"@tauri-apps/plugin-updater": "^2.5.1",
"supabase": "^2.15.8",
"@tauri-apps/plugin-updater": "^2.3.1",
"supabase": "^2.2.1",
"tauri-plugin-keyring-api": "workspace:*",
"tauri-plugin-network-api": "workspace:*",
"tauri-plugin-shellx-api": "^2.0.14",
"tauri-plugin-system-info-api": "workspace:*",
"valibot": "^1.0.0",
"zod": "^3.24.2"
"valibot": "^1.0.0-beta.11",
"zod": "^3.24.1"
},
"workspaces": [
"apps/*",

View File

@ -1,17 +1,5 @@
# @kksh/api
## 0.1.5
### Patch Changes
- Add clipboard.paste() API
## 0.1.4
### Patch Changes
- Add killPid extension API
## 0.1.2
### Patch Changes
@ -53,16 +41,3 @@
### Patch Changes
- More Icon Options
## 0.1.6
### Patch Changes
- Upgrade kkrpc to 0.2.1, which uses superjson for serialization
## 0.1.7
### Patch Changes
- Upgrade kkrpc to 0.2.2, supports both json and superjson serialization, for backward compatibility
- The previous version breaks extension compatibility.

View File

@ -1,6 +1,6 @@
{
"name": "@kksh/api",
"version": "0.1.7",
"version": "0.1.3",
"type": "module",
"repository": {
"type": "git",
@ -65,17 +65,17 @@
"@tauri-apps/plugin-store": "^2.2.0",
"@tauri-apps/plugin-updater": "^2.3.0",
"@tauri-apps/plugin-upload": "^2.2.1",
"kkrpc": "^0.2.2",
"kkrpc": "^0.1.1",
"lodash": "^4.17.21",
"minimatch": "^10.0.1",
"node-fetch": "^3.3.2",
"semver": "^7.6.3",
"svelte-sonner": "^0.3.28",
"tauri-api-adapter": "^0.3.27",
"tauri-api-adapter": "^0.3.20",
"tauri-plugin-network-api": "2.0.5",
"tauri-plugin-shellx-api": "^2.0.16",
"tauri-plugin-shellx-api": "^2.0.14",
"tauri-plugin-system-info-api": "2.0.8",
"valibot": "^1.0.0"
"valibot": "^1.0.0-beta.10"
},
"files": [
"src",

View File

@ -14,23 +14,26 @@ import type {
writeFile,
writeTextFile
} from "@tauri-apps/plugin-fs"
import type { IShell as IShell1, IPath as ITauriPath } from "tauri-api-adapter"
import type {
IClipboard as _IClipboard,
IShell as IShell1,
IPath as ITauriPath
} from "tauri-api-adapter"
Child,
ChildProcess,
CommandEvents,
hasCommand,
InternalSpawnOptions,
IOPayload,
likelyOnWindows,
OutputEvents,
SpawnOptions
} from "tauri-plugin-shellx-api"
import { EventEmitter, open as shellxOpen } from "tauri-plugin-shellx-api"
import * as v from "valibot"
import { KV, type JarvisExtDB } from "../commands/db"
import type { fileSearch } from "../commands/fileSearch"
import { type AppInfo } from "../models/apps"
import { type ExtData } from "../models/extension"
import { ExtDataField, SearchMode, SQLSortOrder } from "../models/sql"
import type { LightMode, Position, Radius, ThemeColor } from "../models/styles"
import type { DenoSysOptions } from "../permissions/schema"
export type IClipboard = _IClipboard & {
paste: (options?: {}) => Promise<void>
}
type PromiseWrap<T extends (...args: any[]) => any> = (
...args: Parameters<T>
) => Promise<ReturnType<T>>
@ -155,34 +158,23 @@ export interface IUiCustom {
}
export interface IDb {
add: (data: { data: string; dataType?: string; searchText?: string }) => Promise<void>
delete: (dataId: number) => Promise<void>
search: (searchParams: {
dataId?: number
searchMode?: SearchMode
dataType?: string
searchText?: string
afterCreatedAt?: Date
beforeCreatedAt?: Date
limit?: number
orderByCreatedAt?: SQLSortOrder
orderByUpdatedAt?: SQLSortOrder
fields?: ExtDataField[]
}) => Promise<ExtData[]>
retrieveAll: (options: { fields?: ExtDataField[] }) => Promise<ExtData[]>
retrieveAllByType: (dataType: string) => Promise<ExtData[]>
deleteAll: () => Promise<void>
update: (data: { dataId: number; data: string; searchText?: string }) => Promise<void>
add: typeof JarvisExtDB.prototype.add
delete: typeof JarvisExtDB.prototype.delete
search: typeof JarvisExtDB.prototype.search
retrieveAll: typeof JarvisExtDB.prototype.retrieveAll
retrieveAllByType: typeof JarvisExtDB.prototype.retrieveAllByType
deleteAll: typeof JarvisExtDB.prototype.deleteAll
update: typeof JarvisExtDB.prototype.update
}
/**
* A key-value store built on top of the Database API (based on sqlite)
*/
export interface IKV {
get: <T = string>(key: string) => Promise<T | null | undefined>
set: (key: string, value: string) => Promise<void>
exists: (key: string) => Promise<boolean>
delete: (key: string) => Promise<void>
get: typeof KV.prototype.get
set: typeof KV.prototype.set
exists: typeof KV.prototype.exists
delete: typeof KV.prototype.delete
}
export interface IFs {

Some files were not shown because too many files have changed in this diff Show More