create-tinybase 1.0.0 → 1.1.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 (48) hide show
  1. package/cli.js +2 -2
  2. package/package.json +2 -1
  3. package/screenshots/chat.png +0 -0
  4. package/screenshots/drawing.png +0 -0
  5. package/screenshots/game.png +0 -0
  6. package/screenshots/todos.png +0 -0
  7. package/templates/README.md.hbs +18 -19
  8. package/templates/client/.npmrc.hbs +1 -0
  9. package/templates/client/index.html.hbs +46 -9
  10. package/templates/client/package.json.hbs +28 -19
  11. package/templates/client/public/browser.svg +81 -0
  12. package/templates/client/public/js.svg +5 -0
  13. package/templates/client/public/pglite.svg +8 -0
  14. package/templates/client/public/react.svg +9 -0
  15. package/templates/client/public/sqlite.svg +58 -0
  16. package/templates/client/public/svelte.svg +23 -0
  17. package/templates/client/public/sync.svg +4 -0
  18. package/templates/client/public/ts.svg +6 -0
  19. package/templates/client/src/chat/App.svelte.hbs +31 -0
  20. package/templates/client/src/chat/Message.svelte.hbs +17 -0
  21. package/templates/client/src/chat/MessageInput.svelte.hbs +53 -0
  22. package/templates/client/src/chat/Messages.svelte.hbs +34 -0
  23. package/templates/client/src/chat/UsernameInput.svelte.hbs +45 -0
  24. package/templates/client/src/drawing/App.svelte.hbs +28 -0
  25. package/templates/client/src/drawing/BrushSize.svelte.hbs +45 -0
  26. package/templates/client/src/drawing/BrushSize.tsx.hbs +2 -2
  27. package/templates/client/src/drawing/Canvas.svelte.hbs +145 -0
  28. package/templates/client/src/drawing/ColorPicker.svelte.hbs +39 -0
  29. package/templates/client/src/drawing/DrawingControls.svelte.hbs +22 -0
  30. package/templates/client/src/game/App.svelte.hbs +23 -0
  31. package/templates/client/src/game/Board.svelte.hbs +71 -0
  32. package/templates/client/src/game/Game.svelte.hbs +64 -0
  33. package/templates/client/src/game/GameStatus.svelte.hbs +37 -0
  34. package/templates/client/src/game/GameStatus.tsx.hbs +2 -2
  35. package/templates/client/src/game/Square.svelte.hbs +25 -0
  36. package/templates/client/src/game/Square.tsx.hbs +1 -1
  37. package/templates/client/src/game/gameStatus.ts.hbs +2 -2
  38. package/templates/client/src/index.tsx.hbs +16 -5
  39. package/templates/client/src/shared/Loading.svelte.hbs +7 -0
  40. package/templates/client/src/shared/sqlite.ts.hbs +5 -2
  41. package/templates/client/src/todos/App.svelte.hbs +26 -0
  42. package/templates/client/src/todos/TodoInput.svelte.hbs +45 -0
  43. package/templates/client/src/todos/TodoItem.svelte.hbs +47 -0
  44. package/templates/client/src/todos/TodoList.svelte.hbs +29 -0
  45. package/templates/client/tsconfig.json.hbs +7 -1
  46. package/templates/client/vite-env.d.ts.hbs +11 -1
  47. package/templates/client/vite.config.js.hbs +8 -2
  48. package/templates/server/package.json.hbs +4 -4
package/cli.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{existsSync as f}from"fs";import{dirname as k,join as u}from"path";import{createCLI as x,detectPackageManager as R}from"tinycreate";import{fileURLToPath as S}from"url";const P=k(S(import.meta.url)),C={welcomeMessage:`\u{1F389} Welcome to TinyBase!
3
- `,questions:[{type:"text",name:"projectName",message:"Project name:",initial:"my-tinybase-app",validate:e=>{if(e.length===0)return"Project name is required";const t=u(process.cwd(),e);return f(t)?`Directory "${e}" already exists. Please choose a different name.`:!0}},{type:"select",name:"appType",message:"App type:",choices:[{title:"Todo app",value:"todos"},{title:"Chat app",value:"chat"},{title:"Drawing app",value:"drawing"},{title:"Tic-tac-toe game",value:"game"}],initial:0},{type:"select",name:"language",message:"Language:",choices:[{title:"TypeScript",value:"typescript"},{title:"JavaScript",value:"javascript"}],initial:0},{type:"select",name:"framework",message:"Framework:",choices:[{title:"React",value:"react"},{title:"Vanilla",value:"vanilla"}],initial:0},{type:(e,t)=>t.language==="typescript"?"confirm":null,name:"schemas",message:"Include store schemas?",initial:!1},{type:"select",name:"syncType",message:"Synchronization:",choices:[{title:"None",value:"none"},{title:"Via remote demo server (stateless)",value:"remote"},{title:"Via local node server (stateless)",value:"node"},{title:"Via local DurableObjects server (stateful)",value:"durable-objects"}],initial:1},{type:"select",name:"persistenceType",message:"Persistence:",choices:[{title:"None",value:"none"},{title:"Local Storage",value:"local-storage"},{title:"SQLite",value:"sqlite"},{title:"PGlite",value:"pglite"}],initial:1},{type:"confirm",name:"prettier",message:"Include Prettier?",initial:!0},{type:"confirm",name:"eslint",message:"Include ESLint?",initial:!0},{type:(e,t)=>t.syncType!=="node"&&t.syncType!=="durable-objects"?"confirm":null,name:"installAndRun",message:"Install dependencies and start dev server?",initial:!0}],createContext:e=>{const{projectName:t,language:s,framework:n,appType:o,prettier:m,eslint:g,schemas:l,syncType:d,persistenceType:y,installAndRun:p}=e,i=s==="typescript",v=!i,c=n==="react",b=i?c?"tsx":"ts":c?"jsx":"js",a=d||"remote",T=a!=="none",h=a==="node"||a==="durable-objects",j=a==="durable-objects"?"durable-objects":"node",w=a==="durable-objects",r=y||"local-storage";return{projectName:t,language:s,framework:n,appType:o,prettier:m,eslint:g,schemas:i&&(l===!0||l==="true"),syncType:a,sync:T,server:h,serverType:j,isDurableObject:w,persistenceType:r,persist:r!=="none",persistLocalStorage:r==="local-storage",persistSqlite:r==="sqlite",persistPglite:r==="pglite",installAndRun:p===!0||p==="true",typescript:i,javascript:v,react:c,ext:b}},createDirectories:async(e,t)=>{const{mkdir:s}=await import("fs/promises"),{join:n}=await import("path"),o=t.server;await s(n(e,"client/src"),{recursive:!0}),await s(n(e,"client/public"),{recursive:!0}),o&&await s(n(e,"server"),{recursive:!0})},getFiles:()=>[{template:"README.md.hbs",output:"README.md",prettier:!0}],processIncludedFile:(e,t)=>{const{javascript:s}=t,n=e.prettier??/\.(js|jsx|ts|tsx|css|json|html|md)$/.test(e.output),o=e.transpile??(/\.(ts|tsx)\.hbs$/.test(e.template)&&s===!0);return{...e,prettier:n,transpile:o}},templateRoot:u(P,"templates"),installCommand:"{pm} install",devCommand:"{pm} run dev",workingDirectory:"client",onSuccess:(e,t)=>{const s=t.syncType,n=s==="node"||s==="durable-objects",o=R();console.log("Next steps:"),console.log(),n&&(console.log("To run the server:"),console.log(` cd ${e}/server`),console.log(` ${o} install`),console.log(` ${o} run dev`),console.log()),console.log("To run the client:"),console.log(` cd ${e}/client`),console.log(` ${o} install`),console.log(` ${o} run dev`)}};x(C).catch(e=>{console.error(e),process.exit(1)});
2
+ import{existsSync as q}from"fs";import{dirname as N,join as b}from"path";import{createCLI as z,detectPackageManager as J}from"tinycreate";import{fileURLToPath as B}from"url";const O=N(B(import.meta.url)),_={welcomeMessage:`\u{1F389} Welcome to TinyBase!
3
+ `,questions:[{type:"text",name:"projectName",message:"Project name:",initial:"my-tinybase-app",validate:e=>{if(e.length===0)return"Project name is required";const s=b(process.cwd(),e);return q(s)?`Directory "${e}" already exists. Please choose a different name.`:!0}},{type:"select",name:"appType",message:"App type:",choices:[{title:"Todo app",value:"todos"},{title:"Chat app",value:"chat"},{title:"Drawing app",value:"drawing"},{title:"Tic-tac-toe game",value:"game"}],initial:0},{type:"select",name:"language",message:"Language:",choices:[{title:"TypeScript",value:"typescript"},{title:"JavaScript",value:"javascript"}],initial:0},{type:"select",name:"framework",message:"Framework:",choices:[{title:"React",value:"react"},{title:"Vanilla",value:"vanilla"},{title:"Svelte (experimental)",value:"svelte"}],initial:0},{type:(e,s)=>s.language==="typescript"?"confirm":null,name:"schemas",message:"Include store schemas?",initial:!1},{type:"select",name:"syncType",message:"Synchronization:",choices:[{title:"None",value:"none"},{title:"Via remote demo server (stateless)",value:"remote"},{title:"Via local node server (stateless)",value:"node"},{title:"Via local DurableObjects server (stateful)",value:"durable-objects"}],initial:1},{type:"select",name:"persistenceType",message:"Persistence:",choices:[{title:"None",value:"none"},{title:"Local Storage",value:"local-storage"},{title:"SQLite",value:"sqlite"},{title:"PGlite",value:"pglite"}],initial:1},{type:"confirm",name:"prettier",message:"Include Prettier?",initial:!0},{type:"confirm",name:"eslint",message:"Include ESLint?",initial:!0},{type:(e,s)=>s.syncType!=="node"&&s.syncType!=="durable-objects"?"confirm":null,name:"installAndRun",message:"Install dependencies and start dev server?",initial:!0}],createContext:e=>{const{projectName:s,language:o,framework:a,appType:t,prettier:w,eslint:f,schemas:g,syncType:x,persistenceType:T,installAndRun:d}=e,p=o==="typescript",j=!p,n=a==="react",r=a==="vanilla",m=a==="svelte",i=p?"ts":"js",c=m?"svelte":p?n?"tsx":"ts":n?"jsx":"js",y=m?i:c,l=x||"remote",E=l!=="none",R=l==="node"||l==="durable-objects",k=l==="durable-objects"?"durable-objects":"node",C=l==="durable-objects",u=T||"local-storage",$=u!=="none",D=u==="local-storage",v=u==="sqlite",S=u==="pglite",P=n||m||v||S,h=t==="chat"?"chat interface":t==="drawing"?"drawing canvas":t==="game"?"game":"todo list",F=n?"React":r?"Vanilla JS":"Svelte",A=n?"React-based":r?"vanilla JavaScript":"Svelte-based",L=n?"Entry point that bootstraps and renders the React app":m?"Entry point that bootstraps and mounts the Svelte app":"Entry point that bootstraps the app",M=r?"app":"App",I=r?i:c,V=n?`Main React component that renders the ${h}`:r?"Main application logic":`Main Svelte component that renders the ${h}`;return{projectName:s,language:o,framework:a,appType:t,prettier:w,eslint:f,schemas:p&&(g===!0||g==="true"),syncType:l,sync:E,server:R,serverType:k,isDurableObject:C,persistenceType:u,persist:$,persistLocalStorage:D,persistSqlite:v,persistPglite:S,needsViteConfig:P,frameworkName:F,clientFrameworkDescription:A,entryFileDescription:L,appFileStem:M,appFileExt:I,appFileDescription:V,primaryStoreStem:n?t==="chat"?"ChatStore":t==="drawing"?"CanvasStore":"Store":t==="chat"?"chatStore":t==="drawing"?"canvasStore":"store",primaryStoreExt:n?c:i,primaryStoreDescription:`TinyBase ${t==="chat"?"chat messages":t==="drawing"?"drawing canvas":"main"} store configuration`,needsSettingsStore:t==="chat"||t==="drawing",settingsStoreStem:n?"SettingsStore":"settingsStore",settingsStoreExt:n?c:i,configExt:n?c:i,installAndRun:d===!0||d==="true",typescript:p,javascript:j,react:n,vanilla:r,svelte:m,scriptExt:i,componentExt:c,entryExt:y,ext:y}},createDirectories:async(e,s)=>{const{mkdir:o}=await import("fs/promises"),{join:a}=await import("path"),t=s.server;await o(a(e,"client/src"),{recursive:!0}),await o(a(e,"client/public"),{recursive:!0}),t&&await o(a(e,"server"),{recursive:!0})},getFiles:()=>[{template:"README.md.hbs",output:"README.md",prettier:!0}],processIncludedFile:(e,s)=>{const{javascript:o}=s,a=e.prettier??/\.(js|jsx|ts|tsx|css|json|html|md)$/.test(e.output),t=e.transpile??(/\.(ts|tsx)\.hbs$/.test(e.template)&&o===!0);return{...e,prettier:a,transpile:t}},templateRoot:b(O,"templates"),installCommand:"{pm} install",devCommand:"{pm} run dev",workingDirectory:"client",onSuccess:(e,s)=>{const o=s.syncType,a=o==="node"||o==="durable-objects",t=J();console.log("Next steps:"),console.log(),a&&(console.log("To run the server:"),console.log(` cd ${e}/server`),console.log(` ${t} install`),console.log(` ${t} run dev`),console.log()),console.log("To run the client:"),console.log(` cd ${e}/client`),console.log(` ${t} install`),console.log(` ${t} run dev`)}};z(_).catch(e=>{console.error(e),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-tinybase",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "author": "jamesgpearce",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,6 +17,7 @@
17
17
  "state",
18
18
  "data",
19
19
  "react",
20
+ "svelte",
20
21
  "sqlite",
21
22
  "pglite",
22
23
  "durable-objects"
Binary file
Binary file
Binary file
Binary file
@@ -5,7 +5,7 @@
5
5
 
6
6
  # {{projectName}}
7
7
 
8
- A TinyBase app built with {{#if typescript}}TypeScript{{else}}JavaScript{{/if}} and {{#if react}}React{{else}}Vanilla JS{{/if}}.
8
+ A TinyBase app built with {{#if typescript}}TypeScript{{else}}JavaScript{{/if}} and {{frameworkName}}.
9
9
 
10
10
  ## Getting Started
11
11
 
@@ -43,26 +43,19 @@ This project is organized into {{#if server}}two main directories{{else}}a singl
43
43
 
44
44
  ### Client
45
45
 
46
- The `client` directory contains your {{#if react}}React-based{{else}}vanilla JavaScript{{/if}} web application,
46
+ The `client` directory contains your {{clientFrameworkDescription}} web application,
47
47
  built with Vite and {{#if typescript}}TypeScript{{else}}JavaScript{{/if}}.
48
48
 
49
49
  #### Key Files
50
50
 
51
- - **`index.html`** - The main HTML file that loads your app
52
- - **`src/index.{{ext}}`** - Entry point that bootstraps the application{{#if react}} and renders the React component tree{{/if}}
53
- - **`src/{{#if react}}App{{else}}app{{/if}}.{{ext}}`** - {{#if react}}Main React component that renders the {{#if (eq appType "todos")}}todo list{{else if (eq appType "chat")}}chat interface{{else if (eq appType "drawing")}}drawing
54
- canvas{{else if (eq appType "game")}}game{{/if}}
55
- {{else}}Main application logic{{/if}}
56
- -
57
- **`src/{{#if react}}{{#if (eq appType "chat")}}ChatStore{{else if (eq appType "drawing")}}CanvasStore{{else}}Store{{/if}}{{else}}{{#if (eq appType "chat")}}chatStore{{else if (eq appType "drawing")}}canvasStore{{else}}store{{/if}}{{/if}}.{{ext}}`**
58
- - TinyBase {{#if (eq appType "chat")}}chat messages{{else if (eq appType "drawing")}}drawing canvas{{else}}main{{/if}} store configuration
59
- {{#if (eq appType "chat")}}
60
- - **`src/{{#if react}}SettingsStore{{else}}settingsStore{{/if}}.{{ext}}`** - TinyBase settings store (not synchronized)
51
+ - **`index.html`**: The main HTML file that loads your app
52
+ - **`src/index.{{entryExt}}`**: {{entryFileDescription}}
53
+ - **`src/{{appFileStem}}.{{appFileExt}}`**: {{appFileDescription}}
54
+ - **`src/{{primaryStoreStem}}.{{primaryStoreExt}}`**: {{primaryStoreDescription}}
55
+ {{#if needsSettingsStore}}
56
+ - **`src/{{settingsStoreStem}}.{{settingsStoreExt}}`**: TinyBase settings store (not synchronized)
61
57
  {{/if}}
62
- {{#if (eq appType "drawing")}}
63
- - **`src/{{#if react}}SettingsStore{{else}}settingsStore{{/if}}.{{ext}}`** - TinyBase settings store (not synchronized)
64
- {{/if}}
65
- - **`src/config.{{ext}}`** - Configuration settings{{#if server}} (including server connection URL){{/if}}
58
+ - **`src/config.{{configExt}}`**: Configuration settings{{#if server}} (including server connection URL){{/if}}
66
59
 
67
60
  #### How It Works
68
61
 
@@ -101,7 +94,7 @@ The app uses **TinyBase** to manage application state in a reactive data store.
101
94
 
102
95
  {{/if}}
103
96
  {{#if react}}
104
- **React Integration** - TinyBase provides React hooks that automatically subscribe
97
+ **React Integration**: TinyBase provides React hooks that automatically subscribe
105
98
  to store changes and trigger re-renders when data updates. This means your UI
106
99
  stays in sync with your data with minimal boilerplate.
107
100
 
@@ -112,6 +105,12 @@ The app uses **TinyBase** to manage application state in a reactive data store.
112
105
  {{else}}`Game` uses TinyBase hooks to manage game state{{/if}} to interact
113
106
  with the store.
114
107
 
108
+ {{/if}}
109
+ {{#if svelte}}
110
+ **Svelte Integration**: TinyBase provides Svelte stores and helpers that keep the
111
+ UI reactive as data changes. Components subscribe declaratively so the interface
112
+ stays synchronized with the underlying TinyBase store.
113
+
115
114
  {{/if}}
116
115
  {{#if server}}
117
116
  ### Server
@@ -120,7 +119,7 @@ The app uses **TinyBase** to manage application state in a reactive data store.
120
119
 
121
120
  #### Key Files
122
121
 
123
- - **`index.{{ext}}`** - Server entry point
122
+ - **`index.{{ext}}`**: Server entry point
124
123
 
125
124
  #### How It Works
126
125
 
@@ -157,4 +156,4 @@ The app uses **TinyBase** to manage application state in a reactive data store.
157
156
  ## Learn More
158
157
 
159
158
  - [TinyBase Documentation](https://tinybase.org)
160
- - [TinyBase Examples](https://tinybase.org/demos/)
159
+ - [TinyBase Examples](https://tinybase.org/demos/)
@@ -0,0 +1 @@
1
+ legacy-peer-deps=true
@@ -8,6 +8,12 @@
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
9
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;800&display=swap" />
10
10
  {{includeFile template="client/public/favicon.svg" output="client/public/favicon.svg"}}
11
+ {{#if typescript}}{{includeFile template="client/public/ts.svg" output="client/public/ts.svg"}}{{else}}{{includeFile template="client/public/js.svg" output="client/public/js.svg"}}{{/if}}
12
+ {{#if react}}{{includeFile template="client/public/react.svg" output="client/public/react.svg"}}{{/if}}
13
+ {{#if svelte}}{{includeFile template="client/public/svelte.svg" output="client/public/svelte.svg"}}{{/if}}
14
+ {{#if persistSqlite}}{{includeFile template="client/public/sqlite.svg" output="client/public/sqlite.svg"}}{{/if}}
15
+ {{#if persistPglite}}{{includeFile template="client/public/pglite.svg" output="client/public/pglite.svg"}}{{/if}}
16
+ {{#if sync}}{{includeFile template="client/public/sync.svg" output="client/public/sync.svg"}}{{/if}}
11
17
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
12
18
  <title>
13
19
  TinyBase {{#if (eq appType "chat")}}Chat{{else if (eq appType "drawing")}}Drawing{{else if (eq appType "game")}}Game{{else}}Todos{{/if}}
@@ -69,6 +75,22 @@
69
75
 
70
76
  #topBarInfo {
71
77
  position: relative;
78
+ display: flex;
79
+ align-items: center;
80
+ gap: 0.5rem;
81
+ }
82
+
83
+ #topBarTech {
84
+ display: flex;
85
+ align-items: center;
86
+ gap: 0.375rem;
87
+ }
88
+
89
+ .topBarTechIcon {
90
+ width: 1.25rem;
91
+ height: 1.25rem;
92
+ display: block;
93
+ flex-shrink: 0;
72
94
  }
73
95
 
74
96
  #infoIcon {
@@ -155,6 +177,27 @@
155
177
  TinyBase {{#if (eq appType "chat")}}Chat{{else if (eq appType "drawing")}}Drawing{{else if (eq appType "game")}}Game{{else}}Todos{{/if}}
156
178
  </div>
157
179
  <div id="topBarInfo">
180
+ <div id="topBarTech">
181
+ {{#if typescript}}
182
+ <img src="/ts.svg" class="topBarTechIcon" title="Written in TypeScript" />
183
+ {{else}}
184
+ <img src="/js.svg" class="topBarTechIcon" title="Written in JavaScript" />
185
+ {{/if}}
186
+ {{#if react}}
187
+ <img src="/react.svg" class="topBarTechIcon" title="Built with React" />
188
+ {{/if}}
189
+ {{#if svelte}}
190
+ <img src="/svelte.svg" class="topBarTechIcon" title="Built with Svelte" />
191
+ {{/if}}
192
+ {{#if persistSqlite}}
193
+ <img src="/sqlite.svg" class="topBarTechIcon" title="Persists data to SQLite" />
194
+ {{else if persistPglite}}
195
+ <img src="/pglite.svg" class="topBarTechIcon" title="Persists data to PGlite" />
196
+ {{/if}}
197
+ {{#if sync}}
198
+ <img src="/sync.svg" class="topBarTechIcon" title="Data synchronization enabled" />
199
+ {{/if}}
200
+ </div>
158
201
  <div id="infoIcon">
159
202
  i
160
203
  <div id="infoTooltip">
@@ -165,22 +208,16 @@
165
208
  {{else if (eq appType "drawing")}}
166
209
  A collaborative drawing canvas showcasing TinyBase's state synchronization.
167
210
  {{else if (eq appType "game")}}
168
- A tic-tac-toe game demonstrating turn-based logic and computed game state.
211
+ A tic-tac-toe game demonstrating turn-based logic and computed game state using TinyBase.
169
212
  {{/if}}
170
- <br><br>
171
- Built with: {{#if typescript}}TypeScript{{#if schemas}} (using typed store schemas){{/if}}{{else}}JavaScript{{/if}}{{#if react}} and React{{/if}}.{{#if persist}}
172
- <br><br>
173
- Persistence: {{#if persistLocalStorage}}Local Storage{{else if persistSqlite}}SQLite{{else if persistPglite}}PGlite{{/if}}.{{/if}}{{#if sync}}
174
- <br><br>
175
- Sync: {{#if (eq syncType "remote")}}Remote{{else if (eq syncType "node")}}Node.js Server{{else if (eq syncType "durable-objects")}}Durable Objects{{/if}}.{{/if}}
176
213
  </div>
177
214
  </div>
178
215
  </div>
179
216
  </div>
180
217
  <div id="app"></div>
181
218
  </div>
182
- {{includeFile template="client/src/index.tsx.hbs" output="client/src/index.{{ext}}"}}
183
- <script type="module" src="/src/index.{{ext}}"></script>
219
+ {{includeFile template="client/src/index.tsx.hbs" output="client/src/index.{{entryExt}}"}}
220
+ <script type="module" src="/src/index.{{entryExt}}"></script>
184
221
  </body>
185
222
 
186
223
  </html>
@@ -6,7 +6,12 @@
6
6
  {{includeFile template="client/index.html.hbs" output="client/index.html"}}
7
7
  "dev": "vite"
8
8
  {{#if typescript}}
9
- "build": "tsc && vite build"
9
+ {{#if svelte}}
10
+ "build": "svelte-check --tsconfig ./tsconfig.json && vite build"
11
+ "check": "svelte-check --tsconfig ./tsconfig.json"
12
+ {{else}}
13
+ "build": "tsc && vite build"
14
+ {{/if}}
10
15
  {{else}}
11
16
  "build": "vite build"
12
17
  {{/if}}
@@ -25,14 +30,22 @@
25
30
  "vite": "^7.3.1"
26
31
  {{#if typescript}}
27
32
  "typescript": "^5.9.3"
28
- "@types/node": "^25.0.9"
33
+ "@types/node": "^25.3.5"
34
+ {{#if svelte}}
35
+ "@tsconfig/svelte": "^5.0.5"
36
+ "svelte-check": "^4.3.1"
37
+ {{/if}}
29
38
  {{#if react}}
30
- "@types/react": "^19.2.8"
39
+ "@types/react": "^19.2.14"
31
40
  "@types/react-dom": "^19.2.3"
32
41
  {{/if}}
33
42
  {{/if}}
34
43
  {{#if react}}
35
- "@vitejs/plugin-react": "^5.1.2"
44
+ "@vitejs/plugin-react": "^5.1.4"
45
+ {{/if}}
46
+ {{#if svelte}}
47
+ "@sveltejs/vite-plugin-svelte": "^6.2.1"
48
+ "svelte": "^5.53.11"
36
49
  {{/if}}
37
50
  {{#if persistSqlite}}
38
51
  "vite-plugin-static-copy": "^3.2.0"
@@ -41,7 +54,7 @@
41
54
  "vite-plugin-static-copy": "^3.2.0"
42
55
  {{/if}}
43
56
  {{#if prettier}}
44
- "prettier": "^3.8.0"
57
+ "prettier": "^3.8.1"
45
58
  {{/if}}
46
59
  {{#if eslint}}
47
60
  "eslint": "^9.39.2"
@@ -55,18 +68,18 @@
55
68
  {{/if}}
56
69
  {{/list}}
57
70
  },
58
- "dependencies": {
71
+ "dependencies": {
59
72
  {{#list}}
60
- "tinybase": "^7.3.2"
73
+ "tinybase": "{{#if svelte}}beta{{else}}^8.0.0{{/if}}"
61
74
  {{#if react}}
62
- "react": "^19.2.3"
63
- "react-dom": "^19.2.3"
75
+ "react": "^19.2.4"
76
+ "react-dom": "^19.2.4"
64
77
  {{/if}}
65
78
  {{#if sync}}
66
79
  "reconnecting-websocket": "^4.4.0"
67
80
  {{/if}}
68
81
  {{#if persistSqlite}}
69
- "@sqlite.org/sqlite-wasm": "^3.50.4-build1"
82
+ "@sqlite.org/sqlite-wasm": "^3.51.2-build6"
70
83
  {{/if}}
71
84
  {{#if persistPglite}}
72
85
  "@electric-sql/pglite": "^0.3.15"
@@ -77,20 +90,16 @@
77
90
  {{#if prettier}}
78
91
  {{includeFile template="client/.prettierrc.json.hbs" output="client/.prettierrc.json"}}
79
92
  {{/if}}
93
+ {{#if svelte}}
94
+ {{includeFile template="client/.npmrc.hbs" output="client/.npmrc"}}
95
+ {{/if}}
80
96
  {{#if eslint}}
81
97
  {{includeFile template="client/eslint.config.js.hbs" output="client/eslint.config.js"}}
82
98
  {{/if}}
83
- {{#if react}}
99
+ {{#if needsViteConfig}}
84
100
  {{includeFile template="client/vite.config.js.hbs" output="client/vite.config.js"}}
85
- {{else}}
86
- {{#if persistSqlite}}
87
- {{includeFile template="client/vite.config.js.hbs" output="client/vite.config.js"}}
88
- {{/if}}
89
- {{#if persistPglite}}
90
- {{includeFile template="client/vite.config.js.hbs" output="client/vite.config.js"}}
91
- {{/if}}
92
101
  {{/if}}
93
102
  {{#if typescript}}
94
103
  {{includeFile template="client/tsconfig.json.hbs" output="client/tsconfig.json"}}
95
104
  {{includeFile template="client/vite-env.d.ts.hbs" output="client/src/vite-env.d.ts"}}
96
- {{/if}}
105
+ {{/if}}
@@ -0,0 +1,81 @@
1
+ <?xml version="1.0" ?>
2
+
3
+ <svg width="800px" height="800px" viewBox="0 0 64 64" id="svg5" version="1.1" xml:space="preserve"
4
+ xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
5
+
6
+ <defs id="defs2" />
7
+
8
+ <g id="layer1" transform="translate(-96,-96)">
9
+
10
+ <path d="m 106.00001,105 h 49 v 6 h -49 z" id="path27750"
11
+ style="fill:#3e4f59;fill-opacity:1;fill-rule:evenodd;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1" />
12
+
13
+ <path d="m 106.00001,111 h 49 v 40 h -49 z" id="path27752"
14
+ style="fill:#acbec2;fill-opacity:1;fill-rule:evenodd;stroke-width:2.00001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1" />
15
+
16
+ <path d="m 106.00001,111 v 40 h 29.76953 a 28.484051,41.392605 35.599482 0 0 18.625,-40 z" id="path27754"
17
+ style="fill:#e8edee;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.00002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1" />
18
+
19
+ <path
20
+ d="m 108.00001,104 c -1.64501,0 -3,1.355 -3,3 v 40 c 0,0.55229 0.44772,1 1,1 0.55229,0 1,-0.44771 1,-1 v -40 c 0,-0.56413 0.43587,-1 1,-1 h 45 c 0.56413,0 1,0.43587 1,1 v 3 h -42 c -0.55228,0 -1,0.44772 -1,1 0,0.55229 0.44772,1 1,1 h 42 v 37 c 0,0.56413 -0.43587,1 -1,1 h -49 c -0.55228,0 -1,0.44772 -1,1 0,0.55229 0.44772,1 1,1 h 49 c 1.64501,0 3,-1.35499 3,-3 0,-14 0,-28 0,-42 0,-1.645 -1.35499,-3 -3,-3 z"
21
+ id="path27756"
22
+ style="color:#000000;fill:#000000;fill-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1" />
23
+
24
+ <path
25
+ d="m 151.00001,107 c -0.55229,0 -1,0.44772 -1,1 0,0.55229 0.44771,1 1,1 0.55229,0 1,-0.44771 1,-1 0,-0.55228 -0.44771,-1 -1,-1 z"
26
+ id="path27758"
27
+ style="color:#000000;fill:#ed7161;fill-opacity:1;fill-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1;-inkscape-stroke:none" />
28
+
29
+ <path
30
+ d="m 147.00001,107 c -0.55229,0 -1,0.44772 -1,1 0,0.55229 0.44771,1 1,1 0.55229,0 1,-0.44771 1,-1 0,-0.55228 -0.44771,-1 -1,-1 z"
31
+ id="path27760"
32
+ style="color:#000000;fill:#ecba16;fill-opacity:1;fill-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1;-inkscape-stroke:none" />
33
+
34
+ <path
35
+ d="m 143.00001,107 c -0.55229,0 -1,0.44772 -1,1 0,0.55229 0.44771,1 1,1 0.55229,0 1,-0.44771 1,-1 0,-0.55228 -0.44771,-1 -1,-1 z"
36
+ id="path27762"
37
+ style="color:#000000;fill:#42b05c;fill-opacity:1;fill-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1;-inkscape-stroke:none" />
38
+
39
+ <path d="m 101.00001,150 a 1,1 0 0 0 -1,1 1,1 0 0 0 1,1 1,1 0 0 0 1,-1 1,1 0 0 0 -1,-1 z" id="path27764"
40
+ style="color:#000000;fill:#000000;fill-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1;-inkscape-stroke:none" />
41
+
42
+ <path
43
+ d="m 109.00001,110 c -0.55228,0 -1,0.44772 -1,1 0,0.55229 0.44772,1 1,1 0.55229,0 1,-0.44771 1,-1 0,-0.55228 -0.44771,-1 -1,-1 z"
44
+ id="path27766"
45
+ style="color:#000000;fill:#000000;fill-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1;-inkscape-stroke:none" />
46
+
47
+ <path d="m 119.00597,137.50137 10.99308,6 v -13 l -10.99308,-6 z" id="path2282"
48
+ style="fill:#0588e2;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1" />
49
+
50
+ <path d="m 140.99213,137.50137 -10.99308,6 v -13 l 10.99308,-6 z" id="path2284"
51
+ style="fill:#fe73c5;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1" />
52
+
53
+ <path
54
+ d="m 136.08392,127.18103 -6.08399,3.32031 v 13 l 8.51367,-4.64648 a 15.440572,16.753496 0 0 0 0.16016,-2.39258 15.440572,16.753496 0 0 0 -2.58984,-9.28125 z"
55
+ id="path20371"
56
+ style="fill:#fe93d3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1" />
57
+
58
+ <path d="m 129.99905,130.50137 10.99308,-6 -10.99308,-6 -10.99308,6 z" id="path2292"
59
+ style="fill:#ffa221;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1" />
60
+
61
+ <path
62
+ d="m 126.90228,120.19275 -7.89649,4.30859 10.99414,6 6.08399,-3.32031 a 15.440572,16.753496 0 0 0 -9.18164,-6.98828 z"
63
+ id="ellipse20373"
64
+ style="fill:#ffc343;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1" />
65
+
66
+ <path d="m 133.99061,128.32572 3.00151,-1.64374 v 5.31936 l -2.99885,1.32115 z" id="path8715"
67
+ style="color:#000000;fill:#0588e2;fill-opacity:1;fill-rule:evenodd;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1;-inkscape-stroke:none" />
68
+
69
+ <path
70
+ d="m 136.51165,125.80407 -3.00195,1.64454 a 1.0001,1.0001 0 0 0 -0.51953,0.87695 l 0.004,4.99805 a 1.0001,1.0001 0 0 0 1.40235,0.91406 l 2.99804,-1.32031 a 1.0001,1.0001 0 0 0 0.59756,-0.91602 v -5.32031 a 1.0001,1.0001 0 0 0 -1.48047,-0.87696 z m -0.51953,2.56641 v 2.97852 l -1,0.43945 -0.002,-2.87109 z"
71
+ id="path8717"
72
+ style="color:#000000;fill:#000000;fill-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1;-inkscape-stroke:none" />
73
+
74
+ <path
75
+ d="m 129.99798,117.50134 c -0.16504,0 -0.32911,0.0414 -0.47852,0.12305 l -10.99218,6 c -0.28424,0.15583 -0.44913,0.41533 -0.5,0.69336 -6.7e-4,0.003 -9.6e-4,0.005 -0.002,0.008 -0.008,0.0446 -0.0138,0.0896 -0.0156,0.13476 -7e-4,0.0148 -0.004,0.0253 -0.004,0.041 v 13 c 4.4e-4,0.36567 0.20041,0.70195 0.52149,0.87695 l 10.99218,6 c 0.0519,0.0284 0.10496,0.0499 0.15821,0.0684 0.13097,0.0453 0.26303,0.0585 0.39257,0.0488 a 1.0001,1.0001 0 0 0 0.0469,-0.008 c 0.1217,-0.015 0.2386,-0.0491 0.3457,-0.10547 a 1.0001,1.0001 0 0 0 0.0156,-0.004 l 0.0176,-0.01 0.0137,-0.006 c 0.002,-0.001 0.004,-0.003 0.006,-0.004 l 3.15234,-1.7207 a 1,1 0 0 0 0.39844,-1.35742 1,1 0 0 0 -1.35547,-0.39844 L 131,141.81522 v -10.7207 l 8.99212,-4.90959 v 10.72266 l -3.77148,2.05859 a 1,1 0 0 0 -0.39844,1.35743 1,1 0 0 0 1.35742,0.39843 l 4.29102,-2.34375 a 1.0001,1.0001 0 0 0 0.52148,-0.87695 v -13 a 1.0001,1.0001 0 0 0 -0.41797,-0.79102 c -0.0259,-0.031 -0.0579,-0.061 -0.10351,-0.0859 l -10.99219,-6 c -0.1493,-0.0819 -0.31543,-0.12308 -0.48047,-0.12308 z m 0.002,2.13867 8.90625,4.86133 -8.90625,4.86133 -2.84961,-1.55469 c -0.23241,-0.12684 -0.50564,-0.15638 -0.75976,-0.082 -0.25442,0.0748 -0.46871,0.24766 -0.59571,0.48047 -0.26501,0.48485 -0.0866,1.09273 0.39844,1.35742 l 2.80664,1.53125 v 10.72266 l -8.99414,-4.91016 v -10.72266 l 2.74805,1.50196 c 0.48485,0.26501 1.09273,0.0866 1.35742,-0.39844 0.26501,-0.48485 0.0866,-1.09273 -0.39844,-1.35742 l -2.62114,-1.42972 z"
76
+ id="path8707"
77
+ style="color:#000000;fill:#000000;fill-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.1" />
78
+
79
+ </g>
80
+
81
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 630 630">
2
+ <rect width="630" height="630" fill="#f7df1e" />
3
+ <path
4
+ d="m423.2 492.19c12.69 20.72 29.2 35.95 58.4 35.95 24.53 0 40.2-12.26 40.2-29.2 0-20.3-16.1-27.49-43.1-39.3l-14.8-6.35c-42.72-18.2-71.1-41-71.1-89.2 0-44.4 33.83-78.2 86.7-78.2 37.64 0 64.7 13.1 84.2 47.4l-46.1 29.6c-10.15-18.2-21.1-25.37-38.1-25.37-17.34 0-28.33 11-28.33 25.37 0 17.76 11 24.95 36.4 35.95l14.8 6.34c50.3 21.57 78.7 43.56 78.7 93 0 53.3-41.87 82.5-98.1 82.5-54.98 0-90.5-26.2-107.88-60.54zm-209.13 5.13c9.3 16.5 17.76 30.45 38.1 30.45 19.45 0 31.72-7.61 31.72-37.2v-201.3h59.2v202.1c0 61.3-35.94 89.2-88.4 89.2-47.4 0-74.85-24.53-88.81-54.075z" />
5
+ </svg>
@@ -0,0 +1,8 @@
1
+ <svg width="368" height="305" viewBox="40 40 288 225" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd"
3
+ d="M304 104.551L304 232.47C304 236.891 300.413 240.475 295.991 240.47L263.968 240.432C259.734 240.427 256.272 237.134 255.995 232.971C255.998 232.813 256 232.655 256 232.496L255.999 184.498C255.999 171.244 245.239 160.499 231.984 160.499C219.138 160.499 208.636 150.406 208 137.717V64.5095L264.03 64.5507C286.109 64.5669 304 82.4708 304 104.551ZM207.97 48.5095L208 48.5095L264.041 48.5507C294.953 48.5734 320 73.6388 320 104.551L320 232.47C320 245.735 309.238 256.485 295.972 256.47L263.949 256.432C257.835 256.425 252.258 254.132 248.024 250.363C243.775 254.177 238.158 256.497 231.998 256.496L71.9984 256.486C58.7442 256.485 48 245.74 48 232.486V104.498C48 73.57 73.0722 48.4979 104 48.498L191.97 48.4986H192H193.142H207.97V48.5095ZM144 64.4983L144 120.523C144 133.777 154.745 144.523 168 144.523H192L192 142.914C192 161.462 207.036 176.498 225.584 176.498C233.533 176.498 239.977 182.942 239.977 190.891L239.977 232.432C239.977 232.623 239.98 232.813 239.984 233.003C239.722 237.185 236.247 240.496 231.999 240.496L191.726 240.494L192 184.534C192.022 180.116 188.457 176.517 184.039 176.495C179.621 176.473 176.022 180.038 176 184.456L175.726 240.459L175.726 240.492L127.9 240.489V184.495C127.9 180.077 124.318 176.495 119.9 176.495C115.482 176.495 111.9 180.077 111.9 184.495V240.488L71.9995 240.486C67.5814 240.485 64 236.904 64 232.486V104.498C64 82.4066 81.9087 64.4979 104 64.498L144 64.4983ZM268.04 112.715C268.04 106.088 262.667 100.715 256.04 100.715C249.412 100.715 244.04 106.088 244.04 112.715C244.04 119.343 249.412 124.715 256.04 124.715C262.667 124.715 268.04 119.343 268.04 112.715Z"
4
+ fill="#131517" />
5
+ <path fill-rule="evenodd" clip-rule="evenodd"
6
+ d="M304 104.551L304 232.47C304 236.891 300.413 240.475 295.991 240.47L263.968 240.432C259.734 240.427 256.272 237.134 255.995 232.971C255.998 232.813 256 232.655 256 232.496L255.999 184.498C255.999 171.244 245.239 160.499 231.984 160.499C219.138 160.499 208.636 150.406 208 137.717V64.5095L264.03 64.5507C286.109 64.5669 304 82.4708 304 104.551ZM144 64.4983L144 120.523C144 133.778 154.745 144.523 168 144.523H192L192 142.914C192 161.462 207.036 176.498 225.584 176.498C233.533 176.498 239.977 182.942 239.977 190.891L239.977 232.432C239.977 232.623 239.98 232.813 239.984 233.003C239.722 237.185 236.247 240.496 231.999 240.496L191.726 240.494L192 184.534C192.022 180.116 188.457 176.517 184.039 176.495C179.621 176.473 176.022 180.038 176 184.456L175.726 240.459L175.726 240.492L127.9 240.489V184.495C127.9 180.077 124.318 176.495 119.9 176.495C115.482 176.495 111.9 180.077 111.9 184.495V240.488L71.9995 240.486C67.5814 240.485 64 236.904 64 232.486V104.498C64 82.4066 81.9087 64.4979 104 64.498L144 64.4983ZM268.04 112.715C268.04 106.088 262.667 100.715 256.04 100.715C249.412 100.715 244.04 106.088 244.04 112.715C244.04 119.343 249.412 124.715 256.04 124.715C262.667 124.715 268.04 119.343 268.04 112.715Z"
7
+ fill="#F6F95C" />
8
+ </svg>
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="-11.5 -10.23174 23 20.46348">
2
+ <title>React Logo</title>
3
+ <circle cx="0" cy="0" r="2.05" fill="#61dafb" />
4
+ <g stroke="#61dafb" stroke-width="1" fill="none">
5
+ <ellipse rx="11" ry="4.2" />
6
+ <ellipse rx="11" ry="4.2" transform="rotate(60)" />
7
+ <ellipse rx="11" ry="4.2" transform="rotate(120)" />
8
+ </g>
9
+ </svg>
@@ -0,0 +1,58 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg viewBox="0 0 256 256" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#"
5
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg"
6
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
7
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
8
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg2985" version="1.1"
9
+ inkscape:version="0.48.4 r9939" width="256" height="256" xml:space="preserve"
10
+ sodipodi:docname="sqlite-square-icon.svg">
11
+ <metadata id="metadata2991">
12
+ <rdf:RDF>
13
+ <cc:Work rdf:about="">
14
+ <dc:format>image/svg+xml</dc:format>
15
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
16
+ <dc:title />
17
+ </cc:Work>
18
+ </rdf:RDF>
19
+ </metadata>
20
+ <defs id="defs2989">
21
+ <linearGradient x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
22
+ gradientTransform="matrix(-4.02e-6,-91.8907,-91.8907,4.02e-6,85.8809,161.434)" spreadMethod="pad"
23
+ id="linearGradient3027">
24
+ <stop style="stop-opacity:1;stop-color:#97d9f6" offset="0" id="stop3029" />
25
+ <stop style="stop-opacity:1;stop-color:#0f80cc" offset="0.920245" id="stop3031" />
26
+ <stop style="stop-opacity:1;stop-color:#0f80cc" offset="1" id="stop3033" />
27
+ </linearGradient>
28
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient3027" id="linearGradient5421"
29
+ gradientUnits="userSpaceOnUse" gradientTransform="matrix(-4.02e-7,-9.18907,-9.18907,4.02e-7,8.58809,16.1434)"
30
+ spreadMethod="pad" x1="-15.614694" y1="-9.1082983" x2="-6.7410073" y2="-9.1082983" />
31
+ <linearGradient inkscape:collect="always" xlink:href="#linearGradient3027" id="linearGradient3003"
32
+ gradientUnits="userSpaceOnUse" gradientTransform="matrix(-4.02e-7,-9.18907,-9.18907,4.02e-7,8.58809,16.1434)"
33
+ spreadMethod="pad" x1="-15.614694" y1="-9.1082983" x2="-6.7410073" y2="-9.1082983" />
34
+ </defs>
35
+ <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10"
36
+ gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1920"
37
+ inkscape:window-height="1017" id="namedview2987" showgrid="false" inkscape:zoom="0.73710525" inkscape:cx="217.03271"
38
+ inkscape:cy="157.19018" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1"
39
+ inkscape:current-layer="g2993" fit-margin-top="10" fit-margin-left="10" fit-margin-right="10" fit-margin-bottom="10"
40
+ inkscape:snap-page="true" inkscape:snap-bbox="true" />
41
+ <g id="g2993" inkscape:groupmode="layer" inkscape:label="ink_ext_XXXXXX"
42
+ transform="matrix(1.25,0,0,-1.25,-41.57475,299.78375)">
43
+ <g id="g2998" transform="matrix(1.5512433,0,0,1.5512433,-16.326028,-27.718348)">
44
+ <path
45
+ d="m 103.096,71.5961 c -0.057,0.7219 -0.091,1.191 -0.091,1.191 0,0 -2.189,14.7578 -4.7948,19.1617 -0.4122,0.6981 0.0449,3.5653 1.1953,7.8121 0.6725,-1.1629 3.5115,-6.1371 4.0815,-7.7398 0.642,-1.8121 0.777,-2.3313 0.777,-2.3313 0,0 -1.557,8.0114 -4.112,12.6862 0.56,1.89 1.229,3.979 1.986,6.212 0.968,-1.698 3.285,-5.809 3.795,-7.235 0.103,-0.293 0.19,-0.542 0.268,-0.77 0.025,0.137 0.05,0.274 0.075,0.411 -0.585,2.482 -1.734,6.801 -3.307,9.992 3.49,18.165 15.393,42.445 27.596,53.278 l -79.6048,0 c -5.3352,0 -9.7004,-4.366 -9.7004,-9.701 l 0,-87.7892 c 0,-5.3347 4.3652,-9.7 9.7004,-9.7 l 52.4298,0 c -0.378,4.5762 -0.504,9.6391 -0.294,14.5223"
46
+ style="fill:#0f80cc;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path3005"
47
+ inkscape:connector-curvature="0" />
48
+ <path
49
+ d="m 99.4055,99.7609 c 0.6725,-1.1629 3.5115,-6.1371 4.0815,-7.7398 0.642,-1.8121 0.777,-2.3313 0.777,-2.3313 0,0 -1.557,8.0114 -4.112,12.6862 0.56,1.89 1.229,3.979 1.986,6.212 0.885,-1.553 2.896,-5.12 3.623,-6.81 0.027,0.319 0.054,0.638 0.082,0.954 -0.644,2.475 -1.622,5.715 -2.874,8.254 3.214,16.725 13.559,38.625 24.704,50.448 l -76.7128,0 c -3.7883,0 -6.8711,-3.082 -6.8711,-6.871 l 0,-81.3841 c 17.3738,6.668 38.323,12.7633 56.3529,12.502 -0.6693,2.5812 -1.4315,4.9152 -2.2318,6.2679 -0.4122,0.6981 0.0449,3.5653 1.1953,7.8121"
50
+ style="fill:url(#linearGradient3003);fill-opacity:1;fill-rule:nonzero;stroke:none" id="path3035"
51
+ inkscape:connector-curvature="0" />
52
+ <path
53
+ d="m 149.133,167.137 c -5.452,4.862 -12.053,2.909 -18.568,-2.873 -0.967,-0.859 -1.932,-1.812 -2.892,-2.83 -11.145,-11.823 -21.49,-33.723 -24.704,-50.448 1.252,-2.539 2.23,-5.779 2.874,-8.254 0.165,-0.635 0.314,-1.231 0.433,-1.738 0.283,-1.1999 0.435,-1.978 0.435,-1.978 0,0 -0.1,0.3781 -0.51,1.567 -0.078,0.228 -0.165,0.477 -0.268,0.77 -0.044,0.121 -0.105,0.268 -0.172,0.425 -0.727,1.69 -2.738,5.257 -3.623,6.81 -0.757,-2.233 -1.426,-4.322 -1.986,-6.212 2.555,-4.6748 4.112,-12.6862 4.112,-12.6862 0,0 -0.135,0.5192 -0.777,2.3313 -0.57,1.6027 -3.409,6.5769 -4.0815,7.7398 -1.1504,-4.2468 -1.6075,-7.114 -1.1953,-7.8121 0.8003,-1.3527 1.5625,-3.6867 2.2318,-6.2679 1.512,-5.8149 2.563,-12.8938 2.563,-12.8938 0,0 0.034,-0.4691 0.091,-1.191 -0.21,-4.8832 -0.084,-9.9461 0.294,-14.5223 0.501,-6.0578 1.444,-11.2617 2.646,-14.0468 l 0.816,0.4449 c -1.765,5.4871 -2.482,12.6781 -2.168,20.9711 0.475,12.6761 3.392,27.9629 8.782,43.896 9.106,24.052 21.74,43.35 33.303,52.566 -10.539,-9.518 -24.803,-40.327 -29.073,-51.736 -4.781,-12.776 -8.169,-24.7651 -10.211,-36.2518 3.523,10.7687 14.914,15.3976 14.914,15.3976 0,0 5.587,6.8903 12.116,16.7342 -3.911,-0.892 -10.333,-2.419 -12.484,-3.323 -3.173,-1.331 -4.028,-1.785 -4.028,-1.785 0,0 10.278,6.259 19.096,9.093 12.127,19.1 25.339,46.234 12.034,58.103"
54
+ style="fill:#003b57;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path3045"
55
+ inkscape:connector-curvature="0" />
56
+ </g>
57
+ </g>
58
+ </svg>
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 98.1 118" style="enable-background:new 0 0 98.1 118;" xml:space="preserve">
3
+ <style type="text/css">
4
+ .st0 {
5
+ fill: #FF3E00;
6
+ }
7
+
8
+ .st1 {
9
+ fill: #FFFFFF;
10
+ }
11
+ </style>
12
+ <path class="st0" d="M91.8,15.6C80.9-0.1,59.2-4.7,43.6,5.2L16.1,22.8C8.6,27.5,3.4,35.2,1.9,43.9c-1.3,7.3-0.2,14.8,3.3,21.3
13
+ c-2.4,3.6-4,7.6-4.7,11.8c-1.6,8.9,0.5,18.1,5.7,25.4c11,15.7,32.6,20.3,48.2,10.4l27.5-17.5c7.5-4.7,12.7-12.4,14.2-21.1
14
+ c1.3-7.3,0.2-14.8-3.3-21.3c2.4-3.6,4-7.6,4.7-11.8C99.2,32.1,97.1,22.9,91.8,15.6" />
15
+ <path class="st1" d="M40.9,103.9c-8.9,2.3-18.2-1.2-23.4-8.7c-3.2-4.4-4.4-9.9-3.5-15.3c0.2-0.9,0.4-1.7,0.6-2.6l0.5-1.6l1.4,1
16
+ c3.3,2.4,6.9,4.2,10.8,5.4l1,0.3l-0.1,1c-0.1,1.4,0.3,2.9,1.1,4.1c1.6,2.3,4.4,3.4,7.1,2.7c0.6-0.2,1.2-0.4,1.7-0.7L65.5,72
17
+ c1.4-0.9,2.3-2.2,2.6-3.8c0.3-1.6-0.1-3.3-1-4.6c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7l-10.5,6.7
18
+ c-1.7,1.1-3.6,1.9-5.6,2.4c-8.9,2.3-18.2-1.2-23.4-8.7c-3.1-4.4-4.4-9.9-3.4-15.3c0.9-5.2,4.1-9.9,8.6-12.7l27.5-17.5
19
+ c1.7-1.1,3.6-1.9,5.6-2.5c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.2,0.9-0.4,1.7-0.7,2.6l-0.5,1.6l-1.4-1
20
+ c-3.3-2.4-6.9-4.2-10.8-5.4l-1-0.3l0.1-1c0.1-1.4-0.3-2.9-1.1-4.1c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7L32.4,46.1
21
+ c-1.4,0.9-2.3,2.2-2.6,3.8s0.1,3.3,1,4.6c1.6,2.3,4.4,3.3,7.1,2.6c0.6-0.2,1.2-0.4,1.7-0.7l10.5-6.7c1.7-1.1,3.6-1.9,5.6-2.5
22
+ c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.9,5.2-4.1,9.9-8.6,12.7l-27.5,17.5C44.8,102.5,42.9,103.3,40.9,103.9" />
23
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#ffffff">
2
+ <path
3
+ d="M160-160v-80h110l-16-14q-52-46-73-105t-21-119q0-111 66.5-197.5T400-790v84q-72 26-116 88.5T240-478q0 45 17 87.5t53 78.5l10 10v-98h80v240H160Zm400-10v-84q72-26 116-88.5T720-482q0-45-17-87.5T650-648l-10-10v98h-80v-240h240v80H690l16 14q49 49 71.5 106.5T800-482q0 111-66.5 197.5T560-170Z" />
4
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg fill="none" height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg">
2
+ <rect fill="#3178c6" height="512" width="512" />
3
+ <path clip-rule="evenodd"
4
+ d="m316.939 407.424v50.061c8.138 4.172 17.763 7.3 28.875 9.386s22.823 3.129 35.135 3.129c11.999 0 23.397-1.147 34.196-3.442 10.799-2.294 20.268-6.075 28.406-11.342 8.138-5.266 14.581-12.15 19.328-20.65s7.121-19.007 7.121-31.522c0-9.074-1.356-17.026-4.069-23.857s-6.625-12.906-11.738-18.225c-5.112-5.319-11.242-10.091-18.389-14.315s-15.207-8.213-24.18-11.967c-6.573-2.712-12.468-5.345-17.685-7.9-5.217-2.556-9.651-5.163-13.303-7.822-3.652-2.66-6.469-5.476-8.451-8.448-1.982-2.973-2.974-6.336-2.974-10.091 0-3.441.887-6.544 2.661-9.308s4.278-5.136 7.512-7.118c3.235-1.981 7.199-3.52 11.894-4.615 4.696-1.095 9.912-1.642 15.651-1.642 4.173 0 8.581.313 13.224.938 4.643.626 9.312 1.591 14.008 2.894 4.695 1.304 9.259 2.947 13.694 4.928 4.434 1.982 8.529 4.276 12.285 6.884v-46.776c-7.616-2.92-15.937-5.084-24.962-6.492s-19.381-2.112-31.066-2.112c-11.895 0-23.163 1.278-33.805 3.833s-20.006 6.544-28.093 11.967c-8.086 5.424-14.476 12.333-19.171 20.729-4.695 8.395-7.043 18.433-7.043 30.114 0 14.914 4.304 27.638 12.912 38.172 8.607 10.533 21.675 19.45 39.204 26.751 6.886 2.816 13.303 5.579 19.25 8.291s11.086 5.528 15.415 8.448c4.33 2.92 7.747 6.101 10.252 9.543 2.504 3.441 3.756 7.352 3.756 11.733 0 3.233-.783 6.231-2.348 8.995s-3.939 5.162-7.121 7.196-7.147 3.624-11.894 4.771c-4.748 1.148-10.303 1.721-16.668 1.721-10.851 0-21.597-1.903-32.24-5.71-10.642-3.806-20.502-9.516-29.579-17.13zm-84.159-123.342h64.22v-41.082h-179v41.082h63.906v182.918h50.874z"
5
+ fill="#fff" fill-rule="evenodd" />
6
+ </svg>
@@ -0,0 +1,31 @@
1
+ {{includeFile template="client/src/chat/settingsStore.ts.hbs" output="client/src/settingsStore.ts"}}
2
+ {{includeFile template="client/src/chat/chatStore.ts.hbs" output="client/src/chatStore.ts"}}
3
+ {{includeFile template="client/src/shared/Loading.svelte.hbs" output="client/src/Loading.svelte"}}
4
+ {{includeFile template="client/src/chat/UsernameInput.svelte.hbs" output="client/src/UsernameInput.svelte"}}
5
+ {{includeFile template="client/src/chat/Messages.svelte.hbs" output="client/src/Messages.svelte"}}
6
+ {{includeFile template="client/src/chat/MessageInput.svelte.hbs" output="client/src/MessageInput.svelte"}}
7
+
8
+ <script{{#if typescript}} lang="ts"{{/if}}>
9
+ import {onMount} from 'svelte';
10
+ import Loading from './Loading.svelte';
11
+ import MessageInput from './MessageInput.svelte';
12
+ import Messages from './Messages.svelte';
13
+ import UsernameInput from './UsernameInput.svelte';
14
+ import {chatStoreReady} from './chatStore';
15
+ import {settingsStoreReady} from './settingsStore';
16
+
17
+ let loading = $state(true);
18
+
19
+ onMount(async () => {
20
+ await Promise.all([settingsStoreReady, chatStoreReady]);
21
+ loading = false;
22
+ });
23
+ </script>
24
+
25
+ {#if loading}
26
+ <Loading />
27
+ {:else}
28
+ <UsernameInput />
29
+ <Messages />
30
+ <MessageInput />
31
+ {/if}
@@ -0,0 +1,17 @@
1
+ {{includeFile template="client/src/chat/message.css.hbs" output="client/src/message.css"}}
2
+
3
+ <script{{#if typescript}} lang="ts"{{/if}}>
4
+ import './message.css';
5
+ {{#if typescript}}import type {MessageRow} from './chatStore';{{/if}}
6
+
7
+ let {message}{{#if typescript}}: {message: MessageRow}{{/if}} = $props();
8
+
9
+ const getTime = (timestamp{{#if typescript}}: number{{/if}}) =>
10
+ new Date(timestamp).toLocaleTimeString();
11
+ </script>
12
+
13
+ <div class="message">
14
+ <span class="username">{message.username}:</span>
15
+ <span class="text">{message.text}</span>
16
+ <span class="time">{getTime(message.timestamp)}</span>
17
+ </div>