create-tinybase 0.2.4 → 0.3.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 (79) hide show
  1. package/README.md +62 -18
  2. package/cli.js +2 -2
  3. package/package.json +3 -3
  4. package/screenshots/chat.png +0 -0
  5. package/screenshots/todos.png +0 -0
  6. package/templates/README.md.hbs +13 -12
  7. package/templates/client/eslint.config.js.hbs +7 -0
  8. package/templates/client/package.json.hbs +28 -8
  9. package/templates/client/src/chat/App.tsx.hbs +28 -22
  10. package/templates/client/src/chat/ChatStore.tsx.hbs +108 -27
  11. package/templates/client/src/chat/Message.tsx.hbs +8 -9
  12. package/templates/client/src/chat/MessageInput.tsx.hbs +7 -7
  13. package/templates/client/src/chat/Messages.tsx.hbs +5 -15
  14. package/templates/client/src/chat/SettingsStore.tsx.hbs +86 -9
  15. package/templates/client/src/chat/UsernameInput.tsx.hbs +6 -11
  16. package/templates/client/src/chat/app.ts.hbs +18 -18
  17. package/templates/client/src/chat/chatStore.ts.hbs +97 -35
  18. package/templates/client/src/chat/message.ts.hbs +4 -3
  19. package/templates/client/src/chat/messageInput.css.hbs +1 -1
  20. package/templates/client/src/chat/messageInput.ts.hbs +7 -8
  21. package/templates/client/src/chat/messages.ts.hbs +7 -12
  22. package/templates/client/src/chat/settingsStore.ts.hbs +65 -6
  23. package/templates/client/src/chat/usernameInput.css.hbs +1 -1
  24. package/templates/client/src/chat/usernameInput.ts.hbs +6 -6
  25. package/templates/client/src/chat/utils.ts.hbs +26 -0
  26. package/templates/client/src/drawing/App.tsx.hbs +26 -20
  27. package/templates/client/src/drawing/BrushSize.tsx.hbs +8 -11
  28. package/templates/client/src/drawing/Canvas.tsx.hbs +65 -73
  29. package/templates/client/src/drawing/CanvasStore.tsx.hbs +104 -18
  30. package/templates/client/src/drawing/ColorPicker.tsx.hbs +4 -11
  31. package/templates/client/src/drawing/DrawingControls.tsx.hbs +7 -7
  32. package/templates/client/src/drawing/SettingsStore.tsx.hbs +81 -8
  33. package/templates/client/src/drawing/app.ts.hbs +18 -8
  34. package/templates/client/src/drawing/brushSize.ts.hbs +12 -5
  35. package/templates/client/src/drawing/canvas.ts.hbs +84 -86
  36. package/templates/client/src/drawing/canvasStore.ts.hbs +93 -26
  37. package/templates/client/src/drawing/colorPicker.ts.hbs +3 -3
  38. package/templates/client/src/drawing/drawingControls.ts.hbs +7 -7
  39. package/templates/client/src/drawing/settingsStore.ts.hbs +63 -8
  40. package/templates/client/src/game/App.tsx.hbs +20 -16
  41. package/templates/client/src/game/Board.tsx.hbs +8 -8
  42. package/templates/client/src/game/Game.tsx.hbs +14 -21
  43. package/templates/client/src/game/GameStatus.tsx.hbs +5 -5
  44. package/templates/client/src/game/Square.tsx.hbs +6 -11
  45. package/templates/client/src/game/Store.tsx.hbs +106 -16
  46. package/templates/client/src/game/app.ts.hbs +17 -6
  47. package/templates/client/src/game/board.ts.hbs +7 -7
  48. package/templates/client/src/game/game.ts.hbs +12 -18
  49. package/templates/client/src/game/gameStatus.ts.hbs +3 -3
  50. package/templates/client/src/game/square.ts.hbs +4 -4
  51. package/templates/client/src/game/store.ts.hbs +95 -23
  52. package/templates/client/src/index.tsx.hbs +5 -7
  53. package/templates/client/src/shared/Button.tsx.hbs +3 -3
  54. package/templates/client/src/shared/Input.tsx.hbs +2 -2
  55. package/templates/client/src/shared/Loading.tsx.hbs +5 -0
  56. package/templates/client/src/shared/button.ts.hbs +2 -2
  57. package/templates/client/src/shared/config.ts.hbs +4 -6
  58. package/templates/client/src/shared/input.ts.hbs +2 -2
  59. package/templates/client/src/shared/loading.css.hbs +21 -0
  60. package/templates/client/src/shared/loading.ts.hbs +13 -0
  61. package/templates/client/src/shared/pglite.ts.hbs +10 -0
  62. package/templates/client/src/shared/sqlite.ts.hbs +17 -0
  63. package/templates/client/src/todos/App.tsx.hbs +22 -22
  64. package/templates/client/src/todos/Store.tsx.hbs +106 -23
  65. package/templates/client/src/todos/TodoInput.tsx.hbs +6 -8
  66. package/templates/client/src/todos/TodoItem.tsx.hbs +5 -6
  67. package/templates/client/src/todos/TodoList.tsx.hbs +5 -6
  68. package/templates/client/src/todos/app.ts.hbs +16 -10
  69. package/templates/client/src/todos/store.ts.hbs +94 -30
  70. package/templates/client/src/todos/todoInput.ts.hbs +5 -8
  71. package/templates/client/src/todos/todoItem.ts.hbs +3 -4
  72. package/templates/client/src/todos/todoList.ts.hbs +6 -8
  73. package/templates/client/vite-env.d.ts.hbs +4 -0
  74. package/templates/client/vite.config.js.hbs +53 -3
  75. package/templates/server/index-do.ts.hbs +10 -14
  76. package/templates/server/index-node.ts.hbs +3 -3
  77. package/templates/server/package.json.hbs +5 -5
  78. package/templates/server/wrangler.toml.hbs +1 -1
  79. /package/templates/client/{.prettierrc.hbs → .prettierrc.json.hbs} +0 -0
@@ -0,0 +1,17 @@
1
+ {{addImport "import sqlite3InitModule, {type Database, type Sqlite3Static} from '@sqlite.org/sqlite-wasm';"}}
2
+
3
+ let sqlite3Promise: Promise<Sqlite3Static> | null = null;
4
+ let db: Database | null = null;
5
+
6
+ export const getDb = async () => {
7
+ if (!sqlite3Promise) {
8
+ sqlite3Promise = sqlite3InitModule({
9
+ locateFile: () => '/sqlite3.wasm',
10
+ });
11
+ }
12
+ if (!db) {
13
+ const sqlite3 = await sqlite3Promise;
14
+ db = new sqlite3.oo1.DB('file:local?vfs=kvvfs', 'c');
15
+ }
16
+ return {sqlite3: await sqlite3Promise, db};
17
+ };
@@ -1,32 +1,32 @@
1
- import {StrictMode} from 'react';
2
-
3
- {{#if schemas}}
4
- import {Provider} from 'tinybase/ui-react/with-schemas';
5
- {{else}}
6
- import {Provider} from 'tinybase/ui-react';
7
- {{/if}}
8
- import {Inspector} from 'tinybase/ui-react-inspector';
9
-
1
+ {{addImport "import {StrictMode, useState} from 'react';"}}
2
+ {{includeFile template="client/src/shared/Loading.tsx.hbs" output="client/src/Loading.{{ext}}"}}
3
+ {{addImport "import {Loading} from './Loading';"}}
4
+ {{addImport "import {Provider} from 'tinybase/ui-react';"}}
5
+ {{addImport "import {Inspector} from 'tinybase/ui-react-inspector';"}}
10
6
  {{includeFile template="client/src/todos/Store.tsx.hbs" output="client/src/Store.{{ext}}"}}
11
- import {Store} from './Store';
12
-
7
+ {{addImport "import {Store} from './Store';"}}
13
8
  {{includeFile template="client/src/todos/TodoInput.tsx.hbs" output="client/src/TodoInput.{{ext}}"}}
14
- import {TodoInput} from './TodoInput';
15
-
9
+ {{addImport "import {TodoInput} from './TodoInput';"}}
16
10
  {{includeFile template="client/src/todos/TodoList.tsx.hbs" output="client/src/TodoList.{{ext}}"}}
17
- import {TodoList} from './TodoList';
11
+ {{addImport "import {TodoList} from './TodoList';"}}
12
+
13
+ export const App = () => {
14
+ const [loading, setLoading] = useState(true);
18
15
 
19
- const App = () => {
20
16
  return (
21
17
  <StrictMode>
22
18
  <Provider>
23
- <Store />
24
- <TodoInput />
25
- <TodoList />
26
- <Inspector />
19
+ <Store onReady={()=> setLoading(false)} />
20
+ {loading ? (
21
+ <Loading />
22
+ ) : (
23
+ <>
24
+ <TodoInput />
25
+ <TodoList />
26
+ <Inspector />
27
+ </>
28
+ )}
27
29
  </Provider>
28
30
  </StrictMode>
29
31
  );
30
- };
31
-
32
- export {App};
32
+ };
@@ -1,32 +1,32 @@
1
1
  {{#if schemas}}
2
- import {createMergeableStore} from 'tinybase/with-schemas';
3
- import * as UiReact from 'tinybase/ui-react/with-schemas';
4
- import {type NoValuesSchema} from 'tinybase/with-schemas';
5
- {{else}}
6
- import {createMergeableStore} from 'tinybase';
7
- import {useCreateStore, useProvideStore, useAddRowCallback, useDelRowCallback, useRow, useRowIds, useSetPartialRowCallback} from 'tinybase/ui-react';
8
- {{/if}}
9
-
10
- export const STORE_ID = 'todos';
2
+ {{addImport "import {createMergeableStore, type Row} from 'tinybase/with-schemas';"}}
3
+ {{addImport "import * as UiReact from 'tinybase/ui-react/with-schemas';"}}
4
+ {{addImport "import {type NoValuesSchema} from 'tinybase/with-schemas';"}}
11
5
 
12
- {{#if schemas}}
13
6
  const TODOS_SCHEMA = {
14
7
  todos: {
15
- text: {type: 'string'},
16
- completed: {type: 'boolean'},
8
+ text: {type: 'string', default: ''},
9
+ completed: {type: 'boolean', default: false},
17
10
  },
18
11
  } as const;
19
12
 
20
13
  type Schemas = [typeof TODOS_SCHEMA, NoValuesSchema];
21
14
 
22
- const {useCreateStore, useProvideStore, useAddRowCallback, useDelRowCallback, useRow, useRowIds, useSetPartialRowCallback} = UiReact as UiReact.WithSchemas<Schemas>;
15
+ const {useAddRowCallback, useCreateMergeableStore, {{#if persist}}useCreatePersister, {{/if}}useDelRowCallback, useProvideStore, useRow, useSetPartialRowCallback, useSortedRowIds} = UiReact as UiReact.WithSchemas<Schemas>;
16
+ {{else}}
17
+ {{addImport "import {createMergeableStore} from 'tinybase';"}}
18
+ {{addImport "import {useCreateMergeableStore, useProvideStore, useAddRowCallback, useDelRowCallback, useRow, useSortedRowIds, useSetPartialRowCallback} from 'tinybase/ui-react';"}}
23
19
  {{/if}}
24
20
 
25
- export {useAddRowCallback, useDelRowCallback, useRow, useRowIds, useSetPartialRowCallback};
21
+ export type TodoRow = {{#if schemas}}Row<typeof TODOS_SCHEMA, 'todos'>{{else}}{text: string; completed: boolean}{{/if}};
22
+
23
+ export {useAddRowCallback, useDelRowCallback, useRow, useSortedRowIds, useSetPartialRowCallback};
24
+
25
+ export const STORE_ID = 'todos';
26
26
 
27
- export const Store = () => {
28
- const store = useCreateStore(() =>
29
- createMergeableStore(STORE_ID){{#if schemas}}
27
+ export const Store = ({onReady}: {onReady?: () => void}) => {
28
+ const store = useCreateMergeableStore(() =>
29
+ createMergeableStore(){{#if schemas}}
30
30
  .setTablesSchema(TODOS_SCHEMA){{/if}}
31
31
  .setDefaultContent([
32
32
  {
@@ -41,15 +41,92 @@ todos: {
41
41
 
42
42
  useProvideStore(STORE_ID, store);
43
43
 
44
+ {{#if persist}}
45
+ {{#if persistLocalStorage}}
46
+ {{#if schemas}}
47
+ {{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser/with-schemas';"}}
48
+ {{else}}
49
+ {{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser';"}}
50
+ {{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
51
+ {{/if}}
52
+
53
+ useCreatePersister(
54
+ store,
55
+ (store) => createLocalPersister(store, STORE_ID),
56
+ [],
57
+ async (persister) => {
58
+ await persister.load();
59
+ await persister.startAutoSave();
60
+ onReady?.();
61
+ },
62
+ );
63
+ {{/if}}
64
+ {{#if persistSqlite}}
65
+ {{#if schemas}}
66
+ {{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm/with-schemas';"}}
67
+ {{else}}
68
+ {{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';"}}
69
+ {{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
70
+ {{/if}}
71
+ {{includeFile template="client/src/shared/sqlite.ts.hbs" output="client/src/sqlite.{{ext}}"}}
72
+ {{addImport "import {getDb} from './sqlite';"}}
73
+
74
+ useCreatePersister(
75
+ store,
76
+ async (store) => {
77
+ const {sqlite3, db} = await getDb();
78
+ return createSqliteWasmPersister(store, sqlite3, db, STORE_ID);
79
+ },
80
+ [],
81
+ async (persister) => {
82
+ await persister.load();
83
+ await persister.startAutoSave();
84
+ onReady?.();
85
+ },
86
+ );
87
+ {{/if}}
88
+ {{#if persistPglite}}
89
+ {{#if schemas}}
90
+ {{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite/with-schemas';"}}
91
+ {{else}}
92
+ {{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite';"}}
93
+ {{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
94
+ {{/if}}
95
+ {{includeFile template="client/src/shared/pglite.ts.hbs" output="client/src/pglite.{{ext}}"}}
96
+ {{addImport "import {getPgLite} from './pglite';"}}
97
+
98
+ useCreatePersister(
99
+ store,
100
+ async (store) => {
101
+ const pgLite = await getPgLite();
102
+ return await createPglitePersister(store, pgLite, STORE_ID);
103
+ },
104
+ [],
105
+ async (persister) => {
106
+ await persister.load();
107
+ await persister.startAutoSave();
108
+ onReady?.();
109
+ },
110
+ );
111
+ {{/if}}
112
+ {{/if}}
113
+
44
114
  {{#if sync}}
45
115
  {{includeFile template="client/src/shared/config.ts.hbs" output="client/src/config.ts"}}
46
116
  {{addImport "import {SERVER} from './config';"}}
47
117
  {{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
48
- {{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
49
- {{addImport "import {useCreateSynchronizer} from 'tinybase/ui-react';"}}
50
- {{addImport "import type {MergeableStore} from 'tinybase';"}}
118
+ {{#if schemas}}
119
+ {{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client/with-schemas';"}}
120
+ {{else}}
121
+ {{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
122
+ {{/if}}
123
+ {{#if schemas}}
124
+ {{addImport "import {useCreateSynchronizer} from 'tinybase/ui-react/with-schemas';"}}
125
+ {{else}}
126
+ {{addImport "import {useCreateSynchronizer} from 'tinybase/ui-react';"}}
127
+ {{/if}}
51
128
 
52
- useCreateSynchronizer(store, async (store: MergeableStore) => {
129
+ useCreateSynchronizer(store, async (store) => {
53
130
  const serverPathId = location.pathname;
54
131
  const synchronizer = await createWsSynchronizer(
55
132
  store,
@@ -57,14 +134,20 @@ useProvideStore(STORE_ID, store);
57
134
  );
58
135
  await synchronizer.startSync();
59
136
 
60
- // If the websocket reconnects in the future, do another explicit sync.
61
137
  synchronizer.getWebSocket().addEventListener('open', () => {
62
- synchronizer.load().then(() => synchronizer.save());
138
+ synchronizer.load().then(() => {
139
+ synchronizer.save();
140
+ {{#unless persist}}onReady?.();{{/unless}}
141
+ });
63
142
  });
64
143
 
65
144
  return synchronizer;
66
145
  });
67
146
  {{/if}}
68
147
 
148
+ {{#unless persist}}{{#unless sync}}
149
+ onReady?.();
150
+ {{/unless}}{{/unless}}
151
+
69
152
  return null;
70
153
  };
@@ -1,13 +1,11 @@
1
1
  {{includeFile template="client/src/todos/todoInput.css.hbs" output="client/src/todoInput.css"}}
2
- import './todoInput.css';
3
- import {useState} from 'react';
4
- import {useAddRowCallback, STORE_ID} from './Store';
5
-
2
+ {{addImport "import './todoInput.css';"}}
3
+ {{addImport "import {useState} from 'react';"}}
4
+ {{addImport "import {useAddRowCallback, STORE_ID} from './Store';"}}
6
5
  {{includeFile template="client/src/shared/Button.tsx.hbs" output="client/src/Button.{{ext}}"}}
7
- import {Button} from './Button';
8
-
6
+ {{addImport "import {Button} from './Button';"}}
9
7
  {{includeFile template="client/src/shared/Input.tsx.hbs" output="client/src/Input.{{ext}}"}}
10
- import {Input} from './Input';
8
+ {{addImport "import {Input} from './Input';"}}
11
9
 
12
10
  export const TodoInput = () => {
13
11
  const [text, setText] = useState('');
@@ -27,4 +25,4 @@ return (
27
25
  <Button type="submit" variant="primary">Add</Button>
28
26
  </form>
29
27
  );
30
- };
28
+ };
@@ -1,12 +1,11 @@
1
1
  {{includeFile template="client/src/todos/todoItem.css.hbs" output="client/src/todoItem.css"}}
2
- import './todoItem.css';
3
- import {useDelRowCallback, useRow, useSetPartialRowCallback, STORE_ID} from './Store';
4
-
2
+ {{addImport "import './todoItem.css';"}}
3
+ {{addImport "import {useDelRowCallback, useRow, useSetPartialRowCallback, type TodoRow, STORE_ID} from './Store';"}}
5
4
  {{includeFile template="client/src/shared/Button.tsx.hbs" output="client/src/Button.{{ext}}"}}
6
- import {Button} from './Button';
5
+ {{addImport "import {Button} from './Button';"}}
7
6
 
8
7
  export const TodoItem = ({rowId}: {rowId: string}) => {
9
- const todo = useRow('todos', rowId, STORE_ID);
8
+ const todo = useRow('todos', rowId, STORE_ID) as TodoRow;
10
9
  const setPartialRow = useSetPartialRowCallback('todos', rowId, (e: React.ChangeEvent<HTMLInputElement>) => ({completed: e.target.checked}), [], STORE_ID);
11
10
  const delRow = useDelRowCallback('todos', rowId, STORE_ID);
12
11
 
@@ -17,4 +16,4 @@ const setPartialRow = useSetPartialRowCallback('todos', rowId, (e: React.ChangeE
17
16
  <Button onClick={delRow}>Delete</Button>
18
17
  </div>
19
18
  );
20
- };
19
+ };
@@ -1,12 +1,11 @@
1
1
  {{includeFile template="client/src/todos/todoList.css.hbs" output="client/src/todoList.css"}}
2
- import './todoList.css';
3
- import {useRowIds, STORE_ID} from './Store';
4
-
2
+ {{addImport "import './todoList.css';"}}
3
+ {{addImport "import {useSortedRowIds, STORE_ID} from './Store';"}}
5
4
  {{includeFile template="client/src/todos/TodoItem.tsx.hbs" output="client/src/TodoItem.{{ext}}"}}
6
- import {TodoItem} from './TodoItem';
5
+ {{addImport "import {TodoItem} from './TodoItem';"}}
7
6
 
8
7
  export const TodoList = () => {
9
- const todoIds = useRowIds('todos', STORE_ID);
8
+ const todoIds = useSortedRowIds('todos', undefined, false, 0, undefined, STORE_ID);
10
9
 
11
10
  return (
12
11
  <div id="todoList">
@@ -15,4 +14,4 @@ return (
15
14
  ))}
16
15
  </div>
17
16
  );
18
- };
17
+ };
@@ -1,23 +1,29 @@
1
1
  {{includeFile template="client/src/todos/store.ts.hbs" output="client/src/store.ts"}}
2
- import {store} from './store';
3
-
2
+ {{addImport "import {store, storeReady} from './store';"}}
3
+ {{includeFile template="client/src/shared/loading.ts.hbs" output="client/src/loading.{{ext}}"}}
4
+ {{addImport "import {showLoading, hideLoading} from './loading';"}}
4
5
  {{includeFile template="client/src/todos/todoInput.ts.hbs" output="client/src/todoInput.{{ext}}"}}
5
- import {createTodoInput} from './todoInput';
6
-
6
+ {{addImport "import {createTodoInput} from './todoInput';"}}
7
7
  {{includeFile template="client/src/todos/todoList.ts.hbs" output="client/src/todoList.{{ext}}"}}
8
- import {createTodoList} from './todoList';
8
+ {{addImport "import {createTodoList} from './todoList';"}}
9
9
 
10
- const app = () => {
10
+ export const app = async () => {
11
11
  const appContainer = document.getElementById('app')!;
12
12
 
13
+ // Show loading spinner
14
+ const loadingDiv = showLoading(appContainer);
15
+
16
+ // Wait for store to be ready
17
+ await storeReady;
18
+
19
+ // Remove loading spinner
20
+ hideLoading(loadingDiv);
21
+
13
22
  const todoInputContainer = createTodoInput(store);
14
23
  appContainer.appendChild(todoInputContainer);
15
24
 
16
- // Focus the input after it's in the DOM
17
25
  const input = todoInputContainer.querySelector('input')!;
18
26
  input.focus();
19
27
 
20
28
  appContainer.appendChild(createTodoList(store));
21
- };
22
-
23
- export {app};
29
+ };
@@ -1,21 +1,18 @@
1
1
  {{#if schemas}}
2
- import {createMergeableStore} from 'tinybase/with-schemas';
3
- {{else}}
4
- import {createMergeableStore} from 'tinybase';
5
- {{/if}}
6
-
7
- const STORE_ID = 'todos';
8
-
9
- {{#if schemas}}
2
+ {{addImport "import {createMergeableStore, type Row} from 'tinybase/with-schemas';"}}
10
3
  const TODOS_SCHEMA = {
11
4
  todos: {
12
- text: {type: 'string'},
13
- completed: {type: 'boolean'},
5
+ text: {type: 'string', default: ''},
6
+ completed: {type: 'boolean', default: false},
14
7
  },
15
8
  } as const;
16
-
9
+ {{else}}
10
+ {{addImport "import {createMergeableStore} from 'tinybase';"}}
17
11
  {{/if}}
18
- export const store = createMergeableStore(STORE_ID){{#if schemas}}
12
+
13
+ export type TodoRow = {{#if schemas}}Row<typeof TODOS_SCHEMA, 'todos'>{{else}}{text: string; completed: boolean}{{/if}};
14
+
15
+ export const store = createMergeableStore(){{#if schemas}}
19
16
  .setTablesSchema(TODOS_SCHEMA){{/if}}
20
17
  .setDefaultContent([
21
18
  {
@@ -27,23 +24,90 @@ todos: {
27
24
  {},
28
25
  ]);
29
26
 
30
- {{#if sync}}
31
- {{includeFile template="client/src/shared/config.ts.hbs" output="client/src/config.ts"}}
32
- {{addImport "import {SERVER} from './config';"}}
33
- {{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
34
- {{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
35
-
36
- const serverPathId = location.pathname;
37
- createWsSynchronizer(
38
- store,
39
- new ReconnectingWebSocket(SERVER + serverPathId),
40
- ).then(async (synchronizer) => {
41
- await synchronizer.startSync();
42
-
43
- synchronizer.getWebSocket().addEventListener('open', () => {
44
- synchronizer.load().then(() => synchronizer.save());
45
- });
27
+ let resolveReady: () => void;
28
+ export const storeReady = new Promise<void>((resolve) => {
29
+ resolveReady = resolve;
46
30
  });
47
- {{/if}}
48
31
 
49
- export type TodosStore = typeof store;
32
+ {{#if persist}}
33
+ {{#if persistLocalStorage}}
34
+ {{#if schemas}}
35
+ {{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser/with-schemas';"}}
36
+ {{else}}
37
+ {{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser';"}}
38
+ {{/if}}
39
+
40
+ const persister = createLocalPersister(store, 'todos');
41
+ persister.load().then(() => {
42
+ persister.startAutoSave();
43
+ resolveReady();
44
+ });
45
+ {{/if}}
46
+ {{#if persistSqlite}}
47
+ {{#if schemas}}
48
+ {{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm/with-schemas';"}}
49
+ {{else}}
50
+ {{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';"}}
51
+ {{/if}}
52
+ {{includeFile template="client/src/shared/sqlite.ts.hbs" output="client/src/sqlite.ts"}}
53
+ {{addImport "import {getDb} from './sqlite';"}}
54
+
55
+ getDb().then(({sqlite3, db}) => {
56
+ const persister = createSqliteWasmPersister(store, sqlite3, db, 'todos');
57
+ persister.load().then(() => {
58
+ persister.startAutoSave();
59
+ resolveReady();
60
+ });
61
+ });
62
+ {{/if}}
63
+ {{#if persistPglite}}
64
+ {{#if schemas}}
65
+ {{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite/with-schemas';"}}
66
+ {{else}}
67
+ {{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite';"}}
68
+ {{/if}}
69
+ {{includeFile template="client/src/shared/pglite.ts.hbs" output="client/src/pglite.{{ext}}"}}
70
+ {{addImport "import {getPgLite} from './pglite';"}}
71
+
72
+ getPgLite().then((pgLite) => {
73
+ createPglitePersister(store, pgLite, 'todos').then((persister) => {
74
+ persister.load().then(() => {
75
+ persister.startAutoSave();
76
+ resolveReady();
77
+ });
78
+ });
79
+ });
80
+ {{/if}}
81
+ {{/if}}
82
+
83
+ {{#if sync}}
84
+ {{includeFile template="client/src/shared/config.ts.hbs" output="client/src/config.ts"}}
85
+ {{addImport "import {SERVER} from './config';"}}
86
+ {{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
87
+ {{#if schemas}}
88
+ {{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client/with-schemas';"}}
89
+ {{else}}
90
+ {{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
91
+ {{/if}}
92
+
93
+ const serverPathId = location.pathname;
94
+ createWsSynchronizer(
95
+ store,
96
+ new ReconnectingWebSocket(SERVER + serverPathId),
97
+ ).then(async (synchronizer) => {
98
+ await synchronizer.startSync();
99
+
100
+ synchronizer.getWebSocket().addEventListener('open', () => {
101
+ synchronizer.load().then(() => {
102
+ synchronizer.save();
103
+ {{#unless persist}}resolveReady();{{/unless}}
104
+ });
105
+ });
106
+ });
107
+ {{/if}}
108
+
109
+ {{#unless persist}}{{#unless sync}}
110
+ resolveReady();
111
+ {{/unless}}{{/unless}}
112
+
113
+ export type TodosStore = typeof store;
@@ -1,12 +1,10 @@
1
1
  {{includeFile template="client/src/todos/todoInput.css.hbs" output="client/src/todoInput.css"}}
2
- import './todoInput.css';
3
- import {type TodosStore} from './store';
4
-
2
+ {{addImport "import './todoInput.css';"}}
3
+ {{addImport "import {type TodosStore} from './store';"}}
5
4
  {{includeFile template="client/src/shared/button.ts.hbs" output="client/src/button.{{ext}}"}}
6
- import {createButton} from './button';
7
-
5
+ {{addImport "import {createButton} from './button';"}}
8
6
  {{includeFile template="client/src/shared/input.ts.hbs" output="client/src/input.{{ext}}"}}
9
- import {createInput} from './input';
7
+ {{addImport "import {createInput} from './input';"}}
10
8
 
11
9
  export const createTodoInput = (store: TodosStore): HTMLDivElement => {
12
10
  const container = document.createElement('div');
@@ -19,7 +17,6 @@ const text = input.value.trim();
19
17
  if (text) {
20
18
  store.addRow('todos', {text, completed: false});
21
19
  input.value = '';
22
- input.focus();
23
20
  }
24
21
  };
25
22
 
@@ -35,4 +32,4 @@ container.appendChild(input);
35
32
  container.appendChild(addButton);
36
33
 
37
34
  return container;
38
- };
35
+ };
@@ -1,8 +1,7 @@
1
1
  {{includeFile template="client/src/todos/todoItem.css.hbs" output="client/src/todoItem.css"}}
2
- import './todoItem.css';
3
-
2
+ {{addImport "import './todoItem.css';"}}
4
3
  {{includeFile template="client/src/shared/button.ts.hbs" output="client/src/button.{{ext}}"}}
5
- import {createButton} from './button';
4
+ {{addImport "import {createButton} from './button';"}}
6
5
 
7
6
  export const createTodoItem = (id: string, text: string, completed: boolean, onToggle: () => void, onDelete: () => void): HTMLDivElement => {
8
7
  const item = document.createElement('div');
@@ -25,4 +24,4 @@ item.appendChild(label);
25
24
  item.appendChild(deleteBtn);
26
25
 
27
26
  return item;
28
- };
27
+ };
@@ -1,21 +1,19 @@
1
1
  {{includeFile template="client/src/todos/todoList.css.hbs" output="client/src/todoList.css"}}
2
- import './todoList.css';
3
- import {type TodosStore} from './store';
4
-
2
+ {{addImport "import './todoList.css';"}}
3
+ {{addImport "import {type TodosStore, type TodoRow} from './store';"}}
5
4
  {{includeFile template="client/src/todos/todoItem.ts.hbs" output="client/src/todoItem.{{ext}}"}}
6
- import {createTodoItem} from './todoItem';
5
+ {{addImport "import {createTodoItem} from './todoItem';"}}
7
6
 
8
7
  export const createTodoList = (store: TodosStore): HTMLDivElement => {
9
8
  const list = document.createElement('div');
10
9
  list.id = 'todoList';
11
10
 
12
11
  const render = () => {
13
- const todos = store.getTable('todos');
14
- const todoIds = Object.keys(todos);
12
+ const todoIds = store.getSortedRowIds('todos');
15
13
 
16
14
  list.innerHTML = '';
17
15
  todoIds.forEach((id) => {
18
- const todo = todos[id];
16
+ const todo = store.getRow('todos', id) as TodoRow;
19
17
  const item = createTodoItem(
20
18
  id,
21
19
  todo.text,
@@ -35,4 +33,4 @@ store.addTablesListener(render);
35
33
  render();
36
34
 
37
35
  return list;
38
- };
36
+ };
@@ -0,0 +1,4 @@
1
+ declare module '*.css' {
2
+ const content: string;
3
+ export default content;
4
+ }
@@ -2,11 +2,61 @@
2
2
  {{#if react}}
3
3
  {{addImport "import react from '@vitejs/plugin-react';"}}
4
4
  {{/if}}
5
+ {{#if persistSqlite}}
6
+ {{addImport "import {viteStaticCopy} from 'vite-plugin-static-copy';"}}
7
+ {{/if}}
8
+ {{#if persistPglite}}
9
+ {{addImport "import {viteStaticCopy} from 'vite-plugin-static-copy';"}}
10
+ {{/if}}
5
11
 
6
12
  export default defineConfig({
13
+ plugins: [
7
14
  {{#if react}}
8
- plugins: [react({
9
- jsxRuntime: 'automatic',
10
- })],
15
+ react({
16
+ jsxRuntime: 'automatic',
17
+ }),
18
+ {{/if}}
19
+ {{#if persistSqlite}}
20
+ viteStaticCopy({
21
+ targets: [
22
+ {
23
+ src: 'node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3.wasm',
24
+ dest: '.'
25
+ }
26
+ ]
27
+ }),
28
+ {{/if}}
29
+ {{#if persistPglite}}
30
+ viteStaticCopy({
31
+ targets: [
32
+ {
33
+ src: 'node_modules/@electric-sql/pglite/dist/*.wasm',
34
+ dest: '.'
35
+ }
36
+ ]
37
+ }),
38
+ {{/if}}
39
+ ],
40
+ {{#if persistSqlite}}
41
+ server: {
42
+ headers: {
43
+ 'Cross-Origin-Opener-Policy': 'same-origin',
44
+ 'Cross-Origin-Embedder-Policy': 'require-corp',
45
+ },
46
+ },
47
+ optimizeDeps: {
48
+ exclude: ['@sqlite.org/sqlite-wasm'],
49
+ },
50
+ {{/if}}
51
+ {{#if persistPglite}}
52
+ server: {
53
+ headers: {
54
+ 'Cross-Origin-Opener-Policy': 'same-origin',
55
+ 'Cross-Origin-Embedder-Policy': 'require-corp',
56
+ },
57
+ },
58
+ optimizeDeps: {
59
+ exclude: ['@electric-sql/pglite'],
60
+ },
11
61
  {{/if}}
12
62
  });