@korajs/cli 0.5.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.
Files changed (169) hide show
  1. package/dist/bin.cjs +76 -7
  2. package/dist/bin.cjs.map +1 -1
  3. package/dist/bin.js +3 -3
  4. package/dist/{chunk-PSRM56B7.js → chunk-EEZNRI5W.js} +51 -6
  5. package/dist/chunk-EEZNRI5W.js.map +1 -0
  6. package/dist/{chunk-SOTZIWIF.js → chunk-Q2FBCOQD.js} +2 -2
  7. package/dist/{chunk-3WNFM3QB.js → chunk-VLTPEATY.js} +70 -4
  8. package/dist/chunk-VLTPEATY.js.map +1 -0
  9. package/dist/create.cjs +76 -7
  10. package/dist/create.cjs.map +1 -1
  11. package/dist/create.js +2 -2
  12. package/dist/index.cjs +65 -3
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +3 -3
  15. package/dist/index.d.ts +3 -3
  16. package/dist/index.js +2 -2
  17. package/package.json +6 -6
  18. package/templates/svelte-basic/.env.example +3 -0
  19. package/templates/svelte-basic/README.md.hbs +62 -0
  20. package/templates/svelte-basic/index.html.hbs +12 -0
  21. package/templates/svelte-basic/kora.config.ts +12 -0
  22. package/templates/svelte-basic/package.json.hbs +27 -0
  23. package/templates/svelte-basic/src/App.svelte +131 -0
  24. package/templates/svelte-basic/src/Root.svelte +14 -0
  25. package/templates/svelte-basic/src/index.css +287 -0
  26. package/templates/svelte-basic/src/kora-worker.ts +9 -0
  27. package/templates/svelte-basic/src/main.ts +16 -0
  28. package/templates/svelte-basic/src/modules/todos/todo.mutations.ts +25 -0
  29. package/templates/svelte-basic/src/modules/todos/todo.queries.ts +5 -0
  30. package/templates/svelte-basic/src/modules/todos/todo.schema.ts +10 -0
  31. package/templates/svelte-basic/src/modules/todos/useTodos.ts +25 -0
  32. package/templates/svelte-basic/src/schema.ts +9 -0
  33. package/templates/svelte-basic/src/vite-env.d.ts +12 -0
  34. package/templates/svelte-basic/svelte.config.js +5 -0
  35. package/templates/svelte-basic/tsconfig.json.hbs +14 -0
  36. package/templates/svelte-basic/vite.config.ts +52 -0
  37. package/templates/svelte-sync/.env.example +25 -0
  38. package/templates/svelte-sync/README.md.hbs +105 -0
  39. package/templates/svelte-sync/index.html.hbs +12 -0
  40. package/templates/svelte-sync/kora.config.ts +17 -0
  41. package/templates/svelte-sync/package.json.hbs +43 -0
  42. package/templates/svelte-sync/server.ts +90 -0
  43. package/templates/svelte-sync/src/App.svelte +154 -0
  44. package/templates/svelte-sync/src/Root.svelte +22 -0
  45. package/templates/svelte-sync/src/auth.ts +25 -0
  46. package/templates/svelte-sync/src/index.css +339 -0
  47. package/templates/svelte-sync/src/kora-worker.ts +9 -0
  48. package/templates/svelte-sync/src/main.ts +32 -0
  49. package/templates/svelte-sync/src/modules/todos/todo.mutations.ts +25 -0
  50. package/templates/svelte-sync/src/modules/todos/todo.queries.ts +5 -0
  51. package/templates/svelte-sync/src/modules/todos/todo.schema.ts +10 -0
  52. package/templates/svelte-sync/src/modules/todos/useTodos.ts +25 -0
  53. package/templates/svelte-sync/src/schema.ts +9 -0
  54. package/templates/svelte-sync/src/vite-env.d.ts +21 -0
  55. package/templates/svelte-sync/svelte.config.js +5 -0
  56. package/templates/svelte-sync/tsconfig.json.hbs +14 -0
  57. package/templates/svelte-sync/vite.config.ts +79 -0
  58. package/templates/svelte-tailwind/README.md.hbs +62 -0
  59. package/templates/svelte-tailwind/index.html.hbs +12 -0
  60. package/templates/svelte-tailwind/kora.config.ts +12 -0
  61. package/templates/svelte-tailwind/package.json.hbs +30 -0
  62. package/templates/svelte-tailwind/src/App.svelte +79 -0
  63. package/templates/svelte-tailwind/src/Root.svelte +14 -0
  64. package/templates/svelte-tailwind/src/index.css +7 -0
  65. package/templates/svelte-tailwind/src/kora-worker.ts +9 -0
  66. package/templates/svelte-tailwind/src/main.ts +16 -0
  67. package/templates/svelte-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  68. package/templates/svelte-tailwind/src/modules/todos/todo.queries.ts +5 -0
  69. package/templates/svelte-tailwind/src/modules/todos/todo.schema.ts +10 -0
  70. package/templates/svelte-tailwind/src/modules/todos/useTodos.ts +25 -0
  71. package/templates/svelte-tailwind/src/schema.ts +9 -0
  72. package/templates/svelte-tailwind/src/vite-env.d.ts +12 -0
  73. package/templates/svelte-tailwind/svelte.config.js +5 -0
  74. package/templates/svelte-tailwind/tsconfig.json.hbs +14 -0
  75. package/templates/svelte-tailwind/vite.config.ts +53 -0
  76. package/templates/svelte-tailwind-sync/.env.example +25 -0
  77. package/templates/svelte-tailwind-sync/README.md.hbs +105 -0
  78. package/templates/svelte-tailwind-sync/index.html.hbs +12 -0
  79. package/templates/svelte-tailwind-sync/kora.config.ts +17 -0
  80. package/templates/svelte-tailwind-sync/package.json.hbs +46 -0
  81. package/templates/svelte-tailwind-sync/server.ts +90 -0
  82. package/templates/svelte-tailwind-sync/src/App.svelte +196 -0
  83. package/templates/svelte-tailwind-sync/src/Root.svelte +26 -0
  84. package/templates/svelte-tailwind-sync/src/auth.ts +25 -0
  85. package/templates/svelte-tailwind-sync/src/index.css +7 -0
  86. package/templates/svelte-tailwind-sync/src/kora-worker.ts +9 -0
  87. package/templates/svelte-tailwind-sync/src/main.ts +32 -0
  88. package/templates/svelte-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  89. package/templates/svelte-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  90. package/templates/svelte-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  91. package/templates/svelte-tailwind-sync/src/modules/todos/useTodos.ts +25 -0
  92. package/templates/svelte-tailwind-sync/src/schema.ts +9 -0
  93. package/templates/svelte-tailwind-sync/src/vite-env.d.ts +21 -0
  94. package/templates/svelte-tailwind-sync/svelte.config.js +5 -0
  95. package/templates/svelte-tailwind-sync/tsconfig.json.hbs +14 -0
  96. package/templates/svelte-tailwind-sync/vite.config.ts +80 -0
  97. package/templates/vue-basic/README.md.hbs +62 -0
  98. package/templates/vue-basic/index.html.hbs +12 -0
  99. package/templates/vue-basic/kora.config.ts +12 -0
  100. package/templates/vue-basic/package.json.hbs +26 -0
  101. package/templates/vue-basic/src/App.vue +117 -0
  102. package/templates/vue-basic/src/index.css +287 -0
  103. package/templates/vue-basic/src/kora-worker.ts +9 -0
  104. package/templates/vue-basic/src/main.ts +20 -0
  105. package/templates/vue-basic/src/modules/todos/todo.mutations.ts +25 -0
  106. package/templates/vue-basic/src/modules/todos/todo.queries.ts +5 -0
  107. package/templates/vue-basic/src/modules/todos/todo.schema.ts +10 -0
  108. package/templates/vue-basic/src/modules/todos/useTodos.ts +32 -0
  109. package/templates/vue-basic/src/schema.ts +9 -0
  110. package/templates/vue-basic/src/vite-env.d.ts +17 -0
  111. package/templates/vue-basic/tsconfig.json.hbs +15 -0
  112. package/templates/vue-basic/vite.config.ts +69 -0
  113. package/templates/vue-sync/.env.example +25 -0
  114. package/templates/vue-sync/README.md.hbs +105 -0
  115. package/templates/vue-sync/index.html.hbs +12 -0
  116. package/templates/vue-sync/kora.config.ts +17 -0
  117. package/templates/vue-sync/package.json.hbs +42 -0
  118. package/templates/vue-sync/server.ts +90 -0
  119. package/templates/vue-sync/src/App.vue +135 -0
  120. package/templates/vue-sync/src/auth.ts +25 -0
  121. package/templates/vue-sync/src/index.css +339 -0
  122. package/templates/vue-sync/src/kora-worker.ts +9 -0
  123. package/templates/vue-sync/src/main.ts +43 -0
  124. package/templates/vue-sync/src/modules/todos/todo.mutations.ts +25 -0
  125. package/templates/vue-sync/src/modules/todos/todo.queries.ts +5 -0
  126. package/templates/vue-sync/src/modules/todos/todo.schema.ts +10 -0
  127. package/templates/vue-sync/src/modules/todos/useTodos.ts +32 -0
  128. package/templates/vue-sync/src/schema.ts +9 -0
  129. package/templates/vue-sync/src/vite-env.d.ts +26 -0
  130. package/templates/vue-sync/tsconfig.json.hbs +15 -0
  131. package/templates/vue-sync/vite.config.ts +79 -0
  132. package/templates/vue-tailwind/README.md.hbs +62 -0
  133. package/templates/vue-tailwind/index.html.hbs +12 -0
  134. package/templates/vue-tailwind/kora.config.ts +12 -0
  135. package/templates/vue-tailwind/package.json.hbs +29 -0
  136. package/templates/vue-tailwind/src/App.vue +142 -0
  137. package/templates/vue-tailwind/src/index.css +7 -0
  138. package/templates/vue-tailwind/src/kora-worker.ts +9 -0
  139. package/templates/vue-tailwind/src/main.ts +31 -0
  140. package/templates/vue-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  141. package/templates/vue-tailwind/src/modules/todos/todo.queries.ts +5 -0
  142. package/templates/vue-tailwind/src/modules/todos/todo.schema.ts +10 -0
  143. package/templates/vue-tailwind/src/modules/todos/useTodos.ts +32 -0
  144. package/templates/vue-tailwind/src/schema.ts +9 -0
  145. package/templates/vue-tailwind/src/vite-env.d.ts +17 -0
  146. package/templates/vue-tailwind/tsconfig.json.hbs +15 -0
  147. package/templates/vue-tailwind/vite.config.ts +70 -0
  148. package/templates/vue-tailwind-sync/.env.example +25 -0
  149. package/templates/vue-tailwind-sync/README.md.hbs +105 -0
  150. package/templates/vue-tailwind-sync/index.html.hbs +12 -0
  151. package/templates/vue-tailwind-sync/kora.config.ts +17 -0
  152. package/templates/vue-tailwind-sync/package.json.hbs +45 -0
  153. package/templates/vue-tailwind-sync/server.ts +90 -0
  154. package/templates/vue-tailwind-sync/src/App.vue +245 -0
  155. package/templates/vue-tailwind-sync/src/auth.ts +25 -0
  156. package/templates/vue-tailwind-sync/src/index.css +7 -0
  157. package/templates/vue-tailwind-sync/src/kora-worker.ts +9 -0
  158. package/templates/vue-tailwind-sync/src/main.ts +56 -0
  159. package/templates/vue-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  160. package/templates/vue-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  161. package/templates/vue-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  162. package/templates/vue-tailwind-sync/src/modules/todos/useTodos.ts +32 -0
  163. package/templates/vue-tailwind-sync/src/schema.ts +9 -0
  164. package/templates/vue-tailwind-sync/src/vite-env.d.ts +26 -0
  165. package/templates/vue-tailwind-sync/tsconfig.json.hbs +15 -0
  166. package/templates/vue-tailwind-sync/vite.config.ts +80 -0
  167. package/dist/chunk-3WNFM3QB.js.map +0 -1
  168. package/dist/chunk-PSRM56B7.js.map +0 -1
  169. /package/dist/{chunk-SOTZIWIF.js.map → chunk-Q2FBCOQD.js.map} +0 -0
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", "tauri-react"];
8
+ declare const TEMPLATES: readonly ["react-tailwind-sync", "react-tailwind", "react-sync", "react-basic", "vue-sync", "vue-basic", "vue-tailwind-sync", "vue-tailwind", "svelte-sync", "svelte-basic", "svelte-tailwind-sync", "svelte-tailwind", "tauri-react"];
9
9
  type TemplateName = (typeof TEMPLATES)[number];
10
10
  /** Metadata for a project template */
11
11
  interface TemplateInfo {
@@ -556,13 +556,13 @@ type DatabaseOption = 'none' | 'sqlite' | 'postgres';
556
556
  type DatabaseProviderOption = 'none' | 'local' | 'supabase' | 'neon' | 'railway' | 'vercel-postgres' | 'custom';
557
557
  interface TemplateSelectionInput {
558
558
  platform: PlatformOption;
559
+ framework: FrameworkOption;
559
560
  tailwind: boolean;
560
561
  sync: boolean;
561
562
  db: DatabaseOption;
562
563
  }
563
564
  /**
564
- * Converts high-level scaffold selections into the currently supported
565
- * concrete template names.
565
+ * Converts high-level scaffold selections into a concrete template name.
566
566
  */
567
567
  declare function determineTemplateFromSelections(input: TemplateSelectionInput): TemplateName;
568
568
  declare function isFrameworkValue(value: string): value is FrameworkOption;
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", "tauri-react"];
8
+ declare const TEMPLATES: readonly ["react-tailwind-sync", "react-tailwind", "react-sync", "react-basic", "vue-sync", "vue-basic", "vue-tailwind-sync", "vue-tailwind", "svelte-sync", "svelte-basic", "svelte-tailwind-sync", "svelte-tailwind", "tauri-react"];
9
9
  type TemplateName = (typeof TEMPLATES)[number];
10
10
  /** Metadata for a project template */
11
11
  interface TemplateInfo {
@@ -556,13 +556,13 @@ type DatabaseOption = 'none' | 'sqlite' | 'postgres';
556
556
  type DatabaseProviderOption = 'none' | 'local' | 'supabase' | 'neon' | 'railway' | 'vercel-postgres' | 'custom';
557
557
  interface TemplateSelectionInput {
558
558
  platform: PlatformOption;
559
+ framework: FrameworkOption;
559
560
  tailwind: boolean;
560
561
  sync: boolean;
561
562
  db: DatabaseOption;
562
563
  }
563
564
  /**
564
- * Converts high-level scaffold selections into the currently supported
565
- * concrete template names.
565
+ * Converts high-level scaffold selections into a concrete template name.
566
566
  */
567
567
  declare function determineTemplateFromSelections(input: TemplateSelectionInput): TemplateName;
568
568
  declare function isFrameworkValue(value: string): value is FrameworkOption;
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  writeDockerfileArtifact,
28
28
  writeFlyTomlArtifact,
29
29
  writeRailwayJsonArtifact
30
- } from "./chunk-SOTZIWIF.js";
30
+ } from "./chunk-Q2FBCOQD.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-3WNFM3QB.js";
57
+ } from "./chunk-VLTPEATY.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.5.0",
3
+ "version": "0.6.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.5.0"
39
+ "@korajs/core": "0.6.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/better-sqlite3": "^7.6.13",
@@ -46,10 +46,10 @@
46
46
  "tsup": "^8.3.6",
47
47
  "typescript": "^5.7.3",
48
48
  "vitest": "^3.0.4",
49
- "@korajs/store": "0.5.0",
50
- "@korajs/sync": "0.5.0",
51
- "@korajs/merge": "0.5.0",
52
- "korajs": "0.5.0"
49
+ "@korajs/merge": "0.6.0",
50
+ "@korajs/store": "0.6.0",
51
+ "@korajs/sync": "0.6.0",
52
+ "korajs": "0.6.0"
53
53
  },
54
54
  "license": "MIT",
55
55
  "scripts": {
@@ -0,0 +1,3 @@
1
+ # No environment variables required for local-only mode.
2
+ # To enable sync, see the README for instructions.
3
+ # VITE_SYNC_URL=ws://localhost:3001
@@ -0,0 +1,62 @@
1
+ # {{projectName}}
2
+
3
+ An offline-first application built with [Kora.js](https://ehoneahobed.github.io/kora/).
4
+
5
+ ## Getting Started
6
+
7
+ ```bash
8
+ npm run dev
9
+ ```
10
+
11
+ - **App**: http://localhost:5173
12
+
13
+ ## Project Structure
14
+
15
+ ```
16
+ src/
17
+ schema.ts # Schema entry point
18
+ main.tsx # App initialization and Kora setup
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
26
+ kora-worker.ts # SQLite WASM web worker
27
+ index.css # Styles
28
+ kora.config.ts # Kora configuration
29
+ ```
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
+
34
+ ## Scripts
35
+
36
+ | Script | Description |
37
+ |--------|-------------|
38
+ | `npm run dev` | Start dev server |
39
+ | `npm run build` | Build for production |
40
+ | `npm run preview` | Preview production build |
41
+
42
+ ## Adding Sync
43
+
44
+ To enable real-time sync across devices, update `src/main.tsx`:
45
+
46
+ ```typescript
47
+ const app = createApp({
48
+ schema,
49
+ sync: {
50
+ url: import.meta.env.VITE_SYNC_URL || 'ws://localhost:3001',
51
+ },
52
+ // ...
53
+ })
54
+ ```
55
+
56
+ Then create a `server.ts` and install `@korajs/server`. See the [Sync Configuration guide](https://ehoneahobed.github.io/kora/guide/sync-configuration).
57
+
58
+ ## Learn More
59
+
60
+ - [Documentation](https://ehoneahobed.github.io/kora/)
61
+ - [Storage Configuration](https://ehoneahobed.github.io/kora/guide/storage-configuration)
62
+ - [Schema Design](https://ehoneahobed.github.io/kora/guide/schema-design)
@@ -0,0 +1,12 @@
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
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'korajs/config'
2
+
3
+ export default defineConfig({
4
+ schema: './src/schema.ts',
5
+ dev: {
6
+ port: 5173,
7
+ watch: {
8
+ enabled: true,
9
+ debounceMs: 300,
10
+ },
11
+ },
12
+ })
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "kora dev",
8
+ "build": "vite build",
9
+ "check": "svelte-check --tsconfig ./tsconfig.json",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "korajs": "{{koraVersion}}",
14
+ "@korajs/svelte": "{{koraVersion}}",
15
+ "@korajs/store": "{{koraVersion}}",
16
+ "@sqlite.org/sqlite-wasm": ">=3.51.0-build1",
17
+ "svelte": "^5.0.0",
18
+ "yjs": "^13.6.30"
19
+ },
20
+ "devDependencies": {
21
+ "@korajs/cli": "{{koraVersion}}",
22
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
23
+ "svelte-check": "^4.1.0",
24
+ "typescript": "^5.7.0",
25
+ "vite": "^6.0.0"
26
+ }
27
+ }
@@ -0,0 +1,131 @@
1
+ <script lang="ts">
2
+ import { createTodosStores } from './modules/todos/useTodos'
3
+
4
+ type Filter = 'all' | 'active' | 'completed'
5
+
6
+ const { allTodos, activeTodos, completedTodos, addTodo, toggleTodo, deleteTodo } =
7
+ createTodosStores()
8
+
9
+ let filter = $state<Filter>('all')
10
+ let input = $state('')
11
+
12
+ const filteredTodos = $derived(
13
+ filter === 'active'
14
+ ? $activeTodos
15
+ : filter === 'completed'
16
+ ? $completedTodos
17
+ : $allTodos,
18
+ )
19
+
20
+ function handleSubmit(event: SubmitEvent): void {
21
+ event.preventDefault()
22
+ const title = input.trim()
23
+ if (!title) return
24
+ addTodo.mutate({ title })
25
+ input = ''
26
+ }
27
+
28
+ function clearCompleted(): void {
29
+ for (const todo of $completedTodos) {
30
+ deleteTodo.mutate(todo.id)
31
+ }
32
+ }
33
+
34
+ function formatTime(timestamp: number): string {
35
+ const date = new Date(timestamp)
36
+ const now = new Date()
37
+ if (date.toDateString() === now.toDateString()) {
38
+ return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
39
+ }
40
+ return date.toLocaleDateString([], { month: 'short', day: 'numeric' })
41
+ }
42
+ </script>
43
+
44
+ <div class="app">
45
+ <div class="header">
46
+ <h1>My Tasks</h1>
47
+ </div>
48
+
49
+ <div class="stats">
50
+ <div class="stat-card">
51
+ <div class="label">Total</div>
52
+ <div class="value muted">{$allTodos.length}</div>
53
+ </div>
54
+ <div class="stat-card">
55
+ <div class="label">Remaining</div>
56
+ <div class="value warning">{$activeTodos.length}</div>
57
+ </div>
58
+ <div class="stat-card">
59
+ <div class="label">Done</div>
60
+ <div class="value success">{$completedTodos.length}</div>
61
+ </div>
62
+ </div>
63
+
64
+ <form class="add-form" onsubmit={handleSubmit}>
65
+ <input bind:value={input} type="text" placeholder="What needs to be done?" />
66
+ <button type="submit" disabled={!input.trim()}>Add</button>
67
+ </form>
68
+
69
+ <div class="filters">
70
+ {#each ['all', 'active', 'completed'] as const as f}
71
+ <button
72
+ type="button"
73
+ class="filter-btn {filter === f ? 'active' : ''}"
74
+ onclick={() => (filter = f)}
75
+ >
76
+ {f.charAt(0).toUpperCase() + f.slice(1)}
77
+ <span class="badge">
78
+ {f === 'all'
79
+ ? $allTodos.length
80
+ : f === 'active'
81
+ ? $activeTodos.length
82
+ : $completedTodos.length}
83
+ </span>
84
+ </button>
85
+ {/each}
86
+ </div>
87
+
88
+ <div class="todo-list">
89
+ {#if filteredTodos.length === 0}
90
+ <div class="empty-state">
91
+ {#if filter === 'all'}
92
+ No tasks yet. Add one above!
93
+ {:else if filter === 'active'}
94
+ All caught up! No active tasks.
95
+ {:else}
96
+ No completed tasks yet.
97
+ {/if}
98
+ </div>
99
+ {:else}
100
+ {#each filteredTodos as todo (todo.id)}
101
+ <div class="todo-item">
102
+ <button
103
+ type="button"
104
+ class="toggle {todo.completed ? 'checked' : ''}"
105
+ onclick={() => toggleTodo.mutate(todo.id, { completed: !todo.completed })}
106
+ >
107
+ {todo.completed ? '✓' : ''}
108
+ </button>
109
+ <span class="title {todo.completed ? 'done' : ''}">{String(todo.title)}</span>
110
+ {#if todo.createdAt}
111
+ <span class="time">{formatTime(Number(todo.createdAt))}</span>
112
+ {/if}
113
+ <button type="button" class="delete-btn" onclick={() => deleteTodo.mutate(todo.id)}>
114
+ ×
115
+ </button>
116
+ </div>
117
+ {/each}
118
+ {/if}
119
+ </div>
120
+
121
+ {#if $allTodos.length > 0}
122
+ <div class="footer">
123
+ <span>{$activeTodos.length} item{$activeTodos.length !== 1 ? 's' : ''} left</span>
124
+ {#if $completedTodos.length > 0}
125
+ <button type="button" onclick={clearCompleted}>Clear completed</button>
126
+ {/if}
127
+ </div>
128
+ {/if}
129
+
130
+ <p class="branding">Powered by Kora — offline-first</p>
131
+ </div>
@@ -0,0 +1,14 @@
1
+ <script lang="ts">
2
+ import type { KoraAppLike } from '@korajs/svelte'
3
+ import KoraProvider from '@korajs/svelte/KoraProvider.svelte'
4
+ import App from './App.svelte'
5
+
6
+ let { kora }: { kora: KoraAppLike } = $props()
7
+ </script>
8
+
9
+ <KoraProvider app={kora}>
10
+ {#snippet fallback()}
11
+ <div>Loading...</div>
12
+ {/snippet}
13
+ <App />
14
+ </KoraProvider>
@@ -0,0 +1,287 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ margin: 0;
6
+ padding: 0;
7
+ }
8
+
9
+ :root {
10
+ --color-bg: #0a0a0f;
11
+ --color-surface: #111118;
12
+ --color-border: #1e1e2a;
13
+ --color-border-hover: #2a2a3a;
14
+ --color-text: #e4e4e7;
15
+ --color-text-muted: #71717a;
16
+ --color-text-dim: #3f3f46;
17
+ --color-primary: #6366f1;
18
+ --color-primary-hover: #818cf8;
19
+ --color-success: #34d399;
20
+ --color-warning: #fbbf24;
21
+ --color-danger: #f87171;
22
+ --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
23
+ --radius: 8px;
24
+ }
25
+
26
+ body {
27
+ font-family: var(--font-sans);
28
+ background: var(--color-bg);
29
+ color: var(--color-text);
30
+ line-height: 1.6;
31
+ -webkit-font-smoothing: antialiased;
32
+ }
33
+
34
+ .app {
35
+ max-width: 640px;
36
+ margin: 0 auto;
37
+ padding: 48px 16px;
38
+ }
39
+
40
+ .header {
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: space-between;
44
+ margin-bottom: 32px;
45
+ }
46
+
47
+ .header h1 {
48
+ font-size: 1.5rem;
49
+ font-weight: 700;
50
+ }
51
+
52
+ .local-badge {
53
+ display: inline-flex;
54
+ align-items: center;
55
+ gap: 6px;
56
+ padding: 4px 12px;
57
+ border-radius: 999px;
58
+ background: var(--color-surface);
59
+ font-size: 0.8rem;
60
+ color: var(--color-text-muted);
61
+ }
62
+
63
+ .stats {
64
+ display: grid;
65
+ grid-template-columns: repeat(3, 1fr);
66
+ gap: 12px;
67
+ margin-bottom: 32px;
68
+ }
69
+
70
+ .stat-card {
71
+ background: var(--color-surface);
72
+ border: 1px solid var(--color-border);
73
+ border-radius: var(--radius);
74
+ padding: 16px;
75
+ }
76
+
77
+ .stat-card .label {
78
+ font-size: 0.8rem;
79
+ color: var(--color-text-muted);
80
+ }
81
+
82
+ .stat-card .value {
83
+ font-size: 1.5rem;
84
+ font-weight: 700;
85
+ }
86
+
87
+ .stat-card .value.muted {
88
+ color: var(--color-text-muted);
89
+ }
90
+ .stat-card .value.warning {
91
+ color: var(--color-warning);
92
+ }
93
+ .stat-card .value.success {
94
+ color: var(--color-success);
95
+ }
96
+
97
+ .add-form {
98
+ display: flex;
99
+ gap: 12px;
100
+ margin-bottom: 32px;
101
+ }
102
+
103
+ .add-form input {
104
+ flex: 1;
105
+ padding: 10px 16px;
106
+ border: 1px solid var(--color-border);
107
+ border-radius: var(--radius);
108
+ background: var(--color-surface);
109
+ color: var(--color-text);
110
+ font-size: 0.95rem;
111
+ outline: none;
112
+ }
113
+
114
+ .add-form input:focus {
115
+ border-color: var(--color-primary);
116
+ }
117
+
118
+ .add-form input::placeholder {
119
+ color: var(--color-text-muted);
120
+ }
121
+
122
+ .add-form button {
123
+ padding: 10px 20px;
124
+ border: none;
125
+ border-radius: var(--radius);
126
+ background: var(--color-primary);
127
+ color: white;
128
+ font-size: 0.95rem;
129
+ font-weight: 500;
130
+ cursor: pointer;
131
+ }
132
+
133
+ .add-form button:hover {
134
+ background: var(--color-primary-hover);
135
+ }
136
+
137
+ .add-form button:disabled {
138
+ opacity: 0.5;
139
+ cursor: not-allowed;
140
+ }
141
+
142
+ .filters {
143
+ display: flex;
144
+ gap: 8px;
145
+ margin-bottom: 24px;
146
+ }
147
+
148
+ .filter-btn {
149
+ padding: 6px 16px;
150
+ border: none;
151
+ border-radius: var(--radius);
152
+ background: var(--color-surface);
153
+ color: var(--color-text-muted);
154
+ font-size: 0.85rem;
155
+ font-weight: 500;
156
+ cursor: pointer;
157
+ }
158
+
159
+ .filter-btn:hover {
160
+ background: var(--color-border-hover);
161
+ color: var(--color-text);
162
+ }
163
+
164
+ .filter-btn.active {
165
+ background: var(--color-primary);
166
+ color: white;
167
+ }
168
+
169
+ .filter-btn .badge {
170
+ display: inline-block;
171
+ margin-left: 6px;
172
+ padding: 1px 6px;
173
+ border-radius: 999px;
174
+ font-size: 0.75rem;
175
+ background: rgba(255, 255, 255, 0.15);
176
+ }
177
+
178
+ .filter-btn:not(.active) .badge {
179
+ background: var(--color-border);
180
+ }
181
+
182
+ .todo-list {
183
+ display: flex;
184
+ flex-direction: column;
185
+ gap: 8px;
186
+ }
187
+
188
+ .todo-item {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 12px;
192
+ padding: 12px 16px;
193
+ border: 1px solid var(--color-border);
194
+ border-radius: var(--radius);
195
+ background: var(--color-surface);
196
+ }
197
+
198
+ .todo-item:hover {
199
+ border-color: var(--color-border-hover);
200
+ }
201
+
202
+ .todo-item .toggle {
203
+ background: none;
204
+ border: 2px solid var(--color-text-muted);
205
+ width: 20px;
206
+ height: 20px;
207
+ border-radius: 50%;
208
+ cursor: pointer;
209
+ display: flex;
210
+ align-items: center;
211
+ justify-content: center;
212
+ color: transparent;
213
+ font-size: 0.7rem;
214
+ flex-shrink: 0;
215
+ }
216
+
217
+ .todo-item .toggle.checked {
218
+ border-color: var(--color-success);
219
+ background: var(--color-success);
220
+ color: white;
221
+ }
222
+
223
+ .todo-item .title {
224
+ flex: 1;
225
+ }
226
+
227
+ .todo-item .title.done {
228
+ text-decoration: line-through;
229
+ color: var(--color-text-muted);
230
+ }
231
+
232
+ .todo-item .time {
233
+ font-size: 0.75rem;
234
+ color: var(--color-text-dim);
235
+ }
236
+
237
+ .todo-item .delete-btn {
238
+ background: none;
239
+ border: none;
240
+ color: var(--color-text-dim);
241
+ cursor: pointer;
242
+ padding: 4px;
243
+ opacity: 0;
244
+ transition: opacity 0.15s;
245
+ }
246
+
247
+ .todo-item:hover .delete-btn {
248
+ opacity: 1;
249
+ }
250
+
251
+ .todo-item .delete-btn:hover {
252
+ color: var(--color-danger);
253
+ }
254
+
255
+ .empty-state {
256
+ padding: 48px 0;
257
+ text-align: center;
258
+ color: var(--color-text-dim);
259
+ border: 1px dashed var(--color-border);
260
+ border-radius: var(--radius);
261
+ }
262
+
263
+ .footer {
264
+ display: flex;
265
+ justify-content: space-between;
266
+ margin-top: 24px;
267
+ font-size: 0.85rem;
268
+ color: var(--color-text-muted);
269
+ }
270
+
271
+ .footer button {
272
+ background: none;
273
+ border: none;
274
+ color: var(--color-text-muted);
275
+ cursor: pointer;
276
+ }
277
+
278
+ .footer button:hover {
279
+ color: var(--color-text);
280
+ }
281
+
282
+ .branding {
283
+ margin-top: 48px;
284
+ text-align: center;
285
+ font-size: 0.75rem;
286
+ color: var(--color-text-dim);
287
+ }
@@ -0,0 +1,9 @@
1
+ // Web Worker entry point for SQLite WASM.
2
+ // Loaded automatically by the Kora store to run SQLite in a background thread.
3
+
4
+ // Import the WASM binary URL so Vite resolves it with the correct content hash.
5
+ // Without this, production builds fail because sqlite3 looks for the unhashed filename.
6
+ import sqliteWasmUrl from '@sqlite.org/sqlite-wasm/sqlite3.wasm?url'
7
+ ;(globalThis as Record<string, unknown>).__KORA_SQLITE_WASM_URL = sqliteWasmUrl
8
+
9
+ import '@korajs/store/sqlite-wasm/worker'
@@ -0,0 +1,16 @@
1
+ import { createApp as createKoraApp } from 'korajs'
2
+ import { mount } from 'svelte'
3
+ import Root from './Root.svelte'
4
+ import schema from './schema'
5
+ import './index.css'
6
+ import koraWorkerUrl from './kora-worker.ts?worker&url'
7
+
8
+ const kora = createKoraApp({
9
+ schema,
10
+ store: {
11
+ workerUrl: koraWorkerUrl,
12
+ },
13
+ devtools: true,
14
+ })
15
+
16
+ mount(Root, { target: document.getElementById('app')!, props: { kora } })