@korajs/cli 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +385 -288
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +25 -11
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-KTSRAPSE.js → chunk-CMSX76KM.js} +108 -65
- package/dist/chunk-CMSX76KM.js.map +1 -0
- package/dist/{chunk-ZGYRDYXS.js → chunk-MVP5PDT4.js} +22 -4
- package/dist/chunk-MVP5PDT4.js.map +1 -0
- package/dist/{chunk-E7OCVRYL.js → chunk-YGVO4POI.js} +242 -215
- package/dist/chunk-YGVO4POI.js.map +1 -0
- package/dist/create.cjs +122 -66
- package/dist/create.cjs.map +1 -1
- package/dist/create.js +2 -2
- package/dist/index.cjs +348 -278
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -26
- package/dist/index.d.ts +31 -26
- package/dist/index.js +2 -2
- package/package.json +2 -2
- package/templates/tauri-react/.env.example +7 -0
- package/templates/tauri-react/.github/workflows/release-desktop.yml +107 -0
- package/templates/tauri-react/README.md.hbs +148 -0
- package/templates/tauri-react/dev.ts +37 -0
- package/templates/tauri-react/index.html.hbs +16 -0
- package/templates/tauri-react/kora.config.ts +17 -0
- package/templates/tauri-react/package.json.hbs +44 -0
- package/templates/tauri-react/server.ts +23 -0
- package/templates/tauri-react/src/App.tsx +578 -0
- package/templates/tauri-react/src/AppShell.tsx +116 -0
- package/templates/tauri-react/src/SetupScreen.tsx +239 -0
- package/templates/tauri-react/src/main.tsx +9 -0
- package/templates/tauri-react/src/schema.ts +15 -0
- package/templates/tauri-react/src/sync-config.ts +103 -0
- package/templates/tauri-react/src/updater.ts +38 -0
- package/templates/tauri-react/src-tauri/Cargo.toml +19 -0
- package/templates/tauri-react/src-tauri/build.rs +3 -0
- package/templates/tauri-react/src-tauri/capabilities/default.json +12 -0
- package/templates/tauri-react/src-tauri/icons/128x128.png +0 -0
- package/templates/tauri-react/src-tauri/icons/128x128@2x.png +0 -0
- package/templates/tauri-react/src-tauri/icons/32x32.png +0 -0
- package/templates/tauri-react/src-tauri/icons/icon.icns +0 -0
- package/templates/tauri-react/src-tauri/icons/icon.ico +0 -0
- package/templates/tauri-react/src-tauri/src/lib.rs +8 -0
- package/templates/tauri-react/src-tauri/src/main.rs +6 -0
- package/templates/tauri-react/src-tauri/tauri.conf.json +44 -0
- package/templates/tauri-react/tsconfig.json +15 -0
- package/templates/tauri-react/vite.config.ts +15 -0
- package/dist/chunk-E7OCVRYL.js.map +0 -1
- package/dist/chunk-KTSRAPSE.js.map +0 -1
- package/dist/chunk-ZGYRDYXS.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -5,7 +5,7 @@ import * as citty from 'citty';
|
|
|
5
5
|
declare const PACKAGE_MANAGERS: readonly ["pnpm", "npm", "yarn", "bun"];
|
|
6
6
|
type PackageManager = (typeof PACKAGE_MANAGERS)[number];
|
|
7
7
|
/** Available project templates */
|
|
8
|
-
declare const TEMPLATES: readonly ["react-tailwind-sync", "react-tailwind", "react-sync", "react-basic"];
|
|
8
|
+
declare const TEMPLATES: readonly ["react-tailwind-sync", "react-tailwind", "react-sync", "react-basic", "tauri-react"];
|
|
9
9
|
type TemplateName = (typeof TEMPLATES)[number];
|
|
10
10
|
/** Metadata for a project template */
|
|
11
11
|
interface TemplateInfo {
|
|
@@ -548,11 +548,13 @@ interface ProjectNameValidationResult {
|
|
|
548
548
|
*/
|
|
549
549
|
declare function validateProjectName(name: string): ProjectNameValidationResult;
|
|
550
550
|
|
|
551
|
+
type PlatformOption = 'web' | 'desktop-tauri';
|
|
551
552
|
type FrameworkOption = 'react' | 'vue' | 'svelte' | 'solid';
|
|
552
553
|
type AuthOption = 'none' | 'email-password' | 'oauth';
|
|
553
554
|
type DatabaseOption = 'none' | 'sqlite' | 'postgres';
|
|
554
555
|
type DatabaseProviderOption = 'none' | 'local' | 'supabase' | 'neon' | 'railway' | 'vercel-postgres' | 'custom';
|
|
555
556
|
interface TemplateSelectionInput {
|
|
557
|
+
platform: PlatformOption;
|
|
556
558
|
tailwind: boolean;
|
|
557
559
|
sync: boolean;
|
|
558
560
|
db: DatabaseOption;
|
|
@@ -567,6 +569,32 @@ declare function isAuthValue(value: string): value is AuthOption;
|
|
|
567
569
|
declare function isDatabaseValue(value: string): value is DatabaseOption;
|
|
568
570
|
declare function isDatabaseProviderValue(value: string): value is DatabaseProviderOption;
|
|
569
571
|
|
|
572
|
+
interface CreatePreferences {
|
|
573
|
+
platform: PlatformOption;
|
|
574
|
+
framework: FrameworkOption;
|
|
575
|
+
tailwind: boolean;
|
|
576
|
+
sync: boolean;
|
|
577
|
+
db: DatabaseOption;
|
|
578
|
+
dbProvider: DatabaseProviderOption;
|
|
579
|
+
auth: AuthOption;
|
|
580
|
+
packageManager: PackageManager;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Preference store for scaffold-time defaults in `create-kora-app`.
|
|
584
|
+
*/
|
|
585
|
+
declare class PreferenceStore {
|
|
586
|
+
private readonly store;
|
|
587
|
+
constructor();
|
|
588
|
+
getCreatePreferences(): CreatePreferences | null;
|
|
589
|
+
saveCreatePreferences(preferences: CreatePreferences): void;
|
|
590
|
+
clearCreatePreferences(): void;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Gets preferences from storage or returns defaults when not available.
|
|
594
|
+
*/
|
|
595
|
+
declare function getCreatePreferencesOrDefault(store: PreferenceStore): CreatePreferences;
|
|
596
|
+
declare function getDefaultCreatePreferences(): CreatePreferences;
|
|
597
|
+
|
|
570
598
|
interface SelectOption<T extends string> {
|
|
571
599
|
label: string;
|
|
572
600
|
value: T;
|
|
@@ -613,32 +641,8 @@ declare class ClackPromptClient implements PromptClient {
|
|
|
613
641
|
outro(message: string): void;
|
|
614
642
|
}
|
|
615
643
|
|
|
616
|
-
interface CreatePreferences {
|
|
617
|
-
framework: FrameworkOption;
|
|
618
|
-
tailwind: boolean;
|
|
619
|
-
sync: boolean;
|
|
620
|
-
db: DatabaseOption;
|
|
621
|
-
dbProvider: DatabaseProviderOption;
|
|
622
|
-
auth: AuthOption;
|
|
623
|
-
packageManager: PackageManager;
|
|
624
|
-
}
|
|
625
|
-
/**
|
|
626
|
-
* Preference store for scaffold-time defaults in `create-kora-app`.
|
|
627
|
-
*/
|
|
628
|
-
declare class PreferenceStore {
|
|
629
|
-
private readonly store;
|
|
630
|
-
constructor();
|
|
631
|
-
getCreatePreferences(): CreatePreferences | null;
|
|
632
|
-
saveCreatePreferences(preferences: CreatePreferences): void;
|
|
633
|
-
clearCreatePreferences(): void;
|
|
634
|
-
}
|
|
635
|
-
/**
|
|
636
|
-
* Gets preferences from storage or returns defaults when not available.
|
|
637
|
-
*/
|
|
638
|
-
declare function getCreatePreferencesOrDefault(store: PreferenceStore): CreatePreferences;
|
|
639
|
-
declare function getDefaultCreatePreferences(): CreatePreferences;
|
|
640
|
-
|
|
641
644
|
interface CreateFlags {
|
|
645
|
+
platform?: string;
|
|
642
646
|
framework?: string;
|
|
643
647
|
auth?: string;
|
|
644
648
|
db?: string;
|
|
@@ -648,6 +652,7 @@ interface CreateFlags {
|
|
|
648
652
|
useDefaults: boolean;
|
|
649
653
|
}
|
|
650
654
|
interface PreferenceResolutionResult {
|
|
655
|
+
platform: PlatformOption;
|
|
651
656
|
framework: FrameworkOption;
|
|
652
657
|
auth: AuthOption;
|
|
653
658
|
db: DatabaseOption;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import * as citty from 'citty';
|
|
|
5
5
|
declare const PACKAGE_MANAGERS: readonly ["pnpm", "npm", "yarn", "bun"];
|
|
6
6
|
type PackageManager = (typeof PACKAGE_MANAGERS)[number];
|
|
7
7
|
/** Available project templates */
|
|
8
|
-
declare const TEMPLATES: readonly ["react-tailwind-sync", "react-tailwind", "react-sync", "react-basic"];
|
|
8
|
+
declare const TEMPLATES: readonly ["react-tailwind-sync", "react-tailwind", "react-sync", "react-basic", "tauri-react"];
|
|
9
9
|
type TemplateName = (typeof TEMPLATES)[number];
|
|
10
10
|
/** Metadata for a project template */
|
|
11
11
|
interface TemplateInfo {
|
|
@@ -548,11 +548,13 @@ interface ProjectNameValidationResult {
|
|
|
548
548
|
*/
|
|
549
549
|
declare function validateProjectName(name: string): ProjectNameValidationResult;
|
|
550
550
|
|
|
551
|
+
type PlatformOption = 'web' | 'desktop-tauri';
|
|
551
552
|
type FrameworkOption = 'react' | 'vue' | 'svelte' | 'solid';
|
|
552
553
|
type AuthOption = 'none' | 'email-password' | 'oauth';
|
|
553
554
|
type DatabaseOption = 'none' | 'sqlite' | 'postgres';
|
|
554
555
|
type DatabaseProviderOption = 'none' | 'local' | 'supabase' | 'neon' | 'railway' | 'vercel-postgres' | 'custom';
|
|
555
556
|
interface TemplateSelectionInput {
|
|
557
|
+
platform: PlatformOption;
|
|
556
558
|
tailwind: boolean;
|
|
557
559
|
sync: boolean;
|
|
558
560
|
db: DatabaseOption;
|
|
@@ -567,6 +569,32 @@ declare function isAuthValue(value: string): value is AuthOption;
|
|
|
567
569
|
declare function isDatabaseValue(value: string): value is DatabaseOption;
|
|
568
570
|
declare function isDatabaseProviderValue(value: string): value is DatabaseProviderOption;
|
|
569
571
|
|
|
572
|
+
interface CreatePreferences {
|
|
573
|
+
platform: PlatformOption;
|
|
574
|
+
framework: FrameworkOption;
|
|
575
|
+
tailwind: boolean;
|
|
576
|
+
sync: boolean;
|
|
577
|
+
db: DatabaseOption;
|
|
578
|
+
dbProvider: DatabaseProviderOption;
|
|
579
|
+
auth: AuthOption;
|
|
580
|
+
packageManager: PackageManager;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Preference store for scaffold-time defaults in `create-kora-app`.
|
|
584
|
+
*/
|
|
585
|
+
declare class PreferenceStore {
|
|
586
|
+
private readonly store;
|
|
587
|
+
constructor();
|
|
588
|
+
getCreatePreferences(): CreatePreferences | null;
|
|
589
|
+
saveCreatePreferences(preferences: CreatePreferences): void;
|
|
590
|
+
clearCreatePreferences(): void;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Gets preferences from storage or returns defaults when not available.
|
|
594
|
+
*/
|
|
595
|
+
declare function getCreatePreferencesOrDefault(store: PreferenceStore): CreatePreferences;
|
|
596
|
+
declare function getDefaultCreatePreferences(): CreatePreferences;
|
|
597
|
+
|
|
570
598
|
interface SelectOption<T extends string> {
|
|
571
599
|
label: string;
|
|
572
600
|
value: T;
|
|
@@ -613,32 +641,8 @@ declare class ClackPromptClient implements PromptClient {
|
|
|
613
641
|
outro(message: string): void;
|
|
614
642
|
}
|
|
615
643
|
|
|
616
|
-
interface CreatePreferences {
|
|
617
|
-
framework: FrameworkOption;
|
|
618
|
-
tailwind: boolean;
|
|
619
|
-
sync: boolean;
|
|
620
|
-
db: DatabaseOption;
|
|
621
|
-
dbProvider: DatabaseProviderOption;
|
|
622
|
-
auth: AuthOption;
|
|
623
|
-
packageManager: PackageManager;
|
|
624
|
-
}
|
|
625
|
-
/**
|
|
626
|
-
* Preference store for scaffold-time defaults in `create-kora-app`.
|
|
627
|
-
*/
|
|
628
|
-
declare class PreferenceStore {
|
|
629
|
-
private readonly store;
|
|
630
|
-
constructor();
|
|
631
|
-
getCreatePreferences(): CreatePreferences | null;
|
|
632
|
-
saveCreatePreferences(preferences: CreatePreferences): void;
|
|
633
|
-
clearCreatePreferences(): void;
|
|
634
|
-
}
|
|
635
|
-
/**
|
|
636
|
-
* Gets preferences from storage or returns defaults when not available.
|
|
637
|
-
*/
|
|
638
|
-
declare function getCreatePreferencesOrDefault(store: PreferenceStore): CreatePreferences;
|
|
639
|
-
declare function getDefaultCreatePreferences(): CreatePreferences;
|
|
640
|
-
|
|
641
644
|
interface CreateFlags {
|
|
645
|
+
platform?: string;
|
|
642
646
|
framework?: string;
|
|
643
647
|
auth?: string;
|
|
644
648
|
db?: string;
|
|
@@ -648,6 +652,7 @@ interface CreateFlags {
|
|
|
648
652
|
useDefaults: boolean;
|
|
649
653
|
}
|
|
650
654
|
interface PreferenceResolutionResult {
|
|
655
|
+
platform: PlatformOption;
|
|
651
656
|
framework: FrameworkOption;
|
|
652
657
|
auth: AuthOption;
|
|
653
658
|
db: DatabaseOption;
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
writeDockerfileArtifact,
|
|
28
28
|
writeFlyTomlArtifact,
|
|
29
29
|
writeRailwayJsonArtifact
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-YGVO4POI.js";
|
|
31
31
|
import {
|
|
32
32
|
ClackPromptClient,
|
|
33
33
|
CliError,
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
saveResolvedPreferences,
|
|
55
55
|
shouldSavePreferences,
|
|
56
56
|
validateProjectName
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-CMSX76KM.js";
|
|
58
58
|
export {
|
|
59
59
|
ClackPromptClient,
|
|
60
60
|
CliError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@korajs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Kora.js CLI tooling and project scaffolding",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"conf": "^15.1.0",
|
|
37
37
|
"esbuild": "^0.28.0",
|
|
38
38
|
"validate-npm-package-name": "^7.0.2",
|
|
39
|
-
"@korajs/core": "0.
|
|
39
|
+
"@korajs/core": "0.4.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^25.5.2",
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Sync server URL (used by the Tauri app to connect to the sync server)
|
|
2
|
+
# In development, defaults to ws://localhost:3001/kora-sync
|
|
3
|
+
# For production builds, set this to your deployed server URL:
|
|
4
|
+
# VITE_SYNC_URL=wss://your-server.example.com/kora-sync
|
|
5
|
+
|
|
6
|
+
# Sync server port
|
|
7
|
+
PORT=3001
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Build and release desktop binaries for macOS, Windows, and Linux.
|
|
2
|
+
#
|
|
3
|
+
# Trigger: Push a version tag (e.g., v1.0.0) or run manually.
|
|
4
|
+
#
|
|
5
|
+
# Prerequisites:
|
|
6
|
+
# 1. Generate Tauri signing keys:
|
|
7
|
+
# pnpm tauri signer generate -w ~/.tauri/myapp.key
|
|
8
|
+
# 2. Add secrets to your GitHub repo:
|
|
9
|
+
# - TAURI_SIGNING_PRIVATE_KEY: Contents of ~/.tauri/myapp.key
|
|
10
|
+
# - TAURI_SIGNING_PRIVATE_KEY_PASSWORD: Password used during generation
|
|
11
|
+
# 3. (Optional) For macOS notarization:
|
|
12
|
+
# - APPLE_CERTIFICATE: Base64-encoded .p12 certificate
|
|
13
|
+
# - APPLE_CERTIFICATE_PASSWORD: Certificate password
|
|
14
|
+
# - APPLE_SIGNING_IDENTITY: e.g., "Developer ID Application: Your Name (TEAM_ID)"
|
|
15
|
+
# - APPLE_ID: Apple ID email
|
|
16
|
+
# - APPLE_PASSWORD: App-specific password
|
|
17
|
+
# - APPLE_TEAM_ID: Your Apple Developer Team ID
|
|
18
|
+
#
|
|
19
|
+
# After the workflow runs, a GitHub Release is created with installers
|
|
20
|
+
# for all platforms plus an update manifest (latest.json) for auto-updates.
|
|
21
|
+
|
|
22
|
+
name: Release Desktop
|
|
23
|
+
|
|
24
|
+
on:
|
|
25
|
+
push:
|
|
26
|
+
tags:
|
|
27
|
+
- 'v*'
|
|
28
|
+
workflow_dispatch:
|
|
29
|
+
inputs:
|
|
30
|
+
tag:
|
|
31
|
+
description: 'Version tag (e.g., v1.0.0)'
|
|
32
|
+
required: true
|
|
33
|
+
|
|
34
|
+
permissions:
|
|
35
|
+
contents: write
|
|
36
|
+
|
|
37
|
+
jobs:
|
|
38
|
+
build:
|
|
39
|
+
strategy:
|
|
40
|
+
fail-fast: false
|
|
41
|
+
matrix:
|
|
42
|
+
include:
|
|
43
|
+
- platform: macos-latest
|
|
44
|
+
args: '--target aarch64-apple-darwin'
|
|
45
|
+
rust_target: aarch64-apple-darwin
|
|
46
|
+
- platform: macos-latest
|
|
47
|
+
args: '--target x86_64-apple-darwin'
|
|
48
|
+
rust_target: x86_64-apple-darwin
|
|
49
|
+
- platform: ubuntu-22.04
|
|
50
|
+
args: ''
|
|
51
|
+
rust_target: ''
|
|
52
|
+
- platform: windows-latest
|
|
53
|
+
args: ''
|
|
54
|
+
rust_target: ''
|
|
55
|
+
|
|
56
|
+
runs-on: ${{ matrix.platform }}
|
|
57
|
+
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v4
|
|
60
|
+
|
|
61
|
+
- name: Install system dependencies (Linux)
|
|
62
|
+
if: matrix.platform == 'ubuntu-22.04'
|
|
63
|
+
run: |
|
|
64
|
+
sudo apt-get update
|
|
65
|
+
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
|
66
|
+
|
|
67
|
+
- uses: actions/setup-node@v4
|
|
68
|
+
with:
|
|
69
|
+
node-version: 20
|
|
70
|
+
|
|
71
|
+
- uses: pnpm/action-setup@v4
|
|
72
|
+
with:
|
|
73
|
+
version: 9
|
|
74
|
+
|
|
75
|
+
- name: Install Rust stable
|
|
76
|
+
uses: dtolnay/rust-toolchain@stable
|
|
77
|
+
with:
|
|
78
|
+
targets: ${{ matrix.rust_target }}
|
|
79
|
+
|
|
80
|
+
- name: Rust cache
|
|
81
|
+
uses: swatinem/rust-cache@v2
|
|
82
|
+
with:
|
|
83
|
+
workspaces: './src-tauri -> target'
|
|
84
|
+
|
|
85
|
+
- name: Install dependencies
|
|
86
|
+
run: pnpm install
|
|
87
|
+
|
|
88
|
+
- name: Build and release
|
|
89
|
+
uses: tauri-apps/tauri-action@v0
|
|
90
|
+
env:
|
|
91
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
92
|
+
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
93
|
+
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
94
|
+
# macOS code signing (optional)
|
|
95
|
+
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
|
96
|
+
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
|
97
|
+
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
|
98
|
+
APPLE_ID: ${{ secrets.APPLE_ID }}
|
|
99
|
+
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
|
100
|
+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
101
|
+
with:
|
|
102
|
+
tagName: ${{ github.ref_name || github.event.inputs.tag }}
|
|
103
|
+
releaseName: '${{ github.ref_name || github.event.inputs.tag }}'
|
|
104
|
+
releaseBody: 'See the assets below to download and install.'
|
|
105
|
+
releaseDraft: true
|
|
106
|
+
prerelease: false
|
|
107
|
+
args: ${{ matrix.args }}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
A native desktop application built with [Kora.js](https://ehoneahobed.github.io/kora/) and [Tauri](https://tauri.app). Uses native SQLite for fast local storage and syncs data across devices via a lightweight sync server.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This starts both the sync server and the Tauri desktop app. The first launch compiles the Rust backend (2-5 minutes). Subsequent launches are fast.
|
|
12
|
+
|
|
13
|
+
On first launch, you'll see a setup screen to enter your sync server URL. For local development, use `ws://localhost:3001/kora-sync` (the local sync server started by `pnpm dev`). You can also skip setup to use the app in local-only mode.
|
|
14
|
+
|
|
15
|
+
## Project Structure
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
src/
|
|
19
|
+
schema.ts # Data schema definition
|
|
20
|
+
main.tsx # Entry point
|
|
21
|
+
AppShell.tsx # Setup flow + Kora initialization
|
|
22
|
+
App.tsx # Main application UI
|
|
23
|
+
SetupScreen.tsx # First-launch server configuration
|
|
24
|
+
sync-config.ts # Runtime sync URL storage
|
|
25
|
+
updater.ts # Auto-update checker
|
|
26
|
+
src-tauri/
|
|
27
|
+
Cargo.toml # Rust dependencies (Kora SQLite + updater plugins)
|
|
28
|
+
tauri.conf.json # Window, bundle, and updater config
|
|
29
|
+
capabilities/ # Tauri security permissions
|
|
30
|
+
src/
|
|
31
|
+
main.rs # Rust entry point
|
|
32
|
+
lib.rs # Plugin registration
|
|
33
|
+
server.ts # Kora sync server
|
|
34
|
+
dev.ts # Dev orchestrator (starts sync + Tauri together)
|
|
35
|
+
kora.config.ts # Kora configuration
|
|
36
|
+
.github/workflows/
|
|
37
|
+
release-desktop.yml # CI/CD for cross-platform builds
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Scripts
|
|
41
|
+
|
|
42
|
+
| Script | Description |
|
|
43
|
+
|--------|-------------|
|
|
44
|
+
| `pnpm dev` | Start sync server + Tauri app together |
|
|
45
|
+
| `pnpm dev:app` | Start Tauri app only (no sync server) |
|
|
46
|
+
| `pnpm dev:server` | Start sync server only |
|
|
47
|
+
| `pnpm build` | Build desktop app for distribution |
|
|
48
|
+
| `pnpm deploy:server` | Deploy sync server to the cloud |
|
|
49
|
+
|
|
50
|
+
## Distributing to Organizations
|
|
51
|
+
|
|
52
|
+
This app is designed to be distributed to multiple organizations. Each organization gets the **same binary** and connects to their own sync server.
|
|
53
|
+
|
|
54
|
+
### 1. Deploy a sync server for each organization
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pnpm deploy:server
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or deploy manually to any Node.js host. Each org gets their own server (or share one with multi-tenant scoping).
|
|
61
|
+
|
|
62
|
+
### 2. Build the desktop binary
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pnpm build
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
No need to bake in a sync URL — users configure it on first launch.
|
|
69
|
+
|
|
70
|
+
If you want to pre-configure the URL (e.g., for a specific org):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
VITE_SYNC_URL=wss://acme-corp.example.com/kora-sync pnpm build
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. Set up auto-updates
|
|
77
|
+
|
|
78
|
+
1. Generate signing keys:
|
|
79
|
+
```bash
|
|
80
|
+
pnpm tauri signer generate -w ~/.tauri/myapp.key
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
2. Add the public key to `src-tauri/tauri.conf.json` under `plugins.updater.pubkey`.
|
|
84
|
+
|
|
85
|
+
3. Set the update endpoint:
|
|
86
|
+
```json
|
|
87
|
+
"endpoints": ["https://github.com/YOUR_ORG/YOUR_REPO/releases/latest/download/latest.json"]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
4. Add `TAURI_SIGNING_PRIVATE_KEY` and `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` to your GitHub repo secrets.
|
|
91
|
+
|
|
92
|
+
5. Push a version tag to trigger a release:
|
|
93
|
+
```bash
|
|
94
|
+
git tag v1.0.0 && git push origin v1.0.0
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The GitHub Actions workflow builds for macOS, Windows, and Linux, creating a draft release with all installers.
|
|
98
|
+
|
|
99
|
+
### 4. Distribute installers
|
|
100
|
+
|
|
101
|
+
Built installers are in `src-tauri/target/release/bundle/`:
|
|
102
|
+
|
|
103
|
+
| Platform | Output |
|
|
104
|
+
|----------|--------|
|
|
105
|
+
| macOS | `.dmg` and `.app` bundle |
|
|
106
|
+
| Windows | `.msi` and `.exe` installer |
|
|
107
|
+
| Linux | `.deb`, `.rpm`, `.AppImage` |
|
|
108
|
+
|
|
109
|
+
Share these with your users. Installed apps auto-update when you push new releases.
|
|
110
|
+
|
|
111
|
+
## First-Launch Experience
|
|
112
|
+
|
|
113
|
+
When users install and open the app:
|
|
114
|
+
|
|
115
|
+
1. **Setup screen** asks for the sync server URL
|
|
116
|
+
2. User enters the URL provided by their organization admin
|
|
117
|
+
3. App connects, verifies the server, and saves the URL locally
|
|
118
|
+
4. On subsequent launches, the app connects automatically
|
|
119
|
+
|
|
120
|
+
Users can also skip setup and use the app offline — they can connect later from the settings gear icon.
|
|
121
|
+
|
|
122
|
+
## Environment Variables
|
|
123
|
+
|
|
124
|
+
| Variable | Default | Description |
|
|
125
|
+
|----------|---------|-------------|
|
|
126
|
+
| `VITE_SYNC_URL` | (none) | Pre-configure sync URL at build time |
|
|
127
|
+
| `PORT` | `3001` | Sync server port |
|
|
128
|
+
|
|
129
|
+
## Code Signing
|
|
130
|
+
|
|
131
|
+
### macOS
|
|
132
|
+
|
|
133
|
+
For distribution outside the Mac App Store, you need a Developer ID certificate from Apple:
|
|
134
|
+
|
|
135
|
+
1. Enroll in the [Apple Developer Program](https://developer.apple.com/programs/)
|
|
136
|
+
2. Create a "Developer ID Application" certificate
|
|
137
|
+
3. Add the certificate and credentials to your GitHub secrets (see `.github/workflows/release-desktop.yml`)
|
|
138
|
+
|
|
139
|
+
### Windows
|
|
140
|
+
|
|
141
|
+
For SmartScreen trust, get an EV code signing certificate from a provider like DigiCert or Sectigo. Set the certificate in your CI environment.
|
|
142
|
+
|
|
143
|
+
## Learn More
|
|
144
|
+
|
|
145
|
+
- [Tauri Desktop Guide](https://ehoneahobed.github.io/kora/guide/tauri-desktop)
|
|
146
|
+
- [Deployment Guide](https://ehoneahobed.github.io/kora/guide/deployment)
|
|
147
|
+
- [Sync Configuration](https://ehoneahobed.github.io/kora/guide/sync-configuration)
|
|
148
|
+
- [Schema Design](https://ehoneahobed.github.io/kora/guide/schema-design)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { spawn, type ChildProcess } from 'node:child_process'
|
|
2
|
+
|
|
3
|
+
// Starts both the sync server and the Tauri app with a single command.
|
|
4
|
+
// The sync server runs in the background so the desktop app can sync
|
|
5
|
+
// data across devices during development.
|
|
6
|
+
|
|
7
|
+
const children: ChildProcess[] = []
|
|
8
|
+
|
|
9
|
+
function cleanup() {
|
|
10
|
+
for (const child of children) {
|
|
11
|
+
child.kill()
|
|
12
|
+
}
|
|
13
|
+
process.exit()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
process.on('SIGINT', cleanup)
|
|
17
|
+
process.on('SIGTERM', cleanup)
|
|
18
|
+
|
|
19
|
+
// Start the sync server
|
|
20
|
+
const syncServer = spawn('tsx', ['server.ts'], {
|
|
21
|
+
stdio: 'inherit',
|
|
22
|
+
shell: true,
|
|
23
|
+
})
|
|
24
|
+
children.push(syncServer)
|
|
25
|
+
|
|
26
|
+
// Start the Tauri app (manages Vite dev server + Rust build)
|
|
27
|
+
const tauriApp = spawn('tauri', ['dev'], {
|
|
28
|
+
stdio: 'inherit',
|
|
29
|
+
shell: true,
|
|
30
|
+
})
|
|
31
|
+
children.push(tauriApp)
|
|
32
|
+
|
|
33
|
+
// If the Tauri app exits, stop everything
|
|
34
|
+
tauriApp.on('close', (code) => {
|
|
35
|
+
syncServer.kill()
|
|
36
|
+
process.exit(code ?? 0)
|
|
37
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>{{projectName}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
|
10
|
+
</style>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<div id="root"></div>
|
|
14
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineConfig } from 'korajs/config'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
schema: './src/schema.ts',
|
|
5
|
+
dev: {
|
|
6
|
+
port: 5173,
|
|
7
|
+
sync: {
|
|
8
|
+
enabled: true,
|
|
9
|
+
port: 3001,
|
|
10
|
+
store: 'sqlite',
|
|
11
|
+
},
|
|
12
|
+
watch: {
|
|
13
|
+
enabled: true,
|
|
14
|
+
debounceMs: 300,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx dev.ts",
|
|
8
|
+
"dev:app": "tauri dev",
|
|
9
|
+
"dev:server": "tsx server.ts",
|
|
10
|
+
"build": "tauri build",
|
|
11
|
+
"start:server": "node --import tsx server.ts",
|
|
12
|
+
"deploy:server": "kora deploy"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"korajs": "{{koraVersion}}",
|
|
16
|
+
"@korajs/core": "{{koraVersion}}",
|
|
17
|
+
"@korajs/react": "{{koraVersion}}",
|
|
18
|
+
"@korajs/tauri": "{{koraVersion}}",
|
|
19
|
+
"@tauri-apps/api": "^2.0.0",
|
|
20
|
+
"@tauri-apps/plugin-updater": "^2.0.0",
|
|
21
|
+
"lucide-react": "^0.468.0",
|
|
22
|
+
"react": "^19.0.0",
|
|
23
|
+
"react-dom": "^19.0.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@korajs/cli": "{{koraVersion}}",
|
|
27
|
+
"@korajs/server": "{{koraVersion}}",
|
|
28
|
+
"@tauri-apps/cli": "^2.0.0",
|
|
29
|
+
"@types/react": "^19.0.0",
|
|
30
|
+
"@types/react-dom": "^19.0.0",
|
|
31
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
32
|
+
"better-sqlite3": "^12.9.0",
|
|
33
|
+
"tsx": "^4.19.0",
|
|
34
|
+
"typescript": "^5.7.0",
|
|
35
|
+
"vite": "^6.0.0",
|
|
36
|
+
"ws": "^8.18.0"
|
|
37
|
+
},
|
|
38
|
+
"pnpm": {
|
|
39
|
+
"onlyBuiltDependencies": [
|
|
40
|
+
"better-sqlite3",
|
|
41
|
+
"esbuild"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createProductionServer, createSqliteServerStore } from '@korajs/server'
|
|
2
|
+
|
|
3
|
+
// Sync server for your Kora desktop app.
|
|
4
|
+
// Started automatically by `pnpm dev`, or run standalone with `pnpm dev:server`.
|
|
5
|
+
// Deploy with `pnpm deploy:server` (uses kora deploy).
|
|
6
|
+
//
|
|
7
|
+
// All desktop clients connect to this server to sync data across devices.
|
|
8
|
+
// For production, deploy this server and set VITE_SYNC_URL when building
|
|
9
|
+
// the desktop app: VITE_SYNC_URL=wss://your-server.com/kora-sync pnpm build
|
|
10
|
+
|
|
11
|
+
const store = createSqliteServerStore({ filename: './kora-server.db' })
|
|
12
|
+
|
|
13
|
+
const server = createProductionServer({
|
|
14
|
+
store,
|
|
15
|
+
port: Number(process.env.PORT) || 3001,
|
|
16
|
+
staticDir: './dist',
|
|
17
|
+
syncPath: '/kora-sync',
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
server.start().then((url) => {
|
|
21
|
+
console.log(`Kora sync server running at ${url}`)
|
|
22
|
+
console.log(` Sync endpoint: ${url.replace('http', 'ws')}/kora-sync`)
|
|
23
|
+
})
|