@korajs/cli 0.4.0 → 0.6.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 +1636 -601
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1086 -139
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-MVP5PDT4.js → chunk-EEZNRI5W.js} +51 -6
- package/dist/chunk-EEZNRI5W.js.map +1 -0
- package/dist/{chunk-YGVO4POI.js → chunk-Q2FBCOQD.js} +27 -15
- package/dist/chunk-Q2FBCOQD.js.map +1 -0
- package/dist/{chunk-CMSX76KM.js → chunk-VLTPEATY.js} +172 -99
- package/dist/chunk-VLTPEATY.js.map +1 -0
- package/dist/create.cjs +130 -54
- package/dist/create.cjs.map +1 -1
- package/dist/create.js +2 -2
- package/dist/index.cjs +143 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -6
- package/dist/index.d.ts +8 -6
- package/dist/index.js +2 -2
- package/package.json +9 -3
- package/templates/react-basic/README.md.hbs +10 -1
- package/templates/react-basic/src/App.tsx +112 -126
- package/templates/react-basic/src/index.css +165 -159
- package/templates/react-basic/src/kora-worker.ts +0 -1
- package/templates/react-basic/src/main.tsx +12 -12
- package/templates/react-basic/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-basic/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-basic/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-basic/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-basic/src/schema.ts +6 -12
- package/templates/react-basic/src/vite-env.d.ts +17 -0
- package/templates/react-basic/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-basic/vite.config.ts +57 -50
- package/templates/react-sync/.env.example +19 -0
- package/templates/react-sync/README.md.hbs +43 -1
- package/templates/react-sync/package.json.hbs +3 -0
- package/templates/react-sync/server.ts +90 -26
- package/templates/react-sync/src/App.tsx +132 -130
- package/templates/react-sync/src/auth.ts +25 -0
- package/templates/react-sync/src/index.css +212 -165
- package/templates/react-sync/src/kora-worker.ts +0 -1
- package/templates/react-sync/src/main.tsx +34 -18
- package/templates/react-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-sync/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-sync/src/schema.ts +6 -12
- package/templates/react-sync/src/vite-env.d.ts +17 -0
- package/templates/react-sync/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-sync/vite.config.ts +67 -60
- package/templates/react-tailwind/README.md.hbs +10 -1
- package/templates/react-tailwind/src/App.tsx +186 -197
- package/templates/react-tailwind/src/index.css +3 -3
- package/templates/react-tailwind/src/kora-worker.ts +0 -1
- package/templates/react-tailwind/src/main.tsx +19 -12
- package/templates/react-tailwind/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-tailwind/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-tailwind/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-tailwind/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-tailwind/src/schema.ts +6 -12
- package/templates/react-tailwind/src/vite-env.d.ts +17 -0
- package/templates/react-tailwind/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-tailwind/vite.config.ts +57 -50
- package/templates/react-tailwind-sync/.env.example +19 -0
- package/templates/react-tailwind-sync/README.md.hbs +43 -1
- package/templates/react-tailwind-sync/package.json.hbs +3 -0
- package/templates/react-tailwind-sync/server.ts +90 -26
- package/templates/react-tailwind-sync/src/App.tsx +246 -222
- package/templates/react-tailwind-sync/src/auth.ts +25 -0
- package/templates/react-tailwind-sync/src/index.css +3 -3
- package/templates/react-tailwind-sync/src/kora-worker.ts +0 -1
- package/templates/react-tailwind-sync/src/main.tsx +48 -18
- package/templates/react-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-tailwind-sync/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-tailwind-sync/src/schema.ts +6 -12
- package/templates/react-tailwind-sync/src/vite-env.d.ts +17 -0
- package/templates/react-tailwind-sync/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-tailwind-sync/vite.config.ts +67 -60
- package/templates/svelte-basic/.env.example +3 -0
- package/templates/svelte-basic/README.md.hbs +62 -0
- package/templates/svelte-basic/index.html.hbs +12 -0
- package/templates/svelte-basic/kora.config.ts +12 -0
- package/templates/svelte-basic/package.json.hbs +27 -0
- package/templates/svelte-basic/src/App.svelte +131 -0
- package/templates/svelte-basic/src/Root.svelte +14 -0
- package/templates/svelte-basic/src/index.css +287 -0
- package/templates/svelte-basic/src/kora-worker.ts +9 -0
- package/templates/svelte-basic/src/main.ts +16 -0
- package/templates/svelte-basic/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-basic/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-basic/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-basic/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-basic/src/schema.ts +9 -0
- package/templates/svelte-basic/src/vite-env.d.ts +12 -0
- package/templates/svelte-basic/svelte.config.js +5 -0
- package/templates/svelte-basic/tsconfig.json.hbs +14 -0
- package/templates/svelte-basic/vite.config.ts +52 -0
- package/templates/svelte-sync/.env.example +25 -0
- package/templates/svelte-sync/README.md.hbs +105 -0
- package/templates/svelte-sync/index.html.hbs +12 -0
- package/templates/svelte-sync/kora.config.ts +17 -0
- package/templates/svelte-sync/package.json.hbs +43 -0
- package/templates/svelte-sync/server.ts +90 -0
- package/templates/svelte-sync/src/App.svelte +154 -0
- package/templates/svelte-sync/src/Root.svelte +22 -0
- package/templates/svelte-sync/src/auth.ts +25 -0
- package/templates/svelte-sync/src/index.css +339 -0
- package/templates/svelte-sync/src/kora-worker.ts +9 -0
- package/templates/svelte-sync/src/main.ts +32 -0
- package/templates/svelte-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-sync/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-sync/src/schema.ts +9 -0
- package/templates/svelte-sync/src/vite-env.d.ts +21 -0
- package/templates/svelte-sync/svelte.config.js +5 -0
- package/templates/svelte-sync/tsconfig.json.hbs +14 -0
- package/templates/svelte-sync/vite.config.ts +79 -0
- package/templates/svelte-tailwind/README.md.hbs +62 -0
- package/templates/svelte-tailwind/index.html.hbs +12 -0
- package/templates/svelte-tailwind/kora.config.ts +12 -0
- package/templates/svelte-tailwind/package.json.hbs +30 -0
- package/templates/svelte-tailwind/src/App.svelte +79 -0
- package/templates/svelte-tailwind/src/Root.svelte +14 -0
- package/templates/svelte-tailwind/src/index.css +7 -0
- package/templates/svelte-tailwind/src/kora-worker.ts +9 -0
- package/templates/svelte-tailwind/src/main.ts +16 -0
- package/templates/svelte-tailwind/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-tailwind/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-tailwind/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-tailwind/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-tailwind/src/schema.ts +9 -0
- package/templates/svelte-tailwind/src/vite-env.d.ts +12 -0
- package/templates/svelte-tailwind/svelte.config.js +5 -0
- package/templates/svelte-tailwind/tsconfig.json.hbs +14 -0
- package/templates/svelte-tailwind/vite.config.ts +53 -0
- package/templates/svelte-tailwind-sync/.env.example +25 -0
- package/templates/svelte-tailwind-sync/README.md.hbs +105 -0
- package/templates/svelte-tailwind-sync/index.html.hbs +12 -0
- package/templates/svelte-tailwind-sync/kora.config.ts +17 -0
- package/templates/svelte-tailwind-sync/package.json.hbs +46 -0
- package/templates/svelte-tailwind-sync/server.ts +90 -0
- package/templates/svelte-tailwind-sync/src/App.svelte +196 -0
- package/templates/svelte-tailwind-sync/src/Root.svelte +26 -0
- package/templates/svelte-tailwind-sync/src/auth.ts +25 -0
- package/templates/svelte-tailwind-sync/src/index.css +7 -0
- package/templates/svelte-tailwind-sync/src/kora-worker.ts +9 -0
- package/templates/svelte-tailwind-sync/src/main.ts +32 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-tailwind-sync/src/schema.ts +9 -0
- package/templates/svelte-tailwind-sync/src/vite-env.d.ts +21 -0
- package/templates/svelte-tailwind-sync/svelte.config.js +5 -0
- package/templates/svelte-tailwind-sync/tsconfig.json.hbs +14 -0
- package/templates/svelte-tailwind-sync/vite.config.ts +80 -0
- package/templates/tauri-react/.env.example +20 -0
- package/templates/tauri-react/README.md.hbs +36 -1
- package/templates/tauri-react/dev.ts +11 -11
- package/templates/tauri-react/package.json.hbs +2 -0
- package/templates/tauri-react/server.ts +86 -12
- package/templates/tauri-react/src/App.tsx +769 -544
- package/templates/tauri-react/src/AppShell.tsx +125 -83
- package/templates/tauri-react/src/SetupScreen.tsx +257 -208
- package/templates/tauri-react/src/auth.ts +45 -0
- package/templates/tauri-react/src/main.tsx +3 -3
- package/templates/tauri-react/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/tauri-react/src/modules/todos/todo.queries.ts +5 -0
- package/templates/tauri-react/src/modules/todos/todo.schema.ts +10 -0
- package/templates/tauri-react/src/modules/todos/useTodos.ts +31 -0
- package/templates/tauri-react/src/schema.ts +6 -12
- package/templates/tauri-react/src/sync-config.ts +47 -47
- package/templates/tauri-react/src/updater.ts +15 -15
- package/templates/tauri-react/src/vite-env.d.ts +17 -0
- package/templates/tauri-react/src-tauri/tauri.conf.json +1 -1
- package/templates/tauri-react/tsconfig.json.hbs +14 -0
- package/templates/tauri-react/vite.config.ts +9 -9
- package/templates/vue-basic/README.md.hbs +62 -0
- package/templates/vue-basic/index.html.hbs +12 -0
- package/templates/vue-basic/kora.config.ts +12 -0
- package/templates/vue-basic/package.json.hbs +26 -0
- package/templates/vue-basic/src/App.vue +117 -0
- package/templates/vue-basic/src/index.css +287 -0
- package/templates/vue-basic/src/kora-worker.ts +9 -0
- package/templates/vue-basic/src/main.ts +20 -0
- package/templates/vue-basic/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-basic/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-basic/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-basic/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-basic/src/schema.ts +9 -0
- package/templates/vue-basic/src/vite-env.d.ts +17 -0
- package/templates/vue-basic/tsconfig.json.hbs +15 -0
- package/templates/vue-basic/vite.config.ts +69 -0
- package/templates/vue-sync/.env.example +25 -0
- package/templates/vue-sync/README.md.hbs +105 -0
- package/templates/vue-sync/index.html.hbs +12 -0
- package/templates/vue-sync/kora.config.ts +17 -0
- package/templates/vue-sync/package.json.hbs +42 -0
- package/templates/vue-sync/server.ts +90 -0
- package/templates/vue-sync/src/App.vue +135 -0
- package/templates/vue-sync/src/auth.ts +25 -0
- package/templates/vue-sync/src/index.css +339 -0
- package/templates/vue-sync/src/kora-worker.ts +9 -0
- package/templates/vue-sync/src/main.ts +43 -0
- package/templates/vue-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-sync/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-sync/src/schema.ts +9 -0
- package/templates/vue-sync/src/vite-env.d.ts +26 -0
- package/templates/vue-sync/tsconfig.json.hbs +15 -0
- package/templates/vue-sync/vite.config.ts +79 -0
- package/templates/vue-tailwind/README.md.hbs +62 -0
- package/templates/vue-tailwind/index.html.hbs +12 -0
- package/templates/vue-tailwind/kora.config.ts +12 -0
- package/templates/vue-tailwind/package.json.hbs +29 -0
- package/templates/vue-tailwind/src/App.vue +142 -0
- package/templates/vue-tailwind/src/index.css +7 -0
- package/templates/vue-tailwind/src/kora-worker.ts +9 -0
- package/templates/vue-tailwind/src/main.ts +31 -0
- package/templates/vue-tailwind/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-tailwind/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-tailwind/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-tailwind/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-tailwind/src/schema.ts +9 -0
- package/templates/vue-tailwind/src/vite-env.d.ts +17 -0
- package/templates/vue-tailwind/tsconfig.json.hbs +15 -0
- package/templates/vue-tailwind/vite.config.ts +70 -0
- package/templates/vue-tailwind-sync/.env.example +25 -0
- package/templates/vue-tailwind-sync/README.md.hbs +105 -0
- package/templates/vue-tailwind-sync/index.html.hbs +12 -0
- package/templates/vue-tailwind-sync/kora.config.ts +17 -0
- package/templates/vue-tailwind-sync/package.json.hbs +45 -0
- package/templates/vue-tailwind-sync/server.ts +90 -0
- package/templates/vue-tailwind-sync/src/App.vue +245 -0
- package/templates/vue-tailwind-sync/src/auth.ts +25 -0
- package/templates/vue-tailwind-sync/src/index.css +7 -0
- package/templates/vue-tailwind-sync/src/kora-worker.ts +9 -0
- package/templates/vue-tailwind-sync/src/main.ts +56 -0
- package/templates/vue-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-tailwind-sync/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-tailwind-sync/src/schema.ts +9 -0
- package/templates/vue-tailwind-sync/src/vite-env.d.ts +26 -0
- package/templates/vue-tailwind-sync/tsconfig.json.hbs +15 -0
- package/templates/vue-tailwind-sync/vite.config.ts +80 -0
- package/dist/chunk-CMSX76KM.js.map +0 -1
- package/dist/chunk-MVP5PDT4.js.map +0 -1
- package/dist/chunk-YGVO4POI.js.map +0 -1
- package/templates/tauri-react/tsconfig.json +0 -15
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface ImportMetaEnv {
|
|
2
|
+
readonly VITE_SYNC_URL?: string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
interface ImportMeta {
|
|
6
|
+
readonly env: ImportMetaEnv
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare module '*?worker&url' {
|
|
10
|
+
const url: string
|
|
11
|
+
export default url
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare module '*?url' {
|
|
15
|
+
const url: string
|
|
16
|
+
export default url
|
|
17
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { existsSync, readdirSync
|
|
2
|
-
import {
|
|
1
|
+
import { copyFileSync, existsSync, readdirSync } from 'node:fs'
|
|
2
|
+
import { join, resolve } from 'node:path'
|
|
3
3
|
import react from '@vitejs/plugin-react'
|
|
4
4
|
import type { Plugin } from 'vite'
|
|
5
5
|
import { defineConfig } from 'vite'
|
|
@@ -10,23 +10,23 @@ import { defineConfig } from 'vite'
|
|
|
10
10
|
* cannot be used and SQLite WASM falls back to in-memory storage (no persistence).
|
|
11
11
|
*/
|
|
12
12
|
function crossOriginIsolation(): Plugin {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
13
|
+
return {
|
|
14
|
+
name: 'cross-origin-isolation',
|
|
15
|
+
configureServer(server) {
|
|
16
|
+
server.middlewares.use((_req, res, next) => {
|
|
17
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
18
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
|
|
19
|
+
next()
|
|
20
|
+
})
|
|
21
|
+
},
|
|
22
|
+
configurePreviewServer(server) {
|
|
23
|
+
server.middlewares.use((_req, res, next) => {
|
|
24
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
25
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
|
|
26
|
+
next()
|
|
27
|
+
})
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
@@ -36,50 +36,57 @@ function crossOriginIsolation(): Plugin {
|
|
|
36
36
|
* sqlite3 loads it dynamically) so the full OPFS VFS can initialize without errors
|
|
37
37
|
*/
|
|
38
38
|
function sqliteWasmHotfix(): Plugin {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
return {
|
|
40
|
+
name: 'sqlite-wasm-hotfix',
|
|
41
|
+
apply: 'build',
|
|
42
|
+
closeBundle() {
|
|
43
|
+
const assetsDir = resolve('dist', 'assets')
|
|
44
|
+
if (!existsSync(assetsDir)) return
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
// Copy hashed sqlite3.wasm to unhashed name
|
|
47
|
+
for (const file of readdirSync(assetsDir)) {
|
|
48
|
+
if (/^sqlite3-.+\.wasm$/.test(file)) {
|
|
49
|
+
copyFileSync(join(assetsDir, file), join(assetsDir, 'sqlite3.wasm'))
|
|
50
|
+
break
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
// Copy OPFS async proxy worker (dynamically loaded by sqlite3, not detected by Vite)
|
|
55
|
+
const proxyFile = resolve(
|
|
56
|
+
'node_modules',
|
|
57
|
+
'@sqlite.org',
|
|
58
|
+
'sqlite-wasm',
|
|
59
|
+
'sqlite-wasm',
|
|
60
|
+
'jswasm',
|
|
61
|
+
'sqlite3-opfs-async-proxy.js',
|
|
62
|
+
)
|
|
63
|
+
if (existsSync(proxyFile)) {
|
|
64
|
+
copyFileSync(proxyFile, join(assetsDir, 'sqlite3-opfs-async-proxy.js'))
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
}
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
export default defineConfig({
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
71
|
+
plugins: [react(), crossOriginIsolation(), sqliteWasmHotfix()],
|
|
72
|
+
worker: {
|
|
73
|
+
format: 'es',
|
|
74
|
+
},
|
|
75
|
+
optimizeDeps: {
|
|
76
|
+
exclude: ['@sqlite.org/sqlite-wasm', '@korajs/store'],
|
|
77
|
+
include: ['yjs'],
|
|
78
|
+
},
|
|
79
|
+
resolve: {
|
|
80
|
+
dedupe: ['yjs'],
|
|
81
|
+
},
|
|
82
|
+
server: {
|
|
83
|
+
allowedHosts: true,
|
|
84
|
+
proxy: {
|
|
85
|
+
'/kora-sync': {
|
|
86
|
+
target: 'ws://localhost:3001',
|
|
87
|
+
ws: true,
|
|
88
|
+
rewriteWsOrigin: true,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
85
92
|
})
|
|
@@ -14,14 +14,23 @@ npm run dev
|
|
|
14
14
|
|
|
15
15
|
```
|
|
16
16
|
src/
|
|
17
|
-
schema.ts #
|
|
17
|
+
schema.ts # Schema entry point
|
|
18
18
|
main.tsx # App initialization and Kora setup
|
|
19
19
|
App.tsx # Main application component
|
|
20
|
+
modules/
|
|
21
|
+
todos/
|
|
22
|
+
todo.schema.ts # Todo collection definition
|
|
23
|
+
todo.queries.ts # Reusable todo query builders
|
|
24
|
+
todo.mutations.ts # Todo mutation helpers
|
|
25
|
+
useTodos.ts # React hook connecting todos to the UI
|
|
20
26
|
kora-worker.ts # SQLite WASM web worker
|
|
21
27
|
index.css # Styles
|
|
22
28
|
kora.config.ts # Kora configuration
|
|
23
29
|
```
|
|
24
30
|
|
|
31
|
+
In a feature module, `schema` defines data shape, `queries` contain reads only, `mutations` contain
|
|
32
|
+
writes, and `useTodos.ts` is the React binding that exposes todo data and actions to components.
|
|
33
|
+
|
|
25
34
|
## Scripts
|
|
26
35
|
|
|
27
36
|
| Script | Description |
|
|
@@ -1,212 +1,201 @@
|
|
|
1
|
+
import { CheckCircle2, Circle, ClipboardList, Loader2, Plus, Trash2 } from 'lucide-react'
|
|
1
2
|
import { useState } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
CheckCircle2,
|
|
5
|
-
Circle,
|
|
6
|
-
ClipboardList,
|
|
7
|
-
Loader2,
|
|
8
|
-
Plus,
|
|
9
|
-
Trash2,
|
|
10
|
-
} from 'lucide-react'
|
|
3
|
+
import { useTodos } from './modules/todos/useTodos'
|
|
11
4
|
|
|
12
5
|
type Filter = 'all' | 'active' | 'completed'
|
|
13
6
|
|
|
14
7
|
export function App() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
</p>
|
|
178
|
-
</div>
|
|
179
|
-
</div>
|
|
180
|
-
)
|
|
8
|
+
const { allTodos, activeTodos, completedTodos, addTodo, toggleTodo, deleteTodo } = useTodos()
|
|
9
|
+
const isAdding = addTodo.isLoading
|
|
10
|
+
|
|
11
|
+
const [filter, setFilter] = useState<Filter>('all')
|
|
12
|
+
const [input, setInput] = useState('')
|
|
13
|
+
|
|
14
|
+
const filteredTodos =
|
|
15
|
+
filter === 'active' ? activeTodos : filter === 'completed' ? completedTodos : allTodos
|
|
16
|
+
|
|
17
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
18
|
+
e.preventDefault()
|
|
19
|
+
const title = input.trim()
|
|
20
|
+
if (title) {
|
|
21
|
+
addTodo.mutate({ title })
|
|
22
|
+
setInput('')
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const clearCompleted = () => {
|
|
27
|
+
for (const todo of completedTodos) {
|
|
28
|
+
deleteTodo.mutate(todo.id)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className="min-h-screen bg-gray-950 text-gray-100">
|
|
34
|
+
<div className="mx-auto max-w-2xl px-4 py-12">
|
|
35
|
+
{/* Header */}
|
|
36
|
+
<div className="flex items-center justify-between mb-8">
|
|
37
|
+
<div className="flex items-center gap-3">
|
|
38
|
+
<ClipboardList className="h-8 w-8 text-indigo-400" />
|
|
39
|
+
<h1 className="text-2xl font-bold">My Tasks</h1>
|
|
40
|
+
</div>
|
|
41
|
+
<span className="rounded-full bg-gray-800 px-3 py-1.5 text-sm text-gray-400">
|
|
42
|
+
Local-first
|
|
43
|
+
</span>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{/* Stats */}
|
|
47
|
+
<div className="grid grid-cols-3 gap-4 mb-8">
|
|
48
|
+
<StatCard label="Total" value={allTodos.length} color="text-gray-300" />
|
|
49
|
+
<StatCard label="Remaining" value={activeTodos.length} color="text-amber-400" />
|
|
50
|
+
<StatCard label="Done" value={completedTodos.length} color="text-emerald-400" />
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
{/* Add form */}
|
|
54
|
+
<form onSubmit={handleSubmit} className="flex gap-3 mb-8">
|
|
55
|
+
<input
|
|
56
|
+
type="text"
|
|
57
|
+
value={input}
|
|
58
|
+
onChange={(e) => setInput(e.target.value)}
|
|
59
|
+
placeholder="What needs to be done?"
|
|
60
|
+
className="flex-1 rounded-lg border border-gray-700 bg-gray-900 px-4 py-3 text-gray-100 placeholder-gray-500 outline-none focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500"
|
|
61
|
+
/>
|
|
62
|
+
<button
|
|
63
|
+
type="submit"
|
|
64
|
+
disabled={isAdding || !input.trim()}
|
|
65
|
+
className="flex items-center gap-2 rounded-lg bg-indigo-600 px-5 py-3 font-medium text-white transition hover:bg-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
66
|
+
>
|
|
67
|
+
{isAdding ? <Loader2 className="h-4 w-4 animate-spin" /> : <Plus className="h-4 w-4" />}
|
|
68
|
+
Add
|
|
69
|
+
</button>
|
|
70
|
+
</form>
|
|
71
|
+
|
|
72
|
+
{/* Filter tabs */}
|
|
73
|
+
<div className="flex gap-2 mb-6">
|
|
74
|
+
{(['all', 'active', 'completed'] as const).map((f) => {
|
|
75
|
+
const count =
|
|
76
|
+
f === 'all'
|
|
77
|
+
? allTodos.length
|
|
78
|
+
: f === 'active'
|
|
79
|
+
? activeTodos.length
|
|
80
|
+
: completedTodos.length
|
|
81
|
+
return (
|
|
82
|
+
<button
|
|
83
|
+
key={f}
|
|
84
|
+
onClick={() => setFilter(f)}
|
|
85
|
+
className={`flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition ${
|
|
86
|
+
filter === f
|
|
87
|
+
? 'bg-indigo-600 text-white'
|
|
88
|
+
: 'bg-gray-800 text-gray-400 hover:bg-gray-700 hover:text-gray-200'
|
|
89
|
+
}`}
|
|
90
|
+
>
|
|
91
|
+
{f.charAt(0).toUpperCase() + f.slice(1)}
|
|
92
|
+
<span
|
|
93
|
+
className={`rounded-full px-2 py-0.5 text-xs ${
|
|
94
|
+
filter === f ? 'bg-indigo-500 text-white' : 'bg-gray-700 text-gray-400'
|
|
95
|
+
}`}
|
|
96
|
+
>
|
|
97
|
+
{count}
|
|
98
|
+
</span>
|
|
99
|
+
</button>
|
|
100
|
+
)
|
|
101
|
+
})}
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
{/* Todo list */}
|
|
105
|
+
<div className="space-y-2">
|
|
106
|
+
{filteredTodos.length === 0 ? (
|
|
107
|
+
<EmptyState filter={filter} />
|
|
108
|
+
) : (
|
|
109
|
+
filteredTodos.map((todo) => (
|
|
110
|
+
<div
|
|
111
|
+
key={todo.id}
|
|
112
|
+
className="group flex items-center gap-3 rounded-lg border border-gray-800 bg-gray-900 px-4 py-3 transition hover:border-gray-700"
|
|
113
|
+
>
|
|
114
|
+
<button
|
|
115
|
+
onClick={() => toggleTodo.mutate(todo.id, { completed: !todo.completed })}
|
|
116
|
+
className="shrink-0 text-gray-500 hover:text-indigo-400 transition"
|
|
117
|
+
>
|
|
118
|
+
{todo.completed ? (
|
|
119
|
+
<CheckCircle2 className="h-5 w-5 text-emerald-400" />
|
|
120
|
+
) : (
|
|
121
|
+
<Circle className="h-5 w-5" />
|
|
122
|
+
)}
|
|
123
|
+
</button>
|
|
124
|
+
<span
|
|
125
|
+
className={`flex-1 ${
|
|
126
|
+
todo.completed ? 'text-gray-500 line-through' : 'text-gray-100'
|
|
127
|
+
}`}
|
|
128
|
+
>
|
|
129
|
+
{String(todo.title)}
|
|
130
|
+
</span>
|
|
131
|
+
{todo.createdAt && (
|
|
132
|
+
<span className="text-xs text-gray-600">
|
|
133
|
+
{formatTime(Number(todo.createdAt))}
|
|
134
|
+
</span>
|
|
135
|
+
)}
|
|
136
|
+
<button
|
|
137
|
+
onClick={() => deleteTodo.mutate(todo.id)}
|
|
138
|
+
className="shrink-0 text-gray-600 opacity-0 transition hover:text-red-400 group-hover:opacity-100"
|
|
139
|
+
>
|
|
140
|
+
<Trash2 className="h-4 w-4" />
|
|
141
|
+
</button>
|
|
142
|
+
</div>
|
|
143
|
+
))
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
{/* Footer */}
|
|
148
|
+
{allTodos.length > 0 && (
|
|
149
|
+
<div className="mt-6 flex items-center justify-between text-sm text-gray-500">
|
|
150
|
+
<span>
|
|
151
|
+
{activeTodos.length} item{activeTodos.length !== 1 ? 's' : ''} left
|
|
152
|
+
</span>
|
|
153
|
+
{completedTodos.length > 0 && (
|
|
154
|
+
<button
|
|
155
|
+
onClick={clearCompleted}
|
|
156
|
+
className="text-gray-500 transition hover:text-gray-300"
|
|
157
|
+
>
|
|
158
|
+
Clear completed
|
|
159
|
+
</button>
|
|
160
|
+
)}
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
|
|
164
|
+
<p className="mt-12 text-center text-xs text-gray-700">
|
|
165
|
+
Powered by Kora — offline-first, real-time sync
|
|
166
|
+
</p>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
)
|
|
181
170
|
}
|
|
182
171
|
|
|
183
172
|
function StatCard({ label, value, color }: { label: string; value: number; color: string }) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
173
|
+
return (
|
|
174
|
+
<div className="rounded-lg border border-gray-800 bg-gray-900 p-4">
|
|
175
|
+
<p className="text-sm text-gray-500">{label}</p>
|
|
176
|
+
<p className={`text-2xl font-bold ${color}`}>{value}</p>
|
|
177
|
+
</div>
|
|
178
|
+
)
|
|
190
179
|
}
|
|
191
180
|
|
|
192
181
|
function EmptyState({ filter }: { filter: Filter }) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
182
|
+
const messages: Record<Filter, string> = {
|
|
183
|
+
all: 'No tasks yet. Add one above!',
|
|
184
|
+
active: 'All caught up! No active tasks.',
|
|
185
|
+
completed: 'No completed tasks yet.',
|
|
186
|
+
}
|
|
187
|
+
return (
|
|
188
|
+
<div className="rounded-lg border border-dashed border-gray-800 py-12 text-center text-gray-600">
|
|
189
|
+
{messages[filter]}
|
|
190
|
+
</div>
|
|
191
|
+
)
|
|
203
192
|
}
|
|
204
193
|
|
|
205
194
|
function formatTime(timestamp: number): string {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
195
|
+
const date = new Date(timestamp)
|
|
196
|
+
const now = new Date()
|
|
197
|
+
if (date.toDateString() === now.toDateString()) {
|
|
198
|
+
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
|
199
|
+
}
|
|
200
|
+
return date.toLocaleDateString([], { month: 'short', day: 'numeric' })
|
|
212
201
|
}
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
// Import the WASM binary URL so Vite resolves it with the correct content hash.
|
|
5
5
|
// Without this, production builds fail because sqlite3 looks for the unhashed filename.
|
|
6
6
|
import sqliteWasmUrl from '@sqlite.org/sqlite-wasm/sqlite3.wasm?url'
|
|
7
|
-
|
|
8
7
|
;(globalThis as Record<string, unknown>).__KORA_SQLITE_WASM_URL = sqliteWasmUrl
|
|
9
8
|
|
|
10
9
|
import '@korajs/store/sqlite-wasm/worker'
|