create-tinybase 0.2.5 → 0.3.1

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 (81) hide show
  1. package/README.md +62 -18
  2. package/cli.js +2 -2
  3. package/package.json +7 -4
  4. package/screenshots/chat.png +0 -0
  5. package/screenshots/drawing.png +0 -0
  6. package/screenshots/todos.png +0 -0
  7. package/templates/README.md.hbs +130 -12
  8. package/templates/client/eslint.config.js.hbs +7 -0
  9. package/templates/client/package.json.hbs +28 -8
  10. package/templates/client/src/chat/App.tsx.hbs +28 -22
  11. package/templates/client/src/chat/ChatStore.tsx.hbs +108 -27
  12. package/templates/client/src/chat/Message.tsx.hbs +8 -9
  13. package/templates/client/src/chat/MessageInput.tsx.hbs +7 -7
  14. package/templates/client/src/chat/Messages.tsx.hbs +5 -15
  15. package/templates/client/src/chat/SettingsStore.tsx.hbs +86 -9
  16. package/templates/client/src/chat/UsernameInput.tsx.hbs +6 -11
  17. package/templates/client/src/chat/app.ts.hbs +18 -18
  18. package/templates/client/src/chat/chatStore.ts.hbs +97 -35
  19. package/templates/client/src/chat/message.ts.hbs +4 -3
  20. package/templates/client/src/chat/messageInput.css.hbs +1 -1
  21. package/templates/client/src/chat/messageInput.ts.hbs +7 -8
  22. package/templates/client/src/chat/messages.ts.hbs +7 -12
  23. package/templates/client/src/chat/settingsStore.ts.hbs +65 -6
  24. package/templates/client/src/chat/usernameInput.css.hbs +1 -1
  25. package/templates/client/src/chat/usernameInput.ts.hbs +6 -6
  26. package/templates/client/src/chat/utils.ts.hbs +26 -0
  27. package/templates/client/src/drawing/App.tsx.hbs +26 -20
  28. package/templates/client/src/drawing/BrushSize.tsx.hbs +8 -11
  29. package/templates/client/src/drawing/Canvas.tsx.hbs +65 -73
  30. package/templates/client/src/drawing/CanvasStore.tsx.hbs +104 -18
  31. package/templates/client/src/drawing/ColorPicker.tsx.hbs +4 -11
  32. package/templates/client/src/drawing/DrawingControls.tsx.hbs +7 -7
  33. package/templates/client/src/drawing/SettingsStore.tsx.hbs +81 -8
  34. package/templates/client/src/drawing/app.ts.hbs +18 -8
  35. package/templates/client/src/drawing/brushSize.ts.hbs +12 -5
  36. package/templates/client/src/drawing/canvas.ts.hbs +84 -86
  37. package/templates/client/src/drawing/canvasStore.ts.hbs +93 -26
  38. package/templates/client/src/drawing/colorPicker.ts.hbs +3 -3
  39. package/templates/client/src/drawing/drawingControls.ts.hbs +7 -7
  40. package/templates/client/src/drawing/settingsStore.ts.hbs +63 -8
  41. package/templates/client/src/game/App.tsx.hbs +20 -16
  42. package/templates/client/src/game/Board.tsx.hbs +8 -8
  43. package/templates/client/src/game/Game.tsx.hbs +14 -21
  44. package/templates/client/src/game/GameStatus.tsx.hbs +5 -5
  45. package/templates/client/src/game/Square.tsx.hbs +6 -11
  46. package/templates/client/src/game/Store.tsx.hbs +106 -16
  47. package/templates/client/src/game/app.ts.hbs +17 -6
  48. package/templates/client/src/game/board.ts.hbs +7 -7
  49. package/templates/client/src/game/game.ts.hbs +12 -18
  50. package/templates/client/src/game/gameStatus.ts.hbs +3 -3
  51. package/templates/client/src/game/square.ts.hbs +4 -4
  52. package/templates/client/src/game/store.ts.hbs +95 -23
  53. package/templates/client/src/index.tsx.hbs +5 -7
  54. package/templates/client/src/shared/Button.tsx.hbs +3 -3
  55. package/templates/client/src/shared/Input.tsx.hbs +2 -2
  56. package/templates/client/src/shared/Loading.tsx.hbs +5 -0
  57. package/templates/client/src/shared/button.ts.hbs +2 -2
  58. package/templates/client/src/shared/config.ts.hbs +4 -6
  59. package/templates/client/src/shared/input.ts.hbs +2 -2
  60. package/templates/client/src/shared/loading.css.hbs +21 -0
  61. package/templates/client/src/shared/loading.ts.hbs +13 -0
  62. package/templates/client/src/shared/pglite.ts.hbs +10 -0
  63. package/templates/client/src/shared/sqlite.ts.hbs +17 -0
  64. package/templates/client/src/todos/App.tsx.hbs +22 -22
  65. package/templates/client/src/todos/Store.tsx.hbs +106 -23
  66. package/templates/client/src/todos/TodoInput.tsx.hbs +6 -8
  67. package/templates/client/src/todos/TodoItem.tsx.hbs +5 -6
  68. package/templates/client/src/todos/TodoList.tsx.hbs +5 -6
  69. package/templates/client/src/todos/app.ts.hbs +16 -10
  70. package/templates/client/src/todos/store.ts.hbs +94 -30
  71. package/templates/client/src/todos/todoInput.ts.hbs +5 -8
  72. package/templates/client/src/todos/todoItem.ts.hbs +3 -4
  73. package/templates/client/src/todos/todoList.ts.hbs +6 -8
  74. package/templates/client/vite-env.d.ts.hbs +4 -0
  75. package/templates/client/vite.config.js.hbs +53 -3
  76. package/templates/server/index.ts.hbs +43 -0
  77. package/templates/server/package.json.hbs +6 -7
  78. package/templates/server/wrangler.toml.hbs +1 -1
  79. package/templates/server/index-do.ts.hbs +0 -22
  80. package/templates/server/index-node.ts.hbs +0 -8
  81. /package/templates/client/{.prettierrc.hbs → .prettierrc.json.hbs} +0 -0
@@ -1,17 +1,10 @@
1
1
  {{includeFile template="client/src/drawing/colorPicker.css.hbs" output="client/src/colorPicker.css"}}
2
- import './colorPicker.css';
3
- import {useValue, useSetValueCallback, STORE_ID} from './SettingsStore';
2
+ {{addImport "import './colorPicker.css';"}}
3
+ {{addImport "import {useValueState, STORE_ID} from './SettingsStore';"}}
4
4
 
5
5
  export const ColorPicker = () => {
6
6
  const colors = ['#d81b60', '#1976d2', '#388e3c', '#f57c00', '#7b1fa2', '#fff'];
7
- const currentColor = useValue('brushColor', STORE_ID);
8
-
9
- const setColor = useSetValueCallback(
10
- 'brushColor',
11
- (color: string) => color,
12
- [],
13
- STORE_ID,
14
- );
7
+ const [currentColor, setColor] = useValueState('brushColor', STORE_ID);
15
8
 
16
9
  return (
17
10
  <div id="colorPicker">
@@ -21,4 +14,4 @@ return (
21
14
  ))}
22
15
  </div>
23
16
  );
24
- };
17
+ };
@@ -1,18 +1,18 @@
1
1
  {{includeFile template="client/src/drawing/drawingControls.css.hbs" output="client/src/drawingControls.css"}}
2
- import './drawingControls.css';
3
- import {useDelTableCallback, STORE_ID} from './CanvasStore';
2
+ {{addImport "import './drawingControls.css';"}}
3
+ {{addImport "import {useDelTableCallback, STORE_ID} from './CanvasStore';"}}
4
4
 
5
5
  {{includeFile template="client/src/shared/Button.tsx.hbs" output="client/src/Button.{{ext}}"}}
6
- import {Button} from './Button';
6
+ {{addImport "import {Button} from './Button';"}}
7
7
 
8
8
  {{includeFile template="client/src/drawing/ColorPicker.tsx.hbs" output="client/src/ColorPicker.{{ext}}"}}
9
- import {ColorPicker} from './ColorPicker';
9
+ {{addImport "import {ColorPicker} from './ColorPicker';"}}
10
10
 
11
11
  {{includeFile template="client/src/drawing/BrushSize.tsx.hbs" output="client/src/BrushSize.{{ext}}"}}
12
- import {BrushSize} from './BrushSize';
12
+ {{addImport "import {BrushSize} from './BrushSize';"}}
13
13
 
14
14
  export const DrawingControls = () => {
15
- const clearStrokes = useDelTableCallback('strokes', () => null, [], STORE_ID);
15
+ const clearStrokes = useDelTableCallback('strokes', STORE_ID);
16
16
 
17
17
  return (
18
18
  <div id="drawingControls">
@@ -21,4 +21,4 @@ return (
21
21
  <Button onClick={clearStrokes} variant="primary">Clear</Button>
22
22
  </div>
23
23
  );
24
- };
24
+ };
@@ -1,10 +1,10 @@
1
1
  {{#if schemas}}
2
- import {createStore} from 'tinybase/with-schemas';
3
- import * as UiReact from 'tinybase/ui-react/with-schemas';
4
- import {type NoTablesSchema} from 'tinybase/with-schemas';
2
+ {{addImport "import {createStore} from 'tinybase/with-schemas';"}}
3
+ {{addImport "import * as UiReact from 'tinybase/ui-react/with-schemas';"}}
4
+ {{addImport "import {type NoTablesSchema} from 'tinybase/with-schemas';"}}
5
5
  {{else}}
6
- import {createStore} from 'tinybase';
7
- import {useCreateStore, useProvideStore, useValue, useSetValueCallback, useValues} from 'tinybase/ui-react';
6
+ {{addImport "import {createStore} from 'tinybase';"}}
7
+ {{addImport "import {useCreateStore, useProvideStore, useValueState, useValues} from 'tinybase/ui-react';"}}
8
8
  {{/if}}
9
9
 
10
10
  export const STORE_ID = 'settings';
@@ -17,12 +17,12 @@ export const STORE_ID = 'settings';
17
17
 
18
18
  type Schemas = [NoTablesSchema, typeof VALUES_SCHEMA];
19
19
 
20
- const {useCreateStore, useProvideStore, useValue, useSetValueCallback, useValues} = UiReact as UiReact.WithSchemas<Schemas>;
20
+ const {useCreateStore, {{#if persist}}useCreatePersister, {{/if}}useProvideStore, useValues, useValueState} = UiReact as UiReact.WithSchemas<Schemas>;
21
21
  {{/if}}
22
22
 
23
- export {useValue, useSetValueCallback, useValues};
23
+ export {useValueState, useValues};
24
24
 
25
- export const SettingsStore = () => {
25
+ export const SettingsStore = ({onReady}: {onReady?: () => void}) => {
26
26
  const store = useCreateStore(() =>
27
27
  createStore(){{#if schemas}}
28
28
  .setValuesSchema(VALUES_SCHEMA){{/if}}
@@ -32,5 +32,78 @@ createStore(){{#if schemas}}
32
32
 
33
33
  useProvideStore(STORE_ID, store);
34
34
 
35
+ {{#if persist}}
36
+ {{#if persistLocalStorage}}
37
+ {{#if schemas}}
38
+ {{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser/with-schemas';"}}
39
+ {{else}}
40
+ {{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser';"}}
41
+ {{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
42
+ {{/if}}
43
+
44
+ useCreatePersister(
45
+ store,
46
+ (store) => createLocalPersister(store, STORE_ID),
47
+ [],
48
+ async (persister) => {
49
+ await persister.load();
50
+ await persister.startAutoSave();
51
+ onReady?.();
52
+ },
53
+ );
54
+ {{/if}}
55
+ {{#if persistSqlite}}
56
+ {{#if schemas}}
57
+ {{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm/with-schemas';"}}
58
+ {{else}}
59
+ {{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';"}}
60
+ {{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
61
+ {{/if}}
62
+ {{includeFile template="client/src/shared/sqlite.ts.hbs" output="client/src/sqlite.{{ext}}"}}
63
+ {{addImport "import {getDb} from './sqlite';"}}
64
+ useCreatePersister(
65
+ store,
66
+ async (store) => {
67
+ const {sqlite3, db} = await getDb();
68
+ return createSqliteWasmPersister(store, sqlite3, db, STORE_ID);
69
+ },
70
+ [],
71
+ async (persister) => {
72
+ await persister.load();
73
+ await persister.startAutoSave();
74
+ onReady?.();
75
+ },
76
+ );
77
+ {{/if}}
78
+ {{#if persistPglite}}
79
+ {{#if schemas}}
80
+ {{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite/with-schemas';"}}
81
+ {{else}}
82
+ {{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite';"}}
83
+ {{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
84
+ {{/if}}
85
+ {{includeFile template="client/src/shared/pglite.ts.hbs" output="client/src/pglite.{{ext}}"}}
86
+ {{addImport "import {getPgLite} from './pglite';"}}
87
+
88
+ useCreatePersister(
89
+ store,
90
+ async (store) => {
91
+ const pgLite = await getPgLite();
92
+ return await createPglitePersister(store, pgLite, STORE_ID);
93
+ },
94
+ [],
95
+ async (persister) => {
96
+ await persister.load();
97
+ await persister.startAutoSave();
98
+ onReady?.();
99
+ },
100
+ );
101
+ {{/if}}
102
+ {{/if}}
103
+
104
+ {{#unless persist}}{{#unless sync}}
105
+ onReady?.();
106
+ {{/unless}}{{/unless}}
107
+
35
108
  return null;
36
109
  };
@@ -1,20 +1,30 @@
1
1
  {{includeFile template="client/src/drawing/settingsStore.ts.hbs" output="client/src/settingsStore.ts"}}
2
- import {settingsStore} from './settingsStore';
2
+ {{addImport "import {settingsStore, settingsStoreReady} from './settingsStore';"}}
3
3
 
4
4
  {{includeFile template="client/src/drawing/canvasStore.ts.hbs" output="client/src/canvasStore.ts"}}
5
- import {canvasStore} from './canvasStore';
5
+ {{addImport "import {canvasStore, canvasStoreReady} from './canvasStore';"}}
6
+
7
+ {{includeFile template="client/src/shared/loading.ts.hbs" output="client/src/loading.{{ext}}"}}
8
+ {{addImport "import {showLoading, hideLoading} from './loading';"}}
6
9
 
7
10
  {{includeFile template="client/src/drawing/drawingControls.ts.hbs" output="client/src/drawingControls.{{ext}}"}}
8
- import {createDrawingControls} from './drawingControls';
11
+ {{addImport "import {createDrawingControls} from './drawingControls';"}}
9
12
 
10
13
  {{includeFile template="client/src/drawing/canvas.ts.hbs" output="client/src/canvas.{{ext}}"}}
11
- import {createCanvas} from './canvas';
14
+ {{addImport "import {createCanvas} from './canvas';"}}
12
15
 
13
- const app = () => {
16
+ export const app = async () => {
14
17
  const appContainer = document.getElementById('app')!;
15
18
 
19
+ // Show loading spinner
20
+ const loadingDiv = showLoading(appContainer);
21
+
22
+ // Wait for stores to be ready
23
+ await Promise.all([settingsStoreReady, canvasStoreReady]);
24
+
25
+ // Remove loading spinner
26
+ hideLoading(loadingDiv);
27
+
16
28
  appContainer.appendChild(createDrawingControls(settingsStore, canvasStore));
17
29
  appContainer.appendChild(createCanvas(settingsStore, canvasStore));
18
- };
19
-
20
- export {app};
30
+ };
@@ -1,6 +1,6 @@
1
1
  {{includeFile template="client/src/drawing/brushSize.css.hbs" output="client/src/brushSize.css"}}
2
- import './brushSize.css';
3
- import type {Store as SettingsStore} from './settingsStore';
2
+ {{addImport "import './brushSize.css';"}}
3
+ {{addImport "import type {SettingsStore} from './settingsStore';"}}
4
4
 
5
5
  export const createBrushSize = (store: SettingsStore): HTMLDivElement => {
6
6
  const container = document.createElement('div');
@@ -13,11 +13,15 @@ const sizeSlider = document.createElement('input');
13
13
  sizeSlider.type = 'range';
14
14
  sizeSlider.min = '1';
15
15
  sizeSlider.max = '50';
16
- sizeSlider.value = '5';
17
16
 
18
17
  const sizeLabel = document.createElement('span');
19
18
  sizeLabel.id = 'brushSizeValue';
20
- sizeLabel.textContent = '5';
19
+
20
+ const updateSize = () => {
21
+ const size = store.getValue('brushSize') ?? 5;
22
+ sizeSlider.value = `${size}`;
23
+ sizeLabel.textContent = `${size}`;
24
+ };
21
25
 
22
26
  sizeSlider.addEventListener('input', () => {
23
27
  const size = parseInt(sizeSlider.value);
@@ -25,9 +29,12 @@ store.setValue('brushSize', size);
25
29
  sizeLabel.textContent = `${size}`;
26
30
  });
27
31
 
32
+ store.addValueListener('brushSize', updateSize);
33
+ updateSize();
34
+
28
35
  container.appendChild(label);
29
36
  container.appendChild(sizeSlider);
30
37
  container.appendChild(sizeLabel);
31
38
 
32
39
  return container;
33
- };
40
+ };
@@ -1,8 +1,10 @@
1
1
  {{includeFile template="client/src/drawing/canvas.css.hbs" output="client/src/canvas.css"}}
2
- import './canvas.css';
3
- import {getUniqueId} from 'tinybase';
4
- import type {Store as SettingsStore} from './settingsStore';
5
- import type {Store as CanvasStore} from './canvasStore';
2
+ {{addImport "import './canvas.css';"}}
3
+ {{addImport "import {getHlcFunctions} from 'tinybase';"}}
4
+ {{addImport "import type {SettingsStore} from './settingsStore';"}}
5
+ {{addImport "import type {CanvasStore, StrokeRow} from './canvasStore';"}}
6
+
7
+ const [getNextHlc] = getHlcFunctions();
6
8
 
7
9
  export const createCanvas = (settingsStore: SettingsStore, canvasStore: CanvasStore): HTMLCanvasElement => {
8
10
  const canvas = document.createElement('canvas');
@@ -13,91 +15,87 @@ canvas.height = 400;
13
15
  const ctx = canvas.getContext('2d')!;
14
16
  let isDrawing = false;
15
17
  let currentStrokeId: string | null = null;
16
- let pointIndex = 0;
17
18
 
18
19
  const draw = () => {
19
20
  ctx.fillStyle = '#111';
20
21
  ctx.fillRect(0, 0, canvas!.width, canvas!.height);
21
22
 
22
- const strokes = canvasStore.getTable('strokes');
23
- Object.entries(strokes).forEach(([id, stroke]: [string, any]) => {
24
- const points: Array<{x: number; y: number}> = [];
25
- let i = 0;
26
- while (stroke[`x${i}`] !== undefined && stroke[`y${i}`] !== undefined) {
27
- points.push({x: stroke[`x${i}`], y: stroke[`y${i}`]});
28
- i++;
23
+ const sortedIds = canvasStore.getSortedRowIds('strokes');
24
+ sortedIds.forEach((id) => {
25
+ const stroke = canvasStore.getRow('strokes', id) as StrokeRow;
26
+ if (stroke.points) {
27
+ const pointsArray = JSON.parse(stroke.points) as number[];
28
+ if (pointsArray.length >= 2) {
29
+ ctx.strokeStyle = stroke.color;
30
+ ctx.lineWidth = stroke.size * 2;
31
+ ctx.lineCap = 'round';
32
+ ctx.lineJoin = 'round';
33
+ ctx.beginPath();
34
+ ctx.moveTo(pointsArray[0], pointsArray[1]);
35
+ for (let i = 2; i < pointsArray.length; i +=2) { ctx.lineTo(pointsArray[i], pointsArray[i + 1]); } ctx.stroke(); } } }); }; const addPoint=(e: MouseEvent | TouchEvent)=> {
36
+ if (!currentStrokeId) return;
37
+
38
+ const rect = canvas!.getBoundingClientRect();
39
+ const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
40
+ const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
41
+
42
+ const pointsArray = JSON.parse(canvasStore.getCell('strokes', currentStrokeId, 'points') as string ?? '[]') as number[];
43
+ pointsArray.push(clientX - rect.left, clientY - rect.top);
44
+ canvasStore.setCell('strokes', currentStrokeId, 'points', JSON.stringify(pointsArray));
45
+ };
46
+
47
+ const startStroke = (e: MouseEvent | TouchEvent) => {
48
+ const {brushColor, brushSize} = settingsStore.getValues();
49
+ currentStrokeId = getNextHlc();
50
+
51
+ canvasStore.setRow('strokes', currentStrokeId, {
52
+ color: brushColor,
53
+ size: brushSize,
54
+ points: '[]',
55
+ });
56
+
57
+ addPoint(e);
58
+ };
59
+
60
+ canvas.addEventListener('mousedown', (e) => {
61
+ isDrawing = true;
62
+ startStroke(e);
63
+ });
64
+
65
+ canvas.addEventListener('mouseup', () => {
66
+ isDrawing = false;
67
+ currentStrokeId = null;
68
+ });
69
+
70
+ canvas.addEventListener('mouseleave', () => {
71
+ isDrawing = false;
72
+ currentStrokeId = null;
73
+ });
74
+
75
+ canvas.addEventListener('mousemove', (e) => {
76
+ if (isDrawing) addPoint(e);
77
+ });
78
+
79
+ canvas.addEventListener('touchstart', (e) => {
80
+ isDrawing = true;
81
+ startStroke(e);
82
+ e.preventDefault();
83
+ });
84
+
85
+ canvas.addEventListener('touchmove', (e) => {
86
+ if (isDrawing) {
87
+ addPoint(e);
88
+ e.preventDefault();
29
89
  }
30
- if (points.length > 0) {
31
- ctx.strokeStyle = stroke.color;
32
- ctx.lineWidth = stroke.size * 2;
33
- ctx.lineCap = 'round';
34
- ctx.lineJoin = 'round';
35
- ctx.beginPath();
36
- ctx.moveTo(points[0].x, points[0].y);
37
- for (let i = 1; i < points.length; i++) { ctx.lineTo(points[i].x, points[i].y); } ctx.stroke(); } }); }; const addPoint=(e: MouseEvent | TouchEvent)=> {
38
- if (!currentStrokeId) return;
39
-
40
- const rect = canvas!.getBoundingClientRect();
41
- const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
42
- const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
43
-
44
- canvasStore.setCell('strokes', currentStrokeId, `x${pointIndex}`, clientX - rect.left);
45
- canvasStore.setCell('strokes', currentStrokeId, `y${pointIndex}`, clientY - rect.top);
46
- pointIndex++;
47
- };
48
-
49
- const startStroke = (e: MouseEvent | TouchEvent) => {
50
- const brush = settingsStore.getValues() as any;
51
- currentStrokeId = getUniqueId();
52
- pointIndex = 0;
53
-
54
- canvasStore.setRow('strokes', currentStrokeId, {
55
- color: brush.brushColor,
56
- size: brush.brushSize,
57
- });
58
-
59
- addPoint(e);
60
- };
61
-
62
- canvas.addEventListener('mousedown', (e) => {
63
- isDrawing = true;
64
- startStroke(e);
65
- });
66
-
67
- canvas.addEventListener('mouseup', () => {
68
- isDrawing = false;
69
- currentStrokeId = null;
70
- });
71
-
72
- canvas.addEventListener('mouseleave', () => {
73
- isDrawing = false;
74
- currentStrokeId = null;
75
- });
76
-
77
- canvas.addEventListener('mousemove', (e) => {
78
- if (isDrawing) addPoint(e);
79
- });
80
-
81
- canvas.addEventListener('touchstart', (e) => {
82
- isDrawing = true;
83
- startStroke(e);
84
- e.preventDefault();
85
- });
86
-
87
- canvas.addEventListener('touchmove', (e) => {
88
- if (isDrawing) {
89
- addPoint(e);
90
- e.preventDefault();
91
- }
92
- });
93
-
94
- canvas.addEventListener('touchend', () => {
95
- isDrawing = false;
96
- currentStrokeId = null;
97
- });
98
-
99
- canvasStore.addTablesListener(draw);
100
- draw();
101
-
102
- return canvas;
103
- };
90
+ });
91
+
92
+ canvas.addEventListener('touchend', () => {
93
+ isDrawing = false;
94
+ currentStrokeId = null;
95
+ });
96
+
97
+ canvasStore.addTablesListener(draw);
98
+ draw();
99
+
100
+ return canvas;
101
+ };
@@ -1,42 +1,109 @@
1
1
  {{#if schemas}}
2
- import {createMergeableStore} from 'tinybase/with-schemas';
2
+ {{addImport "import {createMergeableStore, type Row} from 'tinybase/with-schemas';"}}
3
3
  {{else}}
4
- import {createMergeableStore} from 'tinybase';
4
+ {{addImport "import {createMergeableStore} from 'tinybase';"}}
5
5
  {{/if}}
6
- import {getUniqueId} from 'tinybase';
7
-
8
- const STORE_ID = 'canvas';
9
6
 
10
7
  {{#if schemas}}
11
8
  const TABLES_SCHEMA = {
12
9
  strokes: {
13
- color: {type: 'string'},
14
- size: {type: 'number'},
10
+ color: {type: 'string', default: ''},
11
+ size: {type: 'number', default: 1},
12
+ points: {type: 'string', default: '[]'},
15
13
  },
16
14
  } as const;
17
15
 
18
16
  {{/if}}
19
- export const canvasStore = createMergeableStore(STORE_ID){{#if schemas}}
17
+ export const canvasStore = createMergeableStore(){{#if schemas}}
20
18
  .setTablesSchema(TABLES_SCHEMA){{/if}}
21
19
  .setDefaultContent([{strokes: {}}, {}]);
22
20
 
23
- {{#if sync}}
24
- {{includeFile template="client/src/shared/config.ts.hbs" output="client/src/config.ts"}}
25
- {{addImport "import {SERVER} from './config';"}}
26
- {{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
27
- {{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
28
-
29
- const serverPathId = location.pathname;
30
- createWsSynchronizer(
31
- canvasStore,
32
- new ReconnectingWebSocket(SERVER + serverPathId),
33
- ).then(async (synchronizer) => {
34
- await synchronizer.startSync();
35
-
36
- synchronizer.getWebSocket().addEventListener('open', () => {
37
- synchronizer.load().then(() => synchronizer.save());
38
- });
21
+ export type StrokeRow = {{#if schemas}}Row<typeof TABLES_SCHEMA, 'strokes'>{{else}}{color: string; size: number; points: string}{{/if}};
22
+
23
+ let resolveCanvasReady: () => void;
24
+ export const canvasStoreReady = new Promise<void>((resolve) => {
25
+ resolveCanvasReady = resolve;
39
26
  });
40
- {{/if}}
41
27
 
42
- export type CanvasStore = typeof canvasStore;
28
+ {{#if persist}}
29
+ {{#if persistLocalStorage}}
30
+ {{#if schemas}}
31
+ {{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser/with-schemas';"}}
32
+ {{else}}
33
+ {{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser';"}}
34
+ {{/if}}
35
+
36
+ const canvasPersister = createLocalPersister(canvasStore, 'canvas');
37
+ canvasPersister.load().then(() => {
38
+ canvasPersister.startAutoSave();
39
+ resolveCanvasReady();
40
+ });
41
+ {{/if}}
42
+ {{#if persistSqlite}}
43
+ {{#if schemas}}
44
+ {{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm/with-schemas';"}}
45
+ {{else}}
46
+ {{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';"}}
47
+ {{/if}}
48
+ {{includeFile template="client/src/shared/sqlite.ts.hbs" output="client/src/sqlite.ts"}}
49
+ {{addImport "import {getDb} from './sqlite';"}}
50
+
51
+ getDb().then(({sqlite3, db}) => {
52
+ const canvasPersister = createSqliteWasmPersister(canvasStore, sqlite3, db, 'canvas');
53
+ canvasPersister.load().then(() => {
54
+ canvasPersister.startAutoSave();
55
+ resolveCanvasReady();
56
+ });
57
+ });
58
+ {{/if}}
59
+ {{#if persistPglite}}
60
+ {{#if schemas}}
61
+ {{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite/with-schemas';"}}
62
+ {{else}}
63
+ {{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite';"}}
64
+ {{/if}}
65
+ {{includeFile template="client/src/shared/pglite.ts.hbs" output="client/src/pglite.{{ext}}"}}
66
+ {{addImport "import {getPgLite} from './pglite';"}}
67
+
68
+ getPgLite().then((pgLite) => {
69
+ createPglitePersister(canvasStore, pgLite, 'canvas').then((canvasPersister) => {
70
+ canvasPersister.load().then(() => {
71
+ canvasPersister.startAutoSave();
72
+ resolveCanvasReady();
73
+ });
74
+ });
75
+ });
76
+ {{/if}}
77
+ {{/if}}
78
+
79
+ {{#if sync}}
80
+ {{includeFile template="client/src/shared/config.ts.hbs" output="client/src/config.ts"}}
81
+ {{addImport "import {SERVER} from './config';"}}
82
+ {{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
83
+ {{#if schemas}}
84
+ {{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client/with-schemas';"}}
85
+ {{else}}
86
+ {{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
87
+ {{/if}}
88
+
89
+ const serverPathId = location.pathname;
90
+ createWsSynchronizer(
91
+ canvasStore,
92
+ new ReconnectingWebSocket(SERVER + serverPathId),
93
+ ).then(async (synchronizer) => {
94
+ await synchronizer.startSync();
95
+
96
+ synchronizer.getWebSocket().addEventListener('open', () => {
97
+ synchronizer.load().then(() => {
98
+ synchronizer.save();
99
+ {{#unless persist}}resolveCanvasReady();{{/unless}}
100
+ });
101
+ });
102
+ });
103
+ {{/if}}
104
+
105
+ {{#unless persist}}{{#unless sync}}
106
+ resolveCanvasReady();
107
+ {{/unless}}{{/unless}}
108
+
109
+ export type CanvasStore = typeof canvasStore;
@@ -1,6 +1,6 @@
1
1
  {{includeFile template="client/src/drawing/colorPicker.css.hbs" output="client/src/colorPicker.css"}}
2
- import './colorPicker.css';
3
- import type {Store as SettingsStore} from './settingsStore';
2
+ {{addImport "import './colorPicker.css';"}}
3
+ {{addImport "import type {SettingsStore} from './settingsStore';"}}
4
4
 
5
5
  export const createColorPicker = (store: SettingsStore): HTMLDivElement => {
6
6
  const colors = ['#d81b60', '#1976d2', '#388e3c', '#f57c00', '#7b1fa2', '#fff'];
@@ -31,4 +31,4 @@ store.addValueListener('brushColor', updateActive);
31
31
  updateActive();
32
32
 
33
33
  return container;
34
- };
34
+ };
@@ -1,16 +1,16 @@
1
1
  {{includeFile template="client/src/drawing/drawingControls.css.hbs" output="client/src/drawingControls.css"}}
2
- import './drawingControls.css';
3
- import type {Store as SettingsStore} from './settingsStore';
4
- import type {Store as CanvasStore} from './canvasStore';
2
+ {{addImport "import './drawingControls.css';"}}
3
+ {{addImport "import type {SettingsStore} from './settingsStore';"}}
4
+ {{addImport "import type {CanvasStore} from './canvasStore';"}}
5
5
 
6
6
  {{includeFile template="client/src/shared/button.ts.hbs" output="client/src/button.{{ext}}"}}
7
- import {createButton} from './button';
7
+ {{addImport "import {createButton} from './button';"}}
8
8
 
9
9
  {{includeFile template="client/src/drawing/colorPicker.ts.hbs" output="client/src/colorPicker.{{ext}}"}}
10
- import {createColorPicker} from './colorPicker';
10
+ {{addImport "import {createColorPicker} from './colorPicker';"}}
11
11
 
12
12
  {{includeFile template="client/src/drawing/brushSize.ts.hbs" output="client/src/brushSize.{{ext}}"}}
13
- import {createBrushSize} from './brushSize';
13
+ {{addImport "import {createBrushSize} from './brushSize';"}}
14
14
 
15
15
  export const createDrawingControls = (settingsStore: SettingsStore, canvasStore: CanvasStore): HTMLDivElement => {
16
16
  const controls = document.createElement('div');
@@ -23,4 +23,4 @@ const clearButton = createButton('Clear', () => canvasStore.delTable('strokes'),
23
23
  controls.appendChild(clearButton);
24
24
 
25
25
  return controls;
26
- };
26
+ };