@korajs/cli 0.0.0-canary-20261008174129

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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/dist/bin.cjs +9465 -0
  4. package/dist/bin.cjs.map +1 -0
  5. package/dist/bin.js +3150 -0
  6. package/dist/bin.js.map +1 -0
  7. package/dist/chunk-5NI2FEQL.js +92 -0
  8. package/dist/chunk-5NI2FEQL.js.map +1 -0
  9. package/dist/chunk-6C4BHSRA.js +82 -0
  10. package/dist/chunk-6C4BHSRA.js.map +1 -0
  11. package/dist/chunk-7Y4J2GXP.js +796 -0
  12. package/dist/chunk-7Y4J2GXP.js.map +1 -0
  13. package/dist/chunk-BURFQVMV.js +719 -0
  14. package/dist/chunk-BURFQVMV.js.map +1 -0
  15. package/dist/chunk-TVIRMDER.js +2569 -0
  16. package/dist/chunk-TVIRMDER.js.map +1 -0
  17. package/dist/create.cjs +1368 -0
  18. package/dist/create.cjs.map +1 -0
  19. package/dist/create.js +11 -0
  20. package/dist/create.js.map +1 -0
  21. package/dist/index.cjs +3489 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.cts +772 -0
  24. package/dist/index.d.ts +772 -0
  25. package/dist/index.js +129 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lab-manager-KUDII6BT.js +280 -0
  28. package/dist/lab-manager-KUDII6BT.js.map +1 -0
  29. package/dist/schema-loader-GGHECMTM.js +9 -0
  30. package/dist/schema-loader-GGHECMTM.js.map +1 -0
  31. package/dist/spectator-manager-FTH2NFSE.js +145 -0
  32. package/dist/spectator-manager-FTH2NFSE.js.map +1 -0
  33. package/dist/studio-server-XLGZNGG5.js +1747 -0
  34. package/dist/studio-server-XLGZNGG5.js.map +1 -0
  35. package/dist/vite.cjs +351 -0
  36. package/dist/vite.cjs.map +1 -0
  37. package/dist/vite.d.cts +161 -0
  38. package/dist/vite.d.ts +161 -0
  39. package/dist/vite.js +321 -0
  40. package/dist/vite.js.map +1 -0
  41. package/package.json +79 -0
  42. package/templates/react-basic/.env.example +3 -0
  43. package/templates/react-basic/AGENTS.md +87 -0
  44. package/templates/react-basic/README.md.hbs +62 -0
  45. package/templates/react-basic/index.html.hbs +12 -0
  46. package/templates/react-basic/kora.config.ts +13 -0
  47. package/templates/react-basic/package.json.hbs +35 -0
  48. package/templates/react-basic/src/App.tsx +132 -0
  49. package/templates/react-basic/src/index.css +287 -0
  50. package/templates/react-basic/src/kora-worker.ts +9 -0
  51. package/templates/react-basic/src/main.tsx +24 -0
  52. package/templates/react-basic/src/modules/todos/todo.mutations.ts +25 -0
  53. package/templates/react-basic/src/modules/todos/todo.queries.ts +5 -0
  54. package/templates/react-basic/src/modules/todos/todo.schema.ts +10 -0
  55. package/templates/react-basic/src/modules/todos/useTodos.ts +31 -0
  56. package/templates/react-basic/src/schema.ts +9 -0
  57. package/templates/react-basic/src/vite-env.d.ts +18 -0
  58. package/templates/react-basic/tsconfig.json.hbs +14 -0
  59. package/templates/react-basic/vite.config.ts +88 -0
  60. package/templates/react-sync/.env.example +25 -0
  61. package/templates/react-sync/AGENTS.md +87 -0
  62. package/templates/react-sync/README.md.hbs +106 -0
  63. package/templates/react-sync/index.html.hbs +12 -0
  64. package/templates/react-sync/kora.config.ts +17 -0
  65. package/templates/react-sync/package.json.hbs +44 -0
  66. package/templates/react-sync/server.ts +141 -0
  67. package/templates/react-sync/src/App.tsx +160 -0
  68. package/templates/react-sync/src/auth.ts +25 -0
  69. package/templates/react-sync/src/index.css +350 -0
  70. package/templates/react-sync/src/kora-worker.ts +9 -0
  71. package/templates/react-sync/src/main.tsx +52 -0
  72. package/templates/react-sync/src/modules/todos/todo.mutations.ts +25 -0
  73. package/templates/react-sync/src/modules/todos/todo.queries.ts +5 -0
  74. package/templates/react-sync/src/modules/todos/todo.schema.ts +10 -0
  75. package/templates/react-sync/src/modules/todos/useTodos.ts +31 -0
  76. package/templates/react-sync/src/schema.ts +9 -0
  77. package/templates/react-sync/src/vite-env.d.ts +19 -0
  78. package/templates/react-sync/tsconfig.json.hbs +14 -0
  79. package/templates/react-sync/vite.config.ts +105 -0
  80. package/templates/react-tailwind/.env.example +3 -0
  81. package/templates/react-tailwind/AGENTS.md +87 -0
  82. package/templates/react-tailwind/README.md.hbs +62 -0
  83. package/templates/react-tailwind/index.html.hbs +12 -0
  84. package/templates/react-tailwind/kora.config.ts +13 -0
  85. package/templates/react-tailwind/package.json.hbs +38 -0
  86. package/templates/react-tailwind/src/App.tsx +201 -0
  87. package/templates/react-tailwind/src/index.css +7 -0
  88. package/templates/react-tailwind/src/kora-worker.ts +9 -0
  89. package/templates/react-tailwind/src/main.tsx +31 -0
  90. package/templates/react-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  91. package/templates/react-tailwind/src/modules/todos/todo.queries.ts +5 -0
  92. package/templates/react-tailwind/src/modules/todos/todo.schema.ts +10 -0
  93. package/templates/react-tailwind/src/modules/todos/useTodos.ts +31 -0
  94. package/templates/react-tailwind/src/schema.ts +9 -0
  95. package/templates/react-tailwind/src/vite-env.d.ts +18 -0
  96. package/templates/react-tailwind/tsconfig.json.hbs +14 -0
  97. package/templates/react-tailwind/vite.config.ts +95 -0
  98. package/templates/react-tailwind-sync/.env.example +25 -0
  99. package/templates/react-tailwind-sync/AGENTS.md +89 -0
  100. package/templates/react-tailwind-sync/README.md.hbs +106 -0
  101. package/templates/react-tailwind-sync/index.html.hbs +12 -0
  102. package/templates/react-tailwind-sync/kora.config.ts +17 -0
  103. package/templates/react-tailwind-sync/package.json.hbs +47 -0
  104. package/templates/react-tailwind-sync/server.ts +141 -0
  105. package/templates/react-tailwind-sync/src/App.tsx +293 -0
  106. package/templates/react-tailwind-sync/src/auth.ts +25 -0
  107. package/templates/react-tailwind-sync/src/index.css +7 -0
  108. package/templates/react-tailwind-sync/src/kora-worker.ts +9 -0
  109. package/templates/react-tailwind-sync/src/kora.ts +32 -0
  110. package/templates/react-tailwind-sync/src/main.tsx +44 -0
  111. package/templates/react-tailwind-sync/src/modules/todos/todo.mutations.ts +21 -0
  112. package/templates/react-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  113. package/templates/react-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  114. package/templates/react-tailwind-sync/src/modules/todos/useTodos.ts +31 -0
  115. package/templates/react-tailwind-sync/src/schema.ts +9 -0
  116. package/templates/react-tailwind-sync/src/vite-env.d.ts +19 -0
  117. package/templates/react-tailwind-sync/tsconfig.json.hbs +14 -0
  118. package/templates/react-tailwind-sync/vite.config.ts +112 -0
  119. package/templates/svelte-basic/.env.example +3 -0
  120. package/templates/svelte-basic/AGENTS.md +76 -0
  121. package/templates/svelte-basic/README.md.hbs +62 -0
  122. package/templates/svelte-basic/index.html.hbs +12 -0
  123. package/templates/svelte-basic/kora.config.ts +12 -0
  124. package/templates/svelte-basic/package.json.hbs +33 -0
  125. package/templates/svelte-basic/src/App.svelte +128 -0
  126. package/templates/svelte-basic/src/Root.svelte +14 -0
  127. package/templates/svelte-basic/src/index.css +287 -0
  128. package/templates/svelte-basic/src/kora-worker.ts +9 -0
  129. package/templates/svelte-basic/src/main.ts +16 -0
  130. package/templates/svelte-basic/src/modules/todos/todo.mutations.ts +25 -0
  131. package/templates/svelte-basic/src/modules/todos/todo.queries.ts +5 -0
  132. package/templates/svelte-basic/src/modules/todos/todo.schema.ts +10 -0
  133. package/templates/svelte-basic/src/modules/todos/useTodos.ts +25 -0
  134. package/templates/svelte-basic/src/schema.ts +9 -0
  135. package/templates/svelte-basic/src/vite-env.d.ts +12 -0
  136. package/templates/svelte-basic/svelte.config.js +5 -0
  137. package/templates/svelte-basic/tsconfig.json.hbs +14 -0
  138. package/templates/svelte-basic/vite.config.ts +57 -0
  139. package/templates/svelte-sync/.env.example +25 -0
  140. package/templates/svelte-sync/AGENTS.md +76 -0
  141. package/templates/svelte-sync/README.md.hbs +106 -0
  142. package/templates/svelte-sync/index.html.hbs +12 -0
  143. package/templates/svelte-sync/kora.config.ts +17 -0
  144. package/templates/svelte-sync/package.json.hbs +43 -0
  145. package/templates/svelte-sync/server.ts +141 -0
  146. package/templates/svelte-sync/src/App.svelte +151 -0
  147. package/templates/svelte-sync/src/Root.svelte +22 -0
  148. package/templates/svelte-sync/src/auth.ts +25 -0
  149. package/templates/svelte-sync/src/index.css +339 -0
  150. package/templates/svelte-sync/src/kora-worker.ts +9 -0
  151. package/templates/svelte-sync/src/main.ts +32 -0
  152. package/templates/svelte-sync/src/modules/todos/todo.mutations.ts +25 -0
  153. package/templates/svelte-sync/src/modules/todos/todo.queries.ts +5 -0
  154. package/templates/svelte-sync/src/modules/todos/todo.schema.ts +10 -0
  155. package/templates/svelte-sync/src/modules/todos/useTodos.ts +25 -0
  156. package/templates/svelte-sync/src/schema.ts +9 -0
  157. package/templates/svelte-sync/src/vite-env.d.ts +22 -0
  158. package/templates/svelte-sync/svelte.config.js +5 -0
  159. package/templates/svelte-sync/tsconfig.json.hbs +14 -0
  160. package/templates/svelte-sync/vite.config.ts +91 -0
  161. package/templates/svelte-tailwind/AGENTS.md +76 -0
  162. package/templates/svelte-tailwind/README.md.hbs +62 -0
  163. package/templates/svelte-tailwind/index.html.hbs +12 -0
  164. package/templates/svelte-tailwind/kora.config.ts +12 -0
  165. package/templates/svelte-tailwind/package.json.hbs +36 -0
  166. package/templates/svelte-tailwind/src/App.svelte +76 -0
  167. package/templates/svelte-tailwind/src/Root.svelte +14 -0
  168. package/templates/svelte-tailwind/src/index.css +7 -0
  169. package/templates/svelte-tailwind/src/kora-worker.ts +9 -0
  170. package/templates/svelte-tailwind/src/main.ts +16 -0
  171. package/templates/svelte-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  172. package/templates/svelte-tailwind/src/modules/todos/todo.queries.ts +5 -0
  173. package/templates/svelte-tailwind/src/modules/todos/todo.schema.ts +10 -0
  174. package/templates/svelte-tailwind/src/modules/todos/useTodos.ts +25 -0
  175. package/templates/svelte-tailwind/src/schema.ts +9 -0
  176. package/templates/svelte-tailwind/src/vite-env.d.ts +12 -0
  177. package/templates/svelte-tailwind/svelte.config.js +5 -0
  178. package/templates/svelte-tailwind/tsconfig.json.hbs +14 -0
  179. package/templates/svelte-tailwind/vite.config.ts +64 -0
  180. package/templates/svelte-tailwind-sync/.env.example +25 -0
  181. package/templates/svelte-tailwind-sync/AGENTS.md +76 -0
  182. package/templates/svelte-tailwind-sync/README.md.hbs +106 -0
  183. package/templates/svelte-tailwind-sync/index.html.hbs +12 -0
  184. package/templates/svelte-tailwind-sync/kora.config.ts +17 -0
  185. package/templates/svelte-tailwind-sync/package.json.hbs +46 -0
  186. package/templates/svelte-tailwind-sync/server.ts +141 -0
  187. package/templates/svelte-tailwind-sync/src/App.svelte +193 -0
  188. package/templates/svelte-tailwind-sync/src/Root.svelte +26 -0
  189. package/templates/svelte-tailwind-sync/src/auth.ts +25 -0
  190. package/templates/svelte-tailwind-sync/src/index.css +7 -0
  191. package/templates/svelte-tailwind-sync/src/kora-worker.ts +9 -0
  192. package/templates/svelte-tailwind-sync/src/main.ts +32 -0
  193. package/templates/svelte-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  194. package/templates/svelte-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  195. package/templates/svelte-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  196. package/templates/svelte-tailwind-sync/src/modules/todos/useTodos.ts +25 -0
  197. package/templates/svelte-tailwind-sync/src/schema.ts +9 -0
  198. package/templates/svelte-tailwind-sync/src/vite-env.d.ts +22 -0
  199. package/templates/svelte-tailwind-sync/svelte.config.js +5 -0
  200. package/templates/svelte-tailwind-sync/tsconfig.json.hbs +14 -0
  201. package/templates/svelte-tailwind-sync/vite.config.ts +98 -0
  202. package/templates/tauri-react/.env.example +27 -0
  203. package/templates/tauri-react/.github/workflows/release-desktop.yml +107 -0
  204. package/templates/tauri-react/AGENTS.md +91 -0
  205. package/templates/tauri-react/README.md.hbs +183 -0
  206. package/templates/tauri-react/dev.ts +37 -0
  207. package/templates/tauri-react/index.html.hbs +16 -0
  208. package/templates/tauri-react/kora.config.ts +17 -0
  209. package/templates/tauri-react/package.json.hbs +46 -0
  210. package/templates/tauri-react/server.ts +148 -0
  211. package/templates/tauri-react/src/App.tsx +803 -0
  212. package/templates/tauri-react/src/AppShell.tsx +158 -0
  213. package/templates/tauri-react/src/SetupScreen.tsx +288 -0
  214. package/templates/tauri-react/src/auth.ts +45 -0
  215. package/templates/tauri-react/src/main.tsx +9 -0
  216. package/templates/tauri-react/src/modules/todos/todo.mutations.ts +25 -0
  217. package/templates/tauri-react/src/modules/todos/todo.queries.ts +5 -0
  218. package/templates/tauri-react/src/modules/todos/todo.schema.ts +10 -0
  219. package/templates/tauri-react/src/modules/todos/useTodos.ts +31 -0
  220. package/templates/tauri-react/src/schema.ts +9 -0
  221. package/templates/tauri-react/src/sync-config.ts +103 -0
  222. package/templates/tauri-react/src/updater.ts +38 -0
  223. package/templates/tauri-react/src/vite-env.d.ts +19 -0
  224. package/templates/tauri-react/src-tauri/Cargo.toml +19 -0
  225. package/templates/tauri-react/src-tauri/build.rs +3 -0
  226. package/templates/tauri-react/src-tauri/capabilities/default.json +12 -0
  227. package/templates/tauri-react/src-tauri/icons/128x128.png +0 -0
  228. package/templates/tauri-react/src-tauri/icons/128x128@2x.png +0 -0
  229. package/templates/tauri-react/src-tauri/icons/32x32.png +0 -0
  230. package/templates/tauri-react/src-tauri/icons/icon.icns +0 -0
  231. package/templates/tauri-react/src-tauri/icons/icon.ico +0 -0
  232. package/templates/tauri-react/src-tauri/src/lib.rs +8 -0
  233. package/templates/tauri-react/src-tauri/src/main.rs +6 -0
  234. package/templates/tauri-react/src-tauri/tauri.conf.json +44 -0
  235. package/templates/tauri-react/tsconfig.json.hbs +14 -0
  236. package/templates/tauri-react/vite.config.ts +15 -0
  237. package/templates/vue-basic/AGENTS.md +84 -0
  238. package/templates/vue-basic/README.md.hbs +62 -0
  239. package/templates/vue-basic/index.html.hbs +12 -0
  240. package/templates/vue-basic/kora.config.ts +12 -0
  241. package/templates/vue-basic/package.json.hbs +32 -0
  242. package/templates/vue-basic/src/App.vue +117 -0
  243. package/templates/vue-basic/src/index.css +287 -0
  244. package/templates/vue-basic/src/kora-worker.ts +9 -0
  245. package/templates/vue-basic/src/main.ts +20 -0
  246. package/templates/vue-basic/src/modules/todos/todo.mutations.ts +25 -0
  247. package/templates/vue-basic/src/modules/todos/todo.queries.ts +5 -0
  248. package/templates/vue-basic/src/modules/todos/todo.schema.ts +10 -0
  249. package/templates/vue-basic/src/modules/todos/useTodos.ts +32 -0
  250. package/templates/vue-basic/src/schema.ts +9 -0
  251. package/templates/vue-basic/src/vite-env.d.ts +17 -0
  252. package/templates/vue-basic/tsconfig.json.hbs +15 -0
  253. package/templates/vue-basic/vite.config.ts +74 -0
  254. package/templates/vue-sync/.env.example +25 -0
  255. package/templates/vue-sync/AGENTS.md +84 -0
  256. package/templates/vue-sync/README.md.hbs +106 -0
  257. package/templates/vue-sync/index.html.hbs +12 -0
  258. package/templates/vue-sync/kora.config.ts +17 -0
  259. package/templates/vue-sync/package.json.hbs +42 -0
  260. package/templates/vue-sync/server.ts +141 -0
  261. package/templates/vue-sync/src/App.vue +135 -0
  262. package/templates/vue-sync/src/auth.ts +25 -0
  263. package/templates/vue-sync/src/index.css +339 -0
  264. package/templates/vue-sync/src/kora-worker.ts +9 -0
  265. package/templates/vue-sync/src/main.ts +47 -0
  266. package/templates/vue-sync/src/modules/todos/todo.mutations.ts +25 -0
  267. package/templates/vue-sync/src/modules/todos/todo.queries.ts +5 -0
  268. package/templates/vue-sync/src/modules/todos/todo.schema.ts +10 -0
  269. package/templates/vue-sync/src/modules/todos/useTodos.ts +32 -0
  270. package/templates/vue-sync/src/schema.ts +9 -0
  271. package/templates/vue-sync/src/vite-env.d.ts +27 -0
  272. package/templates/vue-sync/tsconfig.json.hbs +15 -0
  273. package/templates/vue-sync/vite.config.ts +91 -0
  274. package/templates/vue-tailwind/AGENTS.md +84 -0
  275. package/templates/vue-tailwind/README.md.hbs +62 -0
  276. package/templates/vue-tailwind/index.html.hbs +12 -0
  277. package/templates/vue-tailwind/kora.config.ts +12 -0
  278. package/templates/vue-tailwind/package.json.hbs +35 -0
  279. package/templates/vue-tailwind/src/App.vue +135 -0
  280. package/templates/vue-tailwind/src/index.css +7 -0
  281. package/templates/vue-tailwind/src/kora-worker.ts +9 -0
  282. package/templates/vue-tailwind/src/main.ts +31 -0
  283. package/templates/vue-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  284. package/templates/vue-tailwind/src/modules/todos/todo.queries.ts +5 -0
  285. package/templates/vue-tailwind/src/modules/todos/todo.schema.ts +10 -0
  286. package/templates/vue-tailwind/src/modules/todos/useTodos.ts +32 -0
  287. package/templates/vue-tailwind/src/schema.ts +9 -0
  288. package/templates/vue-tailwind/src/vite-env.d.ts +17 -0
  289. package/templates/vue-tailwind/tsconfig.json.hbs +15 -0
  290. package/templates/vue-tailwind/vite.config.ts +75 -0
  291. package/templates/vue-tailwind-sync/.env.example +25 -0
  292. package/templates/vue-tailwind-sync/AGENTS.md +84 -0
  293. package/templates/vue-tailwind-sync/README.md.hbs +106 -0
  294. package/templates/vue-tailwind-sync/index.html.hbs +12 -0
  295. package/templates/vue-tailwind-sync/kora.config.ts +17 -0
  296. package/templates/vue-tailwind-sync/package.json.hbs +45 -0
  297. package/templates/vue-tailwind-sync/server.ts +141 -0
  298. package/templates/vue-tailwind-sync/src/App.vue +245 -0
  299. package/templates/vue-tailwind-sync/src/auth.ts +25 -0
  300. package/templates/vue-tailwind-sync/src/index.css +7 -0
  301. package/templates/vue-tailwind-sync/src/kora-worker.ts +9 -0
  302. package/templates/vue-tailwind-sync/src/main.ts +56 -0
  303. package/templates/vue-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  304. package/templates/vue-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  305. package/templates/vue-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  306. package/templates/vue-tailwind-sync/src/modules/todos/useTodos.ts +32 -0
  307. package/templates/vue-tailwind-sync/src/schema.ts +9 -0
  308. package/templates/vue-tailwind-sync/src/vite-env.d.ts +27 -0
  309. package/templates/vue-tailwind-sync/tsconfig.json.hbs +15 -0
  310. package/templates/vue-tailwind-sync/vite.config.ts +92 -0
@@ -0,0 +1,132 @@
1
+ import { useState } from 'react'
2
+ import { useTodos } from './modules/todos/useTodos'
3
+
4
+ type Filter = 'all' | 'active' | 'completed'
5
+
6
+ export function App() {
7
+ const { allTodos, activeTodos, completedTodos, addTodo, toggleTodo, deleteTodo } = useTodos()
8
+
9
+ const [filter, setFilter] = useState<Filter>('all')
10
+ const [input, setInput] = useState('')
11
+
12
+ const filteredTodos =
13
+ filter === 'active' ? activeTodos : filter === 'completed' ? completedTodos : allTodos
14
+
15
+ const handleSubmit = (e: React.FormEvent) => {
16
+ e.preventDefault()
17
+ const title = input.trim()
18
+ if (title) {
19
+ addTodo.mutate({ title })
20
+ setInput('')
21
+ }
22
+ }
23
+
24
+ const clearCompleted = () => {
25
+ for (const todo of completedTodos) {
26
+ deleteTodo.mutate(todo.id)
27
+ }
28
+ }
29
+
30
+ return (
31
+ <div className="app">
32
+ <div className="header">
33
+ <h1>My Tasks</h1>
34
+ <span className="local-badge">Local-first</span>
35
+ </div>
36
+
37
+ <div className="stats">
38
+ <div className="stat-card">
39
+ <div className="label">Total</div>
40
+ <div className="value muted">{allTodos.length}</div>
41
+ </div>
42
+ <div className="stat-card">
43
+ <div className="label">Remaining</div>
44
+ <div className="value warning">{activeTodos.length}</div>
45
+ </div>
46
+ <div className="stat-card">
47
+ <div className="label">Done</div>
48
+ <div className="value success">{completedTodos.length}</div>
49
+ </div>
50
+ </div>
51
+
52
+ <form className="add-form" onSubmit={handleSubmit}>
53
+ <input
54
+ type="text"
55
+ value={input}
56
+ onChange={(e) => setInput(e.target.value)}
57
+ placeholder="What needs to be done?"
58
+ />
59
+ <button type="submit" disabled={!input.trim()}>
60
+ Add
61
+ </button>
62
+ </form>
63
+
64
+ <div className="filters">
65
+ {(['all', 'active', 'completed'] as const).map((f) => {
66
+ const count =
67
+ f === 'all'
68
+ ? allTodos.length
69
+ : f === 'active'
70
+ ? activeTodos.length
71
+ : completedTodos.length
72
+ return (
73
+ <button
74
+ key={f}
75
+ className={`filter-btn ${filter === f ? 'active' : ''}`}
76
+ onClick={() => setFilter(f)}
77
+ >
78
+ {f.charAt(0).toUpperCase() + f.slice(1)}
79
+ <span className="badge">{count}</span>
80
+ </button>
81
+ )
82
+ })}
83
+ </div>
84
+
85
+ <div className="todo-list">
86
+ {filteredTodos.length === 0 ? (
87
+ <div className="empty-state">
88
+ {filter === 'all' && 'No tasks yet. Add one above!'}
89
+ {filter === 'active' && 'All caught up! No active tasks.'}
90
+ {filter === 'completed' && 'No completed tasks yet.'}
91
+ </div>
92
+ ) : (
93
+ filteredTodos.map((todo) => (
94
+ <div key={todo.id} className="todo-item">
95
+ <button
96
+ className={`toggle ${todo.completed ? 'checked' : ''}`}
97
+ onClick={() => toggleTodo.mutate(todo.id, { completed: !todo.completed })}
98
+ >
99
+ {todo.completed ? '\u2713' : ''}
100
+ </button>
101
+ <span className={`title ${todo.completed ? 'done' : ''}`}>{String(todo.title)}</span>
102
+ {todo.createdAt && <span className="time">{formatTime(Number(todo.createdAt))}</span>}
103
+ <button className="delete-btn" onClick={() => deleteTodo.mutate(todo.id)}>
104
+ \u00d7
105
+ </button>
106
+ </div>
107
+ ))
108
+ )}
109
+ </div>
110
+
111
+ {allTodos.length > 0 && (
112
+ <div className="footer">
113
+ <span>
114
+ {activeTodos.length} item{activeTodos.length !== 1 ? 's' : ''} left
115
+ </span>
116
+ {completedTodos.length > 0 && <button onClick={clearCompleted}>Clear completed</button>}
117
+ </div>
118
+ )}
119
+
120
+ <p className="branding">Powered by Kora &mdash; offline-first, real-time sync</p>
121
+ </div>
122
+ )
123
+ }
124
+
125
+ function formatTime(timestamp: number): string {
126
+ const date = new Date(timestamp)
127
+ const now = new Date()
128
+ if (date.toDateString() === now.toDateString()) {
129
+ return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
130
+ }
131
+ return date.toLocaleDateString([], { month: 'short', day: 'numeric' })
132
+ }
@@ -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,24 @@
1
+ import { KoraProvider } from '@korajs/react'
2
+ import { createApp } from 'korajs'
3
+ import { StrictMode } from 'react'
4
+ import { createRoot } from 'react-dom/client'
5
+ import { App } from './App'
6
+ import schema from './schema'
7
+ import './index.css'
8
+ import koraWorkerUrl from './kora-worker.ts?worker&url'
9
+
10
+ const app = createApp({
11
+ schema,
12
+ store: {
13
+ workerUrl: koraWorkerUrl,
14
+ },
15
+ devtools: import.meta.env.DEV,
16
+ })
17
+
18
+ createRoot(document.getElementById('root')!).render(
19
+ <StrictMode>
20
+ <KoraProvider app={app} fallback={<div>Loading...</div>}>
21
+ <App />
22
+ </KoraProvider>
23
+ </StrictMode>,
24
+ )
@@ -0,0 +1,25 @@
1
+ import type { CollectionAccessor } from 'korajs'
2
+
3
+ export interface CreateTodoInput {
4
+ title: string
5
+ }
6
+
7
+ export interface UpdateTodoStatusInput {
8
+ completed: boolean
9
+ }
10
+
11
+ export function createTodo(todos: CollectionAccessor, data: CreateTodoInput) {
12
+ return todos.insert({ title: data.title })
13
+ }
14
+
15
+ export function updateTodoStatus(
16
+ todos: CollectionAccessor,
17
+ id: string,
18
+ data: UpdateTodoStatusInput,
19
+ ) {
20
+ return todos.update(id, { completed: data.completed })
21
+ }
22
+
23
+ export function deleteTodo(todos: CollectionAccessor, id: string) {
24
+ return todos.delete(id)
25
+ }
@@ -0,0 +1,5 @@
1
+ import type { CollectionAccessor } from 'korajs'
2
+
3
+ export function orderedTodos(todos: CollectionAccessor) {
4
+ return todos.where({}).orderBy('createdAt', 'desc')
5
+ }
@@ -0,0 +1,10 @@
1
+ import { t } from 'korajs'
2
+
3
+ export const todos = {
4
+ fields: {
5
+ title: t.string(),
6
+ completed: t.boolean().default(false),
7
+ createdAt: t.timestamp().auto(),
8
+ },
9
+ indexes: ['completed', 'createdAt'],
10
+ }
@@ -0,0 +1,31 @@
1
+ import { useCollection, useMutation, useQuery } from '@korajs/react'
2
+ import {
3
+ type CreateTodoInput,
4
+ type UpdateTodoStatusInput,
5
+ createTodo,
6
+ deleteTodo,
7
+ updateTodoStatus,
8
+ } from './todo.mutations'
9
+ import { orderedTodos } from './todo.queries'
10
+
11
+ export function useTodos() {
12
+ const todos = useCollection('todos')
13
+ const allTodos = useQuery(orderedTodos(todos))
14
+ const addTodo = useMutation((data: CreateTodoInput) => createTodo(todos, data))
15
+ const toggleTodo = useMutation((id: string, data: UpdateTodoStatusInput) =>
16
+ updateTodoStatus(todos, id, data),
17
+ )
18
+ const removeTodo = useMutation((id: string) => deleteTodo(todos, id))
19
+
20
+ const activeTodos = allTodos.filter((todo) => !todo.completed)
21
+ const completedTodos = allTodos.filter((todo) => !!todo.completed)
22
+
23
+ return {
24
+ allTodos,
25
+ activeTodos,
26
+ completedTodos,
27
+ addTodo,
28
+ toggleTodo,
29
+ deleteTodo: removeTodo,
30
+ }
31
+ }
@@ -0,0 +1,9 @@
1
+ import { defineSchema } from 'korajs'
2
+ import { todos } from './modules/todos/todo.schema'
3
+
4
+ export default defineSchema({
5
+ version: 1,
6
+ collections: {
7
+ todos,
8
+ },
9
+ })
@@ -0,0 +1,18 @@
1
+ interface ImportMetaEnv {
2
+ readonly DEV: boolean
3
+ readonly VITE_SYNC_URL?: string
4
+ }
5
+
6
+ interface ImportMeta {
7
+ readonly env: ImportMetaEnv
8
+ }
9
+
10
+ declare module '*?worker&url' {
11
+ const url: string
12
+ export default url
13
+ }
14
+
15
+ declare module '*?url' {
16
+ const url: string
17
+ export default url
18
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "noUncheckedIndexedAccess": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "outDir": "dist"
12
+ },
13
+ "include": ["src"]
14
+ }
@@ -0,0 +1,88 @@
1
+ import { copyFileSync, existsSync, readdirSync } from 'node:fs'
2
+ import { join, resolve } from 'node:path'
3
+ import { koraServiceWorker } from '@korajs/cli/vite'
4
+ import react from '@vitejs/plugin-react'
5
+ import type { Plugin } from 'vite'
6
+ import { defineConfig } from 'vite'
7
+
8
+ /**
9
+ * Adds Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers
10
+ * enabling SharedArrayBuffer where browsers support it while keeping common
11
+ * third-party embeds such as YouTube and Vimeo usable. Kora falls back to
12
+ * durable IndexedDB storage when SharedArrayBuffer is unavailable.
13
+ */
14
+ function crossOriginIsolation(): Plugin {
15
+ const embedderPolicy = process.env.KORA_COEP_POLICY || 'credentialless'
16
+
17
+ return {
18
+ name: 'cross-origin-isolation',
19
+ configureServer(server) {
20
+ server.middlewares.use((_req, res, next) => {
21
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
22
+ res.setHeader('Cross-Origin-Embedder-Policy', embedderPolicy)
23
+ next()
24
+ })
25
+ },
26
+ configurePreviewServer(server) {
27
+ server.middlewares.use((_req, res, next) => {
28
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
29
+ res.setHeader('Cross-Origin-Embedder-Policy', embedderPolicy)
30
+ next()
31
+ })
32
+ },
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Fixes sqlite3 WASM assets in production builds:
38
+ * 1. Copies the hashed sqlite3.wasm to an unhashed name so Emscripten's locateFile works
39
+ * 2. Copies sqlite3-opfs-async-proxy.js from node_modules (not bundled by Vite since
40
+ * sqlite3 loads it dynamically) so the full OPFS VFS can initialize without errors
41
+ */
42
+ function sqliteWasmHotfix(): Plugin {
43
+ return {
44
+ name: 'sqlite-wasm-hotfix',
45
+ apply: 'build',
46
+ closeBundle() {
47
+ const assetsDir = resolve('dist', 'assets')
48
+ if (!existsSync(assetsDir)) return
49
+
50
+ // Copy hashed sqlite3.wasm to unhashed name
51
+ for (const file of readdirSync(assetsDir)) {
52
+ if (/^sqlite3-.+\.wasm$/.test(file)) {
53
+ copyFileSync(join(assetsDir, file), join(assetsDir, 'sqlite3.wasm'))
54
+ break
55
+ }
56
+ }
57
+
58
+ // Copy OPFS async proxy worker (dynamically loaded by sqlite3, not detected by Vite)
59
+ const proxyFile = resolve(
60
+ 'node_modules',
61
+ '@sqlite.org',
62
+ 'sqlite-wasm',
63
+ 'sqlite-wasm',
64
+ 'jswasm',
65
+ 'sqlite3-opfs-async-proxy.js',
66
+ )
67
+ if (existsSync(proxyFile)) {
68
+ copyFileSync(proxyFile, join(assetsDir, 'sqlite3-opfs-async-proxy.js'))
69
+ }
70
+ },
71
+ }
72
+ }
73
+
74
+ // koraServiceWorker() (build only, keep it last): precaches the built app shell so the
75
+ // app opens offline, with a "new version available" reload prompt. See @korajs/cli/vite.
76
+ export default defineConfig({
77
+ plugins: [react(), crossOriginIsolation(), sqliteWasmHotfix(), koraServiceWorker()],
78
+ worker: {
79
+ format: 'es',
80
+ },
81
+ optimizeDeps: {
82
+ exclude: ['@sqlite.org/sqlite-wasm', '@korajs/store'],
83
+ include: ['yjs'],
84
+ },
85
+ resolve: {
86
+ dedupe: ['yjs'],
87
+ },
88
+ })
@@ -0,0 +1,25 @@
1
+ # Kora Sync Server
2
+ # WebSocket URL for the sync server (used by the client)
3
+ VITE_SYNC_URL=ws://localhost:3001
4
+
5
+ # Sync server port
6
+ PORT=3001
7
+
8
+ # Server database
9
+ # Leave DATABASE_URL empty to use SQLite at KORA_SERVER_DB.
10
+ KORA_SERVER_DB=./.kora/kora-server.db
11
+ DATABASE_URL=
12
+
13
+ # Operational endpoints (/__kora, /__kora/status, /__kora/events, /__kora/metrics, backups)
14
+ # Set these in production. Use Authorization: Bearer <token> when calling protected endpoints.
15
+ KORA_ADMIN_TOKEN=
16
+ KORA_METRICS_TOKEN=
17
+ KORA_BACKUP_TOKEN=
18
+
19
+ # Optional sync path
20
+ KORA_SYNC_PATH=/kora-sync
21
+
22
+ # Auth
23
+ # Set this to enable built-in /auth/* routes and authenticated sync.
24
+ # Generate with: node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"
25
+ KORA_AUTH_SECRET=