Compare commits

..

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

421 changed files with 10771 additions and 22134 deletions

View File

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

View File

@ -15,7 +15,7 @@ body:
placeholder: Bug description placeholder: Bug description
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: expected-behavior id: expected-behavior
attributes: attributes:
@ -40,11 +40,4 @@ body:
label: Contributes label: Contributes
options: options:
- label: I am willing to submit a PR to fix this issue - label: I am willing to submit a PR to fix this issue
- label: I am willing to submit a PR with failing tests - 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

View File

@ -2,7 +2,7 @@ blank_issues_enabled: false
contact_links: contact_links:
- name: GitHub Discussions - name: GitHub Discussions
url: https://github.com/kunkunsh/kunkun/discussions url: https://github.com/kunkunsh/kunkun/discussions
about: Discussions and questions here about: Please ask and answer questions here.
- name: 💬 Discord - name: 💬 Discord
url: https://discord.gg/7dzw3TYeTU 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: options:
- label: I am willing to submit a PR to implement this feature - 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: on:
schedule: schedule:
- cron: "22 22 * * *" - cron: "22 22 * * *"
pull_request:
branches:
- develop
workflow_dispatch: workflow_dispatch:
inputs: inputs:
updater: updater:
description: "Enable updater?" description: "Enable updater?"
required: true required: true
type: boolean type: boolean
default: false default: true
platform_windows: platform_windows:
description: "windows" description: "windows"
required: true required: true
type: boolean type: boolean
default: false default: true
platform_linux: platform_linux:
description: "linux" description: "linux"
required: true required: true
@ -93,8 +90,8 @@ jobs:
id: setting id: setting
run: | run: |
matrix="" 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\"" matrix="\"windows-latest\",\"ubuntu-22.04\",\"macos-14\",\"macos-12\""
build_mode="" build_mode=""
build_path="release" build_path="release"
retention_days='1' retention_days='1'
@ -109,7 +106,7 @@ jobs:
matrix="${matrix}\"macos-14\"," matrix="${matrix}\"macos-14\","
fi fi
if [ "${{ inputs.platform_macos_x86_64 }}" == "true" ]; then if [ "${{ inputs.platform_macos_x86_64 }}" == "true" ]; then
matrix="${matrix}\"macos-13\"," matrix="${matrix}\"macos-12\","
fi fi
if [ -z "${matrix}" ]; then if [ -z "${matrix}" ]; then
matrix="\"windows-latest\"," matrix="\"windows-latest\","
@ -140,6 +137,8 @@ jobs:
RETENTION_DAYS: ${{ needs.preprocess.outputs.retention_days }} RETENTION_DAYS: ${{ needs.preprocess.outputs.retention_days }}
FILE_PREFIX: ${{ needs.preprocess.outputs.file_prefix }} FILE_PREFIX: ${{ needs.preprocess.outputs.file_prefix }}
# BUILD_TIME: ${{ needs.preprocess.outputs.build_time }} # 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 NO_STRIP: true
steps: steps:
- name: Checkout repository - name: Checkout repository
@ -173,6 +172,10 @@ jobs:
if: startsWith(matrix.os, 'macos') if: startsWith(matrix.os, 'macos')
run: | run: |
brew install protobuf brew install protobuf
- name: Install and Protobuf (Ubuntu)
if: matrix.os == 'ubuntu-22.04'
run: |
sudo apt install -y protobuf-compiler
- name: Install protoc and openssl for windows - name: Install protoc and openssl for windows
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
run: | run: |
@ -185,7 +188,7 @@ jobs:
if: matrix.os == 'ubuntu-22.04' if: matrix.os == 'ubuntu-22.04'
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libxdo-dev protobuf-compiler sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: Install Rust - name: Install Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
@ -205,15 +208,12 @@ jobs:
run: pnpm prepare run: pnpm prepare
- name: Build Packages - name: Build Packages
env:
NODE_OPTIONS: --max-old-space-size=4096
run: pnpm build run: pnpm build
- name: Build the App - name: Build the app (Windows)
working-directory: apps/desktop working-directory: apps/desktop
env: env:
CI: false CI: false
NODE_OPTIONS: --max-old-space-size=4096
run: pnpm tauri build ${{ env.BUILD_MODE}} ${{ matrix.os == 'windows-latest' && '-b nsis' || '' }} run: pnpm tauri build ${{ env.BUILD_MODE}} ${{ matrix.os == 'windows-latest' && '-b nsis' || '' }}
- name: Rename macos-aarch64 - name: Rename macos-aarch64
@ -221,7 +221,7 @@ jobs:
run: mv target/${{ env.BUILD_PATH }}/bundle/dmg/*.dmg target/${{ env.BUILD_PATH }}/bundle/dmg/${{ env.FILE_PREFIX }}-aarch64.dmg run: mv target/${{ env.BUILD_PATH }}/bundle/dmg/*.dmg target/${{ env.BUILD_PATH }}/bundle/dmg/${{ env.FILE_PREFIX }}-aarch64.dmg
- name: Rename macos-x86_64 - name: Rename macos-x86_64
if: matrix.os == 'macos-13' if: matrix.os == 'macos-12'
run: mv target/${{ env.BUILD_PATH }}/bundle/dmg/*.dmg target/${{ env.BUILD_PATH }}/bundle/dmg/${{ env.FILE_PREFIX }}-amd64.dmg run: mv target/${{ env.BUILD_PATH }}/bundle/dmg/*.dmg target/${{ env.BUILD_PATH }}/bundle/dmg/${{ env.FILE_PREFIX }}-amd64.dmg
- name: Rename windows - name: Rename windows

View File

@ -5,7 +5,6 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- develop
jobs: jobs:
build-test: build-test:
@ -57,9 +56,7 @@ jobs:
run: pnpm build run: pnpm build
- name: JS Test - name: JS Test
if: matrix.os == 'ubuntu-24.04' if: matrix.os == 'ubuntu-24.04'
run: | run: pnpm test
pnpm test
pnpm lint
- name: Cargo Build and Test - name: Cargo Build and Test
if: matrix.os == 'ubuntu-24.04' if: matrix.os == 'ubuntu-24.04'
run: | run: |

View File

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

View File

@ -1,7 +1,5 @@
name: JSR Publish name: JSR Publish
on: on:
push:
branches: [develop, main]
workflow_dispatch: workflow_dispatch:
jobs: jobs:

View File

@ -1,23 +0,0 @@
name: Ninja i18n action
on: pull_request_target
# explicitly configure permissions, in case your GITHUB_TOKEN workflow permissions are set to read-only in repository settings
permissions:
pull-requests: write # Necessary to comment on PRs
issues: read # Necessary to read issue comments
contents: read # Necessary to access the repo content
jobs:
ninja-i18n:
name: Ninja i18n - GitHub Lint Action
runs-on: ubuntu-latest
steps:
- name: Run Ninja i18n
# @main ensures that the latest version of the action is used
uses: opral/ninja-i18n-action@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,39 +0,0 @@
name: NPM Package Publish
on:
push:
branches: [develop, main]
release:
types: [created]
workflow_dispatch:
jobs:
publish-npm:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-node@v4
with:
node-version: "22.x"
registry-url: "https://registry.npmjs.org"
- uses: pnpm/action-setup@v4
- run: pnpm install
- run: pnpm build
working-directory: packages/api
- name: Check if version is already published
working-directory: packages/api
run: |
PACKAGE_VERSION=$(node -p "require('./package.json').version")
npm view @kksh/api@$PACKAGE_VERSION
continue-on-error: true
id: check_version
- name: Publish
working-directory: packages/api
if: steps.check_version.outcome != 'success'
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}

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 url = https://github.com/kunkunsh/tauri-plugin-user-input.git
[submodule "vendors/tauri-plugin-keyring"] [submodule "vendors/tauri-plugin-keyring"]
path = 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/ .svelte-kit/
target/ target/
vendors/**
vendors vendors
.nuxt/ .nuxt/

View File

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

View File

@ -26,15 +26,13 @@ If you are interested in contributing to the project, please read the following
- [cmake](https://cmake.org/) - [cmake](https://cmake.org/)
- MacOS: `brew install cmake` - MacOS: `brew install cmake`
- Linux: `sudo apt install -y 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 ### Setup
```bash ```bash
git clone https://github.com/kunkunsh/kunkun.git --recursive git clone https://github.com/kunkunsh/kunkun.git --recursive
pnpm install pnpm install
pnpm build # build submodules pnpm prepare
``` ```
### Run Desktop App ### Run Desktop App
@ -45,17 +43,3 @@ pnpm --filter @kksh/desktop tauri dev
cd apps/desktop cd apps/desktop
pnpm tauri dev 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`.
Use `en.json` as a reference.

1086
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" mdns-sd = "0.11.1"
tauri-plugin-network = { path = "./vendors/tauri-plugin-network" } tauri-plugin-network = { path = "./vendors/tauri-plugin-network" }
tauri-plugin-keyring = { path = "./vendors/tauri-plugin-keyring" } tauri-plugin-keyring = { path = "./vendors/tauri-plugin-keyring" }
tauri-plugin-shellx = { version = "2.0.16" } tauri-plugin-clipboard = "2.1.8"
tauri-plugin-clipboard = "2.1.11"
mac-security-rs = { path = "./packages/mac-security-rs" } mac-security-rs = { path = "./packages/mac-security-rs" }
log = "0.4.22" log = "0.4.22"
strum = "0.26" strum = "0.26"

195
README.md
View File

@ -1,188 +1,33 @@
![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
> [!WARNING] > Kunkun is a cross-platform extensible app launcher like Raycast or Alfred.
> 🚧 Work in Progress 🚧 > All extensions run in a sandboxed environment by default to ensure security.
> 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.
## Demo Video and Instructions
- https://docs.kunkun.sh/guides/demo/
- Download extension from https://kunkun.sh/download
[![wakatime](https://wakatime.com/badge/user/94be0fbf-cb9d-411d-8526-d0c4a4e82e1a/project/455bfd3f-4faf-4c2a-afe9-556d9ee1a0f7.svg)](https://wakatime.com/badge/user/94be0fbf-cb9d-411d-8526-d0c4a4e82e1a/project/455bfd3f-4faf-4c2a-afe9-556d9ee1a0f7)
![GitHub last commit](https://img.shields.io/github/last-commit/kunkunsh/kunkun) ![GitHub last commit](https://img.shields.io/github/last-commit/kunkunsh/kunkun)
[![YouTube badge][]][YouTube link] [![YouTube badge][]][YouTube link]
[![](https://dcbadge.limes.pink/api/server/7dzw3TYeTU)](https://discord.gg/7dzw3TYeTU) [![Discord Invite](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 badge]: https://img.shields.io/youtube/channel/subscribers/UC1gJeFbvRcQXDC_C8nKetdA?style=social
[YouTube link]: https://www.youtube.com/@huakun [YouTube link]: https://www.youtube.com/@huakun
<table> ## Download
<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>
<table> - From the Website: https://kunkun.sh/download/
<tr> - From GitHub Releases: https://github.com/kunkunsh/kunkun/releases
<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>
</table> ## Platforms
<a href="https://star-history.com/#kunkunsh/kunkun&Date"> - [x] MacOS
<picture> - [x] Linux
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=kunkunsh/kunkun&type=Date&theme=dark" /> - [x] Windows
<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>
## Sample Extensions
##### Kunkun Dance
![](https://i.imgur.com/PRuhafm.gif) ![](https://i.imgur.com/PRuhafm.gif)
##### Extension Store
![](https://i.imgur.com/JypIC1Z.png)
##### Battery Health
![](https://i.imgur.com/SrIIlCa.png)
##### IP Info
![](https://i.imgur.com/6rxNyTt.png)
##### Image Format Conversion
![](https://i.imgur.com/nxaEaYW.png)
##### List of Commands
![](https://i.imgur.com/2Cv7M1i.gif)
##### Extension Details in Store (Permission Inspector)
![](https://i.imgur.com/ZztHnOT.png)
##### Video Info
![](https://i.imgur.com/imtXN2D.png)
##### Video Conversion
![](https://i.imgur.com/qhr7c7b.png)
##### 3D Git Skyline
![](https://i.imgur.com/itYe0pQ.png)
##### Letterboxd Movie Search
![](https://i.imgur.com/EQVXOym.png)
##### Key Displayer
![](https://i.imgur.com/j6nkVRH.png)
##### JWT Inspector
![](https://i.imgur.com/NHvsUvG.png)
##### Image Info
![](https://i.imgur.com/NLFXPOu.png)
##### Hacker News
![](https://i.imgur.com/dMHapVA.png)
##### QRCode Generator
![](https://i.imgur.com/1tEbTjJ.png)
##### File Transfer
![](https://i.imgur.com/LWcZvDV.png)
![](https://i.imgur.com/GAhQVmw.png)
##### Disk Speed Test
![](https://i.imgur.com/8ISVrRe.png)
##### 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,47 +1,5 @@
# kksh # 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
- Updated dependencies
- @kksh/api@0.1.2
## 0.0.32
### Patch Changes
- Updated dependencies
- @kksh/api@0.1.1
## 0.0.31
### Patch Changes
- Updated dependencies
- @kksh/api@0.1.0
## 0.0.30
### Patch Changes
- Updated dependencies
- @kksh/api@0.0.53
## 0.0.29 ## 0.0.29
### Patch Changes ### Patch Changes

View File

@ -0,0 +1,78 @@
import os from "os"
import path from "path"
import { getRootDir } from "@/constants"
import type { BuildResult } from "@/types"
import { buildWithDockerAndValidate } from "@/utils"
import { $ } from "bun"
import { afterAll, expect, test } from "bun:test"
import fs from "fs-extra"
import { verifyCmd } from "../src/commands/verify"
const rootDir = getRootDir()
const createKKDir = path.join(rootDir, "../create-kunkun")
const createKKDistDir = path.join(createKKDir, "dist")
const createKKIndexjsPath = path.join(createKKDistDir, "index.mjs")
const testDir = path.join(os.tmpdir(), "kunkun-cli-test")
console.log("Test Dir: ", testDir)
const templateNames = ["react", "vue", "nuxt", "svelte", "sveltekit", "next", "template"]
fs.rmdirSync(testDir, { recursive: true })
fs.mkdirpSync(testDir)
const testTemplateDirs: string[] = []
for (const templateName of templateNames) {
const folderName = `${templateName}-ext`
await $`node ${createKKIndexjsPath} --outdir ${testDir} --name ${folderName} --template ${templateName}`
const templateDir = path.join(testDir, folderName)
console.log("templateDir", templateDir)
await $`pnpm install`.cwd(templateDir).quiet()
await $`pnpm build`.cwd(templateDir).quiet()
testTemplateDirs.push(templateDir)
}
test("Build And Verify", async () => {
for (const templateDir of testTemplateDirs) {
expect(verifyCmd(templateDir, false)).toBeTrue()
}
})
const testDirDocker = path.join(os.tmpdir(), "kunkun-cli-test-docker")
fs.rmdirSync(testDirDocker, { recursive: true })
fs.mkdirpSync(testDirDocker)
const templateData: Record<string, { dir: string; buildResult: BuildResult }> = {}
await Promise.all(
templateNames.map(async (templateName) => {
const folderName = `${templateName}-ext`
await $`node ${createKKIndexjsPath} --outdir ${testDirDocker} --name ${folderName} --template ${templateName}`
const templateDir = path.join(testDirDocker, folderName)
console.log("templateDir:", templateDir)
const buildResult = await buildWithDockerAndValidate(templateDir)
templateData[templateName] = {
dir: templateDir,
buildResult
}
})
)
console.log(templateData)
test("Template Exist", () => {
Object.entries(templateData).forEach(async ([templateName, { dir }]) => {
console.log("Expect dir exist: ", dir)
expect(fs.existsSync(dir)).toBeTrue()
})
})
test("Build Result Tarball Exist", () => {
Object.entries(templateData).forEach(async ([templateName, { buildResult, dir }]) => {
const expectedTarballPath = path.join(dir, buildResult.tarballFilename)
expect(fs.existsSync(expectedTarballPath)).toBeTrue()
})
})
afterAll(() => {
fs.rmdirSync(testDir, { recursive: true })
fs.rmdirSync(testDirDocker, { recursive: true })
})

View File

@ -1,8 +1,8 @@
#!/usr/bin/env node #!/usr/bin/env node
import fs from "fs" import fs from "fs"
import path from "path" import path from "path"
import { verifyCmd } from "@/commands" import { buildCmd, verifyCmd } from "@/commands"
import { NODE_ENV } from "@/constants" import { getDockerFolder, NODE_ENV } from "@/constants"
import logger from "@/logger" import logger from "@/logger"
import { program } from "commander" import { program } from "commander"
import { version } from "./package.json" import { version } from "./package.json"
@ -39,4 +39,13 @@ program
} }
}) })
program
.command("build [project_path]")
.option("--entrypoint [path]", "Use custom entrypoint.sh (for debug purpose)")
.description("Build extension with docker and validate (You must have docker installed)")
.action((projectPath: string | undefined, opts: { entrypoint?: string }) => {
logger.info("cwd:", cwd)
buildCmd(computeProjectDir(projectPath), opts.entrypoint)
})
program.parse() program.parse()

View File

@ -1,6 +1,9 @@
export { buildWithDocker, buildWithDockerAndValidate } from "@/utils"
export type { BuildResult } from "@/types"
export { export {
verifyCustomUiCommand, verifyCustomUiCommand,
verifyTemplateUiCommand, verifyTemplateUiCommand,
verifySingleProject, verifySingleProject,
verifyCmd verifyCmd
} from "@/commands/verify" } from "@/commands/verify"
export { buildCmd } from "@/commands/build"

View File

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

View File

@ -0,0 +1,20 @@
import fs from "fs"
import path from "path"
import { getRootDir } from "@/constants"
import { buildWithDockerAndValidate } from "@/utils"
export async function buildCmd(projectPath: string, entrypoint?: string) {
const rootDir = getRootDir()
const entrypointPath = entrypoint
? fs.existsSync(entrypoint)
? entrypoint
: path.join(rootDir, entrypoint)
: undefined
const buildResult = await buildWithDockerAndValidate(
projectPath,
entrypointPath && fs.existsSync(entrypointPath) ? entrypointPath : undefined
)
console.log(buildResult)
}
export default buildCmd

View File

@ -1 +1,2 @@
export { default as verifyCmd } from "./verify" export { default as verifyCmd } from "./verify"
export { default as buildCmd } from "./build"

View File

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

185
apps/cli/src/utils.ts Normal file
View File

@ -0,0 +1,185 @@
import { exec, spawn } from "child_process"
import crypto from "crypto"
import path from "path"
import { ExtPackageJson } from "@kksh/api/models"
import fs from "fs-extra"
import * as v from "valibot"
import { getDockerEntrypoint } from "./constants"
import logger from "./logger"
import type { BuildResult } from "./types"
/**
* Package Name can be scoped or not
* Use regex to extract package name
* @param packageName
* @param version
*/
export function computeTarballName(packageName: string, version: string): string {
const scoped = packageName.startsWith("@")
if (scoped) {
const [scope, name] = packageName.split("/")
return `${scope.substring(1)}-${name}-${version}.tgz`
} else {
return `${packageName}-${version}.tgz`
}
}
export function computeFileHash(filePath: string, algorithm: string): Promise<string> {
return new Promise((resolve, reject) => {
const hash = crypto.createHash(algorithm)
const stream = fs.createReadStream(filePath)
stream.on("data", (data) => {
// @ts-ignore
hash.update(data)
})
stream.on("end", () => {
const shasum = hash.digest("hex")
resolve(shasum)
})
stream.on("error", (err) => {
reject(err)
})
})
}
export function computeFileSha1(filePath: string): Promise<string> {
return computeFileHash(filePath, "sha1")
}
export function computeFileSha512(filePath: string): Promise<string> {
return computeFileHash(filePath, "sha512")
}
export function computeHash(buffer: Buffer, algorithm: "sha1" | "sha256" | "sha512") {
const hash = crypto.createHash(algorithm)
// @ts-ignore
hash.update(buffer)
return hash.digest("hex")
}
/**
* Docker is used to build each individual extension for safety
* Packages could potentially modify other extensions if they share environment.
* There is also a possibility of leaking environment variables.
* docker run -v $(pwd)/scripts/docker/entrypoint.sh:/entrypoint.sh \
* -v $(pwd)/extensions/$ext:/workspace \
* -w /workspace --rm \
* --platform=linux/amd64 \
* node:20 /entrypoint.sh
* @param extPath
* @returns shasum of the tarball parsed from stderr output
*/
export function buildWithDocker(
extPath: string,
entrypoint?: string
): Promise<{
stderrShasum: string
stderrTarballFilename: string
pkg: ExtPackageJson
}> {
logger.info(`Building ${extPath}`)
return new Promise((resolve, reject) => {
const pkg = v.parse(ExtPackageJson, fs.readJsonSync(path.join(extPath, "package.json")))
const dockerEntrypoint = entrypoint ? entrypoint : getDockerEntrypoint()
logger.info("Docker Entrypoint", dockerEntrypoint)
const dockerCmd = `
run -v ${dockerEntrypoint}:/entrypoint.sh -v ${extPath}:/workspace -w /workspace --rm huakunshen/kunkun-ext-builder:latest /entrypoint.sh`
logger.info("dockerCmd", dockerCmd)
const args = dockerCmd
.split(" ")
.filter((arg) => arg.length > 0)
.filter((arg) => arg !== "\n")
const subprocess = spawn("docker", args)
let stderrShasum = ""
let stderrTarballFilename = ""
subprocess.stdout.on("data", (data) => {
console.log(`stdout: ${data}`)
})
subprocess.stderr.on("data", (data) => {
const dataStr = data.toString()
console.error(`stderr: ${dataStr}`)
// if (data instanceof String) {
if (dataStr.includes("npm notice shasum")) {
console.log("shasum found")
const shasumMatch = dataStr.match(/npm notice shasum:\s+([a-f0-9]+)/)
if (shasumMatch) {
stderrShasum = shasumMatch[1]
console.log("Parsed shasum:", stderrShasum)
}
}
if (dataStr.includes("npm notice filename:")) {
const tarballFilename = dataStr.match(/npm notice filename:\s+([^\s]+)/)
if (tarballFilename) {
stderrTarballFilename = tarballFilename[1]
console.log("Parsed tarball:", stderrTarballFilename)
}
} else if (dataStr.includes("filename:")) {
const tarballFilename = dataStr.match(/filename:\s+([^\s]+)/)
if (tarballFilename) {
stderrTarballFilename = tarballFilename[1]
console.log("Parsed tarball:", stderrTarballFilename)
}
}
// } else {
// console.error("data is not string");
// }
})
subprocess.on("close", (code) => {
console.log(`child process exited with code ${code}`)
if (stderrShasum.trim().length === 0 || stderrTarballFilename.trim().length === 0) {
return reject("shasum or tarball filename not found")
}
if (code !== 0) {
return reject(`child process exited with code ${code}`)
} else {
return resolve({ stderrShasum, stderrTarballFilename, pkg })
}
})
})
}
/**
* Use this function to build an extension with docker and validate the tarball
* If this passes, the tarball is ready to be inserted into the database
* @param extPath Extension Path
* @returns
*/
export function buildWithDockerAndValidate(
extPath: string,
entrypoint?: string
): Promise<BuildResult> {
return buildWithDocker(extPath, entrypoint)
.then((res) => {
const parsedTarballPath = path.join(extPath, res.stderrTarballFilename)
if (!fs.existsSync(parsedTarballPath)) {
console.error(`Tarball not found: ${parsedTarballPath}`)
process.exit(1)
}
return computeFileSha1(parsedTarballPath).then((computedShasum) => {
if (computedShasum !== res.stderrShasum) {
console.error(
`Shasum mismatch: Computed(${computedShasum}) !== Output from docker(${res.stderrShasum})`
)
process.exit(1)
} else {
console.log("Shasum matches")
}
return {
shasum: computedShasum,
tarballFilename: res.stderrTarballFilename,
tarballPath: parsedTarballPath,
extPath: extPath,
pkg: res.pkg
}
})
})
.catch((err) => {
console.error(err)
process.exit(1)
})
}

View File

@ -1,53 +1,5 @@
# create-kunkun # 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
- Updated dependencies
- @kksh/api@0.1.2
## 0.1.44
### Patch Changes
- Updated dependencies
- @kksh/api@0.1.1
## 0.1.43
### Patch Changes
- Updated dependencies
- @kksh/api@0.1.0
## 0.1.42
### Patch Changes
- Update sveltekit template
## 0.1.40
### Patch Changes
- Updated dependencies
- @kksh/api@0.0.53
## 0.1.38 ## 0.1.38
### Patch Changes ### Patch Changes

View File

@ -15,18 +15,18 @@ const distDir = path.join(getRootDir(), "dist")
const indexjsPath = path.join(distDir, "index.mjs") const indexjsPath = path.join(distDir, "index.mjs")
const templateNames = ["template", "react", "vue", "nuxt", "svelte", "sveltekit"] const templateNames = ["template", "react", "vue", "nuxt", "svelte", "sveltekit"]
if (fs.existsSync(testDir)) { fs.rmdirSync(testDir, { recursive: true })
fs.rmdirSync(testDir, { recursive: true })
}
fs.mkdirpSync(testDir) fs.mkdirpSync(testDir)
for (const templateName of templateNames) { await Promise.all(
const folderName = `${templateName}-ext` templateNames.map(async (templateName) => {
await $`node ${indexjsPath} --outdir ${testDir} --name ${folderName} --template ${templateName}` const folderName = `${templateName}-ext`
const templateDir = path.join(testDir, folderName) await $`node ${indexjsPath} --outdir ${testDir} --name ${folderName} --template ${templateName}`
await $`rm -rf node_modules`.cwd(templateDir) // this doesn't work within bun test const templateDir = path.join(testDir, folderName)
await $`pnpm install`.cwd(templateDir) // this doesn't work within bun test await $`rm -rf node_modules`.cwd(templateDir) // this doesn't work within bun test
const out = await $`pnpm run build`.cwd(templateDir) await $`pnpm install`.cwd(templateDir) // this doesn't work within bun test
} await $`pnpm run build`.cwd(templateDir)
})
)
test("Build Artifact Existence", () => { test("Build Artifact Existence", () => {
templateNames.forEach(async (templateName) => { templateNames.forEach(async (templateName) => {

View File

@ -55,10 +55,7 @@ for (const p of fs.readdirSync(tmpDistTemplatesPath)) {
// Replace local template with remote schema // Replace local template with remote schema
patchManifestJsonSchema(pkgJsonPath) patchManifestJsonSchema(pkgJsonPath)
// remove node_modules // remove node_modules
const nodeModulesPath = path.join(distPath, "templates", p, "node_modules") fs.rmdirSync(path.join(distPath, "templates", p, "node_modules"), { recursive: true })
if (fs.existsSync(nodeModulesPath)) {
fs.rmdirSync(nodeModulesPath, { recursive: true })
}
} }
} }

View File

@ -94,7 +94,7 @@ async function copyTemplate(templateTgz: string, targetFolderName: string): Prom
message: "Select an Extension Template", message: "Select an Extension Template",
choices: [ choices: [
{ {
name: "Template UI (Web Worker)", name: "Preset Template (Web Worker)",
value: "template", value: "template",
description: description:
"Write regular logic in TypeScript in OOP manner to render extension UI based on predefined template." "Write regular logic in TypeScript in OOP manner to render extension UI based on predefined template."

View File

@ -1,7 +1,7 @@
{ {
"name": "create-kunkun", "name": "create-kunkun",
"type": "module", "type": "module",
"version": "0.1.49", "version": "0.1.38",
"bin": { "bin": {
"create-kunkun": "dist/index.mjs" "create-kunkun": "dist/index.mjs"
}, },
@ -15,7 +15,7 @@
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"get-folder-size": "^5.0.0", "get-folder-size": "^5.0.0",
"tar": "^7.4.3", "tar": "^7.4.3",
"vitest": "^2.1.9" "vitest": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5.0.0" "typescript": "^5.0.0"
@ -27,7 +27,7 @@
"commander": "^12.1.0", "commander": "^12.1.0",
"fs-extra": "^11.2.0", "fs-extra": "^11.2.0",
"handlebars": "^4.7.8", "handlebars": "^4.7.8",
"valibot": "^1.0.0" "valibot": "^1.0.0-beta.10"
}, },
"files": [ "files": [
"dist" "dist"

View File

@ -11,11 +11,9 @@ export function getLatestNpmPkgInfo(pkgName: string): Promise<Record<string, any
} }
export function getLatestNpmPkgVersion(pkgName: string): Promise<string> { export function getLatestNpmPkgVersion(pkgName: string): Promise<string> {
return getLatestNpmPkgInfo(pkgName) return getLatestNpmPkgInfo(pkgName).then(
.then((data) => v.parse(v.object({ version: v.string() }), data).version) (data) => v.parse(v.object({ version: v.string() }), data).version
.catch((err) => { )
throw new Error(`Failed to get latest version of ${pkgName}: ${err.message}`)
})
} }
/** /**

View File

@ -1,2 +0,0 @@
src-tauri
src/lib/paraglide/**

View File

@ -1,7 +1,7 @@
import type { AttributifyAttributes } from "@unocss/preset-attributify" import type { AttributifyAttributes } from "@unocss/preset-attributify"
declare module "svelte/elements" { declare module "svelte/elements" {
// eslint-disable-next-line @typescript-eslint/no-empty-object-type // eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unused-vars
interface HTMLAttributes<T> extends AttributifyAttributes {} interface HTMLAttributes<T> extends AttributifyAttributes {}
} }

View File

@ -14,4 +14,4 @@
}, },
"typescript": true, "typescript": true,
"registry": "https://next.shadcn-svelte.com/registry" "registry": "https://next.shadcn-svelte.com/registry"
} }

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,42 +0,0 @@
import js from "@eslint/js"
import prettier from "eslint-config-prettier"
import svelte from "eslint-plugin-svelte"
import globals from "globals"
import ts from "typescript-eslint"
export default ts.config(
js.configs.recommended,
...ts.configs.recommended,
...svelte.configs["flat/recommended"],
prettier,
...svelte.configs["flat/prettier"],
{
languageOptions: {
globals: {
...globals.browser,
...globals.node
}
}
},
{
files: ["**/*.svelte"],
languageOptions: {
parserOptions: {
parser: ts.parser
}
}
},
{
ignores: ["build/", ".svelte-kit/", "dist/"]
},
{
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
// The following 2 rules are disabled because they cause errors that I am unable to solve
"@typescript-eslint/no-unused-expressions": "off",
"svelte/no-inner-declarations": "off"
}
}
)

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

@ -1,91 +0,0 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"app_name": "KunKun",
"secondary_app_name": "KunKun",
"common_edit": "Edit",
"common_clear": "Clear",
"common_check": "Check",
"common_install": "Install",
"home_command_input_placeholder": "Type \"\/\" to search...",
"home_command_input_dropdown_quit": "Quit",
"home_command_input_dropdown_developer_title": "Developer",
"home_command_input_dropdown_close_window": "Close Window",
"home_command_input_dropdown_toggle_devtools": "Toggle Devtools",
"home_command_input_dropdown_reload_window": "Reload Window",
"home_command_input_dropdown_open_preference": "Open Preference",
"home_command_input_dropdown_toggle_dev_extension_hmr": "Toggle Dev Extension HMR",
"command_group_heading_dev_ext": "Dev Extensions",
"command_group_heading_ext": "Extensions",
"command_group_heading_quick_links": "Quick Links",
"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",
"settings_menu_add_dev_ext": "Add Dev Extension",
"settings_menu_about": "About",
"settings_general_launch_at_login": "Launch at Login",
"settings_general_hotkey": "Hotkey",
"settings_general_menu_bar_icon": "Menu Bar Icon",
"settings_general_hide_on_blur": "Hide On Blur",
"settings_general_extension_auto_upgrade": "Extension Auto Upgrade",
"settings_general_dev_extension_hmr": "Dev Extension HMR",
"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",
"settings_about_source_code": "Source Code",
"settings_about_extensions_source_code": "Extensions Source Code",
"settings_about_check_for_updates": "Check for Updates",
"settings_set_dev_ext_title": "Set Dev Extension Path",
"settings_set_dev_ext_description": "This is where your extensions will be installed.",
"settings_set_dev_ext_enter_path": "Enter Path",
"settings_extensions_title": "Your Extensions",
"settings_extensions_table_col_name": "Name",
"settings_extensions_table_col_identifier": "Identifier",
"settings_extensions_table_col_type": "Type",
"settings_extensions_table_col_version": "Version",
"settings_extensions_table_col_uninstall": "Uninstall",
"settings_add_dev_ext_title": "Add Dev Extension",
"settings_add_dev_ext_description": "There are 4 options to install an extension in developer mode. Either load it from your local tarball file, local folder, a tarball remote URL, or npm package name.",
"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",
"troubleshooters_sidebar_title": "Troubleshooters",
"troubleshooters_sidebar_extension_loading_title": "Extension Loading",
"troubleshooters_sidebar_extension_window_title": "Extension Window",
"troubleshooters_sidebar_mdns_debugger_title": "MDNS Debugger",
"troubleshooters_extension_window_title": "Extension Window Troubleshooter",
"troubleshooters_extension_window_refresh_every_second": "Refresh Every Second",
"troubleshooters_extension_window_refresh": "Refresh",
"troubleshooters_extension_window_refreshed": "Refreshed {count} times",
"troubleshooters_extension_loading_title": "Extension Loading Troubleshooter",
"troubleshooters_extension_loading_table_col_identifier": "Identifier",
"troubleshooters_extension_loading_table_col_path": "Path",
"troubleshooters_extension_loading_table_col_error": "Error"
}

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

@ -1,85 +0,0 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"app_name": "KunKun",
"secondary_app_name": "KunKun",
"common_edit": "Редактировать",
"common_clear": "Очистить",
"common_check": "Проверить",
"common_install": "Установить",
"home_command_input_placeholder": "Нажмите \"/\" для поиска...",
"home_command_input_dropdown_quit": "Выйти",
"home_command_input_dropdown_developer_title": "Разработчик",
"home_command_input_dropdown_close_window": "Закрыть окно",
"home_command_input_dropdown_toggle_devtools": "Вкл/выкл инструменты разработчика",
"home_command_input_dropdown_reload_window": "Перезагрузить окно",
"home_command_input_dropdown_open_preference": "Открыть настройки",
"home_command_input_dropdown_toggle_dev_extension_hmr": "Вкл/выкл горячую замену (HMR) у dev-расширений",
"command_group_heading_dev_ext": "Dev-расширения",
"command_group_heading_ext": "Расширения",
"command_group_heading_quick_links": "Быстрые ссылки",
"settings_menu_settings": "Настройки",
"settings_menu_general": "Общие",
"settings_menu_developer": "Разработчик",
"settings_menu_extensions": "Расширения",
"settings_menu_set_dev_ext": "Dev-расширения",
"settings_menu_add_dev_ext": "Добавить dev-расширение",
"settings_menu_about": "О программе",
"settings_general_launch_at_login": "Автозагрузка при входе в систему",
"settings_general_hotkey": "Сочетание клавиш",
"settings_general_menu_bar_icon": "Иконка в строке меню",
"settings_general_hide_on_blur": "Скрыть окно при потере фокуса",
"settings_general_extension_auto_upgrade": "Автообновление расширений",
"settings_general_dev_extension_hmr": "Горячая замена модуля (HMR) у dev-расширений",
"settings_general_join_beta_updates": "Получать бета-обновления",
"settings_general_developer_mode": "Режим разработчика",
"settings_general_language": "Язык",
"settings_general_loading_animation": "Анимация загрузки",
"settings_about_version": "Версия",
"settings_about_author": "Автор",
"settings_about_source_code": "Исходный код",
"settings_about_extensions_source_code": "Исходный код расширения",
"settings_about_check_for_updates": "Проверить на наличие обновлений",
"settings_set_dev_ext_title": "Настроить путь для dev-расширений",
"settings_set_dev_ext_description": "Это место, где будут установлены ваши расширения.",
"settings_set_dev_ext_enter_path": "Введите путь",
"settings_extensions_title": "Ваши расширения",
"settings_extensions_table_col_name": "Название",
"settings_extensions_table_col_identifier": "Идентификатор",
"settings_extensions_table_col_type": "Тип",
"settings_extensions_table_col_version": "Версия",
"settings_extensions_table_col_uninstall": "Удалить",
"settings_add_dev_ext_title": "Добавить dev-расширение",
"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_install_tarball_from_url": "Установить tar-архив по URL-адресу",
"troubleshooters_sidebar_title": "Диагностика",
"troubleshooters_sidebar_extension_loading_title": "Загрузка расширения",
"troubleshooters_sidebar_extension_window_title": "Окно расширения",
"troubleshooters_sidebar_mdns_debugger_title": "mDNS-отладка",
"troubleshooters_extension_window_title": "Диагностика окна расишрения",
"troubleshooters_extension_window_refresh_every_second": "Обновлять каждую секунду",
"troubleshooters_extension_window_refresh": "Обновить",
"troubleshooters_extension_window_refreshed": "Обновлено {count} раз",
"troubleshooters_extension_loading_title": "Диагностика загрузки расширений",
"troubleshooters_extension_loading_table_col_identifier": "Идентификатор",
"troubleshooters_extension_loading_table_col_path": "Путь",
"troubleshooters_extension_loading_table_col_error": "Ошибка"
}

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

@ -1,91 +0,0 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"app_name": "KunKun",
"secondary_app_name": "鲲鲲",
"common_edit": "编辑",
"common_clear": "清除",
"common_check": "检查",
"common_install": "安装",
"home_command_input_placeholder": "按 \"/\" 开始搜索...",
"home_command_input_dropdown_quit": "退出",
"home_command_input_dropdown_developer_title": "开发者",
"home_command_input_dropdown_close_window": "关闭窗口",
"home_command_input_dropdown_toggle_devtools": "切换开发者工具",
"home_command_input_dropdown_reload_window": "重新加载窗口",
"home_command_input_dropdown_open_preference": "打开设置",
"home_command_input_dropdown_toggle_dev_extension_hmr": "切换开发插件 HMR",
"command_group_heading_dev_ext": "开发插件",
"command_group_heading_ext": "插件",
"command_group_heading_quick_links": "快捷链接",
"settings_menu_settings": "设置",
"settings_menu_general": "通用",
"settings_menu_app_search_paths": "应用搜索路径",
"settings_menu_developer": "开发者",
"settings_menu_extensions": "插件",
"settings_menu_set_dev_ext": "设置开发插件",
"settings_menu_add_dev_ext": "添加开发插件",
"settings_menu_about": "关于",
"settings_general_launch_at_login": "开机启动",
"settings_general_hotkey": "快捷键",
"settings_general_menu_bar_icon": "菜单栏图标",
"settings_general_hide_on_blur": "隐藏在后台",
"settings_general_extension_auto_upgrade": "自动更新插件",
"settings_general_dev_extension_hmr": "开发模式",
"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": "作者",
"settings_about_source_code": "源代码",
"settings_about_extensions_source_code": "插件源代码",
"settings_about_check_for_updates": "检查更新",
"settings_set_dev_ext_title": "设置开发插件安装路径",
"settings_set_dev_ext_description": "这是你的开发插件将被安装的地方。",
"settings_set_dev_ext_enter_path": "输入路径",
"settings_extensions_title": "你的插件",
"settings_extensions_table_col_name": "名称",
"settings_extensions_table_col_identifier": "标识符",
"settings_extensions_table_col_type": "类型",
"settings_extensions_table_col_version": "版本",
"settings_extensions_table_col_uninstall": "卸载",
"settings_add_dev_ext_title": "添加开发插件",
"settings_add_dev_ext_description": "有四种方法可以在开发模式下安装插件。 从本地 tarball 文件、tarball 远程 URL、npm 包名称或远程 URL 加载。",
"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 安装",
"troubleshooters_sidebar_title": "故障排除器",
"troubleshooters_sidebar_extension_loading_title": "插件加载",
"troubleshooters_sidebar_extension_window_title": "插件窗口",
"troubleshooters_sidebar_mdns_debugger_title": "MDNS 调试器",
"troubleshooters_extension_window_title": "插件窗口故障排除器",
"troubleshooters_extension_window_refresh_every_second": "每秒刷新",
"troubleshooters_extension_window_refresh": "刷新",
"troubleshooters_extension_window_refreshed": "已刷新 {count} 次",
"troubleshooters_extension_loading_title": "插件加载故障排除器",
"troubleshooters_extension_loading_table_col_identifier": "标识符",
"troubleshooters_extension_loading_table_col_path": "路径",
"troubleshooters_extension_loading_table_col_error": "错误"
}

View File

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

View File

@ -1,9 +1,9 @@
export default { export default {
plugins: { plugins: {
tailwindcss: { tailwindcss: {
config: "tailwind.config.ts" config: "tailwind.config.ts"
// config: "../../packages/ui/tailwind.config.ts" // config: "../../packages/ui/tailwind.config.ts"
}, },
autoprefixer: {} autoprefixer: {}
} }
} };

View File

@ -1 +0,0 @@
cache

View File

@ -1 +0,0 @@
9800f324a732adb2ed22a4b600a0025820caaba8c4d886b0949b04616bba4726

View File

@ -1,15 +0,0 @@
{
"$schema": "https://inlang.com/schema/project-settings",
"sourceLanguageTag": "en",
"languageTags": ["en", "zh", "ru", "pt", "vi", "de"],
"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",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js"
],
"plugin.inlang.messageFormat": {
"pathPattern": "./messages/{languageTag}.json"
}
}

View File

@ -1,15 +1,13 @@
import assert from "assert" import assert from "assert"
import fs from "fs"
import { compressString, decompressString } from "@kksh/utils" import { compressString, decompressString } from "@kksh/utils"
import { $ } from "bun"
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* Download Dance JSON */ /* Download Dance JSON */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
console.log("Downloading Dance Data...") console.log("Downloading Dance Data...")
const rawData = await fetch("https://dance.kunkun.sh/api/data").then((res) => res.text()) const rawData = await fetch("https://dance.kunkun.sh/api/data").then((res) => res.text())
if (!fs.existsSync("./src/data")) {
fs.mkdirSync("./src/data", { recursive: true })
}
function formatFileSize(size: number) { function formatFileSize(size: number) {
return `${(size / 1024).toFixed(2)} KB` return `${(size / 1024).toFixed(2)} KB`
} }

View File

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

View File

@ -24,7 +24,6 @@
"core:event:default", "core:event:default",
"core:window:default", "core:window:default",
"core:window:allow-set-size", "core:window:allow-set-size",
"core:window:allow-set-enabled",
"core:window:allow-start-dragging", "core:window:allow-start-dragging",
"core:window:allow-set-focus", "core:window:allow-set-focus",
"core:window:allow-toggle-maximize", "core:window:allow-toggle-maximize",
@ -40,7 +39,6 @@
"core:webview:allow-create-webview", "core:webview:allow-create-webview",
"core:webview:allow-create-webview-window", "core:webview:allow-create-webview-window",
"core:app:default", "core:app:default",
"core:app:allow-app-hide",
"core:resources:default", "core:resources:default",
"core:menu:default", "core:menu:default",
"core:tray:default", "core:tray:default",
@ -61,7 +59,6 @@
"shellx:allow-execute", "shellx:allow-execute",
"shellx:allow-open", "shellx:allow-open",
"shellx:allow-kill", "shellx:allow-kill",
"shellx:allow-kill-pid",
"shellx:allow-spawn", "shellx:allow-spawn",
"shellx:allow-stdin-write", "shellx:allow-stdin-write",
"shellx:allow-fix-path-env", "shellx:allow-fix-path-env",
@ -114,7 +111,28 @@
"identifier": "fs:scope", "identifier": "fs:scope",
"allow": [ "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", "deep-link:default"
"autostart:allow-enable",
"autostart:allow-disable",
"autostart:allow-is-enabled"
] ]
} }

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")] #[cfg(target_os = "macos")]
use tauri::ActivationPolicy; use tauri::ActivationPolicy;
use tauri::Manager; use tauri::Manager;
use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_deep_link::DeepLinkExt;
use tauri_plugin_jarvis::{ use tauri_plugin_jarvis::{
constants::KUNKUN_PUBLISH,
db::JarvisDB, db::JarvisDB,
server::Protocol, server::Protocol,
utils::{ utils::{
@ -27,7 +25,7 @@ use utils::server::tauri_file_server;
pub fn run() { pub fn run() {
let context = tauri::generate_context!(); let context = tauri::generate_context!();
let mut builder = tauri::Builder::default(); let mut builder = tauri::Builder::default();
// let app_data_path = tauri::path::PathResolver::app_data_dir().unwrap();
// let db_key = if cfg!(debug_assertions) { // let db_key = if cfg!(debug_assertions) {
// None // None
// } else { // } else {
@ -65,29 +63,14 @@ pub fn run() {
// .build(), // .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; let shell_unlocked = true;
builder = builder builder = builder
.plugin(tauri_plugin_single_instance::init(|app, args, _cwd| { .plugin(tauri_plugin_single_instance::init(|app, args, cwd| {
let window = app.get_webview_window("main").expect("no main window"); let _ = app
.get_webview_window("main")
// if toggle is passed, we want to show/hide the main window .expect("no main window")
if args.get(1).map_or(false, |arg| arg == "toggle") { .set_focus();
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( .plugin(
tauri_plugin_log::Builder::new() tauri_plugin_log::Builder::new()
@ -108,22 +91,13 @@ pub fn run() {
.build(), .build(),
) )
.plugin(tauri_plugin_cli::init()) .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_user_input::init())
.plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_svelte::init())
.plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_autostart::init(
MacosLauncher::LaunchAgent,
Some(vec![]),
))
.plugin(tauri_plugin_upload::init()) .plugin(tauri_plugin_upload::init())
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_store::Builder::default().build()) .plugin(tauri_plugin_store::Builder::default().build())
.plugin(tauri_plugin_global_shortcut::Builder::new().build()) .plugin(tauri_plugin_global_shortcut::Builder::new().build())
.plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_dialog::init())
@ -136,9 +110,7 @@ pub fn run() {
.plugin(tauri_plugin_keyring::init()) .plugin(tauri_plugin_keyring::init())
.plugin(tauri_plugin_network::init()) .plugin(tauri_plugin_network::init())
.plugin(tauri_plugin_system_info::init()) .plugin(tauri_plugin_system_info::init())
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![commands::keyring::get_stronghold_key]);
commands::keyring::get_stronghold_key,
]);
let app = builder let app = builder
.register_uri_scheme_protocol("appicon", |_app, request| { .register_uri_scheme_protocol("appicon", |_app, request| {
@ -149,7 +121,7 @@ pub fn run() {
}) })
.register_uri_scheme_protocol("ext", |app, request| { .register_uri_scheme_protocol("ext", |app, request| {
let app_handle = app.app_handle(); let app_handle = app.app_handle();
// app_handle.
let win_label = app.webview_label(); let win_label = app.webview_label();
let jarvis_state = app_handle.state::<tauri_plugin_jarvis::JarvisState>(); let jarvis_state = app_handle.state::<tauri_plugin_jarvis::JarvisState>();
let window_ext_map = jarvis_state.window_label_ext_map.lock().unwrap(); let window_ext_map = jarvis_state.window_label_ext_map.lock().unwrap();
@ -236,8 +208,7 @@ pub fn run() {
} }
// setup::deeplink::setup_deeplink(app); // setup::deeplink::setup_deeplink(app);
// #[cfg(all(target_os = "macos", debug_assertions))] // #[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 mut store = StoreBuilder::new("appConfig.bin").build(app.handle().clone());
// let store = app.handle().store_builder("appConfig.json").build()?; // let store = app.handle().store_builder("appConfig.json").build()?;
@ -336,29 +307,7 @@ pub fn run() {
app.run(|_app_handle, event| match event { app.run(|_app_handle, event| match event {
// tauri::RunEvent::Exit => todo!(), // tauri::RunEvent::Exit => todo!(),
// tauri::RunEvent::ExitRequested { code, api, .. } => todo!(), // tauri::RunEvent::ExitRequested { code, api, .. } => todo!(),
tauri::RunEvent::WindowEvent { label, event, .. } => { // tauri::RunEvent::WindowEvent { label, event, .. } => todo!(),
if label == "main" {
match event {
// tauri::WindowEvent::Resized(physical_size) => todo!(),
// tauri::WindowEvent::Moved(physical_position) => todo!(),
tauri::WindowEvent::CloseRequested { api, .. } => {
api.prevent_close();
log::info!("main window close requested, hiding");
let window = _app_handle.get_webview_window("main").unwrap();
window.hide().unwrap();
}
// tauri::WindowEvent::Destroyed => todo!(),
// tauri::WindowEvent::Focused(_) => todo!(),
// tauri::WindowEvent::ScaleFactorChanged {
// scale_factor,
// new_inner_size,
// } => todo!(),
// tauri::WindowEvent::DragDrop(drag_drop_event) => todo!(),
// tauri::WindowEvent::ThemeChanged(theme) => todo!(),
_ => {}
}
}
}
// tauri::RunEvent::WebviewEvent { label, event, .. } => todo!(), // tauri::RunEvent::WebviewEvent { label, event, .. } => todo!(),
// tauri::RunEvent::Ready => todo!(), // tauri::RunEvent::Ready => todo!(),
// tauri::RunEvent::Resumed => todo!(), // tauri::RunEvent::Resumed => todo!(),

View File

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

View File

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

View File

@ -1,13 +1,13 @@
<!doctype html> <!doctype html>
<html lang="%paraglide.lang%" dir="%paraglide.textDirection%"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Kunkun Desktop App</title> <title>Kunkun Desktop App</title>
%sveltekit.head% %sveltekit.head%
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div> <div style="display: contents">%sveltekit.body%</div>
</body> </body>
</html> </html>

View File

@ -1,6 +0,0 @@
// file initialized by the Paraglide-SvelteKit CLI - Feel free to edit it
import { sequence } from "@sveltejs/kit/hooks"
import { i18n } from "$lib/i18n"
// add your own hooks as part of the sequence here
export const handle = sequence(i18n.handle())

View File

@ -1,4 +0,0 @@
// file initialized by the Paraglide-SvelteKit CLI - Feel free to edit it
import { i18n } from "$lib/i18n"
export const reroute = i18n.reroute()

View File

@ -1,17 +1,14 @@
import { i18n } from "@/i18n"
import { appConfig, appState, auth, extensions } from "@/stores" import { appConfig, appState, auth, extensions } from "@/stores"
import { checkUpdateAndInstall } from "@/utils/updater" import { checkUpdateAndInstall } from "@/utils/updater"
import { setTransparentTitlebar } from "@kksh/api/commands" import { setTransparentTitlebar } from "@kksh/api/commands"
import { IconEnum } from "@kksh/api/models" import { IconEnum } from "@kksh/api/models"
import type { BuiltinCmd } from "@kksh/ui/types" import type { BuiltinCmd } from "@kksh/ui/types"
import { commandScore } from "@kksh/ui/utils"
import { getVersion } from "@tauri-apps/api/app" import { getVersion } from "@tauri-apps/api/app"
import { appDataDir } from "@tauri-apps/api/path" import { appDataDir } from "@tauri-apps/api/path"
import { WebviewWindow } from "@tauri-apps/api/webviewWindow" import { WebviewWindow } from "@tauri-apps/api/webviewWindow"
import { exit } from "@tauri-apps/plugin-process" import { exit } from "@tauri-apps/plugin-process"
import { dev } from "$app/environment" import { dev } from "$app/environment"
import { goto } from "$app/navigation" import { goto } from "$app/navigation"
import Fuse from "fuse.js"
import { toast } from "svelte-sonner" import { toast } from "svelte-sonner"
import { derived } from "svelte/store" import { derived } from "svelte/store"
import * as clipboard from "tauri-plugin-clipboard-api" import * as clipboard from "tauri-plugin-clipboard-api"
@ -28,7 +25,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "Go to Extension Store", description: "Go to Extension Store",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/extension/store")) goto("/app/extension/store")
} }
}, },
{ {
@ -39,7 +36,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
}, },
description: "", description: "",
function: async () => { function: async () => {
goto(i18n.resolveRoute("/app/auth")) goto("/app/auth")
} }
}, },
{ {
@ -101,7 +98,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
}, },
description: "", description: "",
function: async () => { function: async () => {
goto(i18n.resolveRoute("/app/extension/file-transfer")) goto("/app/extension/file-transfer")
appState.clearSearchTerm() appState.clearSearchTerm()
} }
}, },
@ -115,7 +112,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "", description: "",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/settings/add-dev-extension")) goto("/app/settings/add-dev-extension")
} }
}, },
{ {
@ -140,7 +137,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
function: async () => { function: async () => {
// const appStateStore = useAppStateStore() // const appStateStore = useAppStateStore()
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/settings/set-dev-ext-path")) goto("/app/settings/set-dev-ext-path")
} }
}, },
{ {
@ -152,6 +149,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "", description: "",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
// goto("/app/window-troubleshooter")
const winLabel = `main:extension-window-troubleshooter-${uuidv4()}` const winLabel = `main:extension-window-troubleshooter-${uuidv4()}`
console.log(winLabel) console.log(winLabel)
new WebviewWindow(winLabel, { new WebviewWindow(winLabel, {
@ -170,7 +168,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "", description: "",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/help/deno-install")) goto("/app/help/deno-install")
}, },
keywords: ["help", "deno", "install"] keywords: ["help", "deno", "install"]
}, },
@ -183,7 +181,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "", description: "",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/help/ffmpeg-install")) goto("/app/help/ffmpeg-install")
}, },
keywords: ["help", "ffmpeg", "install"] keywords: ["help", "ffmpeg", "install"]
}, },
@ -196,7 +194,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "", description: "",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/help/brew-install")) goto("/app/help/brew-install")
}, },
keywords: ["help", "brew", "install", "homebrew"] keywords: ["help", "brew", "install", "homebrew"]
}, },
@ -209,7 +207,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "", description: "",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/help/onboarding")) goto("/app/help/onboarding")
}, },
flags: { flags: {
dev: true, dev: true,
@ -225,7 +223,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "", description: "",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/extension/permission-inspector")) goto("/app/extension/permission-inspector")
}, },
keywords: ["extension"] keywords: ["extension"]
}, },
@ -238,27 +236,10 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "", description: "",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/troubleshooters/extension-loading")) goto("/app/troubleshooters/extension-loading")
}, },
keywords: ["extension", "troubleshooter"] 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", name: "Create Quicklink",
icon: { icon: {
@ -268,7 +249,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "Create a Quicklink", description: "Create a Quicklink",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/extension/create-quick-link")) goto("/app/extension/create-quick-link")
} }
}, },
{ {
@ -280,6 +261,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "Display the current key", description: "Display the current key",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
// goto("/app/extension/key-displayer")
const label = `main:extension:key-displayer-${uuidv4()}` const label = `main:extension:key-displayer-${uuidv4()}`
new WebviewWindow(label, { new WebviewWindow(label, {
url: "/app/extension/key-displayer", url: "/app/extension/key-displayer",
@ -302,7 +284,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
}, },
description: "Open Settings", description: "Open Settings",
function: async () => { function: async () => {
goto(i18n.resolveRoute("/app/settings")) goto("/app/settings")
appState.clearSearchTerm() appState.clearSearchTerm()
} }
}, },
@ -362,7 +344,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
}, },
description: "Dance", description: "Dance",
function: async () => { function: async () => {
goto(i18n.resolveRoute("/app/dance")) goto("/app/dance")
} }
}, },
{ {
@ -403,7 +385,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
description: "Clipboard History", description: "Clipboard History",
function: async () => { function: async () => {
appState.clearSearchTerm() appState.clearSearchTerm()
goto(i18n.resolveRoute("/app/extension/clipboard")) goto("/app/extension/clipboard")
} }
}, },
{ {
@ -428,7 +410,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
visible: false visible: false
}) })
setTimeout(() => { setTimeout(() => {
window.show().then(() => window.setFocus()) window.show()
}, 2_000) }, 2_000)
} }
}, },
@ -440,7 +422,7 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
}, },
description: "MDNS Debugger", description: "MDNS Debugger",
function: async () => { function: async () => {
goto(i18n.resolveRoute("/app/troubleshooters/mdns-debugger")) goto("/app/troubleshooters/mdns-debugger")
}, },
flags: { flags: {
developer: true developer: true
@ -493,19 +475,10 @@ export const rawBuiltinCmds: BuiltinCmd[] = [
} }
].map((cmd) => ({ ...cmd, id: uuidv4() })) ].map((cmd) => ({ ...cmd, id: uuidv4() }))
export const fuse = new Fuse<BuiltinCmd>(rawBuiltinCmds, { export const builtinCmds = derived(appConfig, ($appConfig) => {
includeScore: true, return rawBuiltinCmds.filter((cmd) => {
threshold: 0.2, const passDeveloper = cmd.flags?.developer ? $appConfig.developerMode : true
keys: ["name"] const passDev = cmd.flags?.dev ? dev : true
}) return passDeveloper && passDev
})
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
}) })

View File

@ -1,41 +1,18 @@
import { i18n } from "@/i18n"
import { appState } from "@/stores" import { appState } from "@/stores"
import { winExtMap } from "@/stores/winExtMap" 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 { trimSlash } from "@/utils/url"
import { constructExtensionSupportDir } from "@kksh/api" 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 { HeadlessWorkerExtension } from "@kksh/api/headless"
import { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models" import { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models"
import { constructJarvisServerAPIWithPermissions, type IApp } from "@kksh/api/ui" import { constructJarvisServerAPIWithPermissions, type IApp } from "@kksh/api/ui"
import { db } from "@kksh/drizzle"
import { launchNewExtWindow, loadExtensionManifestFromDisk } from "@kksh/extension" import { launchNewExtWindow, loadExtensionManifestFromDisk } from "@kksh/extension"
import type { IKunkunFullServerAPI } from "@kunkunapi/src/api/server"
import { convertFileSrc } from "@tauri-apps/api/core" import { convertFileSrc } from "@tauri-apps/api/core"
import * as path from "@tauri-apps/api/path" import * as path from "@tauri-apps/api/path"
import { getCurrentWindow } from "@tauri-apps/api/window"
import * as fs from "@tauri-apps/plugin-fs" import * as fs from "@tauri-apps/plugin-fs"
import { info } from "@tauri-apps/plugin-log"
import { platform } from "@tauri-apps/plugin-os" import { platform } from "@tauri-apps/plugin-os"
import { goto } from "$app/navigation" import { goto } from "$app/navigation"
import { RPCChannel, WorkerParentIO } from "kkrpc/browser" import { RPCChannel, WorkerParentIO } from "kkrpc/browser"
import * 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()
})
export type KunkunTemplateExtParams = v.InferOutput<typeof KunkunTemplateExtParams>
export async function createExtSupportDir(extPath: string) { export async function createExtSupportDir(extPath: string) {
const extSupportDir = await constructExtensionSupportDir(extPath) const extSupportDir = await constructExtensionSupportDir(extPath)
@ -44,29 +21,16 @@ export async function createExtSupportDir(extPath: string) {
} }
} }
function setTemplateExtParams(extPath: string, cmdName: string, url?: string) {
localStorage.setItem(
"kunkun-template-ext-params",
JSON.stringify({ extPath, cmdName, url } satisfies KunkunTemplateExtParams)
)
}
export async function onTemplateUiCmdSelect( export async function onTemplateUiCmdSelect(
ext: ExtPackageJsonExtra, ext: ExtPackageJsonExtra,
cmd: TemplateUiCmd, cmd: TemplateUiCmd,
{ isDev, hmr }: { isDev: boolean; hmr: boolean } { isDev, hmr }: { isDev: boolean; hmr: boolean }
) { ) {
await createExtSupportDir(ext.extPath) await createExtSupportDir(ext.extPath)
// console.log("onTemplateUiCmdSelect", ext, cmd, isDev, hmr)
const url = `/app/extension/ui-worker?extPath=${encodeURIComponent(ext.extPath)}&cmdName=${encodeURIComponent(cmd.name)}` const url = `/app/extension/ui-worker?extPath=${encodeURIComponent(ext.extPath)}&cmdName=${encodeURIComponent(cmd.name)}`
setTemplateExtParams(ext.extPath, cmd.name, url)
if (cmd.window) { if (cmd.window) {
const winLabel = await winExtMap.registerExtensionWithWindow({ extPath: ext.extPath }) 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)
const window = launchNewExtWindow(winLabel, url, cmd.window) const window = launchNewExtWindow(winLabel, url, cmd.window)
window.onCloseRequested(async (event) => { window.onCloseRequested(async (event) => {
await winExtMap.unregisterExtensionFromWindow(winLabel) await winExtMap.unregisterExtensionFromWindow(winLabel)
@ -74,7 +38,7 @@ export async function onTemplateUiCmdSelect(
} else { } else {
return winExtMap return winExtMap
.registerExtensionWithWindow({ windowLabel: "main", extPath: ext.extPath }) .registerExtensionWithWindow({ windowLabel: "main", extPath: ext.extPath })
.then(() => goto(i18n.resolveRoute(url))) .then(() => goto(url))
} }
} }
@ -88,7 +52,6 @@ export async function onHeadlessCmdSelect(
const loadedExt = await loadExtensionManifestFromDisk( const loadedExt = await loadExtensionManifestFromDisk(
await path.join(ext.extPath, "package.json") await path.join(ext.extPath, "package.json")
) )
const scriptPath = await path.join(loadedExt.extPath, cmd.main) const scriptPath = await path.join(loadedExt.extPath, cmd.main)
const workerScript = await fs.readTextFile(scriptPath) const workerScript = await fs.readTextFile(scriptPath)
const blob = new Blob([workerScript], { type: "application/javascript" }) const blob = new Blob([workerScript], { type: "application/javascript" })
@ -98,57 +61,25 @@ export async function onHeadlessCmdSelect(
if (!extInfoInDB) { if (!extInfoInDB) {
return return
} }
const serverAPI: IKunkunFullServerAPI = constructJarvisServerAPIWithPermissions( const serverAPI: Record<string, any> = constructJarvisServerAPIWithPermissions(
loadedExt.kunkun.permissions, loadedExt.kunkun.permissions,
loadedExt.extPath, 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()
}
}
) )
const serverAPI2 = { serverAPI.iframeUi = undefined
...serverAPI, serverAPI.workerUi = undefined
iframeUi: undefined, serverAPI.db = new db.JarvisExtDB(extInfoInDB.extId)
helper: helperAPI, serverAPI.kv = new db.KV(extInfoInDB.extId)
workerUi: undefined, serverAPI.app = {
db: new db.JarvisExtDB(extInfoInDB.extId), language: () => Promise.resolve("en")
kv: new db.KV(extInfoInDB.extId), } satisfies IApp
app: {
language: () => Promise.resolve("en")
} satisfies IApp
}
const io = new WorkerParentIO(worker) const io = new WorkerParentIO(worker)
const kkrpcSerialization = decideKkrpcSerialization(loadedExt) const rpc = new RPCChannel<typeof serverAPI, HeadlessWorkerExtension>(io, {
info( expose: serverAPI
`Establishing kkrpc connection for ${loadedExt.kunkun.identifier} with serialization: ${kkrpcSerialization}`
)
const rpc = new RPCChannel<typeof serverAPI2, HeadlessCommand>(io, {
expose: serverAPI2,
serialization: {
version: kkrpcSerialization
}
}) })
const workerAPI = rpc.getAPI() const workerAPI = rpc.getAPI()
await workerAPI.load() await workerAPI.load()
} }
function setIframeExtParams(extPath: string, url: string) {
localStorage.setItem(
"kunkun-iframe-ext-params",
JSON.stringify({ url, extPath } satisfies KunkunIframeExtParams)
)
}
export async function onCustomUiCmdSelect( export async function onCustomUiCmdSelect(
ext: ExtPackageJsonExtra, ext: ExtPackageJsonExtra,
cmd: CustomUiCmd, cmd: CustomUiCmd,
@ -166,9 +97,6 @@ export async function onCustomUiCmdSelect(
: decodeURIComponent(convertFileSrc(`${trimSlash(cmd.main)}`, "ext")) : decodeURIComponent(convertFileSrc(`${trimSlash(cmd.main)}`, "ext"))
} }
let url2 = `/app/extension/ui-iframe?url=${encodeURIComponent(url)}&extPath=${encodeURIComponent(ext.extPath)}` let url2 = `/app/extension/ui-iframe?url=${encodeURIComponent(url)}&extPath=${encodeURIComponent(ext.extPath)}`
// url2 = `/dev?url=${encodeURIComponent(url)}&extPath=${encodeURIComponent(ext.extPath)}`
setIframeExtParams(ext.extPath, url)
if (cmd.window) { if (cmd.window) {
const winLabel = await winExtMap.registerExtensionWithWindow({ const winLabel = await winExtMap.registerExtensionWithWindow({
extPath: ext.extPath, extPath: ext.extPath,
@ -178,12 +106,7 @@ export async function onCustomUiCmdSelect(
const addr = await spawnExtensionFileServer(winLabel) const addr = await spawnExtensionFileServer(winLabel)
const newUrl = `http://${addr}` const newUrl = `http://${addr}`
url2 = `/app/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}` url2 = `/app/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
setIframeExtParams(ext.extPath, newUrl)
} }
localStorage.setItem(
"kunkun-iframe-ext-params",
JSON.stringify({ url, extPath: ext.extPath } satisfies KunkunIframeExtParams)
)
const window = launchNewExtWindow(winLabel, url2, cmd.window) const window = launchNewExtWindow(winLabel, url2, cmd.window)
window.onCloseRequested(async (event) => { window.onCloseRequested(async (event) => {
await winExtMap.unregisterExtensionFromWindow(winLabel) await winExtMap.unregisterExtensionFromWindow(winLabel)
@ -195,15 +118,13 @@ export async function onCustomUiCmdSelect(
extPath: ext.extPath, extPath: ext.extPath,
dist: cmd.dist dist: cmd.dist
}) })
const _platform = platform() if (platform() === "windows" && !useDevMain) {
if ((_platform === "windows" || _platform === "linux") && !useDevMain) {
const addr = await spawnExtensionFileServer(winLabel) // addr has format "127.0.0.1:<port>" const addr = await spawnExtensionFileServer(winLabel) // addr has format "127.0.0.1:<port>"
console.log("Extension file server address: ", addr) console.log("Extension file server address: ", addr)
const newUrl = `http://${addr}` const newUrl = `http://${addr}`
url2 = `/app/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}` url2 = `/app/extension/ui-iframe?url=${encodeURIComponent(newUrl)}&extPath=${encodeURIComponent(ext.extPath)}`
setIframeExtParams(ext.extPath, newUrl)
} }
goto(i18n.resolveRoute(url2)) goto(url2)
} }
appState.clearSearchTerm() appState.clearSearchTerm()
} }

View File

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

View File

@ -1,27 +1,4 @@
import { getSystemCommands } from "@kksh/api/commands" import { getSystemCommands } from "@kksh/api/commands"
import type { SysCommand } from "@kksh/api/models" 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 systemCommands: SysCommand[] = 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 systemCommandsFiltered = derived(
// [systemCommands, appState],
// ([$systemCommands, $appState]) => {
// return $systemCommands.filter((cmd) => commandScore(cmd.name, $appState.searchTerm) > 0.5)
// }
// )

View File

@ -4,7 +4,6 @@
import { onDestroy, onMount, type Snippet } from "svelte" import { onDestroy, onMount, type Snippet } from "svelte"
let unlisteners: UnlistenFn[] = [] let unlisteners: UnlistenFn[] = []
type Payload = { paths: string[]; position: { x: number; y: number } }
const { const {
children, children,
onEnter, onEnter,
@ -13,10 +12,10 @@
onOver onOver
}: { }: {
children: Snippet children: Snippet
onEnter?: EventCallback<Payload> onEnter?: (event: any) => void
onDrop?: EventCallback<{ paths: string[] }> onDrop?: EventCallback<{ paths: string[] }>
onCancelled?: EventCallback<Payload> onCancelled?: (event: any) => void
onOver?: EventCallback<void> onOver?: (event: any) => void
} = $props() } = $props()
const appWin = getCurrentWebviewWindow() const appWin = getCurrentWebviewWindow()

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)}> <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} /> <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={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 Auto Install
</Button> --> </Button>
</div> </div>

View File

@ -1,15 +1,15 @@
<script lang="ts"> <script lang="ts">
import { goHome } from "@/utils/route" import { goHome } from "@/utils/route"
import { Button, Sidebar } from "@kksh/svelte5" import { Button, SideBar } from "@kksh/svelte5"
import { Constants } from "@kksh/ui" import { Constants } from "@kksh/ui"
import { ArrowLeftIcon } from "lucide-svelte" import { ArrowLeftIcon } from "lucide-svelte"
const { useSidebar } = Sidebar const { useSidebar } = SideBar
const sidebar = useSidebar() const sidebar = useSidebar()
</script> </script>
<div class="fixed flex h-10 w-full items-center gap-2 pl-1 pt-1" data-tauri-drag-region> <div class="fixed flex h-10 w-full items-center gap-2 pl-1 pt-1" data-tauri-drag-region>
<Sidebar.Trigger class="z-50" /> <SideBar.Trigger class="z-50" />
{#if sidebar.state === "collapsed"} {#if sidebar.state === "collapsed"}
<Button <Button
variant="outline" variant="outline"

View File

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

View File

@ -1,63 +0,0 @@
<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"
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}
<Command.Item
class="flex justify-between"
onSelect={async () => {
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")
}
await getCurrentWindow().hide()
appState.clearSearchTerm()
}}
value={`app:${idx}:${app.app_desktop_path}`}
>
<span class="flex gap-2">
<IconMultiplexer
icon={iconPath
? {
type: IconEnum.RemoteUrl,
value: convertFileSrc(iconPath, "appicon")
}
: {
type: IconEnum.Iconify,
value: "mdi:application"
}}
class="!h-5 !w-5 shrink-0"
/>
<span>{app.name}</span>
<!-- <span>{app.app_path_exe}</span> -->
</span>
</Command.Item>
{/each}
</DraggableCommandGroup>

View File

@ -1,135 +1,43 @@
<script lang="ts"> <script lang="ts">
import HotkeyPick from "@/components/standalone/settings/hotkey-pick.svelte" import HotkeyPick from "@/components/standalone/settings/hotkey-pick.svelte"
import { LanguageMap } from "@/constants"
import { i18n, switchToLanguage } from "@/i18n"
import * as m from "@/paraglide/messages"
import {
availableLanguageTags,
languageTag,
setLanguageTag,
type AvailableLanguageTag
} from "@/paraglide/runtime"
import { appConfig } from "@/stores" import { appConfig } from "@/stores"
import { Select, Switch } from "@kksh/svelte5" import { Button, 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
}))
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")
</script> </script>
<ul class="rounded-lg border"> <ul class="rounded-lg border">
<li> <li>
<span>{m.settings_general_launch_at_login()}</span> <span>Launch at Login</span>
<Switch <Switch bind:checked={$appConfig.launchAtLogin} />
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
})
})
}}
/>
</li> </li>
<li class=""> <li class="">
<span>{m.settings_general_hotkey()}</span> <span>Hotkey</span>
<HotkeyPick /> <HotkeyPick />
</li> </li>
<li> <li>
<span>{m.settings_general_menu_bar_icon()}</span> <span>Menu Bar Icon</span>
<Switch bind:checked={$appConfig.showInTray} /> <Switch bind:checked={$appConfig.showInTray} />
</li> </li>
<li> <li>
<span>{m.settings_general_hide_on_blur()}</span> <span>Hide On Blur</span>
<Switch bind:checked={$appConfig.hideOnBlur} /> <Switch bind:checked={$appConfig.hideOnBlur} />
</li> </li>
<li> <li>
<span>{m.settings_general_extension_auto_upgrade()}</span> <span>Extension Auto Upgrade</span>
<Switch bind:checked={$appConfig.extensionAutoUpgrade} /> <Switch bind:checked={$appConfig.extensionAutoUpgrade} />
</li> </li>
<li> <li>
<span>{m.settings_general_dev_extension_hmr()}</span> <span>Dev Extension HMR</span>
<Switch bind:checked={$appConfig.hmr} /> <Switch bind:checked={$appConfig.hmr} />
</li> </li>
<li> <li>
<span>{m.settings_general_join_beta_updates()}</span> <span>Join Beta Updates</span>
<Switch bind:checked={$appConfig.joinBetaProgram} /> <Switch bind:checked={$appConfig.joinBetaProgram} />
</li> </li>
<li> <li>
<span>{m.settings_general_developer_mode()}</span> <span>Developer Mode</span>
<Switch bind:checked={$appConfig.developerMode} /> <Switch bind:checked={$appConfig.developerMode} />
</li> </li>
<li>
<span>{m.settings_general_language()}</span>
<Select.Root type="single" name="language" bind:value={language}>
<Select.Trigger class="w-fit">
{triggerContent}
</Select.Trigger>
<Select.Content>
<Select.Group>
<Select.GroupHeading>{m.settings_general_language()}</Select.GroupHeading>
{#each languages as lang}
<Select.Item
onclick={() => {
appConfig.setLanguage(lang.value)
switchToLanguage(lang.value as AvailableLanguageTag)
}}
value={lang.value}
label={lang.label}>{lang.label}</Select.Item
>
{/each}
</Select.Group>
</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> </ul>
<style scoped> <style scoped>

View File

@ -1,9 +1,12 @@
<script lang="ts"> <script lang="ts">
import InstallCodeBlock from "@/components/common/install-code-block.svelte" import InstallCodeBlock from "@/components/common/install-code-block.svelte"
import Icon from "@iconify/svelte" import Icon from "@iconify/svelte"
import { IconEnum } from "@kksh/api/models"
import { Button, Tabs } from "@kksh/svelte5"
import { TauriLink } from "@kksh/ui" import { TauriLink } from "@kksh/ui"
import { platform } from "@tauri-apps/plugin-os" import { platform } from "@tauri-apps/plugin-os"
import { onMount } from "svelte" import { onMount } from "svelte"
import { toast } from "svelte-sonner"
import { whereIsCommand } from "tauri-plugin-shellx-api" import { whereIsCommand } from "tauri-plugin-shellx-api"
let brewPath = $state("") let brewPath = $state("")
@ -18,7 +21,12 @@
</script> </script>
<h1 class="font-mono text-2xl font-bold">Install Homebrew</h1> <h1 class="font-mono text-2xl font-bold">Install Homebrew</h1>
<TauriLink href="/app/help/brew-install" class="flex items-center"> <TauriLink
href="/app/help/brew-install"
icon={IconEnum.Iconify}
iconValue="devicon:homebrew"
class="flex items-center"
>
<span class="text-lg">Homebrew Website</span> <span class="text-lg">Homebrew Website</span>
<Icon icon="devicon:homebrew" class="h-6 w-6" /> <Icon icon="devicon:homebrew" class="h-6 w-6" />
</TauriLink> </TauriLink>

View File

@ -51,18 +51,18 @@
runtime environment for executing extension code safely. It is optional but recommended. runtime environment for executing extension code safely. It is optional but recommended.
</p> </p>
<p class="font-mono text-sm">Choose any installation method below.</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>. 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"> <p class="font-mono text-sm text-red-400">
After installation, ensure the `deno` command is accessible from your system's PATH. After installation, ensure the `deno` command is accessible from your system's PATH.
</p> </p>
<!-- {#if _platform === "macos" || _platform === "linux"} {#if _platform === "macos" || _platform === "linux"}
<p class="font-mono text-sm text-red-400"> <p class="font-mono text-sm text-red-400">
Installation with <span class="font-bold text-green-500">curl</span> command likely requires manual 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. configuration. So auto install is disabled. Please copy the command and run it in a terminal.
</p> </p>
{/if} --> {/if}
{#if denoPath} {#if denoPath}
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span></span> <span></span>

View File

@ -1,9 +1,7 @@
<script lang="ts"> <script lang="ts">
import DragNDrop from "@/components/common/DragNDrop.svelte" import DragNDrop from "@/components/common/DragNDrop.svelte"
import DevExtPathForm from "@/components/standalone/settings/DevExtPathForm.svelte" import DevExtPathForm from "@/components/standalone/settings/DevExtPathForm.svelte"
import { i18n } from "@/i18n" import { appConfig, extensions } from "@/stores"
import * as m from "@/paraglide/messages"
import { appConfig, appState, extensions } from "@/stores"
import { goBackOnEscape } from "@/utils/key" import { goBackOnEscape } from "@/utils/key"
import { goBack } from "@/utils/route" import { goBack } from "@/utils/route"
import { IconEnum } from "@kksh/api/models" import { IconEnum } from "@kksh/api/models"
@ -65,12 +63,10 @@
} }
async function pickExtFolders() { async function pickExtFolders() {
appState.setLockHideOnBlur(true)
const selected = await openFileSelector({ const selected = await openFileSelector({
directory: true, directory: true,
multiple: true // allow install multiple extensions at once multiple: true // allow install multiple extensions at once
}) })
appState.setLockHideOnBlur(false)
if (!selected) { if (!selected) {
return toast.warning("No File Selected") return toast.warning("No File Selected")
} }
@ -91,9 +87,8 @@
async function pickExtFiles() { async function pickExtFiles() {
if (!$appConfig.devExtensionPath) { if (!$appConfig.devExtensionPath) {
toast.warning("Please set the dev extension path in the settings") toast.warning("Please set the dev extension path in the settings")
return goto(i18n.resolveRoute("/app/settings/set-dev-ext-path")) return goto("/app/settings/set-dev-ext-path")
} }
appState.setLockHideOnBlur(true)
const selected = await openFileSelector({ const selected = await openFileSelector({
directory: false, directory: false,
multiple: true, // allow install multiple extensions at once multiple: true, // allow install multiple extensions at once
@ -104,7 +99,6 @@
} }
] ]
}) })
appState.setLockHideOnBlur(false)
if (!selected) { if (!selected) {
return toast.warning("No File Selected") return toast.warning("No File Selected")
} }
@ -115,34 +109,20 @@
</script> </script>
<div class="my-3 flex justify-center gap-3"> <div class="my-3 flex justify-center gap-3">
<Button size="sm" onclick={pickExtFolders}> <Button size="sm" onclick={pickExtFolders}>Install from Extension Folders</Button>
{m.settings_add_dev_ext_install_from_ext_folders()} <Button size="sm" onclick={pickExtFiles}>Install from Extension Tarball File</Button>
</Button>
<Button size="sm" onclick={pickExtFiles}>
{m.settings_add_dev_ext_install_from_ext_files()}
</Button>
</div> </div>
<StrikeSeparator class="my-1"> <StrikeSeparator class="my-1">
<h3 class="text-muted-foreground font-mono text-sm"> <h3 class="text-muted-foreground font-mono text-sm">Drag and Drop</h3>
{m.settings_add_dev_ext_drag_and_drop_strike()}
</h3>
</StrikeSeparator> </StrikeSeparator>
<Layouts.Center> <Layouts.Center>
<DragNDrop <DragNDrop
onDrop={(e) => { onDrop={(e) => {
console.log(e)
handleDragNDropInstall(e.payload.paths) handleDragNDropInstall(e.payload.paths)
}} }}
onEnter={(evt) => { onEnter={() => (dragging = true)}
console.log(evt)
dragging = true
}}
onOver={(evt) => {
console.log(evt)
}}
onCancelled={() => (dragging = false)} onCancelled={() => (dragging = false)}
> >
<Card.Root <Card.Root
@ -157,21 +137,15 @@
icon={{ value: "mdi:folder-cog-outline", type: IconEnum.Iconify }} icon={{ value: "mdi:folder-cog-outline", type: IconEnum.Iconify }}
class="h-10 w-10" class="h-10 w-10"
/> />
<small class="select-none font-mono text-xs"> <small class="select-none font-mono text-xs">Drag and Drop</small>
{m.settings_add_dev_ext_drag_and_drop()} <small class="select-none font-mono text-xs">Extension Folder or Tarball</small>
</small>
<small class="select-none font-mono text-xs">
{m.settings_add_dev_ext_drag_and_drop2()}
</small>
</div> </div>
</button> </button>
</Card.Root> </Card.Root>
</DragNDrop> </DragNDrop>
</Layouts.Center> </Layouts.Center>
<StrikeSeparator class="my-1"> <StrikeSeparator class="my-1">
<h3 class="text-muted-foreground font-mono text-sm"> <h3 class="text-muted-foreground font-mono text-sm">Install Tarball From URL</h3>
{m.settings_add_dev_ext_install_tarball_from_url()}
</h3>
</StrikeSeparator> </StrikeSeparator>
<InstallTarballUrlForm /> <InstallTarballUrlForm />
<InstallNpmPackageNameForm /> <InstallNpmPackageNameForm />

View File

@ -1,11 +1,13 @@
<script lang="ts"> <script lang="ts">
import * as m from "@/paraglide/messages"
import { appConfig } from "@/stores" import { appConfig } from "@/stores"
import Icon from "@iconify/svelte" import Icon from "@iconify/svelte"
import { Button, Input } from "@kksh/svelte5" import { Button, Input } from "@kksh/svelte5"
import { open } from "@tauri-apps/plugin-dialog" import { open } from "@tauri-apps/plugin-dialog"
import { exists } from "@tauri-apps/plugin-fs" import { exists } from "@tauri-apps/plugin-fs"
import { toast } from "svelte-sonner" import { toast } from "svelte-sonner"
import { superForm, type Infer, type SuperValidated } from "sveltekit-superforms"
import { zodClient } from "sveltekit-superforms/adapters"
import { z } from "zod"
let devExtPath = $state<string | undefined>(undefined) let devExtPath = $state<string | undefined>(undefined)
@ -29,18 +31,13 @@
</script> </script>
<form class="flex w-full items-center space-x-2"> <form class="flex w-full items-center space-x-2">
<Input <Input disabled type="text" placeholder="Enter Path" bind:value={$appConfig.devExtensionPath} />
disabled
type="text"
placeholder={m.settings_set_dev_ext_enter_path()}
bind:value={$appConfig.devExtensionPath}
/>
<Button size="sm" type="button" onclick={clear}> <Button size="sm" type="button" onclick={clear}>
{m.common_clear()} Clear
<Icon icon="material-symbols:delete-outline" class="ml-1 h-5 w-5" /> <Icon icon="material-symbols:delete-outline" class="ml-1 h-5 w-5" />
</Button> </Button>
<Button size="sm" type="button" onclick={pickDirectory}> <Button size="sm" type="button" onclick={pickDirectory}>
{m.common_edit()} Edit
<Icon icon="flowbite:edit-outline" class="ml-1 h-5 w-5" /> <Icon icon="flowbite:edit-outline" class="ml-1 h-5 w-5" />
</Button> </Button>
</form> </form>

View File

@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { i18n } from "@/i18n"
import { appConfig, extensions } from "@/stores" import { appConfig, extensions } from "@/stores"
import { Input } from "@kksh/svelte5" import { Input } from "@kksh/svelte5"
import { Form } from "@kksh/ui" import { Form } from "@kksh/ui"
@ -18,7 +17,7 @@
toast.warning( toast.warning(
"Please set the dev extension path in the settings to install tarball extension" "Please set the dev extension path in the settings to install tarball extension"
) )
return goto(i18n.resolveRoute("/app/settings/set-dev-ext-path")) return goto("/app/settings/set-dev-ext-path")
} }
await extensions await extensions
.installFromNpmPackageName(data.name, $appConfig.devExtensionPath) .installFromNpmPackageName(data.name, $appConfig.devExtensionPath)

View File

@ -1,6 +1,4 @@
<script lang="ts"> <script lang="ts">
import { i18n } from "@/i18n"
import * as m from "@/paraglide/messages"
import { appConfig, extensions } from "@/stores" import { appConfig, extensions } from "@/stores"
import { Input } from "@kksh/svelte5" import { Input } from "@kksh/svelte5"
import { Form } from "@kksh/ui" import { Form } from "@kksh/ui"
@ -21,7 +19,7 @@
toast.warning( toast.warning(
"Please set the dev extension path in the settings to install tarball extension" "Please set the dev extension path in the settings to install tarball extension"
) )
return goto(i18n.resolveRoute("/app/settings/set-dev-ext-path")) return goto("/app/settings/set-dev-ext-path")
} }
await extensions await extensions
.installFromTarballUrl(data.url, $appConfig.devExtensionPath) .installFromTarballUrl(data.url, $appConfig.devExtensionPath)
@ -55,7 +53,7 @@
{#snippet children({ props })} {#snippet children({ props })}
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Input {...props} bind:value={$formData.url} placeholder="Tarball URL" /> <Input {...props} bind:value={$formData.url} placeholder="Tarball URL" />
<Form.Button class="my-1">{m.common_install()}</Form.Button> <Form.Button class="my-1">Install</Form.Button>
</div> </div>
{/snippet} {/snippet}
</Form.Control> </Form.Control>

View File

@ -1,10 +1,10 @@
import { appIsDev } from "@kksh/api/commands" import { appIsDev } from "@kksh/api/commands"
import { appDataDir, join } from "@tauri-apps/api/path" import { appDataDir, join } from "@tauri-apps/api/path"
import * as fs from "@tauri-apps/plugin-fs" import * as fs from "@tauri-apps/plugin-fs"
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from "$env/static/public" import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_PROJECT_ID } from "$env/static/public"
export const SUPABASE_ANON_KEY = PUBLIC_SUPABASE_ANON_KEY export const SUPABASE_ANON_KEY = PUBLIC_SUPABASE_ANON_KEY
export const SUPABASE_URL = PUBLIC_SUPABASE_URL export const SUPABASE_URL = `https://${PUBLIC_SUPABASE_PROJECT_ID}.supabase.co`
export const SUPABASE_GRAPHQL_ENDPOINT = `${SUPABASE_URL}/graphql/v1` export const SUPABASE_GRAPHQL_ENDPOINT = `${SUPABASE_URL}/graphql/v1`
export function getExtensionsFolder() { export function getExtensionsFolder() {
return appDataDir() return appDataDir()
@ -17,13 +17,4 @@ export function getExtensionsFolder() {
}) })
} }
export const IS_IN_TAURI = export const IS_IN_TAURI =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
typeof window !== "undefined" && (window as any).__TAURI_INTERNALS__ !== undefined typeof window !== "undefined" && (window as any).__TAURI_INTERNALS__ !== undefined
export const LanguageMap = {
en: "English",
zh: "中文",
ru: "Русский",
pt: "Português",
vi: "Tiếng Việt"
}

View File

@ -3,16 +3,16 @@
* It's designed to allow all components to access a shared state. * 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. * 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 { getContext, setContext } from "svelte"
import type { Writable } from "svelte/store" import type { Writable } from "svelte/store"
export const APP_CONFIG_CONTEXT_KEY = Symbol("appConfig") export const APP_CONFIG_CONTEXT_KEY = Symbol("appConfig")
export function getAppConfigContext(): Writable<AppConfigState> { export function getAppConfigContext(): Writable<AppConfig> {
return getContext(APP_CONFIG_CONTEXT_KEY) return getContext(APP_CONFIG_CONTEXT_KEY)
} }
export function setAppConfigContext(appConfig: Writable<AppConfigState>) { export function setAppConfigContext(appConfig: Writable<AppConfig>) {
setContext(APP_CONFIG_CONTEXT_KEY, 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 { getContext, setContext } from "svelte"
import type { Writable } from "svelte/store" import type { Writable } from "svelte/store"

View File

@ -1,17 +0,0 @@
// file initialized by the Paraglide-SvelteKit CLI - Feel free to edit it
import { createI18n } from "@inlang/paraglide-sveltekit"
import { goto } from "$app/navigation"
import { page } from "$app/state"
import * as runtime from "$lib/paraglide/runtime.js"
import { tick } from "svelte"
export const i18n = createI18n(runtime)
export function switchToLanguage(newLanguage: runtime.AvailableLanguageTag) {
runtime.setLanguageTag(newLanguage)
const canonicalPath = i18n.route(page.url.pathname)
const localisedPath = i18n.resolveRoute(canonicalPath, newLanguage)
tick().then(() => {
goto(localisedPath)
})
}

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,25 +1,23 @@
import { getExtensionsFolder } from "@/constants" 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 { updateTheme, type ThemeConfig } from "@kksh/svelte5"
import { LoadingAnimation, PersistedAppConfig, type AppConfigState } from "@kksh/types" import { PersistedAppConfig, type AppConfig } from "@kksh/types"
import { debug, error, info } from "@tauri-apps/plugin-log" import { debug, error } from "@tauri-apps/plugin-log"
import * as os from "@tauri-apps/plugin-os" import * as os from "@tauri-apps/plugin-os"
import { load } from "@tauri-apps/plugin-store" import { load } from "@tauri-apps/plugin-store"
import { Store } from "@tauri-store/svelte"
import { toast } from "svelte-sonner"
import { get, writable } from "svelte/store" import { get, writable } from "svelte/store"
import * as v from "valibot" import * as v from "valibot"
export const defaultAppConfig: AppConfigState = { export const defaultAppConfig: AppConfig = {
isInitialized: false, isInitialized: false,
platform: "macos", platform: "macos",
language: "en",
theme: { theme: {
theme: "zinc", theme: "zinc",
radius: 0.5, radius: 0.5,
lightMode: "auto" lightMode: "auto"
}, },
triggerHotkey: null, triggerHotkey: null,
launchAtLogin: true,
showInTray: true, showInTray: true,
devExtensionPath: null, devExtensionPath: null,
extensionsInstallDir: undefined, extensionsInstallDir: undefined,
@ -28,82 +26,68 @@ export const defaultAppConfig: AppConfigState = {
extensionAutoUpgrade: true, extensionAutoUpgrade: true,
joinBetaProgram: false, joinBetaProgram: false,
onBoarded: false, onBoarded: false,
developerMode: false, developerMode: false
appSearchPaths: [],
loadingAnimation: "kunkun-dancing"
} }
export const appConfigLoaded = writable(false) export const appConfigLoaded = writable(false)
interface AppConfigAPI { interface AppConfigAPI {
init: () => Promise<void> init: () => Promise<void>
get: () => AppConfigState get: () => AppConfig
setTheme: (theme: ThemeConfig) => void setTheme: (theme: ThemeConfig) => void
setDevExtensionPath: (devExtensionPath: string | null) => void setDevExtensionPath: (devExtensionPath: string | null) => void
setTriggerHotkey: (triggerHotkey: string[]) => void setTriggerHotkey: (triggerHotkey: string[]) => void
setOnBoarded: (onBoarded: boolean) => void setOnBoarded: (onBoarded: boolean) => void
setLanguage: (language: string) => void
addAppSearchPath: (appSearchPath: SearchPath) => void
removeAppSearchPath: (appSearchPath: SearchPath) => void
} }
class AppConfigStore extends Store<AppConfigState> implements AppConfigAPI { function createAppConfig(): WithSyncStore<AppConfig> & AppConfigAPI {
constructor() { const store = createTauriSyncStore("app-config", defaultAppConfig)
super("app-config", defaultAppConfig, {
saveOnChange: true async function init() {
})
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() {
debug("Initializing app config") debug("Initializing app config")
const extensionsInstallDir = await getExtensionsFolder() const persistStore = await load("kk-config.json", { autoSave: true })
this.update((config) => ({ const loadedConfig = await persistStore.get("config")
...config, const parseRes = v.safeParse(PersistedAppConfig, loadedConfig)
isInitialized: true, if (parseRes.success) {
platform: os.platform(), console.log("Parse Persisted App Config Success", parseRes.output)
extensionsInstallDir 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) appConfigLoaded.set(true)
store.subscribe(async (config) => {
console.log("Saving app config", config)
await persistStore.set("config", config)
updateTheme(config.theme)
})
} }
get() { return {
return get(this) ...store,
} get: () => get(store),
setTheme(theme: ThemeConfig) { setTheme: (theme: ThemeConfig) => store.update((config) => ({ ...config, theme })),
this.update((config) => ({ ...config, theme })) setDevExtensionPath: (devExtensionPath: string | null) => {
} console.log("setDevExtensionPath", devExtensionPath)
setDevExtensionPath(devExtensionPath: string | null) { store.update((config) => ({ ...config, devExtensionPath }))
info(`setDevExtensionPath ${devExtensionPath}`) },
this.update((config) => ({ ...config, devExtensionPath })) setTriggerHotkey: (triggerHotkey: string[]) => {
} store.update((config) => ({ ...config, triggerHotkey }))
setTriggerHotkey(triggerHotkey: string[]) { },
this.update((config) => ({ ...config, triggerHotkey })) setOnBoarded: (onBoarded: boolean) => {
} store.update((config) => ({ ...config, onBoarded }))
setOnBoarded(onBoarded: boolean) { },
this.update((config) => ({ ...config, onBoarded })) init
}
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 }))
} }
} }
// export const appConfig = createAppConfig() export const appConfig = createAppConfig()
export const appConfig = new AppConfigStore()

View File

@ -1,25 +1,23 @@
import { Action as ActionSchema } from "@kksh/api/models" import { findAllArgsInLink } from "@/cmds/quick-links"
import { Action as ActionSchema, CmdTypeEnum } from "@kksh/api/models"
import type { AppState } from "@kksh/types" import type { AppState } from "@kksh/types"
import { get, writable, type Writable } from "svelte/store" import type { CmdValue } from "@kksh/ui/types"
import { derived, get, writable, type Writable } from "svelte/store"
export const defaultAppState: AppState = { export const defaultAppState: AppState = {
searchTerm: "", searchTerm: "",
highlightedCmd: "", highlightedCmd: "",
loadingBar: false, loadingBar: false,
defaultAction: "", defaultAction: "",
actionPanel: undefined, 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
} }
interface AppStateAPI { interface AppStateAPI {
clearSearchTerm: () => void clearSearchTerm: () => void
get: () => AppState get: () => AppState
setLoadingBar: (loadingBar: boolean) => void setLoadingBar: (loadingBar: boolean) => void
setDefaultAction: (defaultAction: string | null) => void setDefaultAction: (defaultAction: string) => void
setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => void setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => void
setLockHideOnBlur: (lockHideOnBlur: boolean) => void
setFullScreenLoading: (fullScreenLoading: boolean) => void
} }
function createAppState(): Writable<AppState> & AppStateAPI { function createAppState(): Writable<AppState> & AppStateAPI {
@ -34,17 +32,11 @@ function createAppState(): Writable<AppState> & AppStateAPI {
setLoadingBar: (loadingBar: boolean) => { setLoadingBar: (loadingBar: boolean) => {
store.update((state) => ({ ...state, loadingBar })) store.update((state) => ({ ...state, loadingBar }))
}, },
setDefaultAction: (defaultAction: string | null) => { setDefaultAction: (defaultAction: string) => {
store.update((state) => ({ ...state, defaultAction })) store.update((state) => ({ ...state, defaultAction }))
}, },
setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => { setActionPanel: (actionPanel?: ActionSchema.ActionPanel) => {
store.update((state) => ({ ...state, actionPanel })) store.update((state) => ({ ...state, actionPanel }))
},
setLockHideOnBlur: (lockHideOnBlur: boolean) => {
store.update((state) => ({ ...state, lockHideOnBlur }))
},
setFullScreenLoading: (fullScreenLoading: boolean) => {
store.update((state) => ({ ...state, fullScreenLoading }))
} }
} }
} }

View File

@ -1,48 +0,0 @@
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[]>([])
return {
...store,
get: () => get(store),
init: async () => {
await refreshApplicationsList()
let apps = await getAllApps()
if (platform() === "macos") {
apps = apps.filter((app) => {
return (
!app.app_desktop_path.includes("Parallels") &&
!app.app_desktop_path.startsWith("/Library/Application Support") &&
!app.app_desktop_path.startsWith("/System/Library/CoreServices") &&
!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)
})

View File

@ -1,11 +1,11 @@
import type { CustomUiCmd, ExtPackageJsonExtra, HeadlessCmd, TemplateUiCmd } from "@kksh/api/models" import { getExtensionsFolder } from "@/constants"
import { db } from "@kksh/drizzle" import { db } from "@kksh/api/commands"
import type { ExtPackageJson, ExtPackageJsonExtra } from "@kksh/api/models"
import * as extAPI from "@kksh/extension" import * as extAPI from "@kksh/extension"
import * as path from "@tauri-apps/api/path" import * as path from "@tauri-apps/api/path"
import Fuse from "fuse.js" import * as fs from "@tauri-apps/plugin-fs"
import { derived, get, writable, type Writable } from "svelte/store" import { derived, get, writable, type Readable, type Writable } from "svelte/store"
import { appConfig } from "./appConfig" import { appConfig } from "./appConfig"
import { appState } from "./appState"
function createExtensionsStore(): Writable<ExtPackageJsonExtra[]> & { function createExtensionsStore(): Writable<ExtPackageJsonExtra[]> & {
init: () => Promise<void> init: () => Promise<void>
@ -27,7 +27,6 @@ function createExtensionsStore(): Writable<ExtPackageJsonExtra[]> & {
tarballUrl: string, tarballUrl: string,
extras?: { overwritePackageJson?: string } extras?: { overwritePackageJson?: string }
) => Promise<ExtPackageJsonExtra> ) => Promise<ExtPackageJsonExtra>
reloadExtension: (extPath: string) => Promise<void>
} { } {
const store = writable<ExtPackageJsonExtra[]>([]) const store = writable<ExtPackageJsonExtra[]>([])
@ -41,22 +40,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) * Get all extensions installed from the store (non-dev extensions)
*/ */
@ -210,7 +193,6 @@ function createExtensionsStore(): Writable<ExtPackageJsonExtra[]> & {
return { return {
...store, ...store,
init, init,
reloadExtension,
getExtensionsFromStore, getExtensionsFromStore,
findStoreExtensionByIdentifier, findStoreExtensionByIdentifier,
registerNewExtensionByPath, registerNewExtensionByPath,
@ -225,62 +207,20 @@ function createExtensionsStore(): Writable<ExtPackageJsonExtra[]> & {
} }
export const extensions = createExtensionsStore() 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) & { export const installedStoreExts: Readable<ExtPackageJsonExtra[]> = derived(
ext: ExtPackageJsonExtra extensions,
} ($extensionsStore) => {
const extContainerPath = get(appConfig).extensionsInstallDir
export const cmdsFuse = new Fuse<StoreExtCmd>([], { if (!extContainerPath) return []
includeScore: true, return $extensionsStore.filter((ext) => !extAPI.isExtPathInDev(extContainerPath, ext.extPath))
threshold: 0.2, }
keys: ["name"] )
}) export const devStoreExts: Readable<ExtPackageJsonExtra[]> = derived(
export const devCmdsFuse = new Fuse<StoreExtCmd>([], { extensions,
includeScore: true, ($extensionsStore) => {
threshold: 0.2, const extContainerPath = get(appConfig).extensionsInstallDir
keys: ["name"] if (!extContainerPath) return []
}) return $extensionsStore.filter((ext) => extAPI.isExtPathInDev(extContainerPath, ext.extPath))
}
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
})

View File

@ -4,4 +4,3 @@ export * from "./winExtMap"
export * from "./extensions" export * from "./extensions"
export * from "./auth" export * from "./auth"
export * from "./quick-links" export * from "./quick-links"
export * from "./apps"

View File

@ -1,26 +0,0 @@
import { get, writable, type Writable } from "svelte/store"
export interface KeyStoreAPI {
get: () => string[]
getSet: () => Set<string>
keydown: (key: string) => void
keyup: (key: string) => void
}
function createKeysStore(): Writable<string[]> & KeyStoreAPI {
const store = writable<string[]>([])
return {
...store,
get: () => get(store),
getSet: () => new Set(get(store)),
keydown: (key: string) => {
store.update((state) => [...state, key])
},
keyup: (key: string) => {
store.update((state) => state.filter((k) => k !== key))
}
}
}
export const keys = createKeysStore()

View File

@ -1,16 +1,7 @@
import type { Icon } from "@kksh/api/models" import type { Icon } from "@kksh/api/models"
import { createQuickLinkCommand, getAllQuickLinkCommands } from "@kksh/extension/db" import { createQuickLinkCommand, getAllQuickLinkCommands } from "@kksh/extension/db"
import type { QuickLink } from "@kksh/ui/types" import type { CmdQuery, QuickLink } from "@kksh/ui/types"
import { commandScore } from "@kksh/ui/utils" import { get, writable, type Writable } from "svelte/store"
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 { export interface QuickLinkAPI {
get: () => QuickLink[] get: () => QuickLink[]
@ -28,9 +19,7 @@ function createQuickLinksStore(): Writable<QuickLink[]> & QuickLinkAPI {
async function refresh() { async function refresh() {
const cmds = await getAllQuickLinkCommands() const cmds = await getAllQuickLinkCommands()
const items = cmds.map((cmd) => ({ link: cmd.data.link, name: cmd.name, icon: cmd.data.icon })) store.set(cmds.map((cmd) => ({ link: cmd.data.link, name: cmd.name, icon: cmd.data.icon })))
store.set(items)
fuse.setCollection(items)
} }
async function createQuickLink(name: string, link: string, icon: Icon) { async function createQuickLink(name: string, link: string, icon: Icon) {
@ -48,22 +37,3 @@ function createQuickLinksStore(): Writable<QuickLink[]> & QuickLinkAPI {
} }
export const quickLinks = createQuickLinksStore() 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) {
// return false
// }
// return (
// commandScore(
// lnk.name,
// $appState.searchTerm
// // []
// ) > 0.5
// )
// })
// })

View File

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

View File

@ -1,13 +1,7 @@
import * as sb from "@supabase/supabase-js" import { createSB, SupabaseAPI } from "@kksh/supabase"
import { SUPABASE_ANON_KEY, SUPABASE_URL } from "./constants" import { SUPABASE_ANON_KEY, SUPABASE_URL } from "./constants"
// export const supabase = createSB(SUPABASE_URL, SUPABASE_ANON_KEY) 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 storage = supabase.storage export const storage = supabase.storage
export const supabaseExtensionsStorage = supabase.storage.from("extensions") 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,4 +1,3 @@
import { i18n } from "@/i18n"
import { emitRefreshDevExt } from "@/utils/tauri-events" import { emitRefreshDevExt } from "@/utils/tauri-events"
import { import {
DEEP_LINK_PATH_AUTH_CONFIRM, DEEP_LINK_PATH_AUTH_CONFIRM,
@ -58,15 +57,15 @@ export async function handleKunkunProtocol(parsedUrl: URL) {
const parsed = v.parse(StorePathSearchParams, params) const parsed = v.parse(StorePathSearchParams, params)
openMainWindow() openMainWindow()
if (parsed.identifier) { if (parsed.identifier) {
goto(i18n.resolveRoute(`/app/extension/store/${parsed.identifier}`)) goto(`/extension/store/${parsed.identifier}`)
} else { } else {
goto(i18n.resolveRoute("/app/extension/store")) goto("/app/extension/store")
} }
} else if (href.startsWith(DEEP_LINK_PATH_REFRESH_DEV_EXTENSION)) { } else if (href.startsWith(DEEP_LINK_PATH_REFRESH_DEV_EXTENSION)) {
emitRefreshDevExt() emitRefreshDevExt()
} else if (href.startsWith(DEEP_LINK_PATH_AUTH_CONFIRM)) { } else if (href.startsWith(DEEP_LINK_PATH_AUTH_CONFIRM)) {
openMainWindow() openMainWindow()
goto(i18n.resolveRoute(`/app/auth/confirm?${parsedUrl.searchParams.toString()}`)) goto(`/auth/confirm?${parsedUrl.searchParams.toString()}`)
} else { } else {
console.error("Invalid path:", pathname) console.error("Invalid path:", pathname)
toast.error("Invalid path", { toast.error("Invalid path", {

View File

@ -1,19 +0,0 @@
/**
* This file contains APIs for helper
*/
import { i18n } from "@/i18n"
import type { IHelper } from "@kksh/api"
import { goto } from "$app/navigation"
export const helperAPI: IHelper = {
guideInstallDeno: function (): Promise<void> {
return goto(i18n.resolveRoute("/app/help/deno-install"))
},
guideInstallFfmpeg: function (): Promise<void> {
return goto(i18n.resolveRoute("/app/help/ffmpeg-install"))
},
guideInstallHomebrew: function (): Promise<void> {
return goto(i18n.resolveRoute("/app/help/brew-install"))
}
}

View File

@ -1,11 +1,7 @@
import { app } from "@tauri-apps/api" import { getAllWindows } from "@tauri-apps/api/window"
import { getAllWindows, getCurrentWindow, type Window } from "@tauri-apps/api/window"
import { isRegistered, register, unregister } from "@tauri-apps/plugin-global-shortcut" import { isRegistered, register, unregister } from "@tauri-apps/plugin-global-shortcut"
import { debug, info, warn } from "@tauri-apps/plugin-log" import { debug, info } 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 { 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. * Tauri global shortcut doesn't accept 'Meta' Key. This function maps browser detected keys to Tauri-accepted keys.
@ -18,14 +14,9 @@ export function mapKeyToTauriKey(key: string): string {
return key 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) { export async function registerAppHotkey(hotkeyStr: string) {
if (await isRegistered(hotkeyStr)) { if (await isRegistered(hotkeyStr)) {
warn(`Hotkey (${hotkeyStr}) already registered`) debug(`Hotkey (${hotkeyStr}) already registered`)
await unregister(hotkeyStr) await unregister(hotkeyStr)
} }
info(`Registering hotkey: ${hotkeyStr}`) info(`Registering hotkey: ${hotkeyStr}`)
@ -48,17 +39,13 @@ export async function registerAppHotkey(hotkeyStr: string) {
mainWin.setFocus() mainWin.setFocus()
} }
} else { } 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) { export async function updateAppHotkey(newHotkey: string[], oldHotkey?: string[] | null) {
if (oldHotkey) { if (oldHotkey) {
const hotkeyStr = oldHotkey.map(mapKeyToTauriKey).join("+") const hotkeyStr = oldHotkey.map(mapKeyToTauriKey).join("+")
@ -69,44 +56,3 @@ export async function updateAppHotkey(newHotkey: string[], oldHotkey?: string[]
const hotkeyStr = newHotkey.map(mapKeyToTauriKey).join("+") const hotkeyStr = newHotkey.map(mapKeyToTauriKey).join("+")
return registerAppHotkey(hotkeyStr) 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 +0,0 @@
export { cn } from "./tailwind"

View File

@ -1,50 +0,0 @@
import { appConfig, extensions } from "@/stores"
import { getCurrentWindow } from "@tauri-apps/api/window"
import { error, 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() {
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()
// console.warn("contextmenu disabled in release mode", event)
// })
}
}
export function initMainWindow() {
/* -------------------------------------------------------------------------- */
/* Register App Hotkey */
/* -------------------------------------------------------------------------- */
const triggerHotkey = appConfig.get().triggerHotkey
if (triggerHotkey && triggerHotkey.length > 0) {
const hotkeyStr = triggerHotkey.map(mapKeyToTauriKey).join("+")
info(`Registering hotkey: ${hotkeyStr}`)
registerAppHotkey(hotkeyStr)
} else {
console.log("No hotkey found in config")
}
}

View File

@ -1,6 +1,6 @@
export function setsEqual<T>(set1: Set<T>, set2: Set<T>) { export function setsEqual<T>(set1: Set<T>, set2: Set<T>) {
if (set1.size !== set2.size) return false if (set1.size !== set2.size) return false
for (const item of set1) { for (let item of set1) {
if (!set2.has(item)) return false if (!set2.has(item)) return false
} }
return true return true

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