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.
- package/README.md +62 -18
- package/cli.js +2 -2
- package/package.json +3 -3
- package/screenshots/chat.png +0 -0
- package/screenshots/todos.png +0 -0
- package/templates/README.md.hbs +13 -12
- package/templates/client/eslint.config.js.hbs +7 -0
- package/templates/client/package.json.hbs +28 -8
- package/templates/client/src/chat/App.tsx.hbs +28 -22
- package/templates/client/src/chat/ChatStore.tsx.hbs +108 -27
- package/templates/client/src/chat/Message.tsx.hbs +8 -9
- package/templates/client/src/chat/MessageInput.tsx.hbs +7 -7
- package/templates/client/src/chat/Messages.tsx.hbs +5 -15
- package/templates/client/src/chat/SettingsStore.tsx.hbs +86 -9
- package/templates/client/src/chat/UsernameInput.tsx.hbs +6 -11
- package/templates/client/src/chat/app.ts.hbs +18 -18
- package/templates/client/src/chat/chatStore.ts.hbs +97 -35
- package/templates/client/src/chat/message.ts.hbs +4 -3
- package/templates/client/src/chat/messageInput.css.hbs +1 -1
- package/templates/client/src/chat/messageInput.ts.hbs +7 -8
- package/templates/client/src/chat/messages.ts.hbs +7 -12
- package/templates/client/src/chat/settingsStore.ts.hbs +65 -6
- package/templates/client/src/chat/usernameInput.css.hbs +1 -1
- package/templates/client/src/chat/usernameInput.ts.hbs +6 -6
- package/templates/client/src/chat/utils.ts.hbs +26 -0
- package/templates/client/src/drawing/App.tsx.hbs +26 -20
- package/templates/client/src/drawing/BrushSize.tsx.hbs +8 -11
- package/templates/client/src/drawing/Canvas.tsx.hbs +65 -73
- package/templates/client/src/drawing/CanvasStore.tsx.hbs +104 -18
- package/templates/client/src/drawing/ColorPicker.tsx.hbs +4 -11
- package/templates/client/src/drawing/DrawingControls.tsx.hbs +7 -7
- package/templates/client/src/drawing/SettingsStore.tsx.hbs +81 -8
- package/templates/client/src/drawing/app.ts.hbs +18 -8
- package/templates/client/src/drawing/brushSize.ts.hbs +12 -5
- package/templates/client/src/drawing/canvas.ts.hbs +84 -86
- package/templates/client/src/drawing/canvasStore.ts.hbs +93 -26
- package/templates/client/src/drawing/colorPicker.ts.hbs +3 -3
- package/templates/client/src/drawing/drawingControls.ts.hbs +7 -7
- package/templates/client/src/drawing/settingsStore.ts.hbs +63 -8
- package/templates/client/src/game/App.tsx.hbs +20 -16
- package/templates/client/src/game/Board.tsx.hbs +8 -8
- package/templates/client/src/game/Game.tsx.hbs +14 -21
- package/templates/client/src/game/GameStatus.tsx.hbs +5 -5
- package/templates/client/src/game/Square.tsx.hbs +6 -11
- package/templates/client/src/game/Store.tsx.hbs +106 -16
- package/templates/client/src/game/app.ts.hbs +17 -6
- package/templates/client/src/game/board.ts.hbs +7 -7
- package/templates/client/src/game/game.ts.hbs +12 -18
- package/templates/client/src/game/gameStatus.ts.hbs +3 -3
- package/templates/client/src/game/square.ts.hbs +4 -4
- package/templates/client/src/game/store.ts.hbs +95 -23
- package/templates/client/src/index.tsx.hbs +5 -7
- package/templates/client/src/shared/Button.tsx.hbs +3 -3
- package/templates/client/src/shared/Input.tsx.hbs +2 -2
- package/templates/client/src/shared/Loading.tsx.hbs +5 -0
- package/templates/client/src/shared/button.ts.hbs +2 -2
- package/templates/client/src/shared/config.ts.hbs +4 -6
- package/templates/client/src/shared/input.ts.hbs +2 -2
- package/templates/client/src/shared/loading.css.hbs +21 -0
- package/templates/client/src/shared/loading.ts.hbs +13 -0
- package/templates/client/src/shared/pglite.ts.hbs +10 -0
- package/templates/client/src/shared/sqlite.ts.hbs +17 -0
- package/templates/client/src/todos/App.tsx.hbs +22 -22
- package/templates/client/src/todos/Store.tsx.hbs +106 -23
- package/templates/client/src/todos/TodoInput.tsx.hbs +6 -8
- package/templates/client/src/todos/TodoItem.tsx.hbs +5 -6
- package/templates/client/src/todos/TodoList.tsx.hbs +5 -6
- package/templates/client/src/todos/app.ts.hbs +16 -10
- package/templates/client/src/todos/store.ts.hbs +94 -30
- package/templates/client/src/todos/todoInput.ts.hbs +5 -8
- package/templates/client/src/todos/todoItem.ts.hbs +3 -4
- package/templates/client/src/todos/todoList.ts.hbs +6 -8
- package/templates/client/vite-env.d.ts.hbs +4 -0
- package/templates/client/vite.config.js.hbs +53 -3
- package/templates/server/index-do.ts.hbs +10 -14
- package/templates/server/index-node.ts.hbs +3 -3
- package/templates/server/package.json.hbs +5 -5
- package/templates/server/wrangler.toml.hbs +1 -1
- /package/templates/client/{.prettierrc.hbs → .prettierrc.json.hbs} +0 -0
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
{{includeFile template="client/src/drawing/brushSize.css.hbs" output="client/src/brushSize.css"}}
|
|
2
|
-
import {
|
|
3
|
-
import './brushSize.css';
|
|
2
|
+
{{addImport "import {useValueState, STORE_ID} from './SettingsStore';"}}
|
|
3
|
+
{{addImport "import './brushSize.css';"}}
|
|
4
4
|
|
|
5
5
|
export const BrushSize = () => {
|
|
6
|
-
const size =
|
|
6
|
+
const [size, setSize] = useValueState('brushSize', STORE_ID);
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
[],
|
|
12
|
-
STORE_ID,
|
|
13
|
-
);
|
|
8
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
9
|
+
setSize(parseInt(e.target.value));
|
|
10
|
+
};
|
|
14
11
|
|
|
15
12
|
return (
|
|
16
13
|
<div id="brushSize">
|
|
17
14
|
<label>Size:</label>
|
|
18
|
-
<input type="range" min="1" max="50" value={size} onChange={
|
|
15
|
+
<input type="range" min="1" max="50" value={(size as number) ?? 5} onChange={handleChange} />
|
|
19
16
|
<span id="brushSizeValue">{size}</span>
|
|
20
17
|
</div>
|
|
21
18
|
);
|
|
22
|
-
};
|
|
19
|
+
};
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{{includeFile template="client/src/drawing/canvas.css.hbs" output="client/src/canvas.css"}}
|
|
2
|
-
import {useCallback, useEffect, useRef} from 'react';
|
|
3
|
-
import {useTable, useStore, STORE_ID as CANVAS_STORE_ID} from './CanvasStore';
|
|
4
|
-
import {useValues, STORE_ID as SETTINGS_STORE_ID} from './SettingsStore';
|
|
5
|
-
import {
|
|
6
|
-
import './canvas.css';
|
|
2
|
+
{{addImport "import {useCallback, useEffect, useRef} from 'react';"}}
|
|
3
|
+
{{addImport "import {useSortedRowIds, useTable, useStore, type StrokeRow, STORE_ID as CANVAS_STORE_ID} from './CanvasStore';"}}
|
|
4
|
+
{{addImport "import {useValues, STORE_ID as SETTINGS_STORE_ID} from './SettingsStore';"}}
|
|
5
|
+
{{addImport "import {getHlcFunctions} from 'tinybase';"}}
|
|
6
|
+
{{addImport "import './canvas.css';"}}
|
|
7
|
+
|
|
8
|
+
const [getNextHlc] = getHlcFunctions();
|
|
7
9
|
|
|
8
10
|
export const Canvas = () => {
|
|
9
11
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
10
12
|
const isDrawing = useRef(false);
|
|
11
13
|
const currentStrokeId = useRef<string | null>(null);
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
+
const {brushColor, brushSize} = useValues(SETTINGS_STORE_ID);
|
|
15
|
+
const sortedIds = useSortedRowIds('strokes', undefined, false, 0, undefined, CANVAS_STORE_ID);
|
|
14
16
|
const strokes = useTable('strokes', CANVAS_STORE_ID);
|
|
15
17
|
const store = useStore(CANVAS_STORE_ID);
|
|
16
18
|
|
|
@@ -24,77 +26,67 @@ const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
|
24
26
|
ctx.fillStyle = '#111';
|
|
25
27
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
ctx.moveTo(points[0].x, points[0].y);
|
|
42
|
-
for (let i = 1; i < points.length; i++) { ctx.lineTo(points[i].x, points[i].y); } ctx.stroke(); } } }); }, [strokes]); useEffect(()=> {
|
|
43
|
-
draw();
|
|
44
|
-
}, [draw]);
|
|
45
|
-
|
|
46
|
-
const getPoint = (e: MouseEvent | TouchEvent) => {
|
|
47
|
-
const canvas = canvasRef.current;
|
|
48
|
-
if (!canvas) return null;
|
|
29
|
+
sortedIds.forEach((id) => {
|
|
30
|
+
const stroke = store?.getRow('strokes', id) as StrokeRow;
|
|
31
|
+
if (stroke?.points) {
|
|
32
|
+
const pointsArray = JSON.parse(stroke.points) as number[];
|
|
33
|
+
if (pointsArray.length >= 2) {
|
|
34
|
+
ctx.strokeStyle = stroke.color;
|
|
35
|
+
ctx.lineWidth = stroke.size * 2;
|
|
36
|
+
ctx.lineCap = 'round';
|
|
37
|
+
ctx.lineJoin = 'round';
|
|
38
|
+
ctx.beginPath();
|
|
39
|
+
ctx.moveTo(pointsArray[0], pointsArray[1]);
|
|
40
|
+
for (let i = 2; i < pointsArray.length; i +=2) { ctx.lineTo(pointsArray[i], pointsArray[i + 1]); } ctx.stroke(); } } }); }, [sortedIds, strokes, store]); useEffect(()=> {
|
|
41
|
+
draw();
|
|
42
|
+
}, [draw]);
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
const getPoint = (e: MouseEvent | TouchEvent) => {
|
|
45
|
+
const canvas = canvasRef.current;
|
|
46
|
+
if (!canvas) return null;
|
|
53
47
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
};
|
|
58
|
-
};
|
|
48
|
+
const rect = canvas.getBoundingClientRect();
|
|
49
|
+
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
50
|
+
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
59
51
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
return {
|
|
53
|
+
x: clientX - rect.left,
|
|
54
|
+
y: clientY - rect.top,
|
|
55
|
+
};
|
|
56
|
+
};
|
|
64
57
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
});
|
|
58
|
+
const handleStart = (e: React.MouseEvent | React.TouchEvent) => {
|
|
59
|
+
isDrawing.current = true;
|
|
60
|
+
currentStrokeId.current = getNextHlc();
|
|
69
61
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
62
|
+
const point = getPoint(e.nativeEvent);
|
|
63
|
+
if (point && store) {
|
|
64
|
+
store.setRow('strokes', currentStrokeId.current, {
|
|
65
|
+
color: brushColor,
|
|
66
|
+
size: brushSize,
|
|
67
|
+
points: JSON.stringify([point.x, point.y]),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
77
71
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
};
|
|
72
|
+
const handleMove = (e: React.MouseEvent | React.TouchEvent) => {
|
|
73
|
+
if (isDrawing.current && currentStrokeId.current && store) {
|
|
74
|
+
const point = getPoint(e.nativeEvent);
|
|
75
|
+
if (point) {
|
|
76
|
+
const pointsArray = JSON.parse(store.getCell('strokes', currentStrokeId.current, 'points') as string ?? '[]');
|
|
77
|
+
pointsArray.push(point.x, point.y);
|
|
78
|
+
store.setCell('strokes', currentStrokeId.current, 'points', JSON.stringify(pointsArray));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
89
82
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
};
|
|
83
|
+
const handleEnd = () => {
|
|
84
|
+
isDrawing.current = false;
|
|
85
|
+
currentStrokeId.current = null;
|
|
86
|
+
};
|
|
95
87
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
88
|
+
return (
|
|
89
|
+
<canvas ref={canvasRef} width={600} height={400} id="drawingCanvas" onMouseDown={handleStart} onMouseMove={handleMove} onMouseUp={handleEnd} onMouseLeave={handleEnd} onTouchStart={handleStart} onTouchMove={handleMove}
|
|
90
|
+
onTouchEnd={handleEnd} />
|
|
91
|
+
);
|
|
92
|
+
};
|
|
@@ -1,48 +1,127 @@
|
|
|
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';
|
|
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';"}}
|
|
5
5
|
{{else}}
|
|
6
|
-
import {createMergeableStore} from 'tinybase';
|
|
7
|
-
import {
|
|
6
|
+
{{addImport "import {createMergeableStore} from 'tinybase';"}}
|
|
7
|
+
{{addImport "import {useCreateMergeableStore, useProvideStore, useDelTableCallback, useRow, useSortedRowIds, useStore, useTable} from 'tinybase/ui-react';"}}
|
|
8
8
|
{{/if}}
|
|
9
|
-
import {getUniqueId} from 'tinybase';
|
|
10
9
|
|
|
11
10
|
export const STORE_ID = 'canvas';
|
|
12
11
|
|
|
13
12
|
{{#if schemas}}
|
|
14
13
|
const TABLES_SCHEMA = {
|
|
15
14
|
strokes: {
|
|
16
|
-
color: {type: 'string'},
|
|
17
|
-
size: {type: 'number'},
|
|
15
|
+
color: {type: 'string', default: ''},
|
|
16
|
+
size: {type: 'number', default: 1},
|
|
17
|
+
points: {type: 'string', default: '[]'},
|
|
18
18
|
},
|
|
19
19
|
} as const;
|
|
20
20
|
|
|
21
21
|
type Schemas = [typeof TABLES_SCHEMA, NoValuesSchema];
|
|
22
22
|
|
|
23
|
-
const {
|
|
23
|
+
const {useCreateMergeableStore, {{#if persist}}useCreatePersister, {{/if}}useDelTableCallback, useProvideStore, useRow, useSortedRowIds, useStore, useTable} = UiReact as UiReact.WithSchemas<Schemas>;
|
|
24
24
|
{{/if}}
|
|
25
25
|
|
|
26
|
-
export {
|
|
26
|
+
export type StrokeRow = {{#if schemas}}Row<typeof TABLES_SCHEMA, 'strokes'>{{else}}{color: string; size: number; points: string}{{/if}};
|
|
27
27
|
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
export {useDelTableCallback, useRow, useSortedRowIds, useStore, useTable};
|
|
29
|
+
|
|
30
|
+
export const CanvasStore = ({onReady}: {onReady?: () => void}) => {
|
|
31
|
+
const store = useCreateMergeableStore(() =>
|
|
32
|
+
createMergeableStore(){{#if schemas}}
|
|
31
33
|
.setTablesSchema(TABLES_SCHEMA){{/if}}
|
|
32
34
|
.setDefaultContent([{strokes: {}}, {}]),
|
|
33
35
|
);
|
|
34
36
|
|
|
35
37
|
useProvideStore(STORE_ID, store);
|
|
36
38
|
|
|
39
|
+
{{#if persist}}
|
|
40
|
+
{{#if persistLocalStorage}}
|
|
41
|
+
{{#if schemas}}
|
|
42
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser/with-schemas';"}}
|
|
43
|
+
{{else}}
|
|
44
|
+
{{addImport "import {createLocalPersister} from 'tinybase/persisters/persister-browser';"}}
|
|
45
|
+
{{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
|
|
46
|
+
{{/if}}
|
|
47
|
+
|
|
48
|
+
useCreatePersister(
|
|
49
|
+
store,
|
|
50
|
+
(store) => createLocalPersister(store, STORE_ID),
|
|
51
|
+
[],
|
|
52
|
+
async (persister) => {
|
|
53
|
+
await persister.load();
|
|
54
|
+
await persister.startAutoSave();
|
|
55
|
+
onReady?.();
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
{{/if}}
|
|
59
|
+
{{#if persistSqlite}}
|
|
60
|
+
{{#if schemas}}
|
|
61
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm/with-schemas';"}}
|
|
62
|
+
{{else}}
|
|
63
|
+
{{addImport "import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';"}}
|
|
64
|
+
{{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
|
|
65
|
+
{{/if}}
|
|
66
|
+
{{includeFile template="client/src/shared/sqlite.ts.hbs" output="client/src/sqlite.{{ext}}"}}
|
|
67
|
+
{{addImport "import {getDb} from './sqlite';"}}
|
|
68
|
+
|
|
69
|
+
useCreatePersister(
|
|
70
|
+
store,
|
|
71
|
+
async (store) => {
|
|
72
|
+
const {sqlite3, db} = await getDb();
|
|
73
|
+
return createSqliteWasmPersister(store, sqlite3, db, STORE_ID);
|
|
74
|
+
},
|
|
75
|
+
[],
|
|
76
|
+
async (persister) => {
|
|
77
|
+
await persister.load();
|
|
78
|
+
await persister.startAutoSave();
|
|
79
|
+
onReady?.();
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
{{/if}}
|
|
83
|
+
{{#if persistPglite}}
|
|
84
|
+
{{#if schemas}}
|
|
85
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite/with-schemas';"}}
|
|
86
|
+
{{else}}
|
|
87
|
+
{{addImport "import {createPglitePersister} from 'tinybase/persisters/persister-pglite';"}}
|
|
88
|
+
{{addImport "import {useCreatePersister} from 'tinybase/ui-react';"}}
|
|
89
|
+
{{/if}}
|
|
90
|
+
{{includeFile template="client/src/shared/pglite.ts.hbs" output="client/src/pglite.{{ext}}"}}
|
|
91
|
+
{{addImport "import {getPgLite} from './pglite';"}}
|
|
92
|
+
|
|
93
|
+
useCreatePersister(
|
|
94
|
+
store,
|
|
95
|
+
async (store) => {
|
|
96
|
+
const pgLite = await getPgLite();
|
|
97
|
+
return await createPglitePersister(store, pgLite, 'canvas');
|
|
98
|
+
},
|
|
99
|
+
[],
|
|
100
|
+
async (persister) => {
|
|
101
|
+
await persister.load();
|
|
102
|
+
await persister.startAutoSave();
|
|
103
|
+
onReady?.();
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
{{/if}}
|
|
107
|
+
{{/if}}
|
|
108
|
+
|
|
37
109
|
{{#if sync}}
|
|
38
110
|
{{includeFile template="client/src/shared/config.ts.hbs" output="client/src/config.ts"}}
|
|
39
111
|
{{addImport "import {SERVER} from './config';"}}
|
|
40
112
|
{{addImport "import ReconnectingWebSocket from 'reconnecting-websocket';"}}
|
|
41
|
-
{{
|
|
42
|
-
|
|
43
|
-
{{
|
|
113
|
+
{{#if schemas}}
|
|
114
|
+
{{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client/with-schemas';"}}
|
|
115
|
+
{{else}}
|
|
116
|
+
{{addImport "import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';"}}
|
|
117
|
+
{{/if}}
|
|
118
|
+
{{#if schemas}}
|
|
119
|
+
{{addImport "import {useCreateSynchronizer} from 'tinybase/ui-react/with-schemas';"}}
|
|
120
|
+
{{else}}
|
|
121
|
+
{{addImport "import {useCreateSynchronizer} from 'tinybase/ui-react';"}}
|
|
122
|
+
{{/if}}
|
|
44
123
|
|
|
45
|
-
useCreateSynchronizer(store, async (store
|
|
124
|
+
useCreateSynchronizer(store, async (store) => {
|
|
46
125
|
const serverPathId = location.pathname;
|
|
47
126
|
const synchronizer = await createWsSynchronizer(
|
|
48
127
|
store,
|
|
@@ -51,12 +130,19 @@ useProvideStore(STORE_ID, store);
|
|
|
51
130
|
await synchronizer.startSync();
|
|
52
131
|
|
|
53
132
|
synchronizer.getWebSocket().addEventListener('open', () => {
|
|
54
|
-
synchronizer.load().then(() =>
|
|
133
|
+
synchronizer.load().then(() => {
|
|
134
|
+
synchronizer.save();
|
|
135
|
+
{{#unless persist}}onReady?.();{{/unless}}
|
|
136
|
+
});
|
|
55
137
|
});
|
|
56
138
|
|
|
57
139
|
return synchronizer;
|
|
58
140
|
});
|
|
59
141
|
{{/if}}
|
|
60
142
|
|
|
143
|
+
{{#unless persist}}{{#unless sync}}
|
|
144
|
+
onReady?.();
|
|
145
|
+
{{/unless}}{{/unless}}
|
|
146
|
+
|
|
61
147
|
return null;
|
|
62
148
|
};
|
|
@@ -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 {
|
|
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 =
|
|
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',
|
|
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,
|
|
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,
|
|
20
|
+
const {useCreateStore, {{#if persist}}useCreatePersister, {{/if}}useProvideStore, useValues, useValueState} = UiReact as UiReact.WithSchemas<Schemas>;
|
|
21
21
|
{{/if}}
|
|
22
22
|
|
|
23
|
-
export {
|
|
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 {
|
|
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
|
-
|
|
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
|
+
};
|