This commit is contained in:
Huakun Shen 2025-01-18 03:53:22 -05:00
commit fdeaa0f03e
No known key found for this signature in database
45 changed files with 6974 additions and 0 deletions

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
node_modules
# Output
.output
.vercel
/.svelte-kit
/build
# OS
.DS_Store
Thumbs.db
# Env
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
extensions_support/
*.db
node_modules
.pnpm-store

1
.npmrc Normal file
View File

@ -0,0 +1 @@
engine-strict=true

4
.prettierignore Normal file
View File

@ -0,0 +1,4 @@
# Package Managers
package-lock.json
pnpm-lock.yaml
yarn.lock

15
.prettierrc Normal file
View File

@ -0,0 +1,15 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}

15
CHANGELOG.md Normal file
View File

@ -0,0 +1,15 @@
# template-ext-sveltekit
## 0.0.3
### Patch Changes
- Updated dependencies
- @kksh/api@0.0.4
## 0.0.2
### Patch Changes
- Updated dependencies [fba6a49]
- @kksh/svelte@0.0.2

69
README.md Normal file
View File

@ -0,0 +1,69 @@
# Kunkun Custom UI Extension Template (SvelteKit)
[Custom UI Extension Documentation](https://docs.kunkun.sh/extensions/custom-ui-ext/)
This is a template for a custom UI extension.
This type of extension is basically a static website. You can use any frontend framework you like, this template uses [SvelteKit](https://svelte.dev/).
It is assumed that you have some knowledge of frontend development with SvelteKit.
## Development
Development is the same as developing a normal website.
```bash
pnpm install
pnpm dev
pnpm build
```
- To develop and preview the extension in Kunkun, you need to run the `Add Dev Extension` command in Kunkun, and register this extension's path.
In `package.json`, `"devMain"` is the url for development server, and `"main"` is the path to static `.html` file for production.
To load the extension in development mode, you have to enable it with `Toggle Dev Extension Live Load Mode` command in Kunkun. A `Live` badge will be shown on the commands. This indicates that dev extensions will be loaded from `devMain` instead of `main`.
## Advanced
### Rendering Mode
This is a Meta-Framework template, and already configured with SSG rendering mode.
Please do not enable SSR unless you know what you are doing.
There will not be a JS runtime in production, and Kunkun always load the extension as static files.
The main benefit of using a meta-framework is that it comes with routing, and will output multiple `.html` files, which makes multi-command support much easier.
## Verify Build and Publish
```bash
pnpm build # make sure the build npm script works
npx kksh@latest verify # Verify some basic settings before publishing
```
It is recommended to build the extension with the same environment our CI uses.
The docker image used by our CI is `huakunshen/kunkun-ext-builder:latest`.
You can use the following command to build the extension with the same environment our CI uses.
This requires you to have docker installed, and the shell you are using has access to it via `docker` command.
```bash
npx kksh@latest build # Build the extension with
```
`pnpm` is used to install dependencies and build the extension.
The docker image environment also has `node`, `pnpm`, `npm`, `bun`, `deno` installed.
If your build failed, try debug with `huakunshen/kunkun-ext-builder:latest` image in interative mode and bind your extension volume to `/workspace`.
After build successfully, you should find a tarball file ends with `.tgz` in the root of your extension.
The tarball is packaged with `npm pack` command. You can uncompress it to see if it contains all the necessary files.
This tarball is the final product that will be published and installed in Kunkun. You can further verify your extension by installing this tarball directly in Kunkun.
After verifying the tarball, it's ready to be published.
Fork [KunkunExtensions](https://github.com/kunkunsh/KunkunExtensions) repo, add your extension to the `extensions` directory, and create a PR.
Once CI passed and PR merged, you can use your extension in Kunkun.

17
components.json Normal file
View File

@ -0,0 +1,17 @@
{
"$schema": "https://shadcn-svelte.com/schema.json",
"style": "new-york",
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app.css",
"baseColor": "neutral"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks"
},
"typescript": true,
"registry": "https://next.shadcn-svelte.com/registry"
}

213
deno-src/dao.ts Normal file
View File

@ -0,0 +1,213 @@
import { Database } from 'jsr:@db/sqlite@0.12';
import * as v from 'valibot';
import {
TableInfoSchema,
ColumnInfoSchema,
PaginationParamsSchema,
QueryResultSchema,
TableInfo,
PaginationParams,
QueryResult,
ColumnInfo
} from '../src/types.ts';
export class SQLiteBrowser {
private db: Database | null = null;
/**
* Initialize the database connection
* @param dbPath Path to the SQLite database file
*/
init(dbPath: string): void {
if (this.db) {
throw new Error('Database is already initialized');
}
this.db = new Database(dbPath);
}
/**
* Close the database connection
*/
close(): void {
if (!this.db) {
throw new Error('Database is not initialized');
}
this.db.close();
this.db = null;
}
/**
* Get all table names from the database
*/
getTables(): TableInfo[] {
this.ensureConnection();
const results = this.db!.prepare(
`
SELECT name, type
FROM sqlite_master
WHERE type='table'
ORDER BY name;
`
).all();
return results.map((result) => v.parse(TableInfoSchema, result));
}
/**
* Get column information for a specific table
* @param tableName Name of the table
*/
getTableColumns(tableName: string): ColumnInfo[] {
this.ensureConnection();
this.validateTableName(tableName);
const results = this.db!.prepare(`PRAGMA table_info(${tableName})`).all();
return results.map((result) => v.parse(ColumnInfoSchema, result));
}
/**
* Get data from a table with pagination
* @param tableName Name of the table
* @param pagination Pagination parameters
*/
getTableData(
tableName: string,
pagination: PaginationParams
): {
data: QueryResult[];
total: number;
totalPages: number;
} {
this.ensureConnection();
this.validateTableName(tableName);
// Validate pagination params
const validatedPagination = v.parse(PaginationParamsSchema, pagination);
const { page, pageSize } = validatedPagination;
const offset = (page - 1) * pageSize;
const result = this.db!.prepare(`SELECT COUNT(*) as count FROM ${tableName}`).get() as {
count: number;
};
const total = result.count;
const totalPages = Math.ceil(total / pageSize);
const rawData = this.db!.prepare(`SELECT * FROM ${tableName} LIMIT ?1 OFFSET ?2`).all(
pageSize,
offset
);
const data = rawData.map((row) => v.parse(QueryResultSchema, row));
return {
data,
total,
totalPages
};
}
/**
* Execute a raw SQL query
* @param query SQL query string with optional parameters (e.g. "SELECT * FROM table WHERE id = ?1")
*/
executeQuery(query: string): QueryResult[] {
this.ensureConnection();
const results = this.db!.prepare(query).all();
return results.map((result) => v.parse(QueryResultSchema, result));
}
/**
* Insert a new record into a table
* @param tableName Name of the table
* @param data Record data
*/
// insert(tableName: string, data: Record<string, unknown>): number {
// this.ensureConnection();
// this.validateTableName(tableName);
// const columns = Object.keys(data);
// const values = Object.values(data);
// const placeholders = columns.map((_, i) => `?${i + 1}`).join(', ');
// const query = `
// INSERT INTO ${tableName} (${columns.join(', ')})
// VALUES (${placeholders})
// `;
// const result = this.db!.prepare(query).run(...values);
// return result.lastInsertId;
// }
// /**
// * Update records in a table
// * @param tableName Name of the table
// * @param data Update data
// * @param whereClause WHERE clause for the update
// * @param whereParams Parameters for the WHERE clause
// */
// update(
// tableName: string,
// data: Record<string, unknown>,
// whereClause: string,
// whereParams: unknown[] = []
// ): number {
// this.ensureConnection();
// this.validateTableName(tableName);
// const setClause = Object.entries(data)
// .map(([column], index) => `${column} = ?${index + 1}`)
// .join(', ');
// const query = `
// UPDATE ${tableName}
// SET ${setClause}
// WHERE ${whereClause}
// `;
// const params = [...Object.values(data), ...whereParams];
// const result = this.db!.prepare(query).run(...params);
// return result.changes;
// }
// /**
// * Delete records from a table
// * @param tableName Name of the table
// * @param whereClause WHERE clause for the deletion
// * @param whereParams Parameters for the WHERE clause
// */
// delete(tableName: string, whereClause: string, whereParams: unknown[] = []): number {
// this.ensureConnection();
// this.validateTableName(tableName);
// const query = `DELETE FROM ${tableName} WHERE ${whereClause}`;
// const result = this.db!.prepare(query).run(...whereParams);
// return result.changes;
// }
/**
* Create a new table
* @param tableName Name of the table
* @param columns Column definitions
*/
createTable(tableName: string, columns: string): void {
this.ensureConnection();
this.validateTableName(tableName);
const query = `CREATE TABLE IF NOT EXISTS ${tableName} (${columns})`;
this.db!.prepare(query).run();
}
private ensureConnection(): void {
if (!this.db) {
throw new Error('Database is not initialized');
}
}
private validateTableName(tableName: string): void {
// Basic SQL injection prevention
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {
throw new Error('Invalid table name');
}
}
}

10
deno-src/deno.json Normal file
View File

@ -0,0 +1,10 @@
{
"tasks": {
"dev": "deno run --watch main.ts"
},
"imports": {
"@kunkun/api": "jsr:@kunkun/api@^0.0.40",
"@std/assert": "jsr:@std/assert@1",
"valibot": "jsr:@valibot/valibot@^0.42.1"
}
}

957
deno-src/deno.lock generated Normal file
View File

@ -0,0 +1,957 @@
{
"version": "4",
"specifiers": {
"jsr:@db/sqlite@0.12": "0.12.0",
"jsr:@denosaurs/plug@1": "1.0.6",
"jsr:@kunkun/api@^0.0.40": "0.0.40",
"jsr:@std/assert@0.217": "0.217.0",
"jsr:@std/assert@0.221": "0.221.0",
"jsr:@std/assert@1": "1.0.8",
"jsr:@std/encoding@0.221": "0.221.0",
"jsr:@std/fmt@0.221": "0.221.0",
"jsr:@std/fs@0.221": "0.221.0",
"jsr:@std/internal@^1.0.5": "1.0.5",
"jsr:@std/path@0.217": "0.217.0",
"jsr:@std/path@0.221": "0.221.0",
"jsr:@valibot/valibot@~0.42.1": "0.42.1",
"npm:@kksh/api@^0.0.40": "0.0.40",
"npm:@tauri-apps/api@^2.1.1": "2.1.1",
"npm:@tauri-apps/plugin-fs@^2.0.2": "2.0.2",
"npm:@tauri-apps/plugin-os@2": "2.0.0",
"npm:kkrpc@^0.0.12": "0.0.12_typescript@5.7.2",
"npm:lodash@^4.17.21": "4.17.21",
"npm:minimatch@^10.0.1": "10.0.1",
"npm:semver@^7.6.3": "7.6.3",
"npm:svelte-sonner@~0.3.28": "0.3.28_svelte@5.2.7__acorn@8.14.0",
"npm:tauri-api-adapter@~0.3.12": "0.3.13_typescript@5.7.2",
"npm:tauri-plugin-shellx-api@^2.0.14": "2.0.14",
"npm:valibot@0.40": "0.40.0_typescript@5.7.2"
},
"jsr": {
"@db/sqlite@0.12.0": {
"integrity": "dd1ef7f621ad50fc1e073a1c3609c4470bd51edc0994139c5bf9851de7a6d85f",
"dependencies": [
"jsr:@denosaurs/plug",
"jsr:@std/path@0.217"
]
},
"@denosaurs/plug@1.0.6": {
"integrity": "6cf5b9daba7799837b9ffbe89f3450510f588fafef8115ddab1ff0be9cb7c1a7",
"dependencies": [
"jsr:@std/encoding",
"jsr:@std/fmt",
"jsr:@std/fs",
"jsr:@std/path@0.221"
]
},
"@kunkun/api@0.0.40": {
"integrity": "eab67c01e1cc87f3e5e7f7613a302cba7fccb18a1745f1a5508cf48df1e3649e",
"dependencies": [
"npm:@kksh/api",
"npm:@tauri-apps/api",
"npm:@tauri-apps/plugin-fs",
"npm:@tauri-apps/plugin-os",
"npm:kkrpc",
"npm:lodash",
"npm:minimatch",
"npm:semver",
"npm:svelte-sonner",
"npm:tauri-api-adapter",
"npm:tauri-plugin-shellx-api",
"npm:valibot"
]
},
"@std/assert@0.217.0": {
"integrity": "c98e279362ca6982d5285c3b89517b757c1e3477ee9f14eb2fdf80a45aaa9642"
},
"@std/assert@0.221.0": {
"integrity": "a5f1aa6e7909dbea271754fd4ab3f4e687aeff4873b4cef9a320af813adb489a"
},
"@std/assert@1.0.8": {
"integrity": "ebe0bd7eb488ee39686f77003992f389a06c3da1bbd8022184804852b2fa641b",
"dependencies": [
"jsr:@std/internal"
]
},
"@std/encoding@0.221.0": {
"integrity": "d1dd76ef0dc5d14088411e6dc1dede53bf8308c95d1537df1214c97137208e45"
},
"@std/fmt@0.221.0": {
"integrity": "379fed69bdd9731110f26b9085aeb740606b20428ce6af31ef6bd45ef8efa62a"
},
"@std/fs@0.221.0": {
"integrity": "028044450299de8ed5a716ade4e6d524399f035513b85913794f4e81f07da286",
"dependencies": [
"jsr:@std/assert@0.221",
"jsr:@std/path@0.221"
]
},
"@std/internal@1.0.5": {
"integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba"
},
"@std/path@0.217.0": {
"integrity": "1217cc25534bca9a2f672d7fe7c6f356e4027df400c0e85c0ef3e4343bc67d11",
"dependencies": [
"jsr:@std/assert@0.217"
]
},
"@std/path@0.221.0": {
"integrity": "0a36f6b17314ef653a3a1649740cc8db51b25a133ecfe838f20b79a56ebe0095",
"dependencies": [
"jsr:@std/assert@0.221"
]
},
"@valibot/valibot@0.42.1": {
"integrity": "ba0f6f7964aaeec0e4b1f793d575061f325ae6254cbb9d7ff01fb65068a0a23b"
}
},
"npm": {
"@ampproject/remapping@2.3.0": {
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
"dependencies": [
"@jridgewell/gen-mapping",
"@jridgewell/trace-mapping"
]
},
"@isaacs/cliui@8.0.2": {
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dependencies": [
"string-width@5.1.2",
"string-width-cjs@npm:string-width@4.2.3",
"strip-ansi@7.1.0",
"strip-ansi-cjs@npm:strip-ansi@6.0.1",
"wrap-ansi@8.1.0",
"wrap-ansi-cjs@npm:wrap-ansi@7.0.0"
]
},
"@jridgewell/gen-mapping@0.3.5": {
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"dependencies": [
"@jridgewell/set-array",
"@jridgewell/sourcemap-codec",
"@jridgewell/trace-mapping"
]
},
"@jridgewell/resolve-uri@3.1.2": {
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
},
"@jridgewell/set-array@1.2.1": {
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="
},
"@jridgewell/sourcemap-codec@1.5.0": {
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
},
"@jridgewell/trace-mapping@0.3.25": {
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dependencies": [
"@jridgewell/resolve-uri",
"@jridgewell/sourcemap-codec"
]
},
"@kksh/api@0.0.40": {
"integrity": "sha512-g7n/vwGWs+5OMdxHUPOFKaX7vPqzTfXkRPTR33G+fWv6mdaypaS6fOAMnGmcoEgLPzL4RPO1WaL3+ypSCV620A==",
"dependencies": [
"@tauri-apps/api@2.1.1",
"@tauri-apps/cli",
"@tauri-apps/plugin-deep-link",
"@tauri-apps/plugin-dialog",
"@tauri-apps/plugin-fs",
"@tauri-apps/plugin-global-shortcut",
"@tauri-apps/plugin-http",
"@tauri-apps/plugin-log",
"@tauri-apps/plugin-notification",
"@tauri-apps/plugin-os",
"@tauri-apps/plugin-process",
"@tauri-apps/plugin-shell",
"@tauri-apps/plugin-store",
"@tauri-apps/plugin-updater",
"@tauri-apps/plugin-upload",
"kkrpc@0.0.10_typescript@5.7.2",
"lodash",
"minimatch@10.0.1",
"semver",
"svelte-sonner",
"tauri-api-adapter",
"tauri-plugin-network-api@2.0.4",
"tauri-plugin-shellx-api",
"tauri-plugin-system-info-api@2.0.8",
"valibot"
]
},
"@nodelib/fs.scandir@2.1.5": {
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dependencies": [
"@nodelib/fs.stat",
"run-parallel"
]
},
"@nodelib/fs.stat@2.0.5": {
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="
},
"@nodelib/fs.walk@1.2.8": {
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dependencies": [
"@nodelib/fs.scandir",
"fastq"
]
},
"@tauri-apps/api@2.0.1": {
"integrity": "sha512-eoQWT+Tq1qSwQpHV+nw1eNYe5B/nm1PoRjQCRiEOS12I1b+X4PUcREfXVX8dPcBT6GrzWGDtaecY0+1p0Rfqlw=="
},
"@tauri-apps/api@2.1.1": {
"integrity": "sha512-fzUfFFKo4lknXGJq8qrCidkUcKcH2UHhfaaCNt4GzgzGaW2iS26uFOg4tS3H4P8D6ZEeUxtiD5z0nwFF0UN30A=="
},
"@tauri-apps/cli-darwin-arm64@2.1.0": {
"integrity": "sha512-ESc6J6CE8hl1yKH2vJ+ALF+thq4Be+DM1mvmTyUCQObvezNCNhzfS6abIUd3ou4x5RGH51ouiANeT3wekU6dCw=="
},
"@tauri-apps/cli-darwin-x64@2.1.0": {
"integrity": "sha512-TasHS442DFs8cSH2eUQzuDBXUST4ECjCd0yyP+zZzvAruiB0Bg+c8A+I/EnqCvBQ2G2yvWLYG8q/LI7c87A5UA=="
},
"@tauri-apps/cli-linux-arm-gnueabihf@2.1.0": {
"integrity": "sha512-aP7ZBGNL4ny07Cbb6kKpUOSrmhcIK2KhjviTzYlh+pPhAptxnC78xQGD3zKQkTi2WliJLPmBYbOHWWQa57lQ9w=="
},
"@tauri-apps/cli-linux-arm64-gnu@2.1.0": {
"integrity": "sha512-ZTdgD5gLeMCzndMT2f358EkoYkZ5T+Qy6zPzU+l5vv5M7dHVN9ZmblNAYYXmoOuw7y+BY4X/rZvHV9pcGrcanQ=="
},
"@tauri-apps/cli-linux-arm64-musl@2.1.0": {
"integrity": "sha512-NzwqjUCilhnhJzusz3d/0i0F1GFrwCQbkwR6yAHUxItESbsGYkZRJk0yMEWkg3PzFnyK4cWTlQJMEU52TjhEzA=="
},
"@tauri-apps/cli-linux-x64-gnu@2.1.0": {
"integrity": "sha512-TyiIpMEtZxNOQmuFyfJwaaYbg3movSthpBJLIdPlKxSAB2BW0VWLY3/ZfIxm/G2YGHyREkjJvimzYE0i37PnMA=="
},
"@tauri-apps/cli-linux-x64-musl@2.1.0": {
"integrity": "sha512-/dQd0TlaxBdJACrR72DhynWftzHDaX32eBtS5WBrNJ+nnNb+znM3gON6nJ9tSE9jgDa6n1v2BkI/oIDtypfUXw=="
},
"@tauri-apps/cli-win32-arm64-msvc@2.1.0": {
"integrity": "sha512-NdQJO7SmdYqOcE+JPU7bwg7+odfZMWO6g8xF9SXYCMdUzvM2Gv/AQfikNXz5yS7ralRhNFuW32i5dcHlxh4pDg=="
},
"@tauri-apps/cli-win32-ia32-msvc@2.1.0": {
"integrity": "sha512-f5h8gKT/cB8s1ticFRUpNmHqkmaLutT62oFDB7N//2YTXnxst7EpMIn1w+QimxTvTk2gcx6EcW6bEk/y2hZGzg=="
},
"@tauri-apps/cli-win32-x64-msvc@2.1.0": {
"integrity": "sha512-P/+LrdSSb5Xbho1LRP4haBjFHdyPdjWvGgeopL96OVtrFpYnfC+RctB45z2V2XxqFk3HweDDxk266btjttfjGw=="
},
"@tauri-apps/cli@2.1.0": {
"integrity": "sha512-K2VhcKqBhAeS5pNOVdnR/xQRU6jwpgmkSL2ejHXcl0m+kaTggT0WRDQnFtPq6NljA7aE03cvwsbCAoFG7vtkJw==",
"dependencies": [
"@tauri-apps/cli-darwin-arm64",
"@tauri-apps/cli-darwin-x64",
"@tauri-apps/cli-linux-arm-gnueabihf",
"@tauri-apps/cli-linux-arm64-gnu",
"@tauri-apps/cli-linux-arm64-musl",
"@tauri-apps/cli-linux-x64-gnu",
"@tauri-apps/cli-linux-x64-musl",
"@tauri-apps/cli-win32-arm64-msvc",
"@tauri-apps/cli-win32-ia32-msvc",
"@tauri-apps/cli-win32-x64-msvc"
]
},
"@tauri-apps/plugin-deep-link@2.0.0": {
"integrity": "sha512-cDa2k1OrRU5DoKc0IXl1Y8RlFOU107u2phdZfT7FkApsC6TL/VAPs3YOUTT8p9/PZ50EjOKP104HFMqVqnQ0bw==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-dialog@2.0.1": {
"integrity": "sha512-fnUrNr6EfvTqdls/ufusU7h6UbNFzLKvHk/zTuOiBq01R3dTODqwctZlzakdbfSp/7pNwTKvgKTAgl/NAP/Z0Q==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-fs@2.0.2": {
"integrity": "sha512-4YZaX2j7ta81M5/DL8aN10kTnpUkEpkPo1FTYPT8Dd0ImHe3azM8i8MrtjrDGoyBYLPO3zFv7df/mSCYF8oA0Q==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-global-shortcut@2.0.0": {
"integrity": "sha512-pnB4CUwFVjg4twtBSxoLJ4uLFTYxsvOdC1zIbG581pYzhYatOl6mjB+ijD5SSXgiS/jNoqMcfkOF9PWAisurew==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-http@2.0.1": {
"integrity": "sha512-j6IA3pVBybSCwPpsihpX4z8bs6PluuGtr06ahL/xy4D8HunNBTmRmadJrFOQi0gOAbaig4MkQ15nzNLBLy8R1A==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-log@2.0.0": {
"integrity": "sha512-C+NII9vzswqnOQE8k7oRtnaw0z5TZsMmnirRhXkCKDEhQQH9841Us/PC1WHtGiAaJ8za1A1JB2xXndEq/47X/w==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-notification@2.0.0": {
"integrity": "sha512-6qEDYJS7mgXZWLXA0EFL+DVCJh8sJlzSoyw6B50pxhLPVFjc5Vr5DVzl5W3mUHaYhod5wsC984eQnlCCGqxYDA==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-os@2.0.0": {
"integrity": "sha512-M7hG/nNyQYTJxVG/UhTKhp9mpXriwWzrs9mqDreB8mIgqA3ek5nHLdwRZJWhkKjZrnDT4v9CpA9BhYeplTlAiA==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-process@2.0.0": {
"integrity": "sha512-OYzi0GnkrF4NAnsHZU7U3tjSoP0PbeAlO7T1Z+vJoBUH9sFQ1NSLqWYWQyf8hcb3gVWe7P1JggjiskO+LST1ug==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-shell@2.0.1": {
"integrity": "sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-store@2.1.0": {
"integrity": "sha512-GADqrc17opUKYIAKnGHIUgEeTZ2wJGu1ZITKQ1WMuOFdv8fvXRFBAqsqPjE3opgWohbczX6e1NpwmZK1AnuWVw==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-updater@2.0.0": {
"integrity": "sha512-N0cl71g7RPr7zK2Fe5aoIwzw14NcdLcz7XMGFWZVjprsqgDRWoxbnUkknyCQMZthjhGkppCd/wN2MIsUz+eAhQ==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@tauri-apps/plugin-upload@2.1.0": {
"integrity": "sha512-nSIyxp2sAHsj+1RMs3obTP2lC1rzeWcMxHtzLYjnXLnJMXvWlFG0T7jEZ9Sg/OErvQxPVIvDW+12evuFKa5t8Q==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"@types/estree@1.0.6": {
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
},
"acorn-typescript@1.4.13_acorn@8.14.0": {
"integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==",
"dependencies": [
"acorn"
]
},
"acorn@8.14.0": {
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="
},
"ansi-regex@5.0.1": {
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-regex@6.1.0": {
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="
},
"ansi-styles@4.3.0": {
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": [
"color-convert"
]
},
"ansi-styles@6.2.1": {
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
},
"anymatch@3.1.3": {
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dependencies": [
"normalize-path",
"picomatch"
]
},
"aria-query@5.3.2": {
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="
},
"array-union@2.1.0": {
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="
},
"axobject-query@4.1.0": {
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="
},
"balanced-match@1.0.2": {
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"binary-extensions@2.3.0": {
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="
},
"brace-expansion@1.1.11": {
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": [
"balanced-match",
"concat-map"
]
},
"brace-expansion@2.0.1": {
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dependencies": [
"balanced-match"
]
},
"braces@3.0.3": {
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dependencies": [
"fill-range"
]
},
"chokidar@3.6.0": {
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dependencies": [
"anymatch",
"braces",
"fsevents",
"glob-parent",
"is-binary-path",
"is-glob",
"normalize-path",
"readdirp"
]
},
"color-convert@2.0.1": {
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": [
"color-name"
]
},
"color-name@1.1.4": {
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"commander@9.5.0": {
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="
},
"concat-map@0.0.1": {
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"cross-spawn@7.0.6": {
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dependencies": [
"path-key",
"shebang-command",
"which"
]
},
"dir-glob@3.0.1": {
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"dependencies": [
"path-type"
]
},
"eastasianwidth@0.2.0": {
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
},
"emoji-regex@8.0.0": {
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"emoji-regex@9.2.2": {
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
},
"esm-env@1.1.4": {
"integrity": "sha512-oO82nKPHKkzIj/hbtuDYy/JHqBHFlMIW36SDiPCVsj87ntDLcWN+sJ1erdVryd4NxODacFTsdrIE3b7IamqbOg=="
},
"esrap@1.2.2": {
"integrity": "sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==",
"dependencies": [
"@jridgewell/sourcemap-codec",
"@types/estree"
]
},
"fast-glob@3.3.2": {
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"dependencies": [
"@nodelib/fs.stat",
"@nodelib/fs.walk",
"glob-parent",
"merge2",
"micromatch"
]
},
"fastq@1.17.1": {
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
"dependencies": [
"reusify"
]
},
"fill-range@7.1.1": {
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dependencies": [
"to-regex-range"
]
},
"foreground-child@3.3.0": {
"integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
"dependencies": [
"cross-spawn",
"signal-exit"
]
},
"fs.realpath@1.0.0": {
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"fsevents@2.3.3": {
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
},
"function-bind@1.1.2": {
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
},
"glob-parent@5.1.2": {
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dependencies": [
"is-glob"
]
},
"glob@11.0.0": {
"integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==",
"dependencies": [
"foreground-child",
"jackspeak",
"minimatch@10.0.1",
"minipass",
"package-json-from-dist",
"path-scurry"
]
},
"glob@7.2.3": {
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dependencies": [
"fs.realpath",
"inflight",
"inherits",
"minimatch@3.1.2",
"once",
"path-is-absolute"
]
},
"globby@11.1.0": {
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dependencies": [
"array-union",
"dir-glob",
"fast-glob",
"ignore",
"merge2",
"slash"
]
},
"hasown@2.0.2": {
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": [
"function-bind"
]
},
"ignore@5.3.2": {
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="
},
"inflight@1.0.6": {
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dependencies": [
"once",
"wrappy"
]
},
"inherits@2.0.4": {
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"interpret@1.4.0": {
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA=="
},
"is-binary-path@2.1.0": {
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dependencies": [
"binary-extensions"
]
},
"is-core-module@2.15.1": {
"integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
"dependencies": [
"hasown"
]
},
"is-extglob@2.1.1": {
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
},
"is-fullwidth-code-point@3.0.0": {
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"is-glob@4.0.3": {
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dependencies": [
"is-extglob"
]
},
"is-number@7.0.0": {
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
},
"is-reference@3.0.3": {
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
"dependencies": [
"@types/estree"
]
},
"isexe@2.0.0": {
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"jackspeak@4.0.2": {
"integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==",
"dependencies": [
"@isaacs/cliui"
]
},
"kkrpc@0.0.10_typescript@5.7.2": {
"integrity": "sha512-lkQKVnN9f6JrS4ybKbGkV4mtuGhWYLTnaWx60ysytEap+sP5jcTbAuJlSrY6JqlwaohiS0X3ZbvJ2rAXYRdTng==",
"dependencies": [
"typescript",
"ws"
]
},
"kkrpc@0.0.12_typescript@5.7.2": {
"integrity": "sha512-PBk4AhGfkesIdAwmIoj7dHHIp7qN97XT4yr5Rl7h2WL79gxWQVgZRJYLt7Gb17GoLDh991rnL85mhCoPG5VC/Q==",
"dependencies": [
"typescript",
"ws"
]
},
"locate-character@3.0.0": {
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
},
"lodash@4.17.21": {
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lru-cache@11.0.2": {
"integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA=="
},
"magic-string@0.30.13": {
"integrity": "sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==",
"dependencies": [
"@jridgewell/sourcemap-codec"
]
},
"merge2@1.4.1": {
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
},
"micromatch@4.0.8": {
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dependencies": [
"braces",
"picomatch"
]
},
"minimatch@10.0.1": {
"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
"dependencies": [
"brace-expansion@2.0.1"
]
},
"minimatch@3.1.2": {
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": [
"brace-expansion@1.1.11"
]
},
"minimist@1.2.8": {
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
},
"minipass@7.1.2": {
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="
},
"mylas@2.1.13": {
"integrity": "sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg=="
},
"normalize-path@3.0.0": {
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
"once@1.4.0": {
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": [
"wrappy"
]
},
"package-json-from-dist@1.0.1": {
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
},
"path-is-absolute@1.0.1": {
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
},
"path-key@3.1.1": {
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"path-parse@1.0.7": {
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"path-scurry@2.0.0": {
"integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
"dependencies": [
"lru-cache",
"minipass"
]
},
"path-type@4.0.0": {
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
},
"picomatch@2.3.1": {
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
},
"plimit-lit@1.6.1": {
"integrity": "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==",
"dependencies": [
"queue-lit"
]
},
"queue-lit@1.5.2": {
"integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw=="
},
"queue-microtask@1.2.3": {
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
},
"readdirp@3.6.0": {
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dependencies": [
"picomatch"
]
},
"rechoir@0.6.2": {
"integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
"dependencies": [
"resolve"
]
},
"resolve@1.22.8": {
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dependencies": [
"is-core-module",
"path-parse",
"supports-preserve-symlinks-flag"
]
},
"reusify@1.0.4": {
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
},
"rimraf@6.0.1": {
"integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==",
"dependencies": [
"glob@11.0.0",
"package-json-from-dist"
]
},
"run-parallel@1.2.0": {
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dependencies": [
"queue-microtask"
]
},
"semver@7.6.3": {
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
},
"shebang-command@2.0.0": {
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dependencies": [
"shebang-regex"
]
},
"shebang-regex@3.0.0": {
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"shelljs@0.8.5": {
"integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
"dependencies": [
"glob@7.2.3",
"interpret",
"rechoir"
]
},
"shx@0.3.4": {
"integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==",
"dependencies": [
"minimist",
"shelljs"
]
},
"signal-exit@4.1.0": {
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
},
"slash@3.0.0": {
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
},
"string-width@4.2.3": {
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": [
"emoji-regex@8.0.0",
"is-fullwidth-code-point",
"strip-ansi@6.0.1"
]
},
"string-width@5.1.2": {
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dependencies": [
"eastasianwidth",
"emoji-regex@9.2.2",
"strip-ansi@7.1.0"
]
},
"strip-ansi@6.0.1": {
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dependencies": [
"ansi-regex@5.0.1"
]
},
"strip-ansi@7.1.0": {
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dependencies": [
"ansi-regex@6.1.0"
]
},
"supports-preserve-symlinks-flag@1.0.0": {
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
},
"svelte-sonner@0.3.28_svelte@5.2.7__acorn@8.14.0": {
"integrity": "sha512-K3AmlySeFifF/cKgsYNv5uXqMVNln0NBAacOYgmkQStLa/UoU0LhfAACU6Gr+YYC8bOCHdVmFNoKuDbMEsppJg==",
"dependencies": [
"svelte"
]
},
"svelte@5.2.7_acorn@8.14.0": {
"integrity": "sha512-cEhPGuLHiH2+Z8B1FwQgiZJgA39uUmJR4516TKrM5zrp0/cuwJkfhUfcTxhAkznanAF5fXUKzvYR4o+Ksx3ZCQ==",
"dependencies": [
"@ampproject/remapping",
"@jridgewell/sourcemap-codec",
"@types/estree",
"acorn",
"acorn-typescript",
"aria-query",
"axobject-query",
"esm-env",
"esrap",
"is-reference",
"locate-character",
"magic-string",
"zimmerframe"
]
},
"tauri-api-adapter@0.3.13_typescript@5.7.2": {
"integrity": "sha512-ex4z3Zish6by1ew1ialbLc5g3dhly+6ihepJeJwVRQoMJdUTyfCX2dVPYW74i+px1hFXKFu3MYTheS7uBOeCbg==",
"dependencies": [
"@tauri-apps/api@2.1.1",
"@tauri-apps/plugin-dialog",
"@tauri-apps/plugin-fs",
"@tauri-apps/plugin-http",
"@tauri-apps/plugin-log",
"@tauri-apps/plugin-notification",
"@tauri-apps/plugin-os",
"@tauri-apps/plugin-shell",
"@tauri-apps/plugin-upload",
"kkrpc@0.0.12_typescript@5.7.2",
"rimraf",
"shx",
"tauri-plugin-clipboard-api",
"tauri-plugin-network-api@2.0.4_typescript@5.7.2",
"tauri-plugin-shellx-api",
"tauri-plugin-system-info-api@2.0.8_typescript@5.7.2",
"tsc-alias",
"typescript",
"valibot"
]
},
"tauri-plugin-clipboard-api@2.1.11_typescript@5.7.2": {
"integrity": "sha512-VNkGaVPPfRoHg7/rJBcWqsvLvn4/kNEOOlzqwyI9Qdf6g54B3mc31GLZdnq/HWtX0vZskw3J8b/EF9YkASDCBQ==",
"dependencies": [
"@tauri-apps/api@2.0.1",
"valibot"
]
},
"tauri-plugin-network-api@2.0.4": {
"integrity": "sha512-CJWF2g+uQifcIlE/AXUnezVjjbyY0FDBxoz4P6BmjNRR/qubpNMfdUnKLqdjX98o5MIXGW+UnyZTfbJo998dFw==",
"dependencies": [
"@tauri-apps/api@2.1.1",
"valibot"
]
},
"tauri-plugin-network-api@2.0.4_typescript@5.7.2": {
"integrity": "sha512-CJWF2g+uQifcIlE/AXUnezVjjbyY0FDBxoz4P6BmjNRR/qubpNMfdUnKLqdjX98o5MIXGW+UnyZTfbJo998dFw==",
"dependencies": [
"@tauri-apps/api@2.1.1",
"valibot"
]
},
"tauri-plugin-shellx-api@2.0.14": {
"integrity": "sha512-MdSYD2KDw63b7yEIa9Q2GXnbidL5Tk+s92BJX0XvYfHrv2l1fYE2vdRWGnyhvCWmUavyCeiOle5uMxM6QLOb2Q==",
"dependencies": [
"@tauri-apps/api@2.1.1"
]
},
"tauri-plugin-system-info-api@2.0.8": {
"integrity": "sha512-EFdLXNGp6Zu9SNsZCkU+55A8027OnrVw/TQrd0oJHgfZzs4qvm1iMmSvyid4MLftt33iZDhjCzxYijaaOxeKSg==",
"dependencies": [
"@tauri-apps/api@2.1.1",
"valibot"
]
},
"tauri-plugin-system-info-api@2.0.8_typescript@5.7.2": {
"integrity": "sha512-EFdLXNGp6Zu9SNsZCkU+55A8027OnrVw/TQrd0oJHgfZzs4qvm1iMmSvyid4MLftt33iZDhjCzxYijaaOxeKSg==",
"dependencies": [
"@tauri-apps/api@2.1.1",
"valibot"
]
},
"to-regex-range@5.0.1": {
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dependencies": [
"is-number"
]
},
"tsc-alias@1.8.10": {
"integrity": "sha512-Ibv4KAWfFkFdKJxnWfVtdOmB0Zi1RJVxcbPGiCDsFpCQSsmpWyuzHG3rQyI5YkobWwxFPEyQfu1hdo4qLG2zPw==",
"dependencies": [
"chokidar",
"commander",
"globby",
"mylas",
"normalize-path",
"plimit-lit"
]
},
"typescript@5.7.2": {
"integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg=="
},
"valibot@0.40.0_typescript@5.7.2": {
"integrity": "sha512-XHKnaVtwHqxPwnGOsLrwka9CEaL7yNeLNp707OKv/bmT29GnPVdl6PxBOZ6BW7hF66/6QT6iVbOlnW7qVPmoKw==",
"dependencies": [
"typescript"
]
},
"which@2.0.2": {
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dependencies": [
"isexe"
]
},
"wrap-ansi@7.0.0": {
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dependencies": [
"ansi-styles@4.3.0",
"string-width@4.2.3",
"strip-ansi@6.0.1"
]
},
"wrap-ansi@8.1.0": {
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dependencies": [
"ansi-styles@6.2.1",
"string-width@5.1.2",
"strip-ansi@7.1.0"
]
},
"wrappy@1.0.2": {
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"ws@8.18.0": {
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="
},
"zimmerframe@1.1.2": {
"integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="
}
},
"workspace": {
"dependencies": [
"jsr:@kunkun/api@^0.0.40",
"jsr:@std/assert@1",
"jsr:@valibot/valibot@~0.42.1"
]
}
}

4
deno-src/index.ts Normal file
View File

@ -0,0 +1,4 @@
import { expose } from '@kunkun/api/runtime/deno';
import { SQLiteBrowser } from './dao.ts';
expose(new SQLiteBrowser());

18
deno-src/main.ts Normal file
View File

@ -0,0 +1,18 @@
import { SQLiteBrowser } from './dao.ts';
const browser = new SQLiteBrowser();
browser.init('kk.dev.db');
// Get all tables
const tables = browser.getTables();
console.log('Tables in database:', tables);
// Get extensions table columns
const extensionColumns = browser.getTableColumns('extensions');
console.log('Extension columns:', extensionColumns);
// Get extensions data with pagination
const extensionsData = browser.getTableData('extensions', { page: 1, pageSize: 10 });
console.log('Extensions:', extensionsData);
browser.close();

33
eslint.config.js Normal file
View File

@ -0,0 +1,33 @@
import js from '@eslint/js';
import ts from 'typescript-eslint';
import svelte from 'eslint-plugin-svelte';
import prettier from 'eslint-config-prettier';
import globals from 'globals';
/** @type {import('eslint').Linter.Config[]} */
export default [
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/']
}
];

108
package.json Normal file
View File

@ -0,0 +1,108 @@
{
"$schema": "https://schema.kunkun.sh",
"name": "kunkun-ext-sqlite-browser",
"repository": "https://github.com/kunkunsh/kunkun-ext-sqlite-browser",
"version": "0.0.4",
"kunkun": {
"name": "SQLite Browser",
"shortDescription": "SQLite Browser",
"longDescription": "SQLite Browser",
"identifier": "sqlite-browser",
"icon": {
"type": "iconify",
"value": "devicon:sqlite"
},
"demoImages": [],
"permissions": [
"event:drag-drop",
{
"permission": "shell:deno:spawn",
"allow": [
{
"path": "$EXTENSION/deno-src/index.ts",
"env": [
"DENO_SQLITE_PATH",
"DENO_SQLITE_LOCAL",
"DENO_DIR",
"HOME"
],
"read": "*",
"ffi": "*"
}
]
},
"shell:kill",
"dialog:all",
{
"permission": "fs:exists",
"allow": [
{
"path": "**"
}
]
},
"shell:stdin-write",
"notification:all"
],
"customUiCmds": [
{
"main": "/",
"dist": "build",
"devMain": "http://localhost:5173",
"name": "SQLite Browser",
"cmds": []
}
],
"templateUiCmds": []
},
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write ."
},
"dependencies": {
"@kksh/api": "^0.0.52",
"@kksh/svelte5": "0.1.10",
"@tanstack/table-core": "^8.20.5",
"clsx": "^2.1.1",
"mode-watcher": "^0.5.0",
"tailwind-merge": "^2.5.4",
"tailwind-variants": "^0.3.0",
"valibot": "1.0.0-beta.8"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.3.1",
"@sveltejs/adapter-static": "^3.0.6",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "4.0.0",
"@tailwindcss/typography": "^0.5.15",
"@types/eslint": "^9.6.1",
"autoprefixer": "^10.4.20",
"bits-ui": "1.0.0-next.64",
"eslint": "^9.15.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.46.0",
"globals": "^15.12.0",
"lucide-svelte": "^0.460.1",
"postcss": "^8.4.49",
"prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.8",
"prettier-plugin-tailwindcss": "^0.6.9",
"svelte": "^5.2.8",
"svelte-check": "^4.0.9",
"tailwindcss": "^3.4.15",
"typescript": "^5.6.3",
"typescript-eslint": "^8.15.0",
"vite": "^5.4.11"
},
"type": "module",
"files": [
"build",
".gitignore"
],
"packageManager": "pnpm@9.15.3+sha512.1f79bc245a66eb0b07c5d4d83131240774642caaa86ef7d0434ab47c0d16f66b04e21e0c086eb61e62c77efc4d7f7ec071afad3796af64892fae66509173893a"
}

4403
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};

98
src/app.css Normal file
View File

@ -0,0 +1,98 @@
@import url('@kksh/svelte5/themes');
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 0 0% 98%;
--ring: 0 0% 3.9%;
--radius: 0.5rem;
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--ring: 0 0% 83.1%;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

13
src/app.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

12
src/app.html Normal file
View File

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

View File

@ -0,0 +1,231 @@
<script lang="ts">
import ChevronDown from 'lucide-svelte/icons/chevron-down';
import Cell from './cell.svelte';
import {
type ColumnDef,
type ColumnFiltersState,
type PaginationState,
type RowSelectionState,
type SortingState,
type VisibilityState,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel
} from '@tanstack/table-core';
import { createRawSnippet } from 'svelte';
import { DataTable, Table, Input, DropdownMenu, Button } from '@kksh/svelte5';
import DataTableCheckbox from './data-table-checkbox.svelte';
import DataTableEmailButton from './data-table-email-button.svelte';
import DataTableActions from './data-table-actions.svelte';
import type { ColumnInfo, QueryResult } from '../../types';
const { renderComponent, renderSnippet, createSvelteTable, FlexRender } = DataTable;
const {
tableData,
columnsData
}: {
tableData: {
data: QueryResult[];
total: number;
totalPages: number;
};
columnsData: ColumnInfo[];
} = $props();
let columns: ColumnDef<QueryResult>[] = [
{
id: 'select',
header: ({ table }) =>
renderComponent(DataTableCheckbox, {
checked: table.getIsAllPageRowsSelected(),
indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(),
onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
'aria-label': 'Select all'
}),
cell: ({ row }) =>
renderComponent(DataTableCheckbox, {
checked: row.getIsSelected(),
onCheckedChange: (value) => row.toggleSelected(!!value),
'aria-label': 'Select row'
}),
enableSorting: false,
enableHiding: false
},
...columnsData.map((col) => ({
accessorKey: col.name,
cell: ({ row }: { row: any }) => {
// console.log(row);
// const randomSnippet = createRawSnippet<[string]>((getValue) => {
// return {
// render: () => `<div class="lowercase">${getValue()}</div>`
// };
// });
// return renderSnippet(Cell, row.getValue(col.name));
return renderComponent(Cell, {
type: col.type,
value: row.getValue(col.name)
});
}
}))
// {
// id: 'actions',
// enableHiding: false,
// cell: ({ row }) => renderComponent(DataTableActions, { id: row.original.id })
// }
];
let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 10 });
let sorting = $state<SortingState>([]);
let columnFilters = $state<ColumnFiltersState>([]);
let rowSelection = $state<RowSelectionState>({});
let columnVisibility = $state<VisibilityState>({});
const table = $state(
createSvelteTable({
get data() {
return tableData ? tableData.data : [];
},
columns,
state: {
get pagination() {
return pagination;
},
get sorting() {
return sorting;
},
get columnVisibility() {
return columnVisibility;
},
get rowSelection() {
return rowSelection;
},
get columnFilters() {
return columnFilters;
}
},
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onPaginationChange: (updater) => {
if (typeof updater === 'function') {
pagination = updater(pagination);
} else {
pagination = updater;
}
},
onSortingChange: (updater) => {
if (typeof updater === 'function') {
sorting = updater(sorting);
} else {
sorting = updater;
}
},
onColumnFiltersChange: (updater) => {
if (typeof updater === 'function') {
columnFilters = updater(columnFilters);
} else {
columnFilters = updater;
}
},
onColumnVisibilityChange: (updater) => {
if (typeof updater === 'function') {
columnVisibility = updater(columnVisibility);
} else {
columnVisibility = updater;
}
},
onRowSelectionChange: (updater) => {
if (typeof updater === 'function') {
rowSelection = updater(rowSelection);
} else {
rowSelection = updater;
}
}
})
);
</script>
<div class="w-full">
<DropdownMenu.Root>
<DropdownMenu.Trigger >
{#snippet child({ props }: { props: any })}
<Button {...props} variant="outline" class="ml-auto my-2">
Columns <ChevronDown class="ml-2 size-4" />
</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end">
{#each table.getAllColumns().filter((col) => col.getCanHide()) as column}
<DropdownMenu.CheckboxItem
class="capitalize"
controlledChecked
checked={column.getIsVisible()}
onCheckedChange={(value: any) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenu.CheckboxItem>
{/each}
</DropdownMenu.Content>
</DropdownMenu.Root>
<div class="rounded-md border">
<Table.Root>
<Table.Header>
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
<Table.Row>
{#each headerGroup.headers as header (header.id)}
<Table.Head class="[&:has([role=checkbox])]:pl-3">
{#if !header.isPlaceholder}
<FlexRender
content={header.column.columnDef.header}
context={header.getContext()}
/>
{/if}
</Table.Head>
{/each}
</Table.Row>
{/each}
</Table.Header>
<Table.Body>
{#each table.getRowModel().rows as row (row.id)}
<Table.Row data-state={row.getIsSelected() && 'selected'}>
{#each row.getVisibleCells() as cell (cell.id)}
<Table.Cell class="[&:has([role=checkbox])]:pl-3">
<FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} />
</Table.Cell>
{/each}
</Table.Row>
{:else}
<Table.Row>
<Table.Cell colspan={columns.length} class="h-24 text-center">No results.</Table.Cell>
</Table.Row>
{/each}
</Table.Body>
</Table.Root>
</div>
<div class="flex items-center justify-end space-x-2 pt-4">
<div class="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div class="space-x-2">
<Button
variant="outline"
size="sm"
onclick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onclick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</div>

View File

@ -0,0 +1,20 @@
<script lang="ts">
import { ThemeCustomizerButton, type ThemeConfig, updateTheme } from '@kksh/svelte5';
import { ui } from '@kksh/api/ui/iframe';
import { onMount } from 'svelte';
let config: ThemeConfig = {
radius: 0.5,
theme: 'zinc',
lightMode: 'auto'
};
onMount(() => {
ui.getTheme().then((theme) => {
config = theme;
});
});
$: updateTheme(config);
</script>
<ThemeCustomizerButton bind:config />

View File

@ -0,0 +1,70 @@
<script lang="ts">
import { Collapsible, SideBar as Sidebar } from '@kksh/svelte5';
import GalleryVerticalEnd from 'lucide-svelte/icons/gallery-vertical-end';
import Minus from 'lucide-svelte/icons/minus';
import Plus from 'lucide-svelte/icons/plus';
import type { ComponentProps } from 'svelte';
import type { ColumnInfo, TableInfo } from '../../types';
import { dbStore, selectedTable } from '$lib/stores/db';
let {
ref = $bindable(null),
tables,
tableColumnInfo,
...restProps
}: ComponentProps<typeof Sidebar.Root> & {
tables: TableInfo[];
tableColumnInfo: Record<string, ColumnInfo[]>;
} = $props();
</script>
<Sidebar.Root bind:ref {...restProps} class="">
<Sidebar.Content>
<Sidebar.Group>
<Sidebar.Menu>
{#each tables as table (table.name)}
<Collapsible.Root open={true} class="group/collapsible">
<Sidebar.MenuItem
onclick={() => {
console.log(`select ${table.name}`);
}}
>
<a href={`/table/${table.name}`}>
<Sidebar.MenuButton isActive={table.name === $selectedTable}>
{table.name}
</Sidebar.MenuButton>
</a>
<!-- <Collapsible.Trigger>
{#snippet child({ props })}
<Sidebar.MenuButton {...props}>
{table.name}{' '}
<Plus class="ml-auto group-data-[state=open]/collapsible:hidden" />
<Minus class="ml-auto group-data-[state=closed]/collapsible:hidden" />
</Sidebar.MenuButton>
{/snippet}
</Collapsible.Trigger> -->
<!-- {#if tableColumnInfo[table.name]?.length}
<Collapsible.Content>
<Sidebar.MenuSub>
{#each tableColumnInfo[table.name] as item (item.name)}
<Sidebar.MenuSubItem>
<Sidebar.MenuSubButton isActive={true}>
{#snippet child({ props })}
<a href={`/table/${item.name}`} {...props}>
{item.name}
</a>
{/snippet}
</Sidebar.MenuSubButton>
</Sidebar.MenuSubItem>
{/each}
</Sidebar.MenuSub>
</Collapsible.Content>
{/if} -->
</Sidebar.MenuItem>
</Collapsible.Root>
{/each}
</Sidebar.Menu>
</Sidebar.Group>
</Sidebar.Content>
<Sidebar.Rail />
</Sidebar.Root>

View File

@ -0,0 +1,5 @@
<script lang="ts">
let { type, value }: { type: string; value: string | number } = $props();
</script>
<p class="text-ellipsis overflow-hidden text-nowrap truncate max-w-64">{value}</p>

View File

@ -0,0 +1,28 @@
<script lang="ts">
import Ellipsis from 'lucide-svelte/icons/ellipsis';
import { Button, DropdownMenu } from '@kksh/svelte5';
let { id }: { id: string } = $props();
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props }: { props: any })}
<Button {...props} variant="ghost" size="icon" class="relative size-8 p-0">
<span class="sr-only">Open menu</span>
<Ellipsis class="size-4" />
</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Group>
<DropdownMenu.GroupHeading>Actions</DropdownMenu.GroupHeading>
<DropdownMenu.Item onclick={() => navigator.clipboard.writeText(id)}>
Copy payment ID
</DropdownMenu.Item>
</DropdownMenu.Group>
<DropdownMenu.Separator />
<DropdownMenu.Item>View customer</DropdownMenu.Item>
<DropdownMenu.Item>View payment details</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>

View File

@ -0,0 +1,12 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import { Checkbox } from '@kksh/svelte5';
let {
checked = false,
controlledChecked = true,
...restProps
}: ComponentProps<typeof Checkbox> = $props();
</script>
<Checkbox {checked} {controlledChecked} {...restProps} />

View File

@ -0,0 +1,12 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import ArrowUpDown from 'lucide-svelte/icons/arrow-up-down';
import { Button } from '@kksh/svelte5';
let { variant = 'ghost', ...restProps }: ComponentProps<typeof Button> = $props();
</script>
<Button {variant} {...restProps}>
Email
<ArrowUpDown class="ml-2 size-4" />
</Button>

View File

@ -0,0 +1,7 @@
<script lang="ts">
import { dbPath } from '@/stores/db';
import { Button, Input } from '@kksh/svelte5';
</script>
<Input bind:value={$dbPath} disabled />
<Button type="submit">Pick DB</Button>

View File

@ -0,0 +1,20 @@
<script lang="ts">
import { SideBar as Sidebar, Label } from '@kksh/svelte5';
import type { WithElementRef } from 'bits-ui';
import Search from 'lucide-svelte/icons/search';
import type { HTMLFormAttributes } from 'svelte/elements';
let { ref = $bindable(null), ...restProps }: WithElementRef<HTMLFormAttributes> = $props();
</script>
<form bind:this={ref} {...restProps}>
<Sidebar.Group class="py-0">
<Sidebar.GroupContent class="relative">
<Label for="search" class="sr-only">Search</Label>
<Sidebar.Input id="search" placeholder="Search the docs..." class="pl-8" />
<Search
class="pointer-events-none absolute left-2 top-1/2 size-4 -translate-y-1/2 select-none opacity-50"
/>
</Sidebar.GroupContent>
</Sidebar.Group>
</form>

View File

@ -0,0 +1,33 @@
<script lang="ts">
import AppSidebar from '$lib/components/app-sidebar.svelte';
import { SideBar as Sidebar, Separator, Breadcrumb } from '@kksh/svelte5';
</script>
<Sidebar.Provider>
<!-- <AppSidebar /> -->
<Sidebar.Inset>
<header class="flex h-16 shrink-0 items-center gap-2 border-b px-4" data-kunkun-drag-regio>
<Sidebar.Trigger class="-ml-1" />
<Separator orientation="vertical" class="mr-2 h-4" />
<Breadcrumb.Root>
<Breadcrumb.List>
<Breadcrumb.Item class="hidden md:block">
<Breadcrumb.Link href="#">Building Your Application</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Page>Data Fetching</Breadcrumb.Page>
</Breadcrumb.Item>
</Breadcrumb.List>
</Breadcrumb.Root>
</header>
<div class="flex flex-1 flex-col gap-4 p-4">
<div class="grid auto-rows-min gap-4 md:grid-cols-3">
<div class="aspect-video rounded-xl bg-muted/50"></div>
<div class="aspect-video rounded-xl bg-muted/50"></div>
<div class="aspect-video rounded-xl bg-muted/50"></div>
</div>
<div class="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min"></div>
</div>
</Sidebar.Inset>
</Sidebar.Provider>

View File

@ -0,0 +1,27 @@
import { untrack } from "svelte";
const MOBILE_BREAKPOINT = 768;
export class IsMobile {
#current = $state<boolean>(false);
constructor() {
$effect(() => {
return untrack(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
const onChange = () => {
this.#current = window.innerWidth < MOBILE_BREAKPOINT;
};
mql.addEventListener("change", onChange);
onChange();
return () => {
mql.removeEventListener("change", onChange);
};
});
});
}
get current() {
return this.#current;
}
}

1
src/lib/index.ts Normal file
View File

@ -0,0 +1 @@
// place files you want to import through the `$lib` alias in this folder.

50
src/lib/stores/api.ts Normal file
View File

@ -0,0 +1,50 @@
import { get, writable } from 'svelte/store';
import type { SQLiteBrowser } from '../../../deno-src/dao';
import { type Child, type RPCChannel, type DenoCommand, shell } from '@kksh/api/ui/iframe';
type PromisifiedSQLiteBrowser = {
[K in keyof SQLiteBrowser]: SQLiteBrowser[K] extends (...args: infer A) => infer R
? (...args: A) => Promise<R>
: SQLiteBrowser[K];
};
interface State {
rpcChannel?: RPCChannel<{}, PromisifiedSQLiteBrowser>;
process?: Child;
command?: DenoCommand<string>;
}
export function createApiStore() {
const apiStore = writable<State>({});
async function init() {
const { rpcChannel, process, command } = await shell.createDenoRpcChannel<
{},
PromisifiedSQLiteBrowser
>(
'$EXTENSION/deno-src/index.ts',
[],
{
allowEnv: ['DENO_SQLITE_PATH', 'DENO_SQLITE_LOCAL', 'DENO_DIR', 'HOME'],
allowAllRead: true,
allowAllFfi: true
},
{}
);
apiStore.set({
rpcChannel,
process,
command
});
}
async function destroy() {
const _apiStore = get(apiStore);
_apiStore.rpcChannel?.freeCallbacks();
_apiStore.process?.kill();
}
return {
...apiStore,
init,
destroy
};
}
export const apiStore = createApiStore();

24
src/lib/stores/db.ts Normal file
View File

@ -0,0 +1,24 @@
import { writable } from 'svelte/store';
import type { ColumnInfo, TableInfo } from '../../types';
export const dbPath = writable<string | null>(null);
export const selectedTable = writable<string | null>(null);
interface DB {
tables: TableInfo[];
columnInfo: Record<string, ColumnInfo[]>;
}
export function createDbStore() {
const db = writable<DB>({ tables: [], columnInfo: {} });
return {
...db,
setTables: (tables: TableInfo[]) => {
db.update((state) => ({ ...state, tables }));
},
setColumnInfo: (columnInfo: Record<string, ColumnInfo[]>) => {
db.update((state) => ({ ...state, columnInfo }));
}
};
}
export const dbStore = createDbStore();

56
src/lib/utils.ts Normal file
View File

@ -0,0 +1,56 @@
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { cubicOut } from 'svelte/easing';
import type { TransitionConfig } from 'svelte/transition';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
type FlyAndScaleParams = {
y?: number;
x?: number;
start?: number;
duration?: number;
};
export const flyAndScale = (
node: Element,
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
): TransitionConfig => {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
const scaleConversion = (valueA: number, scaleA: [number, number], scaleB: [number, number]) => {
const [minA, maxA] = scaleA;
const [minB, maxB] = scaleB;
const percentage = (valueA - minA) / (maxA - minA);
const valueB = percentage * (maxB - minB) + minB;
return valueB;
};
const styleToString = (style: Record<string, number | string | undefined>): string => {
return Object.keys(style).reduce((str, key) => {
if (style[key] === undefined) return str;
return str + `${key}:${style[key]};`;
}, '');
};
return {
duration: params.duration ?? 200,
delay: 0,
css: (t) => {
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
return styleToString({
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
opacity: t
});
},
easing: cubicOut
};
};

131
src/routes/+layout.svelte Normal file
View File

@ -0,0 +1,131 @@
<script lang="ts">
import '../app.css';
import { dbPath, dbStore } from '@/stores/db';
import { ModeWatcher } from 'mode-watcher';
import {
ThemeWrapper,
updateTheme,
SideBar as Sidebar,
Separator,
Input,
Button
} from '@kksh/svelte5';
import {
dialog,
clipboard,
notification,
ui,
toast,
shell,
fs,
path,
RPCChannel,
Child,
event
} from '@kksh/api/ui/iframe';
import { X } from 'lucide-svelte';
import { onMount } from 'svelte';
import { type SQLiteBrowser } from '../../deno-src/dao';
import AppSidebar from '@/components/app-sidebar.svelte';
import type { DenoCommand } from '@kksh/api/ui/worker';
import type { ColumnInfo } from '../types';
import { apiStore } from '@/stores/api';
import { goto } from '$app/navigation';
const { children } = $props();
type PromisifiedSQLiteBrowser = {
[K in keyof SQLiteBrowser]: SQLiteBrowser[K] extends (...args: infer A) => infer R
? (...args: A) => Promise<R>
: SQLiteBrowser[K];
};
// const dbPathForDev =
// '/Users/hk/Dev/KunkunExtensions/extensions/sqlite-browser/deno-src/kk.dev.db';
onMount(async () => {
ui.registerDragRegion();
ui.showBackButton('bottom-right');
ui.hideRefreshButton();
ui.getTheme().then((theme) => {
updateTheme(theme);
});
apiStore.init();
event.onDragDrop((evt) => {
if (evt.paths && evt.paths.length > 0) {
dbPath.set(evt.paths[0]);
}
});
});
$effect(() => {
$dbPath;
(async () => {
console.log('dbPath changed', $dbPath);
if (!$dbPath || !$apiStore.rpcChannel) {
return;
}
const api = $apiStore.rpcChannel.getAPI();
await api.init($dbPath);
api.getTables().then((tables) => {
dbStore.setTables(tables);
let columnInfo: Record<string, ColumnInfo[]> = {};
// get column info for each table and merge to columnInfo
tables.forEach((table) => {
api.getTableColumns(table.name).then((columns) => {
columnInfo[table.name] = columns;
});
});
dbStore.setColumnInfo(columnInfo);
// console.log(tables);
});
})();
});
</script>
<svelte:window
on:keydown={async (e) => {
if (e.key === 'Escape' && document.activeElement === document.body) {
await apiStore.destroy();
ui.goBack();
}
}}
/>
<ModeWatcher />
<ThemeWrapper>
<Sidebar.Provider>
<AppSidebar tables={$dbStore.tables} tableColumnInfo={$dbStore.columnInfo} />
<div class="w-full overflow-hidden">
<header class="flex h-16 shrink-0 items-center gap-2 border-b px-4" data-kunkun-drag-region>
<Sidebar.Trigger class="-ml-1" />
<Separator orientation="vertical" class="mr-2 h-4" />
<Input bind:value={$dbPath} disabled />
<Button
variant="outline"
class="shrink-0"
size="icon"
onclick={() => {
goto('/');
dbPath.set(null);
}}><X /></Button
>
<Button
onclick={(e) => {
dialog
.open({
title: 'Pick DB'
})
.then(async (path?: string) => {
if (path && (await fs.exists(path))) {
dbPath.set(path);
}
});
}}
>
Pick DB
</Button>
</header>
<!-- <pre>{$page.url.pathname}</pre> -->
{@render children()}
</div>
</Sidebar.Provider>
</ThemeWrapper>

2
src/routes/+layout.ts Normal file
View File

@ -0,0 +1,2 @@
export const prerender = true;
export const ssr = false;

7
src/routes/+page.svelte Normal file
View File

@ -0,0 +1,7 @@
<script>
import { dbStore } from '@/stores/db';
</script>
<div class="flex items-center justify-center h-full">
<p>Drag and Drop Your Database File</p>
</div>

View File

@ -0,0 +1,37 @@
<script lang="ts">
import { goto } from '$app/navigation';
import TableDataTable from '@/components/TableDataTable.svelte';
import { apiStore } from '@/stores/api.js';
import { onMount } from 'svelte';
import type { ColumnInfo, QueryResult } from '../../../types.js';
const { data } = $props();
let tableData = $state<{
data: QueryResult[];
total: number;
totalPages: number;
}>();
let columns = $state<ColumnInfo[]>();
$effect(() => {
columns = [];
tableData = undefined;
console.log('page ', data.name);
(async () => {
if (!$apiStore.rpcChannel) {
goto('/');
}
const api = $apiStore.rpcChannel?.getAPI();
columns = (await api?.getTableColumns(data.name)) ?? [];
tableData = await api?.getTableData(data.name, { page: 0, pageSize: 10 });
console.log('columns', columns);
console.log('tableData', tableData);
})();
});
</script>
<div class="container">
{#if columns != undefined && tableData != undefined}
<TableDataTable columnsData={columns} {tableData} />
{/if}
</div>

View File

@ -0,0 +1,7 @@
import type { PageLoad } from './$types';
export const load: PageLoad = ({ params }) => {
return { name: params.name };
};
export let csr = true;
export let prerender = false;

27
src/types.ts Normal file
View File

@ -0,0 +1,27 @@
import * as v from 'valibot';
export const QueryResultSchema = v.record(v.string(), v.unknown());
export const PaginationParamsSchema = v.object({
page: v.number(),
pageSize: v.number()
});
export const TableInfoSchema = v.object({
name: v.string(),
type: v.string()
});
export const ColumnInfoSchema = v.object({
cid: v.number(),
name: v.string(),
type: v.string(),
notnull: v.number(),
dflt_value: v.unknown(),
pk: v.number()
});
export type QueryResult = v.InferOutput<typeof QueryResultSchema>;
export type PaginationParams = v.InferOutput<typeof PaginationParamsSchema>;
export type TableInfo = v.InferOutput<typeof TableInfoSchema>;
export type ColumnInfo = v.InferOutput<typeof ColumnInfoSchema>;

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

24
svelte.config.js Normal file
View File

@ -0,0 +1,24 @@
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter({
fallback: '400.html'
// fallback: "index.html"
}),
alias: {
'@/*': './src/lib/*'
}
}
};
export default config;

96
tailwind.config.ts Normal file
View File

@ -0,0 +1,96 @@
import { fontFamily } from 'tailwindcss/defaultTheme';
import type { Config } from 'tailwindcss';
const config: Config = {
darkMode: ['class'],
content: [
'./src/**/*.{html,js,svelte,ts}',
'node_modules/@kksh/svelte5/dist/**/*.{html,js,svelte,ts}'
],
safelist: ['dark'],
theme: {
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px'
}
},
extend: {
colors: {
border: 'hsl(var(--border) / <alpha-value>)',
input: 'hsl(var(--input) / <alpha-value>)',
ring: 'hsl(var(--ring) / <alpha-value>)',
background: 'hsl(var(--background) / <alpha-value>)',
foreground: 'hsl(var(--foreground) / <alpha-value>)',
primary: {
DEFAULT: 'hsl(var(--primary) / <alpha-value>)',
foreground: 'hsl(var(--primary-foreground) / <alpha-value>)'
},
secondary: {
DEFAULT: 'hsl(var(--secondary) / <alpha-value>)',
foreground: 'hsl(var(--secondary-foreground) / <alpha-value>)'
},
destructive: {
DEFAULT: 'hsl(var(--destructive) / <alpha-value>)',
foreground: 'hsl(var(--destructive-foreground) / <alpha-value>)'
},
muted: {
DEFAULT: 'hsl(var(--muted) / <alpha-value>)',
foreground: 'hsl(var(--muted-foreground) / <alpha-value>)'
},
accent: {
DEFAULT: 'hsl(var(--accent) / <alpha-value>)',
foreground: 'hsl(var(--accent-foreground) / <alpha-value>)'
},
popover: {
DEFAULT: 'hsl(var(--popover) / <alpha-value>)',
foreground: 'hsl(var(--popover-foreground) / <alpha-value>)'
},
card: {
DEFAULT: 'hsl(var(--card) / <alpha-value>)',
foreground: 'hsl(var(--card-foreground) / <alpha-value>)'
},
sidebar: {
DEFAULT: 'hsl(var(--sidebar-background))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))'
}
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
},
fontFamily: {
sans: [...fontFamily.sans]
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--bits-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--bits-accordion-content-height)" },
to: { height: "0" },
},
"caret-blink": {
"0%,70%,100%": { opacity: "1" },
"20%,50%": { opacity: "0" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"caret-blink": "caret-blink 1.25s ease-out infinite",
},
}
}
};
export default config;

19
tsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

6
vite.config.ts Normal file
View File

@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});