Compare commits

...

615 Commits

Author SHA1 Message Date
Anselm
4f75dc8d97 Release 2024-08-15 16:17:31 +01:00
Anselm
e2c79cccb5 Remove noisy log 2024-08-15 16:17:03 +01:00
Anselm
c14a0e05be Release 2024-08-15 15:30:22 +01:00
Anselm
016dd3a5dd Fix ignoring server peers 2024-08-15 15:30:00 +01:00
Anselm
5c4ca9103c Release 2024-08-15 13:36:33 +01:00
Anselm
b4aad92907 Option to not expect pings 2024-08-15 13:36:08 +01:00
Anselm
56d1e095a1 Release 2024-08-15 13:02:39 +01:00
Anselm Eickhoff
6dee9aae49 Merge pull request #281 from gardencmp/anselm-jazz-190
Remove effect from jazz
2024-08-15 11:51:53 +01:00
Anselm Eickhoff
a10bff981e Merge pull request #284 from gardencmp/anselm-jazz-227
Remove effect from peer communication
2024-08-15 11:51:10 +01:00
Anselm Eickhoff
e333f7884a Merge pull request #313 from gardencmp/anselm-jazz-246
Remove effect from jazz-tools and dependent packages
2024-08-15 11:50:44 +01:00
Anselm
8ea7bf237b Remove effect from storage implementation 2024-08-15 11:13:10 +01:00
Anselm
5e8409fa08 Remove rest of effect use 2024-08-14 17:51:10 +01:00
Anselm
23354c1767 Progress on removing effect 2024-08-14 15:24:20 +01:00
Anselm Eickhoff
0efb69d0db Merge pull request #312 from pax-k/JAZZ-252/make-sure-castas-preserves-subscriptionscope
fix: preserve subscriptionScope for castAs in CoList and CoMap
2024-08-14 14:51:54 +01:00
pax-k
0462c4e41b fix: preserve subscriptionScope for castAs in CoList and CoMap 2024-08-14 16:50:12 +03:00
Anselm
70a5673197 More progress 2024-08-13 12:25:15 +01:00
Anselm Eickhoff
9ec3203485 Merge pull request #285 from gdorsi/main
docs: fix the get started links position
2024-08-12 17:46:41 +01:00
Guido D'Orsi
1a46f9b2e1 docs: fix the get started links position 2024-08-10 16:17:18 +02:00
Anselm
77bb26a8d7 Only use queable in cojson, rework tests 2024-08-09 16:44:50 +01:00
Anselm
2a36dcf592 WIP switch to queueable 2024-08-09 13:59:26 +01:00
Anselm
fc2bcadbe2 Remove effect schema from jazz schema 2024-08-08 18:18:17 +01:00
Anselm
46b0cc1adb Release 2024-08-08 14:44:00 +01:00
Anselm Eickhoff
d75d1c6a3f Merge pull request #279 from pax-k/JAZZ-219/implement-applydiff-on-comap-to-only-update-changed-fields
feat: Implement applyDiff on CoMap to only update changed fields
2024-08-08 13:50:17 +01:00
pax-k
13b236aeed feat: Implement applyDiff on CoMap to only update changed fields 2024-08-08 11:03:08 +03:00
Anselm Eickhoff
1c0a61b0b2 Merge pull request #271 from pax-k/document-max-recommended-tx-size
chore: document MAX_RECOMMENDED_TX_SIZE
2024-08-07 16:32:07 +01:00
Anselm
ceb92438f4 Release 2024-08-07 14:23:03 +01:00
Anselm Eickhoff
9bdd62ed4c Merge pull request #244 from gardencmp/anselm-jazz-187
Remove effectful API for loading/subscribing
2024-08-07 14:20:42 +01:00
pax-k
3f5ef7e799 chore: formatting 2024-08-06 19:35:14 +03:00
pax-k
e7a573fa94 chore: document MAX_RECOMMENDED_TX_SIZE 2024-08-06 19:22:41 +03:00
Anselm Eickhoff
364060eaa7 Merge pull request #249 from Schniz/schniz/allow-optional-encoder-to-be-empty-upon-creation 2024-08-03 19:59:09 +01:00
Gal Schlezinger
a3ddc3d5e0 allow co.optional.encoded to be empty on creation 2024-08-03 21:53:13 +03:00
Anselm Eickhoff
185f747adb Merge pull request #248 from timolins/new-explorer-ux 2024-08-03 13:51:47 +01:00
Timo Lins
895d281088 Remove legacy inspector to resolve TS errors 2024-08-03 13:09:27 +02:00
Timo Lins
b44e4354f7 Merge branch 'new-explorer-ux' of https://github.com/timolins/jazz into new-explorer-ux 2024-08-03 12:59:18 +02:00
Timo Lins
3fcb0665ec Fix TS errors 2024-08-03 12:59:14 +02:00
Tobias Lins
be49d33ce5 Update co-stream-view.tsx 2024-08-03 12:24:30 +02:00
Timo Lins
c7dae1608b Add new entry point 2024-08-03 10:49:29 +02:00
Timo Lins
b020c5868b Move app to legacy app 2024-08-03 10:48:06 +02:00
Timo Lins
eae42d3afe Revert app.tsx changes 2024-08-03 10:47:51 +02:00
Anselm
a816e2436e Remove effectful API for loading/subscribing 2024-07-31 16:21:26 +01:00
Anselm
b09e35e372 release 2024-07-29 10:40:10 +01:00
Anselm
d2c8121c9c Fix storage option in jazz-react 2024-07-29 10:34:39 +01:00
Anselm
380bb88ffa Mostly complete OPFS implementation (single-tab only) 2024-07-29 10:33:18 +01:00
Timo Lins
e0e3726b3c Update title 2024-07-28 18:02:06 +02:00
Timo Lins
c2253a7979 Improve styling of blob page 2024-07-28 17:40:23 +02:00
Timo Lins
9d244226ec Redesign root page, add group support & costream pages
Co-authored-by: Tobias Lins <me@tobi.sh>
2024-07-28 17:26:34 +02:00
Timo Lins
71df5e3a59 Fix native browser controls 2024-07-28 14:53:26 +02:00
Timo Lins
3a738dad88 Persists path in url 2024-07-28 13:53:39 +02:00
Timo Lins
56d301cfde Refactor the new viewer 2024-07-27 23:16:37 +02:00
Timo Lins
5efec6d5ea Move to a table view 2024-07-27 20:58:55 +02:00
Timo Lins
32769b24f1 Add new inspector 2024-07-27 20:10:24 +02:00
Anselm
6ab53c263d Release 2024-07-26 17:23:02 +01:00
Anselm
e7f3e4e242 Increase disconnect timeout for now 2024-07-26 17:21:06 +01:00
Anselm Eickhoff
8bb5201647 Merge pull request #236 from timolins/patch-1
[Homepage] Make current year dynamic in footer
2024-07-22 15:55:16 +01:00
Timo Lins
a9fc94f53d Make current year dynamic in footer 2024-07-22 10:06:48 +02:00
Anselm Eickhoff
ca7c0510d1 Merge pull request #234 from Schniz/schniz/co-optional 2024-07-21 10:14:41 +01:00
Gal Schlezinger
1bf16f0859 add co.optional syntax 2024-07-21 09:10:44 +03:00
Anselm Eickhoff
21b503c188 Merge pull request #224 from datner/datner/give-it-a-try
cojson-transport-ws: reuse runtime and use fibers instead of setTimeout
2024-07-15 14:35:18 +01:00
Anselm
0053e9796c Release 2024-07-15 11:01:31 +01:00
Anselm
e84941b1b1 Fix another bug in CoMap 'has' proxy trap 2024-07-15 10:59:14 +01:00
Anselm
57f6f8d67e Release 2024-07-14 17:55:47 +01:00
Anselm
5b8e69d973 Fix bug in CoMap 'has' trap 2024-07-14 17:55:05 +01:00
Anselm
7213b1bfa3 Release 2024-07-13 13:32:50 +01:00
Anselm Eickhoff
11f0770f08 Merge pull request #230 from tobiaslins/add-support-for-in
Add support for property existence
2024-07-13 13:30:15 +01:00
Tobias Lins
44e6dc3ae8 Remove useless check 2024-07-13 13:57:37 +02:00
Tobias Lins
b5d20d2488 Better implementation 2024-07-13 13:26:28 +02:00
Tobias Lins
0185545838 Add support for property existence 2024-07-13 12:26:10 +02:00
Yuval Datner
8c8f85859c style: prettier 2024-07-12 15:42:52 +03:00
Yuval Datner
104384409e refactor: change to yieldable error 2024-07-12 15:42:27 +03:00
Yuval Datner
179827ae56 small refactor for readability 2024-07-12 15:13:25 +03:00
Yuval Datner
6645829876 do stream stuff 2024-07-12 15:13:23 +03:00
Gal Schlezinger
68cb302722 store jazzPings on global 2024-07-12 15:13:22 +03:00
Gal Schlezinger
8dc33f2790 fix bugs because I misindented things 2024-07-12 15:13:21 +03:00
Gal Schlezinger
5f64ba326c jazzPings 2024-07-12 15:13:20 +03:00
Gal Schlezinger
7ccb15107c cojson-transport-ws: reuse runtime and use fibers instead of setTimeout
when calling Effect.runFork we don't propagate layers
and using fibers can allow us to interrupt ongoing requests
when the pings fail
2024-07-12 15:13:16 +03:00
Anselm
b102964743 Get rid of husky for now 2024-07-12 11:56:54 +01:00
Anselm
216d50a09c Remove old homepage build 2024-07-12 11:55:44 +01:00
Anselm
07ea59fdcb Release 2024-07-12 11:14:15 +01:00
Anselm
932a84a47f Update to Effect 3.5.2 2024-07-12 11:13:41 +01:00
Anselm
34dda7bdbd Release 2024-07-05 11:18:53 +01:00
Anselm
49fa153581 Merge branch 'fix-fs' 2024-07-05 11:17:54 +01:00
Anselm
c80b827775 Release 2024-06-30 16:28:53 +01:00
Anselm Eickhoff
a2bf9f988a Merge pull request #218 from Schniz/fix-mutating-nullable-field 2024-06-30 08:31:57 +01:00
Gal Schlezinger
ac27b2d5c2 jazz-tools: allow to mutate nullable fields into null
when having a co.encoded(Schema.NullOr(Schema.String)), construction
with null works well, but mutating a value into null throws.
This commit fixes it and adds a test that verifies it actually works.
2024-06-30 09:49:15 +03:00
Anselm
c813518fdc Release 2024-06-28 16:27:15 +01:00
Anselm
d5034ed5c3 Provide current res in ProgressiveImg 2024-06-28 16:26:03 +01:00
Anselm
cf2c29a365 Use verce lanalytics & speed insights 2024-06-28 14:54:58 +01:00
Anselm
d948823db6 Add package manager to package.json 2024-06-28 12:08:23 +01:00
Anselm
060ad4630d Resurrect inspector 2024-06-27 16:54:23 +01:00
Anselm
0ddceac4c0 Fix FS bugs 2024-06-26 13:45:09 +01:00
Anselm
a862cb8819 Release 2024-06-25 15:13:55 +01:00
Anselm Eickhoff
4246aed7db Merge pull request #214 from gardencmp/effect-streams-for-peers
Effect streams for peers
2024-06-25 15:12:56 +01:00
Anselm
41554e0e0b Release 2024-06-25 13:57:24 +01:00
Anselm
93c4d8155e Fix CoList.toJSON() 2024-06-25 13:56:20 +01:00
Anselm
24eefd49f1 Release 2024-06-25 13:46:54 +01:00
Anselm
e712f1e8ef Fix circular toJSON bug #215 2024-06-25 13:46:14 +01:00
Anselm
33db0fd654 Keep old hompage lockfile (unrelated) 2024-06-13 22:17:00 +01:00
Anselm
478ded93de Merge branch 'main' into effect-streams-for-peers 2024-06-13 22:12:33 +01:00
Anselm
89ad1fb79d Fix remaining tests by introducing even more special cases 2024-06-13 22:11:25 +01:00
Anselm Eickhoff
1ba40806ec Merge pull request #213 from tobiaslins/fix-demo-auth-nextjs
Fix DemoAuth for Next.js
2024-06-13 17:43:11 +01:00
Tobias Lins
73ae281e4a Fix demoAuth for next.js 2024-06-13 18:41:29 +02:00
Anselm
a35353c987 Use Effect streams 2024-06-13 15:58:21 +01:00
Anselm
1cb91003cc Release 2024-06-12 15:12:00 +01:00
Anselm
d850022491 Fix #210 2024-06-12 15:10:22 +01:00
Anselm
93792ab6f6 Remove lofi conf badge 2024-06-06 12:02:31 +01:00
Anselm
95dfe7af6a Guide fix 2024-06-04 17:56:34 +01:00
Anselm
734258eb17 Release 2024-06-04 12:14:59 +01:00
Anselm
f3bcf96fad Also cache agent ID in RawControlledAccount 2024-06-04 12:13:54 +01:00
Anselm
5cf0bc1911 Release 2024-06-04 12:09:35 +01:00
Anselm
d32a6b275f Formatting 2024-06-04 12:09:16 +01:00
Anselm
6caba9f8e7 Cache currentAgentID in RawAccount 2024-06-04 12:06:13 +01:00
Anselm
641f1dbfbe Release 2024-06-03 17:33:59 +01:00
Anselm
58d9a104d6 Fix CoMaps not initialising properly when passing too many init options 2024-06-03 17:33:24 +01:00
Anselm
7b9d24c8ef Release 2024-06-01 17:25:06 +02:00
Anselm Eickhoff
4225fdd537 Merge pull request #200 from gdorsi/main
fix: improve compatibility with React compiler and concurrent features
2024-06-01 17:24:17 +02:00
Anselm
9fdc91c6de Add changeset 2024-06-01 17:23:18 +02:00
Anselm
93d8c85e5c Formatting 2024-06-01 17:22:38 +02:00
Anselm
929cddc3c3 Release 2024-06-01 17:13:02 +02:00
Anselm
e0bc63f016 Provide way to create accounts as another account 2024-06-01 17:12:42 +02:00
Anselm
b29ac306ea Release 2024-06-01 14:00:50 +02:00
Anselm
e8e883f4d6 Ability to add seed accounts to DemoAuth 2024-06-01 14:00:26 +02:00
Guido D'Orsi
3325ff1cd6 fix: make Jazz react compiler ready 2024-05-31 10:19:33 +02:00
Anselm
4fe14f03b4 Formatting fixes 2024-05-31 09:13:25 +02:00
Anselm
90e2a661e4 Very last fix 2024-05-31 09:00:56 +02:00
Anselm
6ed53ecb79 Last fixes 2024-05-31 08:59:41 +02:00
Anselm
c18775766c More fixes 2024-05-31 08:56:11 +02:00
Anselm
4bb3a6209a Doc fixes 2024-05-31 08:44:45 +02:00
Anselm
0f44a547a4 Lots more guide progress 2024-05-28 17:10:59 +01:00
Anselm
1e2f6d8f14 Release 2024-05-26 20:44:05 +01:00
Anselm Eickhoff
7e5b176930 Merge pull request #199 from tobiaslins/implement-loading-ui
Implement basic `loading` component that is rendered when migrations …
2024-05-26 20:40:00 +01:00
Anselm
b420eab503 Fix type for Provider 2024-05-26 20:37:55 +01:00
Tobias Lins
b370e2e14e Merge branch 'main' into implement-loading-ui 2024-05-26 20:48:38 +02:00
Tobias Lins
1fabee297d Implement basic loading component that is rendered when migrations are running or jazz not ready yet 2024-05-26 20:46:52 +02:00
Anselm
484dc460c5 Lots of doc progress 2024-05-26 17:39:16 +01:00
Anselm
0cb8756124 Fix lint error 2024-05-26 16:16:25 +01:00
Anselm
95d0f0221b Release 2024-05-26 16:15:18 +01:00
Anselm
0c9c0fcd60 Clean up loading & subscription API 2024-05-26 16:14:01 +01:00
Anselm
8be0dd133c Doc improvements 2024-05-26 16:13:25 +01:00
Anselm
e68e0ada0d Chage hero grid 2024-05-25 21:27:20 +01:00
Anselm
49a7349e4d Update copyright year 2024-05-25 21:27:11 +01:00
Anselm
979c7241a4 Guide progress, licenses 2024-05-25 20:54:53 +01:00
Anselm
e011e4a049 Add local-first conf badge 2024-05-25 19:46:36 +01:00
Anselm
92bccf5974 Release 2024-05-25 17:08:37 +01:00
Anselm
2c1d6dcb8f Fix type signature / depth of useCoState 2024-05-25 17:07:41 +01:00
Anselm Eickhoff
75f45ec0b2 Merge pull request #163 from gardencmp/jazz-schema
New unified API
2024-05-25 16:35:54 +01:00
Anselm
c0ebcadda9 Push tags on publish 2024-05-25 16:28:52 +01:00
Anselm
109e9b6a5b Use changeset for publishing, format 2024-05-25 16:28:10 +01:00
Anselm
d0c3d08e42 Add static homepage build output to standalone server 2024-05-25 16:15:05 +01:00
Anselm
7514830edb Increase homepage memory 2024-05-25 16:06:02 +01:00
Anselm
6f27128b87 Fix homepage port 2024-05-25 15:47:10 +01:00
Anselm
376518d4ef Provide correct nomad template 2024-05-25 15:27:58 +01:00
Anselm
272fc85c13 Fix homepage build 2024-05-25 15:21:57 +01:00
Anselm
579e4f93ee Fix homepage build some more 2024-05-25 15:20:29 +01:00
Anselm
a9accdc3a8 Fix homepage build 2024-05-25 15:19:32 +01:00
Anselm
b403db51d4 Remove typedoc output again 2024-05-25 15:03:45 +01:00
Anselm
108cae037f Homepage build 2024-05-25 14:46:00 +01:00
Anselm
c51897ab9e Release 2024-05-25 14:43:57 +01:00
Anselm
67171cb07a Add runtime option for optional refs 2024-05-25 14:42:33 +01:00
Anselm
6ae3bf6ac9 Release 0.7.0 2024-05-25 11:58:43 +01:00
Anselm
b64b15877a Homepage fixes 2024-05-25 11:53:01 +01:00
Anselm
19f52b7361 Alpha release 2024-05-25 10:32:57 +01:00
Anselm
9dcd15dbc8 Fixed bug with newRandomSessionID being called before crypto was ready 2024-05-25 10:32:33 +01:00
Anselm
a423eeea3b Alpha release 2024-05-24 22:52:34 +01:00
Anselm Eickhoff
99be6a3566 Merge pull request #193 from markmelville/ws_close
ignore error on ws close, fixing "Invalid state: Controller is already closed"
2024-05-24 22:51:33 +01:00
Anselm
e97f730c0f Alpha release 2024-05-24 22:37:22 +01:00
Anselm
f5642335ff Fix useAccount 2024-05-24 22:36:53 +01:00
Anselm
bf0f8ec824 Alpha release 2024-05-24 22:28:50 +01:00
Anselm
fb9ef4ea20 Fix noble curves dep 2024-05-24 22:28:13 +01:00
Anselm
86363197cd Alpha release 2024-05-24 22:11:47 +01:00
Anselm
e93187c971 Homepage improvements 2024-05-24 22:11:32 +01:00
Anselm
1a86e13cf1 Implement deep loading, simplify API 2024-05-24 22:10:46 +01:00
Anselm
b863c1d20a Deep loading based on removing nulls 2024-05-23 17:32:04 +01:00
Anselm
4c8d658c25 Only load each image resolution once 2024-05-21 16:48:14 +01:00
Anselm
69d37437ef Fix infinite recursion in subscriptionScope 2024-05-21 16:47:27 +01:00
Anselm
220bdbae62 Fix type of init param for CoMap.create 2024-05-21 16:46:44 +01:00
Anselm
b23b556b79 Fix readme typo 2024-05-21 14:14:47 +01:00
Anselm
ce721cf3d1 Update main readme 2024-05-21 13:47:48 +01:00
Anselm
41363415fe Add lint-staged 2024-05-21 13:44:26 +01:00
Anselm
b91d0769d5 Add husky pre-commit hook 2024-05-21 13:27:45 +01:00
Anselm
ad16690826 Add minimal package READMEs 2024-05-21 13:24:54 +01:00
Anselm
ca7b81c36a Add eslint to the homepage 2024-05-21 13:09:37 +01:00
Anselm
a632ce1477 Apply prettier and make everything testable from root 2024-05-21 12:10:06 +01:00
Anselm
ca7011e9af Apply eslint to all repos 2024-05-21 10:31:48 +01:00
Anselm
34df432ee8 Apply eslint & prettier to cojson-storage-indexeddb 2024-05-21 09:59:29 +01:00
Anselm
dfa7178041 Set up prettier for cojson 2024-05-21 08:27:09 +01:00
Anselm
a3e9a3b686 Extend eslint rules for cojson 2024-05-21 08:22:17 +01:00
Anselm
599db049f2 Remove dotenv 2024-05-21 07:55:48 +01:00
Anselm
7cba6dd690 Factor out crypto impl and provide pure js one 2024-05-20 20:37:43 +01:00
Anselm
2d406c9d58 Fix minor errors 2024-05-18 16:55:14 +01:00
Anselm
d8fe2b10f1 Alpha release 2024-05-18 16:51:28 +01:00
Anselm
c8343626ba Expose experimental OPFS storage 2024-05-18 14:38:59 +01:00
Anselm
5b188ec093 Alpha release 2024-05-16 18:29:44 +01:00
Anselm
7ebbd80049 Ensure accounts are synced after creation 2024-05-16 18:29:13 +01:00
Anselm
74c9e5d36d More LSM/OPFS progress 2024-05-16 18:16:44 +01:00
Anselm
1a3530747f Add changesets 2024-05-16 10:29:13 +01:00
Anselm
79899b9b18 Update lockfile 2024-05-16 10:29:04 +01:00
Anselm
6ef6a2f507 Implement first devtools formatters 2024-05-16 10:28:50 +01:00
Anselm
ab6d15c9f7 Use fresh subscribe context for useProgressiveImg 2024-05-16 10:28:09 +01:00
Anselm
a0dc9139a2 Add ability to declare minimum required data in subscribe & Improve property access tracing 2024-05-16 10:27:27 +01:00
Anselm
220fa319d5 Use lsm storage from cojson & update effect 2024-05-16 10:25:22 +01:00
Anselm
183d505a5e Remove old cojson-storage-lsm module 2024-05-16 10:24:43 +01:00
Anselm
f960e7e736 Optimizations for incoming sync messages 2024-05-16 10:24:20 +01:00
Anselm
d68ac84e03 WIP working-ish version of LSM storage 2024-05-16 10:22:52 +01:00
Mark Melville
30fbe2b6d7 ignore error on ws close 2024-05-14 15:27:34 -06:00
Anselm
e3a00570e1 Refactor 2024-05-13 15:46:23 +01:00
Anselm
499e02685a Merge branch 'lsm-storage' into jazz-schema 2024-05-13 14:32:37 +01:00
Anselm
6b0418f772 Fix image resolution loading 2024-05-13 14:06:15 +01:00
Anselm
1200aae47d Alpha release 2024-05-12 17:27:56 +01:00
Anselm
20fdb09b33 New cache for covalue proxies 2024-05-12 17:27:08 +01:00
Anselm
522b12dc42 CoJSON performance improvement 2024-05-12 17:24:53 +01:00
Anselm
a7cd0dcce5 Pricing update 2024-05-12 17:24:24 +01:00
Anselm
fd86c11336 Alpha release 2024-05-10 21:02:39 +01:00
Anselm
4fc414d744 Extract jazz cli into jazz-run package 2024-05-10 21:00:20 +01:00
Anselm
6d49e9b06c Alpha release 2024-05-10 17:37:40 +01:00
Anselm
8b866e288b Lots more doc improvements 2024-05-10 17:37:00 +01:00
Anselm
bf22588b0e Small homepage fixes 2024-05-10 14:41:24 +01:00
Anselm
60d5ca2811 Alpha release 2024-05-10 14:21:03 +01:00
Anselm
9e5dcdfa69 Update examples 2024-05-10 14:20:50 +01:00
Anselm
3cc39dd5ed Introduce jazz-tools CLI 2024-05-10 14:20:24 +01:00
Anselm
6878060346 Simplify jazz-nodejs 2024-05-10 14:19:57 +01:00
Anselm
9840061137 Clean up exports 2024-05-10 14:19:35 +01:00
Anselm
6f84e00463 homepage/docs improvements 2024-05-10 14:18:48 +01:00
Anselm
1e82d0d34e Allow fall-through of routes 2024-05-10 14:17:46 +01:00
Anselm
f35bc468b3 Separate pnpm workspace for homepage 2024-05-09 11:44:27 +01:00
Anselm
719071c286 Docs & guide progress 2024-05-09 10:46:33 +01:00
Anselm
c4b439e2e6 Lots of doc improvements 2024-05-07 13:16:27 +01:00
Anselm
77c2b56ceb Alpha release 2024-05-03 15:55:58 +01:00
Anselm
0b17b7ad5a Get rid of self generics, new create syntax 2024-05-03 15:55:32 +01:00
Anselm
db3011a1c9 Homepage improvements 2024-05-03 14:27:07 +01:00
Anselm
b47c695b97 Refactor cojson tests 2024-05-03 13:39:04 +01:00
Anselm
55c1c893ba Homepage improvements 2024-05-02 17:37:02 +01:00
Anselm
bde684fe30 Alpha release 2024-05-02 09:24:42 +01:00
Anselm
89b6c9004b CoValue casting & auto-subbing _owner 2024-05-02 09:23:49 +01:00
Anselm Eickhoff
97cdfbddaf Merge pull request #184 from tobiaslins/addmember-chaining 2024-05-02 07:20:44 +01:00
Tobias Lins
584ee2d136 Return this 2024-05-02 07:54:01 +02:00
Anselm
21771c4725 Alpha release 2024-05-01 21:36:51 +01:00
Anselm
7e8f1bed15 Merge branch 'main' into jazz-schema 2024-05-01 21:35:31 +01:00
Anselm
226ae03603 Better variable naming in test 2024-05-01 15:49:14 +01:00
Anselm
96c494f5ee Alpha release 2024-05-01 15:43:04 +01:00
Anselm
23ba00422f Implement group-based profiles & migrations 2024-05-01 15:42:14 +01:00
Anselm
52675c9c68 Alpha release 2024-05-01 10:36:39 +01:00
Anselm
1b113e0114 Fix CoList.splice / RawCoList.append 2024-05-01 10:36:04 +01:00
Anselm
0a930f5eeb Fix Costream[...].all 2024-05-01 10:35:38 +01:00
Anselm
84f5a83648 Homepage improvements 2024-05-01 10:35:11 +01:00
Anselm
5fa277c254 Alpha release 2024-04-29 13:56:11 +01:00
Anselm
d49c7f2dd4 Fix CoMap.Record.toJSON() 2024-04-29 13:55:23 +01:00
Anselm
a78f1688d9 Alpha release 2024-04-29 13:29:45 +01:00
Anselm
cd37a846d8 Make Account -> Profile a lazy ref schema 2024-04-29 13:28:38 +01:00
Anselm
63374ccb6d Alpha release 2024-04-28 17:27:47 +01:00
Anselm
efe2d91fb3 Make sure delete on CoMaps deletes keys 2024-04-28 17:26:47 +01:00
Anselm
234b2a019b Force cojson tests to pass... 2024-04-28 17:12:12 +01:00
Anselm
4bbcd366bc Relax types of CoMap._schema 2024-04-28 16:59:29 +01:00
Anselm
5724f8747a Fix schema of Account & Group 2024-04-28 16:23:17 +01:00
Anselm
38d44103d1 Alpha release 2024-04-27 18:22:11 +01:00
Anselm
96a7ff68e7 Homepage improvements 2024-04-27 18:20:12 +01:00
Anselm
fb78a55f76 Fix getOwnPropertyDescriptor in _refs proxy 2024-04-27 18:19:57 +01:00
Anselm
fdc7fc7bcf Introduce CoMap.Record 2024-04-27 18:13:48 +01:00
Anselm
ed5643aaf1 Alpha release 2024-04-25 09:35:02 +01:00
Anselm
ac431ef9ef Fix CoMap _refs for co.items 2024-04-25 09:34:08 +01:00
Anselm
0940508637 Homepage improvements 2024-04-25 09:33:21 +01:00
Anselm
704af7d04c Alpha release 2024-04-24 14:07:42 +01:00
Anselm
e4e476a834 Add maxWidth option for loading images 2024-04-24 14:07:18 +01:00
Anselm
ece35b3c6f Alpha release 2024-04-24 13:50:50 +01:00
Anselm
b26eab50b3 Include fast-check as dependency to help dev time resolution 2024-04-24 13:50:00 +01:00
Anselm
b42313a285 Fix subscription to account in useAccount 2024-04-24 13:49:15 +01:00
Anselm
129e2c1668 Alpha release 2024-04-23 21:45:30 +01:00
Anselm
87ddb81562 More precise imports from @effect/schema 2024-04-23 21:45:08 +01:00
Anselm
daee49cd9d Alpha release 2024-04-23 21:25:26 +01:00
Anselm
3aaf773b0a Add missing @scure/bip39 dep 2024-04-23 21:24:31 +01:00
Anselm
460478fc65 Alpha release 2024-04-23 21:17:36 +01:00
Anselm
fe8b5f45b9 Use effect 3.0 2024-04-23 21:17:02 +01:00
Anselm
01ac646c8e Alpha release 2024-04-23 21:10:53 +01:00
Anselm
d4b9fbcc60 Make CoMaps even more subclassable 2024-04-23 21:10:35 +01:00
Anselm
1cfa279543 Alpha release 2024-04-23 20:52:08 +01:00
Anselm
e35be73bcc More superclass-compatible CoMaps 2024-04-23 20:51:36 +01:00
Anselm
f8a5c46e18 Alpha release 2024-04-23 20:27:41 +01:00
Anselm
1c7d85ce76 Fix CoStream types 2024-04-23 20:27:18 +01:00
Anselm
19004b4c36 Alpha release 2024-04-23 20:23:50 +01:00
Anselm
930fa689a7 Add .all to CoStreamEntry 2024-04-23 20:22:50 +01:00
Anselm
18a7b2d6b4 Making progress with docs 2024-04-23 15:16:32 +01:00
Anselm
d2e03ff9d3 Alpha release 2024-04-22 19:30:02 +01:00
Anselm
77a9c8395e Fix variance of ID.__type 2024-04-22 19:29:21 +01:00
Anselm
c4151fcb95 Alpha release 2024-04-22 16:16:37 +01:00
Anselm
4c5c21bba2 Work with stricter TS lints 2024-04-22 16:16:02 +01:00
Anselm
f0f6f1b71c Alpha release 2024-04-22 16:02:21 +01:00
Anselm
a9d6d5a1db Reintroduce jazz-nodejs 2024-04-22 15:58:50 +01:00
Anselm
7849ce6de7 Clean up API even more 2024-04-22 15:47:50 +01:00
Anselm
354bdcdbfb Alpha release 2024-04-19 16:40:18 +01:00
Anselm
8ecd3e88c8 Even friendlier for subclassing CoMap 2024-04-19 16:39:51 +01:00
Anselm
85d2b627f1 Alpha release 2024-04-19 16:28:47 +01:00
Anselm
88fd92e4dc More subclass-friendly types in CoMap 2024-04-19 16:27:48 +01:00
Anselm
952982e7ea Alpha release 2024-04-19 15:28:03 +01:00
Anselm
22e7c27af7 Consistent proxy based API 2024-04-19 15:25:05 +01:00
Anselm
59c18c34de Alpha release 2024-04-17 13:26:56 +01:00
Anselm
6acbaede44 Fix keys 2024-04-17 13:26:23 +01:00
Anselm
1a44f875b3 Alpha release 2024-04-17 13:24:09 +01:00
Anselm
9d935fe1d0 Refactors & homepage improvements 2024-04-17 13:23:31 +01:00
Anselm
e5eed5b9b7 Alpha release 2024-04-16 14:01:57 +01:00
Anselm
05a549f04f Makre refs on list more precise 2024-04-16 14:01:32 +01:00
Anselm
a5e68a4fae Alpha release 2024-04-16 13:58:35 +01:00
Anselm
016a9e342a Move unported packages & make refs type more precise 2024-04-16 13:57:41 +01:00
Anselm
627d8950ae Alpha release 2024-04-16 13:21:46 +01:00
Anselm
770ce08c10 Get rid of Co namespace 2024-04-16 13:17:09 +01:00
Anselm
69ac514b3b New alpha 2024-04-15 16:54:39 +01:00
Anselm
b1481748f9 Boom! 2024-04-15 16:50:14 +01:00
Anselm
49944e323f Just before circular references... 2024-04-08 15:42:47 +01:00
Anselm
15310db389 Fix image loading and CoMap proxy issues 2024-04-05 17:22:46 +01:00
Anselm
ea5c5a2604 Rename jazz-js to jazz-tools 2024-04-05 15:43:23 +01:00
Anselm
e461dd1355 Fix test 2024-04-05 15:32:44 +01:00
Anselm
e299c3e9d8 Alpha 0 prerelease 2024-04-05 15:30:49 +01:00
Anselm
406c47271f Finesse types and fix auth impls 2024-04-05 14:54:03 +01:00
Anselm
05c7efea85 More feature parity for demo apps 2024-04-05 11:45:33 +01:00
Anselm
ce7ddf7055 More stuff to make chat app work 2024-04-04 15:19:50 +01:00
Anselm
beb40b5db6 Mostly make chat example work 2024-03-27 18:21:41 +00:00
Anselm
2def752cc4 Flatten CoValue metadata and add edits for CoList 2024-03-26 15:21:28 +00:00
Anselm
bacf3ae86a Change order of Self generic argument again 2024-03-26 10:52:33 +00:00
Anselm
0fef382f2e Docs & homepage improvements 2024-03-25 14:38:39 +00:00
Anselm
95523d8538 Doc organization & type improvements 2024-03-25 12:20:55 +00:00
Anselm
71f7220bfd Start adding doc comments 2024-03-22 15:25:55 +00:00
Anselm
2212c6deac Fix lots of type issues, implement Account & Group 2024-03-22 14:50:41 +00:00
Anselm
fb3efe4cfd Implement tests for BinaryCoStream 2024-03-21 17:33:15 +00:00
Anselm
e66ac6a7d0 Apply strict mode to tests, finish tests for CoStream 2024-03-21 12:06:05 +00:00
Anselm
7ab3908848 Type cleanup, more CoStream implementation 2024-03-19 17:41:36 +00:00
Anselm
921f1fbfe8 Starting test for CoStream 2024-03-18 11:51:01 +00:00
Anselm
2ac455f8b5 Sketch out more in bindings etc 2024-03-18 11:25:18 +00:00
Anselm
1ce881aed2 jazz-js-efs becomes jazz-js 2024-03-15 14:35:51 +00:00
Anselm
b1b5140951 Sketch out coStream interfaces 2024-03-15 14:34:38 +00:00
Anselm
b109c23233 Change "meta" to "co" 2024-03-15 13:58:49 +00:00
Anselm
a7a34a0b6e Tests for CoList subscription and auto-resolution 2024-03-15 10:44:03 +00:00
Anselm
4bf63934e1 Simple CoList resolution 2024-03-14 16:42:41 +00:00
Anselm
16f572282f Kind of implement splice 2024-03-14 16:15:07 +00:00
Anselm
44380c3700 Implement CoList with primitive items 2024-03-14 15:57:31 +00:00
Anselm
dc46cb1386 Move tests to vitest, fix some 2024-03-14 15:38:27 +00:00
Anselm
3ccb1e8ad7 Sketch imageDef 2024-03-13 14:56:17 +00:00
Anselm
d973c5f48b Pull out common constructor methods 2024-03-13 14:30:46 +00:00
Anselm
f4af78c834 Mostly make index signatures work 2024-03-13 14:16:38 +00:00
Anselm
e6d323fd30 Use correct subclasses at runtime, tests passing 2024-03-12 11:39:25 +00:00
Anselm
e6ab56aeb5 Solve lots of type issues 2024-03-12 11:13:43 +00:00
Anselm
779765b649 Fix a bunch of type issues 2024-03-08 14:45:51 +00:00
Anselm
6da730779a Better CoMapInit 2024-03-08 10:16:00 +00:00
Anselm
a3e77edc57 Support optional references 2024-03-07 16:24:22 +00:00
Anselm
ed00308986 Make CoMap subscription work 2024-03-07 12:29:52 +01:00
Anselm
89e9092e0f Make loading work 2024-03-01 14:27:11 +00:00
Anselm
f8b11754c8 Sketch ValueRefs and loading 2024-02-29 17:39:06 +00:00
Anselm
4b38d0793c Test encoding on assignment 2024-02-29 11:29:16 +00:00
Anselm
b2156f8154 Test with more complicated schema in key 2024-02-29 11:25:17 +00:00
Anselm
3a5422e635 Make CoMaps work with effect schema 2024-02-29 11:23:31 +00:00
Anselm
54d3d76868 move pre-effect-schema implementation of jazz-js away 2024-02-29 11:20:34 +00:00
Anselm Eickhoff
f4dc0ec1b7 Merge pull request #177 from tobiaslins/main 2024-02-27 22:51:51 +00:00
Tobias Lins
f500db2dd3 Implement tree view with nested resolving 2024-02-27 21:56:25 +01:00
Anselm
95f64f9934 Actually deploy inspector 2024-02-27 14:45:54 +00:00
Anselm
cccb0e1a21 First draft of inspector 2024-02-27 14:43:08 +00:00
Anselm
b434a4227f Off by 10x 2024-02-27 11:55:40 +00:00
Anselm
6ba4dc1f04 New pricing 2024-02-27 11:49:49 +00:00
Anselm
2fe4c81d1e Start of EffectTS schema based jazz-js 2024-02-27 11:42:41 +00:00
Anselm
5c00264184 test for enumarting props in CoMap 2024-02-24 11:47:35 +01:00
Anselm
c744849c9b Pull out constructor types from schemas 2024-02-23 14:37:25 +01:00
Anselm
f59b278f00 Make simple coValue resolution in extra keys work 2024-02-22 17:45:36 +01:00
Anselm
b26c155d5f Fix effect types and others 2024-02-22 16:35:43 +01:00
Anselm
6da79b8745 Move owner of coValue to first param of constructor 2024-02-22 16:14:51 +01:00
Anselm
0b92591b17 Sketch first test for comap resolution in extra keys 2024-02-22 15:55:58 +01:00
Anselm
974456db54 Support for primitive extra keys in CoMaps 2024-02-22 14:58:27 +01:00
Anselm
a1326a80fe Completely get rid of immutable map for now 2024-02-21 14:45:19 +01:00
Anselm
00d6946b24 Release 2024-02-14 15:41:37 +00:00
Anselm
c4ffde93c0 Simple reconnect logic for jazz-nodejs 2024-02-13 20:19:51 +00:00
Anselm
37bfe967ea Refactor coValue files into parts 2024-02-09 18:16:44 +00:00
Anselm
9abbbfd6fb Release 2024-02-09 11:16:25 +00:00
Anselm
155cd08e39 Mute most annoying log messages 2024-02-09 11:13:18 +00:00
Anselm
e2e6bdf3bd Sketch for new ImageDefinition 2024-02-09 11:09:26 +00:00
Anselm
810c42c743 Implement BinaryCoStream subscription 2024-02-08 17:04:12 +00:00
Anselm
99e4c1301e Lots of fixes to subscription logic and CoValues 2024-02-08 15:54:03 +00:00
Anselm
8c86a831fc Move coValues into their own folder 2024-02-07 10:02:41 +00:00
Anselm
5e976416a4 Implement and test basic CoStream functionality #171 2024-02-06 16:52:11 +00:00
Anselm
0339e14260 Implementation of CoStreams (WIP) 2024-02-04 17:05:10 +00:00
Anselm
4b94fcebf1 Split up tests 2024-01-18 10:56:25 +00:00
Anselm
ddd2a79f37 Subscription & auto-resolution for CoLists 2024-01-18 10:46:00 +00:00
Anselm
01a8f2dab3 Make nested subscriptions work incl edge cases 2024-01-12 22:14:17 +00:00
Anselm
801629d2c1 Fix type errors 2024-01-09 14:43:33 +00:00
Anselm
87d62c941f Nested coValue loading & introduce EffectTS 2024-01-09 14:37:18 +00:00
Anselm
7e6e0fdcc5 Lots of doc improvements 2024-01-05 16:25:27 +00:00
Anselm
a73b07424c Lots of docs improvements 2024-01-04 17:40:57 +00:00
Anselm
0f9b983132 Very first basic tests 2024-01-04 16:48:51 +00:00
Anselm
43e25902d3 Merge branch 'main' into jazz-schema 2024-01-04 14:37:09 +00:00
Anselm
2c27c8517f Add own lockfile to homepage 2024-01-04 13:45:28 +00:00
Anselm Eickhoff
b496058a0e Merge pull request #154 from gardencmp/pnpm-and-turbo
Move to pnpm and turborepo (deploy)
2024-01-04 13:27:05 +00:00
Anselm Eickhoff
4313663bd1 Merge branch 'main' into pnpm-and-turbo 2024-01-04 13:26:52 +00:00
Anselm Eickhoff
dbdbfbd07a Merge pull request #156 from mirshko/patch-1
Fix loading prop always being false
2024-01-04 13:24:14 +00:00
Anselm
184b23d61f Publish 2024-01-03 11:03:57 +00:00
Anselm
5c03b4f668 Parametrize BrowserImage for maxWidth 2024-01-03 11:01:07 +00:00
Anselm
bdbe777d68 Fix ordering bugs with IndexedDB 2024-01-03 10:59:36 +00:00
Anselm
a838a18647 Publish 2024-01-02 18:37:41 +00:00
Anselm
dd8dba63ea Fix migration changes being lost on loaded account 2024-01-02 18:36:18 +00:00
Jeff Reiner
3f5a664ee7 Fix loading prop always being false 2023-12-22 17:37:38 +01:00
Anselm Eickhoff
707292e1ff Merge pull request #152 from tobiaslins/pnpm-and-turbo
Move to pnpm & turborepo
2023-12-21 16:08:49 +00:00
Anselm
9a81b63943 Use pnpm install 2023-12-21 15:51:25 +00:00
Anselm
30216b7b80 Try GITHUB_TOKEN again 2023-12-21 15:49:47 +00:00
Anselm
b2fc91c2ce Use personal access token for ghcr push 2023-12-21 15:42:10 +00:00
Anselm
ef0328833c Try edited event for PRs 2023-12-21 15:30:35 +00:00
Anselm
6a93f17a4a Try to allow ghcr pushes from contributor PRs 2023-12-21 15:24:56 +00:00
Anselm
01bd07ac66 Use pnpm ci 2023-12-21 14:56:58 +00:00
Anselm
88859cfeca Actually add pnpm to CI 2023-12-21 10:23:58 +00:00
Anselm
dfe563e2bc Use pnpm & turbo for CI build 2023-12-21 10:21:53 +00:00
Anselm
7fc0ff981d Update instructions on "ejecting" examples 2023-12-21 10:15:37 +00:00
Anselm
1a9132102d Get rid of lerna 2023-12-21 09:32:08 +00:00
Anselm Eickhoff
d39638282f Merge branch 'main' into pnpm-and-turbo 2023-12-21 09:21:55 +00:00
Anselm Eickhoff
219071654d Merge pull request #153 from tobiaslins/move-to-localStorage 2023-12-21 07:43:36 +00:00
Tobias Lins
7c415db7bd Move to auth to use localStorage instead of sessionStorage 2023-12-21 08:28:03 +01:00
Tobias Lins
4354c340fc Add more dev exmaples 2023-12-21 08:20:43 +01:00
Anselm
a4b484fa36 Publish 2023-12-20 21:49:54 +00:00
Anselm
3757d12dc4 Fix loading of accounts 2023-12-20 21:48:01 +00:00
Anselm
c3a97b29a9 First test skeleton 2023-12-20 21:15:21 +00:00
Tobias Lins
b65e30ec70 Add changeset & publish commands 2023-12-20 22:04:21 +01:00
Tobias Lins
23a1e0266a Everything is building again 2023-12-20 21:49:31 +01:00
Tobias Lins
76acecfe50 further fixes 2023-12-20 21:18:27 +01:00
Tobias Lins
5031c77afb Update index.tsx 2023-12-20 21:05:33 +01:00
Tobias Lins
af90b8c989 Update vite.config.ts 2023-12-20 21:04:20 +01:00
Tobias Lins
d06b4adad0 Missing dependencies 2023-12-20 21:02:43 +01:00
Tobias Lins
b961cde946 Revert changed files 2023-12-20 20:54:06 +01:00
Tobias Lins
8cbbe2f312 Move to pnpm & turborepo 2023-12-20 20:44:01 +01:00
Anselm
c15a49d82d Merge branch 'main' into jazz-schema 2023-12-20 11:36:09 +00:00
Anselm
fc5b670c73 Publish 2023-12-20 11:33:39 +00:00
Anselm
c8adcc4c47 Fix unused var 2023-12-20 11:32:40 +00:00
Anselm
41a755fe41 publish 2023-12-20 11:17:25 +00:00
Anselm
8def1bb29e IndexedDB & timer perf improvements 2023-12-20 11:04:45 +00:00
Anselm
d379b04e33 Publish 2023-12-18 15:15:27 +00:00
Anselm
17a30e054e Add passphrase based auth + example 2023-12-18 15:13:37 +00:00
Anselm
93809911de more WIP 2023-12-16 16:45:46 +00:00
Anselm
edeb2ca9f4 More WIP 2023-12-13 16:47:56 +00:00
Anselm
01662fc3b8 Merge branch 'main' into jazz-schema 2023-12-06 11:51:28 +00:00
Anselm
7d8f4b4c00 Publish jazz-browser-auth0 2023-12-05 19:12:16 +00:00
Anselm
e2a3896bf0 v0.0.0 2023-12-05 19:11:00 +00:00
Anselm
446de8e0ff Fix for not receiving user_metadata 2023-12-05 19:10:15 +00:00
Anselm
5ae6c95878 Use authorization params everywhere 2023-12-05 17:07:18 +00:00
Anselm
7cde349a50 Actually deploy auth0 example 2023-12-05 16:45:41 +00:00
Anselm
61e640f574 Fix auth0 example dependencies 2023-12-05 16:43:18 +00:00
Anselm
ed122d9d8e Publish 2023-12-05 16:41:12 +00:00
Anselm
34817f4536 Auth0 integration and example 2023-12-05 16:38:37 +00:00
Anselm
134d2f0fda work in progress 2023-12-05 12:06:39 +00:00
Anselm
142973827c Merge branch 'main' into jazz-schema 2023-11-28 08:32:52 +00:00
Anselm
0998a0eabf Correct homepage path 2023-11-24 17:09:34 +00:00
Anselm
a96108478b Homepage updates 2023-11-24 17:04:03 +00:00
Anselm
47444888c3 more progress 2023-11-24 16:54:12 +00:00
Anselm
a4769058f4 Merge branch 'main' into jazz-schema 2023-11-08 11:42:49 +00:00
Anselm
a4cf4c40d4 publish 2023-11-08 11:42:36 +00:00
Anselm Eickhoff
934fe4d29b Merge pull request #127 from KyleAMathews/main
fix(jazz-nodejs): also set peersToLoadFrom on newly created accounts
2023-11-07 17:18:02 +00:00
Anselm Eickhoff
408012f2e5 remove redundant peer add 2023-11-07 17:17:41 +00:00
Kyle Mathews
d0078b830e fix(jazz-nodejs): also set peersToLoadFrom on newly created accounts 2023-11-03 14:58:43 -07:00
Anselm
f7f091e18c Simplify CoValue cards 2023-11-02 14:14:58 +00:00
Anselm
a969430247 move homepage to new folder 2023-11-02 12:13:15 +00:00
Anselm Eickhoff
e52948b2b7 Merge pull request #125 from gardencmp/jazz-nodejs
jazz-nodejs MVP
2023-11-02 12:11:56 +00:00
Anselm
53bb1b230b Fix interpolation 2023-11-02 11:53:19 +00:00
Anselm
54e83aeaaa use NOMAD_ADDR secret 2023-11-02 11:43:32 +00:00
Anselm
aa3129cab5 Provide localNode to AccountMigrations 2023-10-27 14:48:36 +01:00
Anselm
90520dddd7 Make addMember and removeMember take loaded Accounts instead of just IDs 2023-10-27 14:30:55 +01:00
Anselm
03eb77070a Allow account migrations to be async 2023-10-27 11:18:41 +01:00
Anselm
4ba5c255b6 Fewer assumptions in jazz-nodejs 2023-10-27 11:00:19 +01:00
Anselm
01817db873 Add docs for jazz-nodejs 2023-10-25 14:31:27 +01:00
Anselm
46fcbd6c01 Implement first version of jazz-nodejs 2023-10-25 14:27:55 +01:00
Anselm
aa3e3de09e Update docs 2023-10-20 11:09:19 +01:00
Anselm
af3d48764d Reset allTweets 2023-10-20 10:50:29 +01:00
Anselm
091f36b736 Update all tweets 2023-10-20 10:38:56 +01:00
Anselm
7107f79f42 Update all tweets 2023-10-20 10:32:30 +01:00
Anselm
9922db2336 Cosmetic fixes 2023-10-20 10:23:40 +01:00
Anselm
75db570198 Use DemoAuth in Twit example 2023-10-20 10:18:14 +01:00
Anselm
28a09f377b Fix weird TypeScript error 2023-10-20 10:11:31 +01:00
Anselm
fd2e0855bb Deploy twit and chat 2023-10-20 09:56:09 +01:00
Anselm
82e1d57bd6 Fix new accounts not synced 2023-10-20 09:51:12 +01:00
Anselm
a2fbb0b0c8 new allTweets list 2023-10-19 13:38:55 +01:00
Anselm
8feddf9932 Fix sqlite type dependency 2023-10-19 13:38:43 +01:00
Anselm Eickhoff
feed34b1cf Merge pull request #122 from gardencmp/react-advanced
Performance improvements & Twit example improvements
2023-10-19 10:57:37 +01:00
Anselm
662c980cf2 First changeset 2023-10-19 00:52:47 +01:00
Anselm
f5ae530890 Add and use mapDefered to ResolvedCoList 2023-10-19 00:38:35 +01:00
Anselm
46bf7dd3ce A ton of performance and twit example improvements 2023-10-18 23:16:39 +01:00
Anselm
5d4eb38204 A bunch of perf improvements and sync fixes 2023-10-18 00:37:41 +01:00
Anselm
66da658075 Twit example improvements & initial stress test 2023-10-17 21:22:04 +01:00
Anselm
3477b74573 Lots of sync improvements, basic peer priority 2023-10-17 21:21:39 +01:00
Anselm
f3de4906b7 Prepare stress test and fix #83 2023-10-17 16:34:17 +01:00
Anselm
caded3f189 Fix unknown signer bug for incoming transactions 2023-10-17 14:39:13 +01:00
Anselm
5196395495 Wording 2023-10-17 12:03:48 +01:00
Anselm
8089a7ed9f Add report to parent frame again 2023-10-17 11:51:41 +01:00
Anselm
99230d31d2 Add plausible script 2023-10-17 11:49:30 +01:00
Anselm Eickhoff
94bca03f59 Merge pull request #121 from gardencmp/new-hp
New homepage PR 2
2023-10-17 11:39:04 +01:00
Anselm
49719b6e6d Fix example deploy 2023-10-17 11:28:10 +01:00
Anselm
1bdb781452 Fix iframe and metadata 2023-10-17 11:19:00 +01:00
Anselm
c336f69a6b Build and deploy homepage 2023-10-17 11:03:59 +01:00
Anselm
c8cb1ce208 Rename font 2023-10-17 09:55:45 +01:00
Anselm
814a6a80cd Lots of homepage improvements 2023-10-16 23:47:51 +01:00
Anselm Eickhoff
5fdfe18b32 Merge pull request #119 from gardencmp/new-hp
Chat demo & start of new homepage
2023-10-13 11:44:40 +01:00
Anselm
7b7a74778b Reduce number of chat example deployments 2023-10-13 11:40:36 +01:00
Anselm
39dbd46556 Publish
- jazz-example-chat@0.0.45
 - jazz-example-file-drop@0.0.62
 - jazz-example-pets@0.0.62
 - jazz-example-todo@0.0.62
 - jazz-example-twit@0.0.62
 - hash-slash@0.1.3
 - jazz-browser@0.4.15
 - jazz-browser-auth-local@0.4.15
 - jazz-browser-media-images@0.4.15
 - jazz-react@0.4.15
 - jazz-react-auth-local@0.4.15
2023-10-13 11:36:13 +01:00
Anselm
1db4a14be4 Update docs 2023-10-13 11:35:53 +01:00
Anselm
4a4ea4e196 Fix issues with packages 2023-10-13 11:35:35 +01:00
Anselm
e0724441eb Deploy chat example 2023-10-13 11:28:08 +01:00
Anselm
5d47895515 Rename hashroute to hash-slash 2023-10-13 11:25:46 +01:00
Anselm
c1dfac7260 Publish
- jazz-example-chat@0.0.44
 - jazz-example-file-drop@0.0.61
 - jazz-example-pets@0.0.61
 - jazz-example-todo@0.0.61
 - jazz-example-twit@0.0.61
 - hashroute@0.1.2
 - jazz-browser@0.4.14
 - jazz-browser-auth-local@0.4.14
 - jazz-browser-media-images@0.4.14
 - jazz-react@0.4.14
 - jazz-react-auth-local@0.4.14
2023-10-13 11:20:40 +01:00
Anselm
bf29cb3bae Make stuff mergeable 2023-10-13 11:20:07 +01:00
Anselm
a0a9b3f851 Merge branch 'main' into new-hp 2023-10-13 11:15:24 +01:00
Anselm
4c4deb22c9 Publish
- jazz-example-chat@0.0.43
 - jazz-example-pets@0.0.19
 - jazz-example-todo@0.0.43
 - jazz-example-twit@0.0.6
 - hashroute@0.1.1
 - jazz-browser@0.4.6
 - jazz-browser-auth-local@0.4.6
 - jazz-browser-media-images@0.4.7
 - jazz-react@0.4.6
 - jazz-react-auth-local@0.4.6
2023-10-13 11:13:24 +01:00
Anselm
a42c497055 Small imrovements to chat example 2023-10-13 11:12:32 +01:00
Anselm
f1dcdb20bc lots of new hp improvements 2023-10-13 11:11:37 +01:00
Anselm Eickhoff
46330ae201 Merge pull request #117 from gardencmp:data-throughput
Improve data throughput of BinaryCoStreams
2023-10-09 11:26:56 +01:00
Anselm
bfe3595b4c Fix errors in file-drop example 2023-10-09 11:23:15 +01:00
Anselm
34c39e6a55 Scale down other examples and add file-drop 2023-10-09 11:12:17 +01:00
Anselm
5a85501919 Publish
- jazz-example-file-drop@0.0.60
 - jazz-example-pets@0.0.60
 - jazz-example-todo@0.0.60
 - jazz-example-twit@0.0.60
 - cojson@0.4.13
 - cojson-simple-sync@0.4.13
 - cojson-storage-indexeddb@0.4.13
 - cojson-storage-sqlite@0.4.13
 - jazz-autosub@0.4.13
 - jazz-browser@0.4.13
 - jazz-browser-auth-local@0.4.13
 - jazz-browser-media-images@0.4.13
 - jazz-react@0.4.13
 - jazz-react-auth-local@0.4.13
2023-10-06 17:31:14 +01:00
Anselm
97a4282e5e Merge branch 'backend-support' into data-throughput 2023-10-06 17:23:13 +01:00
Anselm
39c13b50a3 Update docs 2023-10-06 17:18:30 +01:00
Anselm
ad304e321b Lots of improvements for BinaryCoStreams & file-drop example 2023-10-06 17:09:58 +01:00
Anselm
8c0b2da461 Start custom solution with nextjs & shiki-twoslash 2023-10-06 09:43:16 +01:00
Anselm
72fce45b2b Publish
- jazz-example-pets@0.0.21
 - jazz-example-todo@0.0.45
 - jazz-example-twit@0.0.8
 - cojson@0.4.8
 - cojson-simple-sync@0.4.8
 - cojson-storage-indexeddb@0.4.8
 - cojson-storage-sqlite@0.4.8
 - jazz-autosub@0.4.8
 - jazz-browser@0.4.8
 - jazz-browser-auth-local@0.4.8
 - jazz-browser-media-images@0.4.9
 - jazz-react@0.4.8
 - jazz-react-auth-local@0.4.8
2023-10-04 22:01:28 +01:00
Anselm
1f49d7fda6 Actually fix circular issues for esbuild/vite 2023-10-04 22:00:38 +01:00
Anselm
eec8ee7027 Publish
- jazz-example-pets@0.0.20
 - jazz-example-todo@0.0.44
 - jazz-example-twit@0.0.7
 - cojson@0.4.7
 - cojson-simple-sync@0.4.7
 - cojson-storage-indexeddb@0.4.7
 - cojson-storage-sqlite@0.4.7
 - jazz-autosub@0.4.7
 - jazz-browser@0.4.7
 - jazz-browser-auth-local@0.4.7
 - jazz-browser-media-images@0.4.8
 - jazz-react@0.4.7
 - jazz-react-auth-local@0.4.7
2023-10-04 21:23:52 +01:00
Anselm
188eb2e1e3 Update docs 2023-10-04 21:23:28 +01:00
Anselm
62867b32d9 Get rid of more cyclic imports 2023-10-04 21:22:52 +01:00
Anselm
ccebd2447d Publish
- jazz-example-pets@0.0.19
 - jazz-example-todo@0.0.43
 - jazz-example-twit@0.0.6
 - cojson@0.4.6
 - cojson-simple-sync@0.4.6
 - cojson-storage-indexeddb@0.4.6
 - cojson-storage-sqlite@0.4.6
 - jazz-autosub@0.4.6
 - jazz-browser@0.4.6
 - jazz-browser-auth-local@0.4.6
 - jazz-browser-media-images@0.4.7
 - jazz-react@0.4.6
 - jazz-react-auth-local@0.4.6
2023-10-04 21:01:44 +01:00
Anselm
08dca75789 Address some circular deps in cojson typescript 2023-10-04 21:00:59 +01:00
Anselm
16b3e1381b Sketch of new homepage with nextra + chat example 2023-10-04 19:59:32 +01:00
Anselm
f1cd639a09 Add project list to todo example 2023-10-03 13:59:00 +01:00
Anselm Eickhoff
be18e4de14 Merge pull request #111 from gardencmp/autosub-api
Autosub API
2023-10-01 13:18:40 +01:00
Anselm
7e62c91d44 Publish
- jazz-example-pets@0.0.18
 - jazz-example-twit@0.0.5
 - jazz-browser-media-images@0.4.6
2023-10-01 12:27:10 +01:00
Anselm
b2d5a103b5 move image-blob-reduce types to proper deps 2023-10-01 12:26:53 +01:00
Anselm
4ee2cad39e Publish
- jazz-example-pets@0.0.17
 - jazz-example-todo@0.0.42
 - jazz-example-twit@0.0.4
 - cojson@0.4.5
 - cojson-simple-sync@0.4.5
 - cojson-storage-indexeddb@0.4.5
 - cojson-storage-sqlite@0.4.5
 - jazz-autosub@0.4.5
 - jazz-browser@0.4.5
 - jazz-browser-auth-local@0.4.5
 - jazz-browser-media-images@0.4.5
 - jazz-react@0.4.5
 - jazz-react-auth-local@0.4.5
2023-10-01 12:18:35 +01:00
Anselm
b7c8a0038b Rename syncedQueries -> autosub and clean up a lot of APIs 2023-10-01 12:16:56 +01:00
Anselm
8c27e8c379 doc fixes 2023-09-28 23:05:59 +01:00
Anselm Eickhoff
0133aa47ff Merge pull request #105 from gardencmp/example-twit
Twitter example
2023-09-28 11:51:38 +01:00
Anselm
5659c925a2 Change Twit html title 2023-09-28 11:44:44 +01:00
Anselm
27779ac792 Publish
- jazz-example-pets@0.0.16
 - jazz-example-todo@0.0.41
 - jazz-example-twit@0.0.3
 - cojson@0.4.1
 - cojson-simple-sync@0.4.1
 - cojson-storage-indexeddb@0.4.1
 - cojson-storage-sqlite@0.4.1
 - jazz-browser@0.4.1
 - jazz-browser-auth-local@0.4.1
 - jazz-browser-media-images@0.4.1
 - jazz-react@0.4.1
 - jazz-react-auth-local@0.4.1
2023-09-28 11:25:36 +01:00
Anselm
3f1bfa4629 Improve twit example 2023-09-28 11:25:09 +01:00
Anselm
15a693c3ed Simplify QueriedCoStream 2023-09-28 11:23:23 +01:00
Anselm
b1d620e145 Update docs 2023-09-28 11:23:06 +01:00
Anselm
478fbd0aa9 Bigger inputs on mobile 2023-09-27 22:21:19 +01:00
Anselm
ee906b7351 Add QR code to own profile 2023-09-27 22:13:55 +01:00
Anselm
dd15f21ccb Fix follow button 2023-09-27 21:51:20 +01:00
Anselm
d7cd5fda7c Actually deploy twit example 2023-09-27 21:43:07 +01:00
Anselm
174300b00f Deploy twit example 2023-09-27 21:39:30 +01:00
Anselm
b2c8d8c855 Publish
- jazz-example-pets@0.0.15
 - jazz-example-todo@0.0.40
 - jazz-example-twit@0.0.2
 - cojson@0.4.0
 - cojson-simple-sync@0.4.0
 - cojson-storage-indexeddb@0.4.0
 - cojson-storage-sqlite@0.4.0
 - jazz-browser@0.4.0
 - jazz-browser-auth-local@0.4.0
 - jazz-browser-media-images@0.4.0
 - jazz-react@0.4.0
 - jazz-react-auth-local@0.4.0
2023-09-27 21:37:56 +01:00
Anselm
2bad2b6bfe Update docs 2023-09-27 21:37:22 +01:00
Anselm
880d0ff855 Fix last lint issues 2023-09-27 21:37:04 +01:00
Anselm
e66cbee6cd Implement Twitter example 2023-09-27 21:27:49 +01:00
Anselm
03e470721e AAAAAAAAAA 2023-09-27 15:08:09 +01:00
Anselm
ecf73bcfa7 Basic account initialization, fixes #103 2023-09-26 18:07:14 +01:00
Anselm
2c3a500286 Add root to queried group 2023-09-26 17:47:31 +01:00
Anselm
8b83061cf4 Update docs 2023-09-26 17:42:48 +01:00
Anselm
e75c3207d6 Make Groups and Accounts behave like proper CoValues, fixes #101 2023-09-26 17:42:28 +01:00
Anselm
41d4b5ba0b Ability to add/remove the public as readers & writers #99 2023-09-26 11:19:39 +01:00
Anselm
21fa1b168b First sketch of twit example 2023-09-26 09:50:08 +01:00
Anselm
91e5e7f2ab v0.3.7 2023-09-24 20:25:23 +01:00
Anselm
e3f7e2f1bd Actually use delayOnerror 2023-09-24 20:24:58 +01:00
Anselm
084cf80c60 v0.3.6 2023-09-24 20:16:25 +01:00
Anselm
632e3bbb08 Add option for delay on error when handling peer messages 2023-09-24 20:15:10 +01:00
Anselm
17d17833b2 Publish
- jazz-example-pets@0.0.14
 - jazz-example-todo@0.0.39
 - cojson@0.3.5
 - cojson-simple-sync@0.3.7
 - cojson-storage-indexeddb@0.3.5
 - cojson-storage-sqlite@0.3.7
 - jazz-browser@0.3.5
 - jazz-browser-auth-local@0.3.5
 - jazz-browser-media-images@0.3.5
 - jazz-react@0.3.5
 - jazz-react-auth-local@0.3.5
 - jazz-react-media-images@0.3.5
2023-09-22 15:18:21 +01:00
Anselm
8e22bd9c1e Lint fix 2023-09-22 15:17:44 +01:00
Anselm
98213743f3 deploy bump 2023-09-22 15:15:09 +01:00
Anselm
bb855ed83d Publish
- jazz-example-pets@0.0.13
 - jazz-example-todo@0.0.38
 - cojson@0.3.4
 - cojson-simple-sync@0.3.6
 - cojson-storage-indexeddb@0.3.4
 - cojson-storage-sqlite@0.3.6
 - jazz-browser@0.3.4
 - jazz-browser-auth-local@0.3.4
 - jazz-browser-media-images@0.3.4
 - jazz-react@0.3.4
 - jazz-react-auth-local@0.3.4
 - jazz-react-media-images@0.3.4
2023-09-22 14:33:25 +01:00
Anselm
a8ef49e228 Small lint fixes 2023-09-22 14:32:41 +01:00
Anselm
e0ad32dbd2 Implement exponential falloff, fixes #69 2023-09-22 14:30:55 +01:00
Anselm
62bf769cad Publish
- cojson-simple-sync@0.3.5
 - cojson-storage-sqlite@0.3.5
2023-09-22 10:36:17 +01:00
Anselm
7488ff25b2 Missed one bit of JSON parsing to make more robust 2023-09-22 10:36:02 +01:00
Anselm
b69c9da983 Publish
- cojson-simple-sync@0.3.4
 - cojson-storage-sqlite@0.3.4
2023-09-22 10:25:25 +01:00
Anselm
d30fdef8aa More JSON.parse resiliency in cojson-storage-sqlite 2023-09-22 10:25:08 +01:00
Anselm
9c5a6b9833 Publish
- jazz-example-pets@0.0.12
 - jazz-example-todo@0.0.37
 - cojson@0.3.3
 - cojson-simple-sync@0.3.3
 - cojson-storage-indexeddb@0.3.3
 - cojson-storage-sqlite@0.3.3
 - jazz-browser@0.3.3
 - jazz-browser-auth-local@0.3.3
 - jazz-browser-media-images@0.3.3
 - jazz-react@0.3.3
 - jazz-react-auth-local@0.3.3
 - jazz-react-media-images@0.3.3
2023-09-22 10:09:04 +01:00
Anselm
d300d265c4 manually update cojson 2023-09-22 10:07:55 +01:00
Anselm
1d72ce587f Update version 2023-09-22 09:53:25 +01:00
Anselm
3fdb41dcb9 More resilience against invalid JSON 2023-09-22 09:51:07 +01:00
Anselm
f20de2f04a v0.3.1 2023-09-22 09:36:32 +01:00
Anselm
31b31f111b Shorter logs on failed transactions 2023-09-22 09:34:54 +01:00
Anselm Eickhoff
2ae9fb9778 Fix example comment 2023-09-21 18:00:28 +01:00
Anselm Eickhoff
cd0da0f6bf Merge pull request #94 from gardencmp/ergonomic-covalues
Implement queries
2023-09-21 17:31:31 +01:00
Anselm
cd9bfbb9fa Publish
- jazz-example-pets@0.0.11
 - jazz-example-todo@0.0.36
 - cojson@0.3.0
 - cojson-simple-sync@0.3.0
 - cojson-storage-indexeddb@0.3.0
 - cojson-storage-sqlite@0.3.0
 - jazz-browser@0.3.0
 - jazz-browser-auth-local@0.3.0
 - jazz-browser-media-images@0.3.0
 - jazz-react@0.3.0
 - jazz-react-auth-local@0.3.0
 - jazz-react-media-images@0.3.0
2023-09-21 17:29:23 +01:00
Anselm
ed0428bf97 Pre-release fixes 2023-09-21 17:12:10 +01:00
Anselm
c038a02051 Publish
- jazz-example-pets@0.0.10
 - jazz-example-todo@0.0.35
 - cojson@0.3.0-alpha.0
 - cojson-simple-sync@0.3.0-alpha.0
 - cojson-storage-indexeddb@0.3.0-alpha.0
 - cojson-storage-sqlite@0.3.0-alpha.0
 - jazz-browser@0.3.0-alpha.0
 - jazz-browser-auth-local@0.3.0-alpha.0
 - jazz-browser-media-images@0.3.0-alpha.0
 - jazz-react@0.3.0-alpha.0
 - jazz-react-auth-local@0.3.0-alpha.0
 - jazz-react-media-images@0.3.0-alpha.0
2023-09-21 17:07:06 +01:00
Anselm
31abcfeef4 Walkthrough and doc improvements 2023-09-21 17:02:34 +01:00
Anselm
5f32d9ccf5 Support external routers & more doc improvements 2023-09-21 16:35:13 +01:00
Anselm
0510600104 Lots of doc improvements, cleaner Queried's 2023-09-20 17:48:07 +01:00
Anselm
7f30fbf3c5 move stuff to "co" property in queries 2023-09-20 11:52:57 +01:00
Anselm
3d56260ca4 Lots more consistency and API improvements 2023-09-19 13:17:31 +01:00
Anselm
1137775da9 Publish
- jazz-example-pets@0.0.9
 - jazz-example-todo@0.0.34
 - cojson@0.2.3
 - cojson-simple-sync@0.2.6
 - cojson-storage-sqlite@0.2.6
 - jazz-browser@0.2.5
 - jazz-browser-auth-local@0.2.5
 - jazz-browser-media-images@0.2.5
 - jazz-react@0.2.5
 - jazz-react-auth-local@0.2.5
 - jazz-react-media-images@0.2.5
 - jazz-storage-indexeddb@0.2.5
2023-09-15 16:40:47 +01:00
Anselm
3951fdc938 Implement queries & use in examples 2023-09-15 16:36:48 +01:00
Anselm
5779e357dd Allow CoValues directly where ids would be expected 2023-09-13 17:48:04 +01:00
Anselm
2842d80f26 Improve docs for new packages 2023-09-12 16:55:58 +01:00
Anselm Eickhoff
96387d8023 Merge pull request #89 from gardencmp:stream-txs
Stream transactions
2023-09-12 16:26:09 +01:00
Anselm
6720c19233 Publish
- jazz-example-pets@0.0.8
 - jazz-example-todo@0.0.33
 - cojson-simple-sync@0.2.5
 - cojson-storage-sqlite@0.2.5
 - jazz-browser@0.2.4
 - jazz-browser-auth-local@0.2.4
 - jazz-browser-media-images@0.2.4
 - jazz-react@0.2.4
 - jazz-react-auth-local@0.2.4
 - jazz-react-media-images@0.2.4
 - jazz-storage-indexeddb@0.2.4
2023-09-12 16:17:09 +01:00
Anselm
ef732b4700 Implement saving signatures and streaming txs for SQLite 2023-09-12 16:16:40 +01:00
Anselm
ee7e3ee5a7 Publish
- jazz-example-pets@0.0.7
 - jazz-example-todo@0.0.32
 - cojson@0.2.2
 - cojson-simple-sync@0.2.4
 - cojson-storage-sqlite@0.2.4
 - jazz-browser@0.2.3
 - jazz-browser-auth-local@0.2.3
 - jazz-browser-media-images@0.2.3
 - jazz-react@0.2.3
 - jazz-react-auth-local@0.2.3
 - jazz-react-media-images@0.2.3
 - jazz-storage-indexeddb@0.2.3
2023-09-12 15:26:43 +01:00
Anselm
ceeed88fa5 Less verbose error output 2023-09-12 15:26:22 +01:00
Anselm
79353a1d97 Publish
- cojson-simple-sync@0.2.3
 - cojson-storage-sqlite@0.2.3
2023-09-12 15:22:01 +01:00
Anselm
7fdc42c62f Fix migration 2023-09-12 15:21:45 +01:00
Anselm
3a2e854a88 Publish
- cojson-simple-sync@0.2.2
 - cojson-storage-sqlite@0.2.2
2023-09-12 15:19:12 +01:00
Anselm
661a2d023a Fixes #90 for SQLite 2023-09-12 15:18:53 +01:00
Anselm
6ef5b6b2ab Publish
- jazz-example-pets@0.0.6
 - jazz-example-todo@0.0.31
 - jazz-browser@0.2.2
 - jazz-browser-auth-local@0.2.2
 - jazz-browser-media-images@0.2.2
 - jazz-react@0.2.2
 - jazz-react-auth-local@0.2.2
 - jazz-react-media-images@0.2.2
 - jazz-storage-indexeddb@0.2.2
2023-09-12 14:56:31 +01:00
Anselm
1384ebed84 Fix migration 2023-09-12 14:55:57 +01:00
Anselm
17e53f9998 Publish
- jazz-example-pets@0.0.5
 - jazz-example-todo@0.0.30
 - cojson@0.2.1
 - cojson-simple-sync@0.2.1
 - cojson-storage-sqlite@0.2.1
 - jazz-browser@0.2.1
 - jazz-browser-auth-local@0.2.1
 - jazz-browser-media-images@0.2.1
 - jazz-react@0.2.1
 - jazz-react-auth-local@0.2.1
 - jazz-react-media-images@0.2.1
 - jazz-storage-indexeddb@0.2.1
2023-09-12 14:47:50 +01:00
Anselm
cfb1f39efe update docs 2023-09-12 14:47:17 +01:00
Anselm
2234276dcf Implement extra signatures & fix #90 for IndexedDB 2023-09-12 14:42:47 +01:00
Anselm
bb0a6a0600 yield microtask between incoming messages 2023-09-12 11:22:44 +01:00
Anselm
0a6eb0c10a Lots of fixes around streaming 2023-09-12 11:13:19 +01:00
Anselm
88b67d89e0 First implementation of streaming transactions, also fixes #80 2023-09-11 19:29:52 +01:00
Anselm Eickhoff
1a65d826b2 Update pets README.md 2023-09-11 17:24:01 +01:00
Anselm Eickhoff
6c65ec2b46 Merge pull request #81 from gardencmp/publish-pet-example
Publish pet example
2023-09-11 17:21:16 +01:00
Anselm
5b578a832d Fix job name and missing amtrix 2023-09-11 17:13:16 +01:00
Anselm
042afc52d7 Fix interpolation 2023-09-11 17:10:12 +01:00
Anselm
1b83493964 Use matrix and add pets example 2023-09-11 17:09:14 +01:00
Anselm
3b50da1a74 Remove redundant yarn build step 2023-09-11 17:04:42 +01:00
Anselm
8e0fc74d9f Switch to buildx 2023-09-11 17:03:18 +01:00
Anselm Eickhoff
e28326f32c Merge pull request #79 from gardencmp/anselm-gar-155
Make payload of trusting transactions JSON string instead of immediately-parsed JSON
2023-09-11 16:32:30 +01:00
Anselm
d7e8b0b9da Publish
- jazz-example-pets@0.0.4
 - jazz-example-todo@0.0.29
 - cojson@0.2.0
 - cojson-simple-sync@0.2.0
 - cojson-storage-sqlite@0.2.0
 - jazz-browser@0.2.0
 - jazz-browser-auth-local@0.2.0
 - jazz-browser-media-images@0.2.0
 - jazz-react@0.2.0
 - jazz-react-auth-local@0.2.0
 - jazz-react-media-images@0.2.0
 - jazz-storage-indexeddb@0.2.0
2023-09-11 16:19:44 +01:00
Anselm
c46a1f6b0a Update docs 2023-09-11 16:18:39 +01:00
Anselm
7947918278 lint pet example 2023-09-11 16:11:26 +01:00
Anselm
50c36e7255 Make tx.changes stringified 2023-09-11 16:11:17 +01:00
Anselm
c39a7ed1b7 Implement jazz-browser-media-images 2023-09-11 11:44:55 +01:00
Anselm
83762dbb0f Fix getLastItemsPerAccount 2023-09-10 15:36:41 +01:00
Anselm
7c82e12508 Fix filenames in pets example 2023-09-10 15:20:12 +01:00
Anselm
6db149be36 Complete most of the pets example 2023-09-10 15:15:23 +01:00
Anselm
909a101f99 Publish
- jazz-example-pets@0.0.3
 - jazz-example-todo@0.0.28
 - cojson@0.1.12
 - cojson-simple-sync@0.1.13
 - cojson-storage-sqlite@0.1.10
 - jazz-browser@0.1.12
 - jazz-browser-auth-local@0.1.12
 - jazz-react@0.1.14
 - jazz-react-auth-local@0.1.14
 - jazz-storage-indexeddb@0.1.12
2023-09-08 17:29:07 +01:00
Anselm
df0b6fe138 Update docs 2023-09-08 17:28:53 +01:00
Anselm
0543756016 More optimizations and first support for streaming hashing 2023-09-08 17:28:33 +01:00
490 changed files with 60099 additions and 32898 deletions

8
.changeset/README.md Normal file
View File

@@ -0,0 +1,8 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

24
.changeset/config.json Normal file
View File

@@ -0,0 +1,24 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [
[
"cojson",
"jazz-tools",
"jazz-browser",
"jazz-browser-media-images",
"jazz-react",
"jazz-nodejs",
"jazz-run",
"cojson-transport-ws",
"cojson-storage-indexeddb",
"cojson-storage-sqlite"
]
],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}

View File

@@ -7,50 +7,64 @@ on:
branches: [ "main" ]
jobs:
build-and-deploy:
build-examples:
runs-on: ubuntu-latest
strategy:
matrix:
example: ["chat", "pets", "todo", "inspector"]
# example: ["twit", "chat", "counter-js-auth0", "pets", "twit", "file-drop", "inspector"]
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'yarn'
cache-dependency-path: yarn.lock
- name: Nuke Workspace
run: |
rm package.json yarn.lock;
- name: Yarn Build
run: |
yarn install --frozen-lockfile;
yarn build;
working-directory: ./examples/todo
node-version: 16
cache: 'pnpm'
- uses: satackey/action-docker-layer-caching@v0.0.11
continue-on-error: true
with:
key: docker-layer-caching-${{ github.workflow }}-{hash}
restore-keys: |
docker-layer-caching-${{ github.workflow }}-
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: gardencmp
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker Build & Push
- name: Pnpm Build
run: |
export DOCKER_TAG=ghcr.io/gardencmp/jazz-example-todo:${{github.head_ref || github.ref_name}}-${{github.sha}}-$(date +%s) ;
docker build . --file Dockerfile --tag $DOCKER_TAG;
docker push $DOCKER_TAG;
echo "DOCKER_TAG=$DOCKER_TAG" >> $GITHUB_ENV
working-directory: ./examples/todo
pnpm install
pnpm turbo build;
working-directory: ./examples/${{ matrix.example }}
- name: Docker Build & Push
uses: docker/build-push-action@v4
with:
context: ./examples/${{ matrix.example }}
push: true
tags: ghcr.io/gardencmp/${{github.event.repository.name}}-example-${{ matrix.example }}:${{github.head_ref || github.ref_name}}-${{github.sha}}-${{github.run_number}}-${{github.run_attempt}}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-examples:
runs-on: ubuntu-latest
needs: build-examples
strategy:
matrix:
example: ["chat", "pets", "todo", "inspector"]
# example: ["twit", "chat", "counter-js-auth0", "pets", "twit", "file-drop", "inspector"]
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: gacts/install-nomad@v1
- name: Tailscale
uses: tailscale/github-action@v1
@@ -69,9 +83,9 @@ jobs:
export DOCKER_USER=gardencmp;
export DOCKER_PASSWORD=${{ secrets.DOCKER_PULL_PAT }};
export DOCKER_TAG=${{ env.DOCKER_TAG }};
export DOCKER_TAG=ghcr.io/gardencmp/${{github.event.repository.name}}-example-${{ matrix.example }}:${{github.head_ref || github.ref_name}}-${{github.sha}}-${{github.run_number}}-${{github.run_attempt}};
envsubst '${DOCKER_USER} ${DOCKER_PASSWORD} ${DOCKER_TAG} ${BRANCH_SUFFIX} ${BRANCH_SUBDOMAIN}' < job-template.nomad > job-instance.nomad;
cat job-instance.nomad;
NOMAD_ADDR='http://control1v2-london:4646' nomad job run job-instance.nomad;
working-directory: ./examples/todo
NOMAD_ADDR=${{ secrets.NOMAD_ADDR }} nomad job run job-instance.nomad;
working-directory: ./examples/${{ matrix.example }}

4
.gitignore vendored
View File

@@ -1,4 +1,6 @@
node_modules
yarn-error.log
lerna-debug.log
docsTmp
docsTmp
.DS_Store
.turbo

View File

@@ -1,3 +0,0 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}

3283
DOCS.md

File diff suppressed because it is too large Load Diff

19
LICENSE.txt Normal file
View File

@@ -0,0 +1,19 @@
Copyright 2024, Garden Computing, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,82 +1,12 @@
# Jazz - instant sync
Homepage: [jazz.tools](https://jazz.tools) &mdash; [Discord](https://discord.gg/utDMjHYg42)
Jazz is an open-source toolkit for *secure telepathic data.*
- Ship faster & simplify your frontend and backend
- Get cross-device sync, real-time collaboration & offline support for free
[Jazz Global Mesh](https://jazz.tools/mesh) is serverless sync & storage for Jazz apps. (currently free!)
# Jazz - Instant sync
## What is Secure Telepathic Data?
**Jazz is an open-source toolkit for building apps with *distributed state.***
**Telepathic** means:
- Homepage: [jazz.tools](https://jazz.tools)
- Docs: [jazz.tools/docs](https://jazz.tools/docs)
- Community & support: [Discord](https://discord.gg/utDMjHYg42)
- Updates: [Twitter](https://twitter.com/jazz_tools) & [Email](https://gcmp.io/news)
- **Read and write data as if it was local,** from anywhere in your app.
- **Always have that data synced, instantly.** Across devices of the same user &mdash; or to other users (coming soon: to your backend, workers, etc.)
**Secure** means:
- **Fine-grained, role-based permissions are *baked into* your data.**
- **Permissions are enforced everywhere, locally.** (using cryptography instead of through an API)
- Roles can be changed dynamically, supporting changing teams, invite links and more.
## How to build an app with Jazz?
### Building a new app, completely with Jazz
It's still a bit early, but these are the rough steps:
1. Define your data model with [CoJSON Values](#cojson).
2. Implement permission logic using [CoJSON Groups](#group).
3. Hook up a user interface with [jazz-react](#jazz-react).
The best example is currently the [Todo List app](#example-app-todo-list).
### Gradually adding Jazz to an existing app
Coming soon: Jazz will support gradual adoption by integrating with your existing UI, auth and database.
## Example App: Todo List
The best example of Jazz is currently the Todo List app.
- Live version: https://example-todo.jazz.tools
- Source code: [`./examples/todo`](./examples/todo). See the README there for a walk-through and running instructions.
# Documentation
Note: Since it's early days, this is the only source of documentation so far.
If you want to build something with Jazz, [join the Jazz Discord](https://discord.gg/utDMjHYg42) for encouragement and help!
## Overview: Main Packages
**`cojson`** → [DOCS](./DOCS.md#cojson)
A library implementing abstractions and protocols for "Collaborative JSON". This will soon be standardized and forms the basis of secure telepathic data.
**`jazz-react`** → [DOCS](./DOCS.md#jazz-react)
Provides you with everything you need to build react apps around CoJSON, including reactive hooks for telepathic data, local IndexedDB persistence, support for different auth providers and helpers for simple invite links for CoJSON groups.
### Supporting packages
<small>
**`cojson-simple-sync`**
A generic CoJSON sync server you can run locally if you don't want to use Jazz Global Mesh (the default sync backend, at `wss://sync.jazz.tools`)
**`jazz-browser`** → [DOCS](./DOCS.md#jazz-browser)
framework-agnostic primitives that allow you to use CoJSON in the browser. Used to implement `jazz-react`, will be used to implement bindings for other frameworks in the future.
**`jazz-react-auth-local`** (and `jazz-browser-auth-local`): A simple auth provider that stores cryptographic keys on user devices using WebAuthentication/Passkeys. Lets you build Jazz apps completely without a backend, with end-to-end encryption by default.
**`jazz-storage-indexeddb`**
Provides local, offline-capable persistence. Included and enabled in `jazz-react` by default.
</small>
Copyright 2024 &mdash; Garden Computing, Inc.

View File

@@ -0,0 +1,13 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {},
}

24
examples/chat/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

683
examples/chat/CHANGELOG.md Normal file
View File

@@ -0,0 +1,683 @@
# jazz-example-chat
## 0.0.76
### Patch Changes
- Updated dependencies
- cojson@0.7.29
- jazz-react@0.7.29
- jazz-tools@0.7.29
## 0.0.75
### Patch Changes
- Updated dependencies
- cojson@0.7.28
- jazz-react@0.7.28
- jazz-tools@0.7.28
## 0.0.74
### Patch Changes
- jazz-react@0.7.27
## 0.0.73
### Patch Changes
- Updated dependencies
- cojson@0.7.26
- jazz-react@0.7.26
- jazz-tools@0.7.26
## 0.0.72
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.25
- jazz-react@0.7.25
## 0.0.71
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.24
- jazz-react@0.7.24
## 0.0.70
### Patch Changes
- Updated dependencies
- cojson@0.7.23
- jazz-react@0.7.23
- jazz-tools@0.7.23
## 0.0.69
### Patch Changes
- jazz-react@0.7.22
## 0.0.68
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.21
- jazz-react@0.7.21
## 0.0.67
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.20
- jazz-react@0.7.20
## 0.0.66
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.19
- jazz-react@0.7.19
## 0.0.65
### Patch Changes
- Updated dependencies
- cojson@0.7.18
- jazz-react@0.7.18
- jazz-tools@0.7.18
## 0.0.64
### Patch Changes
- Updated dependencies
- cojson@0.7.17
- jazz-react@0.7.17
- jazz-tools@0.7.17
## 0.0.63
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.16
- jazz-react@0.7.16
## 0.0.62
### Patch Changes
- Updated dependencies
- jazz-react@0.7.15
## 0.0.61
### Patch Changes
- Updated dependencies
- cojson@0.7.14
- jazz-tools@0.7.14
- jazz-react@0.7.14
## 0.0.60
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.13
- jazz-react@0.7.13
## 0.0.59
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.12
- jazz-react@0.7.12
## 0.0.58
### Patch Changes
- Updated dependencies
- cojson@0.7.11
- jazz-react@0.7.11
- jazz-tools@0.7.11
## 0.0.57
### Patch Changes
- Updated dependencies
- cojson@0.7.10
- jazz-react@0.7.10
- jazz-tools@0.7.10
## 0.0.56
### Patch Changes
- Updated dependencies
- cojson@0.7.9
- jazz-react@0.7.9
- jazz-tools@0.7.9
## 0.0.55
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
- jazz-react@0.7.8
## 0.0.54
### Patch Changes
- Updated dependencies [9fdc91c]
- jazz-react@0.7.7
## 0.0.53
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
- jazz-react@0.7.6
## 0.0.52
### Patch Changes
- Updated dependencies
- jazz-react@0.7.5
## 0.0.51
### Patch Changes
- Updated dependencies
- jazz-react@0.7.4
## 0.0.50
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.3
- jazz-react@0.7.3
## 0.0.49
### Patch Changes
- Updated dependencies
- jazz-react@0.7.2
## 0.0.48
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.1
- jazz-react@0.7.1
## 0.0.47
### Patch Changes
- Updated dependencies [8636319]
- Updated dependencies [1a35307]
- Updated dependencies [8636319]
- Updated dependencies [1a35307]
- Updated dependencies [96c494f]
- Updated dependencies [59c18c3]
- Updated dependencies [19f52b7]
- Updated dependencies [8636319]
- Updated dependencies [1a35307]
- Updated dependencies [d8fe2b1]
- Updated dependencies [19004b4]
- Updated dependencies [a78f168]
- Updated dependencies [1200aae]
- Updated dependencies [60d5ca2]
- Updated dependencies [52675c9]
- Updated dependencies [129e2c1]
- Updated dependencies [6d49e9b]
- Updated dependencies [1cfa279]
- Updated dependencies [704af7d]
- Updated dependencies [e97f730]
- Updated dependencies [1a35307]
- Updated dependencies [460478f]
- Updated dependencies [6b0418f]
- Updated dependencies [e299c3e]
- Updated dependencies [ed5643a]
- Updated dependencies [bde684f]
- Updated dependencies [bf0f8ec]
- Updated dependencies [c4151fc]
- Updated dependencies [63374cc]
- Updated dependencies [8636319]
- Updated dependencies [01ac646]
- Updated dependencies [a5e68a4]
- Updated dependencies [8636319]
- Updated dependencies [952982e]
- Updated dependencies [1a35307]
- Updated dependencies [5fa277c]
- Updated dependencies [60d5ca2]
- Updated dependencies [21771c4]
- Updated dependencies [77c2b56]
- Updated dependencies [63374cc]
- Updated dependencies [d2e03ff]
- Updated dependencies [354bdcd]
- Updated dependencies [ece35b3]
- Updated dependencies [60d5ca2]
- Updated dependencies [69ac514]
- Updated dependencies [f8a5c46]
- Updated dependencies [f0f6f1b]
- Updated dependencies [e5eed5b]
- Updated dependencies [1a44f87]
- Updated dependencies [627d895]
- Updated dependencies [1200aae]
- Updated dependencies [63374cc]
- Updated dependencies [ece35b3]
- Updated dependencies [38d4410]
- Updated dependencies [85d2b62]
- Updated dependencies [fd86c11]
- Updated dependencies [52675c9]
- jazz-tools@0.7.0
- cojson@0.7.0
- jazz-react@0.7.0
- hash-slash@0.2.0
## 0.0.47-alpha.42
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.42
- cojson@0.7.0-alpha.42
- jazz-react@0.7.0-alpha.42
## 0.0.47-alpha.41
### Patch Changes
- jazz-tools@0.7.0-alpha.41
- jazz-react@0.7.0-alpha.41
## 0.0.47-alpha.40
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.40
## 0.0.47-alpha.39
### Patch Changes
- Updated dependencies
- cojson@0.7.0-alpha.39
- jazz-react@0.7.0-alpha.39
- jazz-tools@0.7.0-alpha.39
## 0.0.47-alpha.38
### Patch Changes
- Updated dependencies
- Updated dependencies
- Updated dependencies
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.38
- jazz-react@0.7.0-alpha.38
- cojson@0.7.0-alpha.38
## 0.0.47-alpha.37
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.37
- cojson@0.7.0-alpha.37
- jazz-tools@0.7.0-alpha.37
## 0.0.47-alpha.36
### Patch Changes
- Updated dependencies [1a35307]
- Updated dependencies [1a35307]
- Updated dependencies [1a35307]
- Updated dependencies [1a35307]
- Updated dependencies [6b0418f]
- Updated dependencies [1a35307]
- cojson@0.7.0-alpha.36
- jazz-tools@0.7.0-alpha.36
- jazz-react@0.7.0-alpha.36
## 0.0.47-alpha.35
### Patch Changes
- Updated dependencies
- Updated dependencies
- cojson@0.7.0-alpha.35
- jazz-tools@0.7.0-alpha.35
- jazz-react@0.7.0-alpha.35
## 0.0.47-alpha.34
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.34
- jazz-react@0.7.0-alpha.34
## 0.0.47-alpha.33
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.33
## 0.0.47-alpha.32
### Patch Changes
- Updated dependencies
- Updated dependencies
- Updated dependencies
- hash-slash@0.2.0-alpha.3
- jazz-tools@0.7.0-alpha.32
- jazz-react@0.7.0-alpha.32
## 0.0.47-alpha.31
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.31
- jazz-react@0.7.0-alpha.31
## 0.0.47-alpha.30
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.30
- jazz-react@0.7.0-alpha.30
## 0.0.47-alpha.29
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.29
- cojson@0.7.0-alpha.29
- jazz-react@0.7.0-alpha.29
## 0.0.47-alpha.28
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.28
- cojson@0.7.0-alpha.28
- jazz-react@0.7.0-alpha.28
## 0.0.47-alpha.27
### Patch Changes
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.27
- cojson@0.7.0-alpha.27
- jazz-react@0.7.0-alpha.27
## 0.0.47-alpha.26
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.26
- jazz-react@0.7.0-alpha.26
## 0.0.47-alpha.25
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.25
- jazz-react@0.7.0-alpha.25
## 0.0.47-alpha.24
### Patch Changes
- Updated dependencies
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.24
- cojson@0.7.0-alpha.24
- jazz-react@0.7.0-alpha.24
## 0.0.47-alpha.23
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.23
- jazz-react@0.7.0-alpha.23
## 0.0.47-alpha.22
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.22
- jazz-react@0.7.0-alpha.22
## 0.0.47-alpha.21
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.21
- jazz-tools@0.7.0-alpha.21
## 0.0.47-alpha.20
### Patch Changes
- Updated dependencies
- Updated dependencies
- jazz-react@0.7.0-alpha.20
- jazz-tools@0.7.0-alpha.20
## 0.0.47-alpha.19
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.19
- jazz-react@0.7.0-alpha.19
## 0.0.47-alpha.18
### Patch Changes
- jazz-react@0.7.0-alpha.18
## 0.0.47-alpha.17
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.17
- jazz-react@0.7.0-alpha.17
## 0.0.47-alpha.16
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.16
- jazz-react@0.7.0-alpha.16
## 0.0.47-alpha.15
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.15
- jazz-react@0.7.0-alpha.15
## 0.0.47-alpha.14
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.14
- jazz-react@0.7.0-alpha.14
## 0.0.47-alpha.13
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.13
- jazz-react@0.7.0-alpha.13
## 0.0.47-alpha.12
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.12
- jazz-tools@0.7.0-alpha.12
## 0.0.47-alpha.11
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.11
- jazz-tools@0.7.0-alpha.11
- cojson@0.7.0-alpha.11
## 0.0.47-alpha.10
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.10
- jazz-tools@0.7.0-alpha.10
- cojson@0.7.0-alpha.10
## 0.0.47-alpha.9
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.9
- jazz-tools@0.7.0-alpha.9
## 0.0.47-alpha.8
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.8
- jazz-tools@0.7.0-alpha.8
## 0.0.47-alpha.7
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.7
- jazz-tools@0.7.0-alpha.7
- cojson@0.7.0-alpha.7
## 0.0.47-alpha.6
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.6
- jazz-tools@0.7.0-alpha.6
## 0.0.47-alpha.5
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.5
- jazz-tools@0.7.0-alpha.5
- cojson@0.7.0-alpha.5
## 0.0.47-alpha.4
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.4
- jazz-react@0.7.0-alpha.4
## 0.0.47-alpha.3
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.3
- jazz-react@0.7.0-alpha.3
## 0.0.47-alpha.2
### Patch Changes
- Updated dependencies
- hash-slash@0.2.0-alpha.2
- jazz-react@0.7.0-alpha.2
- jazz-tools@0.7.0-alpha.2
## 0.0.47-alpha.1
### Patch Changes
- Updated dependencies
- hash-slash@0.2.0-alpha.1
- jazz-react@0.7.0-alpha.1
- jazz-tools@0.7.0-alpha.1
- cojson@0.7.0-alpha.1
## 0.0.47-alpha.0
### Patch Changes
- Updated dependencies
- hash-slash@0.2.0-alpha.0
- jazz-react@0.7.0-alpha.0
- jazz-tools@0.7.0-alpha.0
- cojson@0.7.0-alpha.0
## 0.0.46
### Patch Changes
- Updated dependencies
- jazz-react@0.5.0
- jazz-react-auth-local@0.4.16

4
examples/chat/Dockerfile Normal file
View File

@@ -0,0 +1,4 @@
FROM caddy:2.7.3-alpine
LABEL org.opencontainers.image.source="https://github.com/gardencmp/jazz"
COPY ./dist /usr/share/caddy/

42
examples/chat/README.md Normal file
View File

@@ -0,0 +1,42 @@
# Jazz Chat Example
Live version: https://example-chat.jazz.tools
## Installing & running the example locally
(this requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation))
Start by checking out `jazz`
```bash
git clone https://github.com/gardencmp/jazz.git
cd jazz/examples/chat
pnpm pack --pack-destination /tmp
mkdir -p ~/jazz-examples/chat # or any other directory
tar -xf /tmp/jazz-example-chat-* --strip-components 1 -C ~/jazz-examples/chat
cd ~/jazz-examples/chat
```
This ensures that you have the example app without git history and independent of the Jazz multi-package monorepo.
Install dependencies:
```bash
pnpm install
```
Start the dev server:
```bash
pnpm dev
```
## Questions / problems / feedback
If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or open an issue or PR to fix something that seems wrong.
## Configuration: sync server
By default, the example app uses [Jazz Global Mesh](https://jazz.tools/mesh) (`wss://sync.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running `npx cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?sync=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/2_main.tsx](./src/2_main.tsx).

14
examples/chat/index.html Normal file
View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/jazz-logo.png" />
<link rel="stylesheet" href="/src/index.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jazz Chat Example</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/app.tsx"></script>
</body>
</html>

View File

@@ -0,0 +1,56 @@
job "chat$BRANCH_SUFFIX" {
region = "global"
datacenters = ["*"]
group "static" {
count = 4
network {
port "http" {
to = 80
}
}
constraint {
attribute = "${node.class}"
operator = "="
value = "mesh"
}
spread {
attribute = "${node.datacenter}"
weight = 100
}
constraint {
distinct_hosts = true
}
task "server" {
driver = "docker"
config {
image = "$DOCKER_TAG"
ports = ["http"]
auth = {
username = "$DOCKER_USER"
password = "$DOCKER_PASSWORD"
}
}
service {
tags = ["public"]
name = "chat$BRANCH_SUFFIX"
port = "http"
provider = "consul"
}
resources {
cpu = 50 # MHz
memory = 50 # MB
}
}
}
}
# deploy bump 4

View File

@@ -0,0 +1,54 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.76",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"format": "echo 'chat example is codegolfed'",
"preview": "vite preview"
},
"lint-staged": {
"*.{ts,tsx}": "eslint --fix",
"*.{js,jsx,mdx,json}": "prettier --write"
},
"dependencies": {
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.4",
"@types/qrcode": "^1.5.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"hash-slash": "workspace:*",
"jazz-tools": "workspace:*",
"jazz-react": "workspace:*",
"cojson": "workspace:*",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.16.0",
"react-router-dom": "^6.16.0",
"react-use": "^17.4.0",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"uniqolor": "^1.1.0"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"eslint": "^8.45.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"postcss": "^8.4.27",
"tailwindcss": "^3.3.3",
"typescript": "^5.0.2",
"vite": "^5.0.10"
}
}

View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

43
examples/chat/src/app.tsx Normal file
View File

@@ -0,0 +1,43 @@
import { CoMap, CoList, co, Group, ID } from "jazz-tools";
import { createJazzReactContext, DemoAuth } from "jazz-react";
import { createRoot } from "react-dom/client";
import { useIframeHashRouter } from "hash-slash";
import { ChatScreen } from "./chatScreen.tsx";
import { StrictMode } from "react";
export class Message extends CoMap {
text = co.string;
}
export class Chat extends CoList.Of(co.ref(Message)) {}
const Jazz = createJazzReactContext({
auth: DemoAuth({ appName: "Jazz Chat" }),
peer: `wss://mesh.jazz.tools/?key=you@example.com`
});
export const { useAccount, useCoState } = Jazz;
function App() {
const { me, logOut } = useAccount();
const createChat = () => {
const group = Group.create({ owner: me });
group.addMember("everyone", "writer");
const chat = Chat.create([], { owner: group });
location.hash = "/chat/" + chat.id;
};
return <div className="flex flex-col items-center justify-between w-screen h-screen p-2 dark:bg-black dark:text-white">
<div className="rounded mb-5 px-2 py-1 text-sm self-end">
{me.profile?.name} · <button onClick={logOut}>Log Out</button>
</div>
{useIframeHashRouter().route({
'/': () => createChat() as never,
'/chat/:id': (id) => <ChatScreen chatID={id as ID<Chat>} />
})}
</div>;
}
createRoot(document.getElementById("root")!)
.render(<StrictMode><Jazz.Provider><App/></Jazz.Provider></StrictMode>);

View File

@@ -0,0 +1,42 @@
import { ID } from 'jazz-tools';
import { Chat, Message, useCoState } from './app.tsx';
export function ChatScreen(props: { chatID: ID<Chat> }) {
const chat = useCoState(Chat, props.chatID, [{}]);
return chat ? <div className='w-full max-w-xl h-full flex flex-col items-stretch'>
{chat.length > 0
? chat.map((msg) => <ChatBubble msg={msg} key={msg.id} />)
: <div className='m-auto text-sm'>(Empty chat)</div>}
<ChatInput onSubmit={(text) => {
chat.push(
Message.create({ text }, { owner: chat._owner })
);
}} />
</div> : <div>Loading...</div>;
}
function ChatBubble(props: { msg: Message }) {
const lastEdit = props.msg._edits.text;
const align = lastEdit.by?.isMe ? 'items-end' : 'items-start';
return <div className={`${align} flex flex-col`}>
<div className='rounded-xl bg-stone-100 dark:bg-stone-700 dark:text-white py-2 px-4 mt-2 min-w-[5rem]'>
{ props.msg.text }
</div>
<div className='text-xs text-neutral-500 ml-2'>
{ lastEdit.by?.profile?.name }{' '}
{ lastEdit.madeAt?.toLocaleTimeString() }
</div>
</div>;
}
function ChatInput(props: { onSubmit: (text: string) => void }) {
return <input className='rounded p-2 border mt-auto dark:bg-black dark:text-white border-stone-300 dark:border-stone-700'
placeholder='Type a message and press Enter'
onKeyDown={({ key, currentTarget: input }) => {
if (key !== 'Enter' || !input.value) return;
props.onSubmit(input.value);
input.value = '';
}} />;
}

View File

@@ -0,0 +1,78 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 20 14.3% 4.1%;
--popover: 0 0% 100%;
--popover-foreground: 20 14.3% 4.1%;
--primary: 24 9.8% 10%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--ring: 20 14.3% 4.1%;
--radius: 0.5rem;
}
.dark {
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%;
--primary: 60 9.1% 97.8%;
--primary-foreground: 24 9.8% 10%;
--secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--ring: 24 5.7% 82.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
margin: 0;
padding: 0;
}
}

1
examples/chat/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,75 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}

View File

@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,16 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
build: {
minify: false
}
})

View File

@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}

24
examples/inspector/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,80 @@
# jazz-example-chat
## 0.0.55
### Patch Changes
- Updated dependencies
- cojson@0.7.29
- cojson-transport-ws@0.7.29
## 0.0.54
### Patch Changes
- Updated dependencies
- cojson@0.7.28
- cojson-transport-ws@0.7.28
## 0.0.53
### Patch Changes
- Updated dependencies
- cojson-transport-ws@0.7.27
## 0.0.52
### Patch Changes
- Updated dependencies
- cojson@0.7.26
- cojson-transport-ws@0.7.26
## 0.0.51
### Patch Changes
- Updated dependencies
- cojson@0.7.23
- cojson-transport-ws@0.7.23
## 0.0.50
### Patch Changes
- Updated dependencies
- cojson-transport-ws@0.7.22
## 0.0.49
### Patch Changes
- Updated dependencies
- cojson@0.7.18
- cojson-transport-ws@0.7.18
## 0.0.48
### Patch Changes
- Updated dependencies
- cojson@0.7.17
- cojson-transport-ws@0.7.17
## 0.0.47
### Patch Changes
- Updated dependencies
- cojson@0.6.7
- jazz-react@0.5.5
- jazz-react-auth-local@0.4.18
## 0.0.46
### Patch Changes
- Updated dependencies
- jazz-react@0.5.0
- jazz-react-auth-local@0.4.16

View File

@@ -0,0 +1,4 @@
FROM caddy:2.7.3-alpine
LABEL org.opencontainers.image.source="https://github.com/gardencmp/jazz"
COPY ./dist /usr/share/caddy/

View File

@@ -0,0 +1,42 @@
# Jazz Chat Example
Live version: https://example-chat.jazz.tools
## Installing & running the example locally
(this requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation))
Start by checking out `jazz`
```bash
git clone https://github.com/gardencmp/jazz.git
cd jazz/examples/chat
pnpm pack --pack-destination /tmp
mkdir -p ~/jazz-examples/chat # or any other directory
tar -xf /tmp/jazz-example-chat-* --strip-components 1 -C ~/jazz-examples/chat
cd ~/jazz-examples/chat
```
This ensures that you have the example app without git history and independent of the Jazz multi-package monorepo.
Install dependencies:
```bash
pnpm install
```
Start the dev server:
```bash
pnpm dev
```
## Questions / problems / feedback
If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or open an issue or PR to fix something that seems wrong.
## Configuration: sync server
By default, the example app uses [Jazz Global Mesh](https://jazz.tools/mesh) (`wss://sync.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running `npx cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?sync=ws://localhost:4200`), or by setting the `sync` parameter of the `<WithJazz>` provider component in [./src/2_main.tsx](./src/2_main.tsx).

View File

@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/jazz-logo.png" />
<link rel="stylesheet" href="/src/index.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jazz Inspector</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/app.tsx"></script>
</body>
</html>

View File

@@ -0,0 +1,56 @@
job "inspector$BRANCH_SUFFIX" {
region = "global"
datacenters = ["*"]
group "static" {
count = 4
network {
port "http" {
to = 80
}
}
constraint {
attribute = "${node.class}"
operator = "="
value = "mesh"
}
spread {
attribute = "${node.datacenter}"
weight = 100
}
constraint {
distinct_hosts = true
}
task "server" {
driver = "docker"
config {
image = "$DOCKER_TAG"
ports = ["http"]
auth = {
username = "$DOCKER_USER"
password = "$DOCKER_PASSWORD"
}
}
service {
tags = ["public"]
name = "inspector$BRANCH_SUFFIX"
port = "http"
provider = "consul"
}
resources {
cpu = 50 # MHz
memory = 50 # MB
}
}
}
}
# deploy bump 4

View File

@@ -0,0 +1,48 @@
{
"name": "jazz-inspector",
"private": true,
"version": "0.0.55",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.4",
"@types/qrcode": "^1.5.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"hash-slash": "workspace:*",
"cojson": "workspace:*",
"cojson-transport-ws": "workspace:*",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.16.0",
"react-router-dom": "^6.16.0",
"react-use": "^17.4.0",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"uniqolor": "^1.1.0"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"eslint": "^8.45.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"postcss": "^8.4.27",
"tailwindcss": "^3.3.3",
"typescript": "^5.0.2",
"vite": "^5.0.10"
}
}

View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -0,0 +1,4 @@
import ReactDOM from "react-dom/client";
import App from "./viewer/new-app";
ReactDOM.createRoot(document.getElementById("root")!).render(<App />);

View File

@@ -0,0 +1,92 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 20 14.3% 4.1%;
--popover: 0 0% 100%;
--popover-foreground: 20 14.3% 4.1%;
--primary: 24 9.8% 10%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--ring: 20 14.3% 4.1%;
--radius: 0.5rem;
}
.dark {
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%;
--primary: 60 9.1% 97.8%;
--primary-foreground: 24 9.8% 10%;
--secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--ring: 24 5.7% 82.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
margin: 0;
padding: 0;
}
}
.animate-in {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
transform: translateZ(400px) translateY(30px) scale(1.05);
opacity: 0.4;
}
to {
transform: translateZ(0) scale(1);
opacity: 1;
}
}

View File

@@ -0,0 +1,18 @@
export function LinkIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-3 h-3"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
/>
</svg>
);
}

View File

@@ -0,0 +1,42 @@
import React from "react";
import { PageInfo } from "./types";
interface BreadcrumbsProps {
path: PageInfo[];
onBreadcrumbClick: (index: number) => void;
}
export const Breadcrumbs: React.FC<BreadcrumbsProps> = ({
path,
onBreadcrumbClick,
}) => {
return (
<div className="z-20 relative bg-indigo-400/10 backdrop-blur-sm rounded-lg inline-flex px-2 py-1 whitespace-pre transition-all items-center space-x-1 min-h-10">
<button
onClick={() => onBreadcrumbClick(-1)}
className="flex items-center justify-center p-1 rounded-sm hover:bg-indigo-500/10 transition-colors"
aria-label="Go to home"
>
<img src="jazz-logo.png" alt="Jazz Logo" className="size-5" />
</button>
{path.map((page, index) => {
return (
<span
key={index}
className="inline-block first:pl-1 last:pr-1"
>
{index === 0 ? null : (
<span className="text-indigo-500/30">{" / "}</span>
)}
<button
onClick={() => onBreadcrumbClick(index)}
className="text-indigo-700 hover:underline"
>
{index === 0 ? page.name || "Root" : page.name}
</button>
</span>
);
})}
</div>
);
};

View File

@@ -0,0 +1,353 @@
import {
CoID,
LocalNode,
RawBinaryCoStream,
RawCoStream,
RawCoValue,
} from "cojson";
import { JsonObject, JsonValue } from "cojson/src/jsonValue";
import { PageInfo } from "./types";
import { base64URLtoBytes } from "cojson/src/base64url";
import { useEffect, useState } from "react";
import { ArrowDownToLine } from "lucide-react";
import {
BinaryStreamItem,
BinaryStreamStart,
CoStreamItem,
} from "cojson/src/coValues/coStream";
import { AccountOrGroupPreview } from "./value-renderer";
// typeguard for BinaryStreamStart
function isBinaryStreamStart(item: unknown): item is BinaryStreamStart {
return (
typeof item === "object" &&
item !== null &&
"type" in item &&
item.type === "start"
);
}
function detectCoStreamType(value: RawCoStream | RawBinaryCoStream) {
const firstKey = Object.keys(value.items)[0];
if (!firstKey)
return {
type: "unknown",
};
const items = value.items[firstKey as never]?.map((v) => v.value);
if (!items)
return {
type: "unknown",
};
const firstItem = items[0];
if (!firstItem)
return {
type: "unknown",
};
// This is a binary stream
if (isBinaryStreamStart(firstItem)) {
return {
type: "binary",
items: items as BinaryStreamItem[],
};
} else {
return {
type: "coStream",
};
}
}
async function getBlobFromCoStream({
items,
onlyFirstChunk = false,
}: {
items: BinaryStreamItem[];
onlyFirstChunk?: boolean;
}) {
if (onlyFirstChunk && items.length > 1) {
items = items.slice(0, 2);
}
const chunks: Uint8Array[] = [];
const binary_U_prefixLength = 8;
let lastProgressUpdate = Date.now();
for (const item of items.slice(1)) {
if (item.type === "end") {
break;
}
if (item.type !== "chunk") {
console.error("Invalid binary stream chunk", item);
return undefined;
}
const chunk = base64URLtoBytes(item.chunk.slice(binary_U_prefixLength));
// totalLength += chunk.length;
chunks.push(chunk);
if (Date.now() - lastProgressUpdate > 100) {
lastProgressUpdate = Date.now();
}
}
const defaultMime = "mimeType" in items[0] ? items[0].mimeType : null;
const blob = new Blob(chunks, defaultMime ? { type: defaultMime } : {});
const mimeType =
defaultMime === "" ? await detectPDFMimeType(blob) : defaultMime;
return {
blob,
mimeType: mimeType as string,
unfinishedChunks: items.length > 1,
totalSize:
"totalSizeBytes" in items[0]
? (items[0].totalSizeBytes as number)
: undefined,
};
}
const detectPDFMimeType = async (blob: Blob): Promise<string> => {
const arrayBuffer = await blob.slice(0, 4).arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
const header = uint8Array.reduce(
(acc, byte) => acc + String.fromCharCode(byte),
"",
);
if (header === "%PDF") {
return "application/pdf";
}
return "application/octet-stream";
};
const BinaryDownloadButton = ({
pdfBlob,
fileName = "document",
label,
mimeType,
}: {
pdfBlob: Blob;
mimeType?: string;
fileName?: string;
label: string;
}) => {
const downloadFile = () => {
const url = URL.createObjectURL(
new Blob([pdfBlob], mimeType ? { type: mimeType } : {}),
);
const link = document.createElement("a");
link.href = url;
link.download =
mimeType === "application/pdf" ? `${fileName}.pdf` : fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
return (
<button
className="flex items-center gap-2 px-2 py-1 text-gray-900 border border-gray-900/10 bg-clip-border shadow-sm transition-colors rounded bg-gray-50 text-sm"
onClick={downloadFile}
>
<ArrowDownToLine size={16} />
{label}
{/* Download {mimeType === "application/pdf" ? "PDF" : "File"} */}
</button>
);
};
const LabelContentPair = ({
label,
content,
}: {
label: string;
content: React.ReactNode;
}) => {
return (
<div className="flex flex-col gap-1.5 ">
<span className="uppercase text-xs font-medium text-gray-600 tracking-wide">
{label}
</span>
<span>{content}</span>
</div>
);
};
function RenderCoBinaryStream({
value,
items,
}: {
items: BinaryStreamItem[];
value: RawBinaryCoStream;
}) {
const [file, setFile] = useState<
| {
blob: Blob;
mimeType: string;
unfinishedChunks: boolean;
totalSize: number | undefined;
}
| undefined
| null
>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// load only the first chunk to get the mime type and size
getBlobFromCoStream({
items,
onlyFirstChunk: true,
})
.then((v) => {
if (v) {
setFile(v);
if (v.mimeType.includes("image")) {
// If it's an image, load the full blob
getBlobFromCoStream({
items,
}).then((s) => {
if (s) setFile(s);
});
}
}
})
.finally(() => setIsLoading(false));
}, [items]);
if (!isLoading && !file) return <div>No blob</div>;
if (isLoading) return <div>Loading...</div>;
if (!file) return <div>No blob</div>;
const { blob, mimeType } = file;
const sizeInKB = (file.totalSize || 0) / 1024;
return (
<div className="space-y-8 mt-4">
<div className="grid grid-cols-3 gap-2 max-w-3xl">
<LabelContentPair
label="Mime Type"
content={
<span className="font-mono bg-gray-100 rounded px-2 py-1 text-sm">
{mimeType || "No mime type"}
</span>
}
/>
<LabelContentPair
label="Size"
content={<span>{sizeInKB.toFixed(2)} KB</span>}
/>
<LabelContentPair
label="Download"
content={
<BinaryDownloadButton
fileName={value.id.toString()}
pdfBlob={blob}
mimeType={mimeType}
label={
mimeType === "application/pdf"
? "Download PDF"
: "Download File"
}
/>
}
/>
</div>
{mimeType === "image/png" || mimeType === "image/jpeg" ? (
<LabelContentPair
label="Preview"
content={
<div className="bg-gray-50 p-3 rounded-sm">
<RenderBlobImage blob={blob} />
</div>
}
/>
) : null}
</div>
);
}
function RenderCoStream({
value,
node,
}: {
value: RawCoStream;
node: LocalNode;
}) {
const streamPerUser = Object.keys(value.items);
const userCoIds = streamPerUser.map(
(stream) => stream.split("_session")[0],
);
return (
<div className="grid grid-cols-3 gap-2">
{userCoIds.map((id, idx) => (
<div
className="bg-gray-100 p-3 rounded-lg transition-colors overflow-hidden bg-white border hover:bg-gray-100/5 cursor-pointer shadow-sm"
key={id}
>
<AccountOrGroupPreview
coId={id as CoID<RawCoValue>}
node={node}
/>
{/* @ts-expect-error - TODO: fix types */}
{value.items[streamPerUser[idx]]?.map(
(item: CoStreamItem<JsonValue>) => (
<div>
{new Date(item.madeAt).toLocaleString()}{" "}
{JSON.stringify(item.value)}
</div>
),
)}
</div>
))}
</div>
);
}
export function CoStreamView({
value,
node,
}: {
data: JsonObject;
onNavigate: (pages: PageInfo[]) => void;
node: LocalNode;
value: RawCoStream;
}) {
// if (!value) return <div>No value</div>;
const streamType = detectCoStreamType(value);
if (streamType.type === "binary") {
if (streamType.items === undefined) {
return <div>No binary stream</div>;
}
return (
<RenderCoBinaryStream
value={value as RawBinaryCoStream}
items={streamType.items}
/>
);
}
if (streamType.type === "coStream") {
return <RenderCoStream value={value} node={node} />;
}
if (streamType.type === "unknown") return <div>Unknown stream type</div>;
return <div>Unknown stream type</div>;
}
function RenderBlobImage({ blob }: { blob: Blob }) {
const urlCreator = window.URL || window.webkitURL;
return <img src={urlCreator.createObjectURL(blob)} />;
}

View File

@@ -0,0 +1,73 @@
import { CoID, LocalNode, RawCoValue } from "cojson";
import { JsonObject } from "cojson/src/jsonValue";
import { CoMapPreview, ValueRenderer } from "./value-renderer";
import clsx from "clsx";
import { PageInfo, isCoId } from "./types";
import { ResolveIcon } from "./type-icon";
export function GridView({
data,
onNavigate,
node,
}: {
data: JsonObject;
onNavigate: (pages: PageInfo[]) => void;
node: LocalNode;
}) {
const entries = Object.entries(data);
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 p-2">
{entries.map(([key, child], childIndex) => (
<div
key={childIndex}
className={clsx(
"bg-gray-100 p-3 rounded-lg transition-colors overflow-hidden",
isCoId(child)
? "bg-white border hover:bg-gray-100/5 cursor-pointer shadow-sm"
: "bg-gray-50",
)}
onClick={() =>
isCoId(child) &&
onNavigate([
{ coId: child as CoID<RawCoValue>, name: key },
])
}
>
<h3 className="truncate">
{isCoId(child) ? (
<span className="font-medium flex justify-between">
{key}
<div className="px-2 py-1 text-xs bg-gray-100 rounded">
<ResolveIcon
coId={child as CoID<RawCoValue>}
node={node}
/>
</div>
</span>
) : (
<span>{key}</span>
)}
</h3>
<div className="mt-2 text-sm">
{isCoId(child) ? (
<CoMapPreview
coId={child as CoID<RawCoValue>}
node={node}
/>
) : (
<ValueRenderer
json={child}
onCoIDClick={(coId) => {
onNavigate([{ coId, name: key }]);
}}
compact
/>
)}
</div>
</div>
))}
</div>
);
}

View File

@@ -0,0 +1,27 @@
import { LocalNode } from "cojson";
import { Breadcrumbs } from "./breadcrumbs";
import { usePagePath } from "./use-page-path";
import { PageInfo } from "./types";
import { PageStack } from "./page-stack";
export default function CoJsonViewer({
defaultPath,
node,
}: {
defaultPath?: PageInfo[];
node: LocalNode;
}) {
const { path, addPages, goToIndex, goBack } = usePagePath(defaultPath);
return (
<div className="w-full h-screen bg-gray-100 p-4 overflow-hidden">
<Breadcrumbs path={path} onBreadcrumbClick={goToIndex} />
<PageStack
path={path}
node={node}
goBack={goBack}
addPages={addPages}
/>
</div>
);
}

View File

@@ -0,0 +1,310 @@
import React, { useState, useEffect } from "react";
import {
LocalNode,
CoID,
RawCoValue,
RawAccount,
AgentSecret,
AccountID,
cojsonInternals,
WasmCrypto,
} from "cojson";
import { createWebSocketPeer } from "cojson-transport-ws";
import { Trash2 } from "lucide-react";
import { Breadcrumbs } from "./breadcrumbs";
import { usePagePath } from "./use-page-path";
import { PageStack } from "./page-stack";
import { resolveCoValue, useResolvedCoValue } from "./use-resolve-covalue";
import clsx from "clsx";
interface Account {
id: CoID<RawAccount>;
secret: AgentSecret;
}
export default function CoJsonViewerApp() {
const [accounts, setAccounts] = useState<Account[]>(() => {
const storedAccounts = localStorage.getItem("inspectorAccounts");
return storedAccounts ? JSON.parse(storedAccounts) : [];
});
const [currentAccount, setCurrentAccount] = useState<Account | null>(() => {
const lastSelectedId = localStorage.getItem("lastSelectedAccountId");
if (lastSelectedId) {
const lastAccount = accounts.find(
(account) => account.id === lastSelectedId,
);
return lastAccount || null;
}
return null;
});
const [localNode, setLocalNode] = useState<LocalNode | null>(null);
const [coValueId, setCoValueId] = useState<CoID<RawCoValue> | "">("");
const { path, addPages, goToIndex, goBack, setPage } = usePagePath();
useEffect(() => {
localStorage.setItem("inspectorAccounts", JSON.stringify(accounts));
}, [accounts]);
useEffect(() => {
if (currentAccount) {
localStorage.setItem("lastSelectedAccountId", currentAccount.id);
} else {
localStorage.removeItem("lastSelectedAccountId");
}
}, [currentAccount]);
useEffect(() => {
if (!currentAccount) {
setLocalNode(null);
goToIndex(-1);
return;
}
WasmCrypto.create().then(async (crypto) => {
const wsPeer = createWebSocketPeer({
id: "mesh",
websocket: new WebSocket("wss://mesh.jazz.tools"),
role: "server",
});
const node = await LocalNode.withLoadedAccount({
accountID: currentAccount.id,
accountSecret: currentAccount.secret,
sessionID: cojsonInternals.newRandomSessionID(
currentAccount.id,
),
peersToLoadFrom: [wsPeer],
crypto,
migration: async () => {
console.log("Not running any migration in inspector");
},
});
setLocalNode(node);
});
}, [currentAccount, goToIndex]);
const addAccount = (id: AccountID, secret: AgentSecret) => {
const newAccount = { id, secret };
setAccounts([...accounts, newAccount]);
setCurrentAccount(newAccount);
};
const deleteCurrentAccount = () => {
if (currentAccount) {
const updatedAccounts = accounts.filter(
(account) => account.id !== currentAccount.id,
);
setAccounts(updatedAccounts);
setCurrentAccount(
updatedAccounts.length > 0 ? updatedAccounts[0] : null,
);
}
};
const handleCoValueIdSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (coValueId) {
setPage(coValueId);
}
};
return (
<div className="w-full h-screen bg-gray-100 p-4 overflow-hidden flex flex-col">
<div className="flex justify-between items-center mb-4">
<Breadcrumbs path={path} onBreadcrumbClick={goToIndex} />
<AccountSwitcher
accounts={accounts}
currentAccount={currentAccount}
setCurrentAccount={setCurrentAccount}
deleteCurrentAccount={deleteCurrentAccount}
localNode={localNode}
/>
</div>
<PageStack
path={path}
node={localNode}
goBack={goBack}
addPages={addPages}
>
{!currentAccount ? (
<AddAccountForm addAccount={addAccount} />
) : (
<form
onSubmit={handleCoValueIdSubmit}
aria-hidden={path.length !== 0}
className={clsx(
"flex flex-col justify-center items-center gap-2 h-full w-full mb-20 ",
"transition-all duration-150",
path.length > 0
? "opacity-0 -translate-y-2 scale-95"
: "opacity-100",
)}
>
<fieldset className="flex flex-col gap-2 text-sm">
<h2 className="text-3xl font-medium text-gray-950 text-center mb-4">
Jazz CoValue Inspector
</h2>
<input
className="border p-4 rounded-lg min-w-[21rem] font-mono"
placeholder="co_z1234567890abcdef123456789"
value={coValueId}
onChange={(e) =>
setCoValueId(
e.target.value as CoID<RawCoValue>,
)
}
/>
<button
type="submit"
className="bg-indigo-500 hover:bg-indigo-500/80 text-white px-4 py-2 rounded-md"
>
Inspect
</button>
<hr />
<button
type="button"
className="border inline-block px-2 py-1.5 text-black rounded"
onClick={() => {
setCoValueId(currentAccount.id);
setPage(currentAccount.id);
}}
>
Inspect My Account
</button>
</fieldset>
</form>
)}
</PageStack>
</div>
);
}
function AccountSwitcher({
accounts,
currentAccount,
setCurrentAccount,
deleteCurrentAccount,
localNode,
}: {
accounts: Account[];
currentAccount: Account | null;
setCurrentAccount: (account: Account | null) => void;
deleteCurrentAccount: () => void;
localNode: LocalNode | null;
}) {
return (
<div className="relative flex items-center gap-1">
<select
value={currentAccount?.id || "add-account"}
onChange={(e) => {
if (e.target.value === "add-account") {
setCurrentAccount(null);
} else {
const account = accounts.find(
(a) => a.id === e.target.value,
);
setCurrentAccount(account || null);
}
}}
className="p-2 px-4 bg-gray-100/50 border border-indigo-500/10 backdrop-blur-sm rounded-md text-indigo-700 appearance-none"
>
{accounts.map((account) => (
<option key={account.id} value={account.id}>
{localNode ? (
<AccountNameDisplay
accountId={account.id}
node={localNode}
/>
) : (
account.id
)}
</option>
))}
<option value="add-account">Add account</option>
</select>
{currentAccount && (
<button
onClick={deleteCurrentAccount}
className="p-3 rounded hover:bg-gray-200 transition-colors"
title="Delete Account"
>
<Trash2 size={16} className="text-gray-500" />
</button>
)}
</div>
);
}
function AddAccountForm({
addAccount,
}: {
addAccount: (id: AccountID, secret: AgentSecret) => void;
}) {
const [id, setId] = useState("");
const [secret, setSecret] = useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
addAccount(id as AccountID, secret as AgentSecret);
setId("");
setSecret("");
};
return (
<form
onSubmit={handleSubmit}
className="flex flex-col gap-2 max-w-md mx-auto h-full justify-center"
>
<h2 className="text-2xl font-medium text-gray-900 mb-3">
Add an Account to Inspect
</h2>
<input
className="border py-2 px-3 rounded-md"
placeholder="Account ID"
value={id}
onChange={(e) => setId(e.target.value)}
/>
<input
type="password"
className="border py-2 px-3 rounded-md"
placeholder="Account Secret"
value={secret}
onChange={(e) => setSecret(e.target.value)}
/>
<button
type="submit"
className="bg-indigo-500 text-white px-4 py-2 rounded-md"
>
Add Account
</button>
</form>
);
}
function AccountNameDisplay({
accountId,
node,
}: {
accountId: CoID<RawAccount>;
node: LocalNode;
}) {
const { snapshot } = useResolvedCoValue(accountId, node);
const [name, setName] = useState<string | null>(null);
useEffect(() => {
if (snapshot && typeof snapshot === "object" && "profile" in snapshot) {
const profileId = snapshot.profile as CoID<RawCoValue>;
resolveCoValue(profileId, node).then((profileResult) => {
if (
profileResult.snapshot &&
typeof profileResult.snapshot === "object" &&
"name" in profileResult.snapshot
) {
setName(profileResult.snapshot.name as string);
}
});
}
}, [snapshot, node]);
return name ? `${name} <${accountId}>` : accountId;
}

View File

@@ -0,0 +1,55 @@
import { Page } from "./page"; // Assuming you have a Page component
import { CoID, LocalNode, RawCoValue } from "cojson";
// Define the structure of a page in the path
interface PageInfo {
coId: CoID<RawCoValue>;
name?: string;
}
// Props for the PageStack component
interface PageStackProps {
path: PageInfo[];
node?: LocalNode | null;
goBack: () => void;
addPages: (pages: PageInfo[]) => void;
children?: React.ReactNode;
}
export function PageStack({
path,
node,
goBack,
addPages,
children,
}: PageStackProps) {
return (
<div className="relative mt-4 h-[calc(100vh-6rem)]">
{children && (
<div className="absolute inset-0 pb-20">{children}</div>
)}
{node &&
path.map((page, index) => (
<Page
key={`${page.coId}-${index}`}
coId={page.coId}
node={node}
name={page.name || page.coId}
onHeaderClick={goBack}
onNavigate={addPages}
isTopLevel={index === path.length - 1}
style={{
transform: `translateZ(${(index - path.length + 1) * 200}px) scale(${
1 - (path.length - index - 1) * 0.05
}) translateY(${-(index - path.length + 1) * -4}%)`,
opacity: 1 - (path.length - index - 1) * 0.05,
zIndex: index,
transitionProperty: "transform, opacity",
transitionDuration: "0.3s",
transitionTimingFunction: "ease-out",
}}
/>
))}
</div>
);
}

View File

@@ -0,0 +1,154 @@
import clsx from "clsx";
import { CoID, LocalNode, RawCoStream, RawCoValue } from "cojson";
import { useEffect, useState } from "react";
import { useResolvedCoValue } from "./use-resolve-covalue";
import { GridView } from "./grid-view";
import { PageInfo } from "./types";
import { TableView } from "./table-viewer";
import { TypeIcon } from "./type-icon";
import { CoStreamView } from "./co-stream-view";
import { AccountOrGroupPreview } from "./value-renderer";
type PageProps = {
coId: CoID<RawCoValue>;
node: LocalNode;
name: string;
onNavigate: (newPages: PageInfo[]) => void;
onHeaderClick?: () => void;
isTopLevel?: boolean;
style: React.CSSProperties;
};
export function Page({
coId,
node,
name,
onNavigate,
onHeaderClick,
style,
isTopLevel,
}: PageProps) {
const { value, snapshot, type, extendedType } = useResolvedCoValue(
coId,
node,
);
const [viewMode, setViewMode] = useState<"grid" | "table">("grid");
const supportsTableView = type === "colist" || extendedType === "record";
// Automatically switch to table view if the page is a CoMap record
useEffect(() => {
if (supportsTableView) {
setViewMode("table");
}
}, [supportsTableView]);
if (snapshot === "unavailable") {
return <div style={style}>Data unavailable</div>;
}
if (!snapshot) {
return <div style={style}></div>;
}
return (
<div
style={style}
className={clsx(
"absolute inset-0 border border-gray-900/5 bg-clip-padding bg-white rounded-xl shadow-lg p-6 animate-in",
)}
>
{!isTopLevel && (
<div
className="absolute inset-x-0 top-0 h-10"
aria-label="Back"
onClick={() => {
onHeaderClick?.();
}}
aria-hidden="true"
></div>
)}
<div className="flex justify-between items-center mb-4">
<div className="flex flex-col gap-2">
<h2 className="text-2xl font-bold flex items-start flex-col gap-1">
<span>
{name}
{typeof snapshot === "object" &&
"name" in snapshot ? (
<span className="text-gray-600 font-medium">
{" "}
{
(
snapshot as {
name: string;
}
).name
}
</span>
) : null}
</span>
</h2>
<div className="flex items-center gap-2">
<span className="text-xs text-gray-700 font-medium py-0.5 px-1 -ml-0.5 rounded bg-gray-700/5 inline-block font-mono">
{type && (
<TypeIcon
type={type}
extendedType={extendedType}
/>
)}
</span>
<span className="text-xs text-gray-700 font-medium py-0.5 px-1 -ml-0.5 rounded bg-gray-700/5 inline-block font-mono">
{coId}
</span>
</div>
</div>
{/* {supportsTableView && (
<button
onClick={toggleViewMode}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors"
>
{viewMode === "grid" ? "Table View" : "Grid View"}
</button>
)} */}
</div>
<div className="overflow-auto max-h-[calc(100%-4rem)]">
{type === "costream" ? (
<CoStreamView
data={snapshot}
onNavigate={onNavigate}
node={node}
value={value as RawCoStream}
/>
) : viewMode === "grid" ? (
<GridView
data={snapshot}
onNavigate={onNavigate}
node={node}
/>
) : (
<TableView
data={snapshot}
node={node}
onNavigate={onNavigate}
/>
)}
{/* --- */}
{extendedType !== "account" && extendedType !== "group" && (
<div className="text-xs text-gray-500 mt-4">
Owned by{" "}
<AccountOrGroupPreview
coId={value.group.id}
node={node}
showId
onClick={() => {
onNavigate([
{ coId: value.group.id, name: "owner" },
]);
}}
/>
</div>
)}
</div>
</div>
);
}

View File

@@ -0,0 +1,142 @@
import { CoID, LocalNode, RawCoValue } from "cojson";
import { JsonObject } from "cojson/src/jsonValue";
import { PageInfo } from "./types";
import { useMemo, useState } from "react";
import { ValueRenderer } from "./value-renderer";
import { LinkIcon } from "../link-icon";
import { useResolvedCoValues } from "./use-resolve-covalue";
export function TableView({
data,
node,
onNavigate,
}: {
data: JsonObject;
node: LocalNode;
onNavigate: (pages: PageInfo[]) => void;
}) {
const [visibleRowsCount, setVisibleRowsCount] = useState(10);
const [coIdArray, visibleRows] = useMemo(() => {
const coIdArray = Array.isArray(data)
? data
: Object.values(data).every(
(k) => typeof k === "string" && k.startsWith("co_"),
)
? Object.values(data).map((k) => k as CoID<RawCoValue>)
: [];
const visibleRows = coIdArray.slice(0, visibleRowsCount);
return [coIdArray, visibleRows];
}, [data, visibleRowsCount]);
const resolvedRows = useResolvedCoValues(visibleRows, node);
const hasMore = visibleRowsCount < coIdArray.length;
if (!coIdArray.length) {
return <div>No data to display</div>;
}
if (resolvedRows.length === 0) {
return <div>Loading...</div>;
}
const keys = Array.from(
new Set(
resolvedRows.flatMap((item) => Object.keys(item.snapshot || {})),
),
);
const loadMore = () => {
setVisibleRowsCount((prevVisibleRows) => prevVisibleRows + 10);
};
return (
<div>
<table className="min-w-full divide-y divide-gray-200">
<thead className="sticky top-0 border-b">
<tr>
{["", ...keys].map((key) => (
<th
key={key}
className="px-4 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 rounded"
>
{key}
</th>
))}
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{resolvedRows
.slice(0, visibleRowsCount)
.map((item, index) => (
<tr key={index}>
<td className="px-1 py-0">
<button
onClick={() =>
onNavigate([
{
coId: item.value!.id,
name: index.toString(),
},
])
}
className="px-4 py-4 whitespace-nowrap text-sm text-gray-500 hover:text-blue-500 hover:bg-gray-100 rounded"
>
<LinkIcon />
</button>
</td>
{keys.map((key) => (
<td
key={key}
className="px-4 py-4 whitespace-nowrap text-sm text-gray-500"
>
<ValueRenderer
json={
(item.snapshot as JsonObject)[
key
]
}
onCoIDClick={(coId) => {
async function handleClick() {
onNavigate([
{
coId: item.value!
.id,
name: index.toString(),
},
{
coId: coId,
name: key,
},
]);
}
handleClick();
}}
/>
</td>
))}
</tr>
))}
</tbody>
</table>
<div className="py-4 text-gray-500 flex items-center justify-between gap-2">
<span>
Showing {Math.min(visibleRowsCount, coIdArray.length)} of{" "}
{coIdArray.length}
</span>
{hasMore && (
<div className="text-center">
<button
onClick={loadMore}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Load More
</button>
</div>
)}
</div>
</div>
);
}

View File

@@ -0,0 +1,47 @@
import { CoID, LocalNode, RawCoValue } from "cojson";
import {
CoJsonType,
ExtendedCoJsonType,
useResolvedCoValue,
} from "./use-resolve-covalue";
export const TypeIcon = ({
type,
extendedType,
}: {
type: CoJsonType;
extendedType?: ExtendedCoJsonType;
}) => {
const iconMap: Record<ExtendedCoJsonType | CoJsonType, string> = {
record: "{} Record",
image: "🖼️ Image",
comap: "{} CoMap",
costream: "≋ CoStream",
colist: "☰ CoList",
account: "👤 Account",
group: "👥 Group",
};
const iconKey = extendedType || type;
const icon = iconMap[iconKey as keyof typeof iconMap];
return icon ? <span className="font-mono">{icon}</span> : null;
};
export const ResolveIcon = ({
coId,
node,
}: {
coId: CoID<RawCoValue>;
node: LocalNode;
}) => {
const { type, extendedType, snapshot } = useResolvedCoValue(coId, node);
if (snapshot === "unavailable" && !type) {
return <div className="text-gray-600 font-medium">Unavailable</div>;
}
if (!type) return <div className="whitespace-pre w-14 font-mono"> </div>;
return <TypeIcon type={type} extendedType={extendedType} />;
};

View File

@@ -0,0 +1,9 @@
import { CoID, RawCoValue } from "cojson";
export type PageInfo = {
coId: CoID<RawCoValue>;
name?: string;
};
export const isCoId = (coId: unknown): coId is CoID<RawCoValue> =>
typeof coId === "string" && coId.startsWith("co_");

View File

@@ -0,0 +1,107 @@
import { useState, useEffect, useCallback } from "react";
import { PageInfo } from "./types";
import { CoID, RawCoValue } from "cojson";
export function usePagePath(defaultPath?: PageInfo[]) {
const [path, setPath] = useState<PageInfo[]>(() => {
const hash = window.location.hash.slice(2); // Remove '#/'
if (hash) {
try {
return decodePathFromHash(hash);
} catch (e) {
console.error("Failed to parse hash:", e);
}
}
return defaultPath || [];
});
const updatePath = useCallback((newPath: PageInfo[]) => {
setPath(newPath);
const hash = encodePathToHash(newPath);
window.location.hash = `#/${hash}`;
}, []);
useEffect(() => {
const handleHashChange = () => {
const hash = window.location.hash.slice(2);
if (hash) {
try {
const newPath = decodePathFromHash(hash);
setPath(newPath);
} catch (e) {
console.error("Failed to parse hash:", e);
}
} else if (defaultPath) {
setPath(defaultPath);
}
};
window.addEventListener("hashchange", handleHashChange);
return () => window.removeEventListener("hashchange", handleHashChange);
}, [defaultPath]);
useEffect(() => {
if (
defaultPath &&
JSON.stringify(path) !== JSON.stringify(defaultPath)
) {
updatePath(defaultPath);
}
}, [defaultPath, path, updatePath]);
const addPages = useCallback(
(newPages: PageInfo[]) => {
updatePath([...path, ...newPages]);
},
[path, updatePath],
);
const goToIndex = useCallback(
(index: number) => {
updatePath(path.slice(0, index + 1));
},
[path, updatePath],
);
const setPage = useCallback(
(coId: CoID<RawCoValue>) => {
updatePath([{ coId, name: "Root" }]);
},
[updatePath],
);
const goBack = useCallback(() => {
if (path.length > 1) {
updatePath(path.slice(0, path.length - 1));
}
}, [path, updatePath]);
return {
path,
setPage,
addPages,
goToIndex,
goBack,
};
}
function encodePathToHash(path: PageInfo[]): string {
return path
.map((page) => {
if (page.name && page.name !== "Root") {
return `${page.coId}:${encodeURIComponent(page.name)}`;
}
return page.coId;
})
.join("/");
}
function decodePathFromHash(hash: string): PageInfo[] {
return hash.split("/").map((segment) => {
const [coId, encodedName] = segment.split(":");
return {
coId,
name: encodedName ? decodeURIComponent(encodedName) : undefined,
} as PageInfo;
});
}

View File

@@ -0,0 +1,152 @@
import { CoID, LocalNode, RawBinaryCoStream, RawCoValue } from "cojson";
import { useEffect, useState } from "react";
export type CoJsonType = "comap" | "costream" | "colist";
export type ExtendedCoJsonType = "image" | "record" | "account" | "group";
type JSON = string | number | boolean | null | JSON[] | { [key: string]: JSON };
type JSONObject = { [key: string]: JSON };
type ResolvedImageDefinition = {
originalSize: [number, number];
placeholderDataURL?: string;
[res: `${number}x${number}`]: RawBinaryCoStream["id"];
};
// Type guard for browser image
export const isBrowserImage = (
coValue: JSONObject,
): coValue is ResolvedImageDefinition => {
return "originalSize" in coValue && "placeholderDataURL" in coValue;
};
export type ResolvedGroup = {
readKey: string;
[key: string]: JSON;
};
export const isGroup = (coValue: JSONObject): coValue is ResolvedGroup => {
return "readKey" in coValue;
};
export type ResolvedAccount = {
profile: {
name: string;
};
[key: string]: JSON;
};
export const isAccount = (coValue: JSONObject): coValue is ResolvedAccount => {
return isGroup(coValue) && "profile" in coValue;
};
export async function resolveCoValue(
coValueId: CoID<RawCoValue>,
node: LocalNode,
): Promise<
| {
value: RawCoValue;
snapshot: JSONObject;
type: CoJsonType | null;
extendedType: ExtendedCoJsonType | undefined;
}
| {
value: undefined;
snapshot: "unavailable";
type: null;
extendedType: undefined;
}
> {
const value = await node.load(coValueId);
if (value === "unavailable") {
return {
value: undefined,
snapshot: "unavailable",
type: null,
extendedType: undefined,
};
}
const snapshot = value.toJSON() as JSONObject;
const type = value.type as CoJsonType;
// Determine extended type
let extendedType: ExtendedCoJsonType | undefined;
if (type === "comap") {
if (isBrowserImage(snapshot)) {
extendedType = "image";
} else if (isAccount(snapshot)) {
extendedType = "account";
} else if (isGroup(snapshot)) {
extendedType = "group";
} else {
// This check is a bit of a hack
// There might be a better way to do this
const children = Object.values(snapshot).slice(0, 10);
if (
children.every(
(c) => typeof c === "string" && c.startsWith("co_"),
) &&
children.length > 3
) {
extendedType = "record";
}
}
}
return {
value,
snapshot,
type,
extendedType,
};
}
export function useResolvedCoValue(
coValueId: CoID<RawCoValue>,
node: LocalNode,
) {
const [result, setResult] =
useState<Awaited<ReturnType<typeof resolveCoValue>>>();
useEffect(() => {
resolveCoValue(coValueId, node).then(setResult);
}, [coValueId, node]);
return (
result || {
value: undefined,
snapshot: undefined,
type: undefined,
extendedType: undefined,
}
);
}
export function useResolvedCoValues(
coValueIds: CoID<RawCoValue>[],
node: LocalNode,
) {
const [results, setResults] = useState<
Awaited<ReturnType<typeof resolveCoValue>>[]
>([]);
useEffect(() => {
console.log("RETECHING", coValueIds);
const fetchResults = async () => {
if (coValueIds.length === 0) return;
const resolvedValues = await Promise.all(
coValueIds.map((coValueId) => resolveCoValue(coValueId, node)),
);
console.log({ resolvedValues });
setResults(resolvedValues);
};
fetchResults();
}, [coValueIds, node]);
return results;
}

View File

@@ -0,0 +1,248 @@
import clsx from "clsx";
import { CoID, JsonValue, LocalNode, RawCoValue } from "cojson";
import { LinkIcon } from "../link-icon";
import {
isBrowserImage,
resolveCoValue,
useResolvedCoValue,
} from "./use-resolve-covalue";
import React, { useEffect, useState } from "react";
// Is there a chance we can pass the actual CoValue here?
export function ValueRenderer({
json,
compact,
onCoIDClick,
}: {
json: JsonValue | undefined;
compact?: boolean;
onCoIDClick?: (childNode: CoID<RawCoValue>) => void;
}) {
if (typeof json === "undefined" || json === undefined) {
return <span className="text-gray-400">undefined</span>;
}
if (json === null) {
return <span className="text-gray-400">null</span>;
}
if (typeof json === "string" && json.startsWith("co_")) {
return (
<span
className={clsx(
"inline-flex gap-1 items-center",
onCoIDClick &&
"text-blue-500 cursor-pointer hover:underline",
)}
onClick={() => {
onCoIDClick?.(json as CoID<RawCoValue>);
}}
>
{json}
{onCoIDClick && <LinkIcon />}
</span>
);
}
if (typeof json === "string") {
return (
<span className="text-green-900 font-mono">
{/* <span className="select-none opacity-70">{'"'}</span> */}
{json}
{/* <span className="select-none opacity-70">{'"'}</span> */}
</span>
);
}
if (typeof json === "number") {
return <span className="text-purple-500">{json}</span>;
}
if (typeof json === "boolean") {
return (
<span
className={clsx(
json
? "text-green-700 bg-green-700/5"
: "text-amber-700 bg-amber-500/5",
"font-mono",
"inline-block px-1 py-0.5 rounded",
)}
>
{json.toString()}
</span>
);
}
if (Array.isArray(json)) {
return (
<span title={JSON.stringify(json)}>
Array <span className="text-gray-500">({json.length})</span>
</span>
);
}
if (typeof json === "object") {
return (
<span
title={JSON.stringify(json, null, 2)}
className="inline-block max-w-64 truncate"
>
{compact ? (
<span>
Object{" "}
<span className="text-gray-500">
({Object.keys(json).length})
</span>
</span>
) : (
JSON.stringify(json, null, 2)
)}
</span>
);
}
return <span>{String(json)}</span>;
}
export const CoMapPreview = ({
coId,
node,
limit = 6,
}: {
coId: CoID<RawCoValue>;
node: LocalNode;
limit?: number;
}) => {
const { value, snapshot, type, extendedType } = useResolvedCoValue(
coId,
node,
);
if (!snapshot) {
return (
<div className="rounded bg-gray-100 animate-pulse whitespace-pre w-24">
{" "}
</div>
);
}
if (snapshot === "unavailable" && !value) {
return <div className="text-gray-500">Unavailable</div>;
}
if (extendedType === "image" && isBrowserImage(snapshot)) {
return (
<div>
<img
src={snapshot.placeholderDataURL}
className="size-8 border-2 border-white drop-shadow-md my-2"
/>
<span className="text-gray-500 text-sm">
{snapshot.originalSize[0]} x {snapshot.originalSize[1]}
</span>
{/* <CoMapPreview coId={value[]} node={node} /> */}
{/* <ProgressiveImg image={value}>
{({ src }) => <img src={src} className={clsx("w-full")} />}
</ProgressiveImg> */}
</div>
);
}
if (extendedType === "record") {
return (
<div>
Record{" "}
<span className="text-gray-500">
({Object.keys(snapshot).length})
</span>
</div>
);
}
if (type === "colist") {
return (
<div>
List{" "}
<span className="text-gray-500">
({(snapshot as unknown as []).length})
</span>
</div>
);
}
return (
<div className="text-sm flex flex-col gap-2 items-start">
<div className="grid grid-cols-[auto_1fr] gap-2">
{Object.entries(snapshot)
.slice(0, limit)
.map(([key, value]) => (
<React.Fragment key={key}>
<span className="font-medium">{key}: </span>
<span>
<ValueRenderer json={value} />
</span>
</React.Fragment>
))}
</div>
{Object.entries(snapshot).length > limit && (
<div className="text-left text-xs text-gray-500 mt-2">
{Object.entries(snapshot).length - limit} more
</div>
)}
</div>
);
};
export function AccountOrGroupPreview({
coId,
node,
showId = false,
onClick,
}: {
coId: CoID<RawCoValue>;
node: LocalNode;
showId?: boolean;
onClick?: (name?: string) => void;
}) {
const { snapshot, extendedType } = useResolvedCoValue(coId, node);
const [name, setName] = useState<string | null>(null);
useEffect(() => {
if (extendedType === "account") {
resolveCoValue(
(snapshot as unknown as { profile: CoID<RawCoValue> }).profile,
node,
).then(({ snapshot }) => {
if (
typeof snapshot === "object" &&
"name" in snapshot &&
typeof snapshot.name === "string"
) {
setName(snapshot.name);
}
});
}
}, [snapshot, node, extendedType]);
if (!snapshot) return <span>Loading...</span>;
if (extendedType !== "account" && extendedType !== "group") {
return <span>CoID is not an account or group</span>;
}
const displayName =
extendedType === "account" ? name || "Account" : "Group";
const displayText = showId ? `${displayName} (${coId})` : displayName;
const props = onClick
? {
onClick: () => onClick(displayName),
className: "text-blue-500 cursor-pointer hover:underline",
}
: {
className: "text-gray-500",
};
return <span {...props}>{displayText}</span>;
}

1
examples/inspector/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,75 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}

View File

@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,16 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
build: {
minify: false
}
})

View File

@@ -5,6 +5,7 @@ module.exports = {
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'prettier'
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',

View File

@@ -0,0 +1,9 @@
/** @type {import("prettier").Config} */
const config = {
trailingComma: "all",
tabWidth: 4,
semi: true,
singleQuote: false,
};
export default config;

707
examples/pets/CHANGELOG.md Normal file
View File

@@ -0,0 +1,707 @@
# jazz-example-pets
## 0.0.94
### Patch Changes
- jazz-react@0.7.29
- jazz-tools@0.7.29
- jazz-browser-media-images@0.7.29
## 0.0.93
### Patch Changes
- jazz-react@0.7.28
- jazz-tools@0.7.28
- jazz-browser-media-images@0.7.28
## 0.0.92
### Patch Changes
- jazz-browser-media-images@0.7.27
- jazz-react@0.7.27
## 0.0.91
### Patch Changes
- Updated dependencies
- jazz-react@0.7.26
- jazz-tools@0.7.26
- jazz-browser-media-images@0.7.26
## 0.0.90
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.25
- jazz-browser-media-images@0.7.25
- jazz-react@0.7.25
## 0.0.89
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.24
- jazz-browser-media-images@0.7.24
- jazz-react@0.7.24
## 0.0.88
### Patch Changes
- Updated dependencies
- jazz-react@0.7.23
- jazz-tools@0.7.23
- jazz-browser-media-images@0.7.23
## 0.0.87
### Patch Changes
- jazz-browser-media-images@0.7.22
- jazz-react@0.7.22
## 0.0.86
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.21
- jazz-browser-media-images@0.7.21
- jazz-react@0.7.21
## 0.0.85
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.20
- jazz-browser-media-images@0.7.20
- jazz-react@0.7.20
## 0.0.84
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.19
- jazz-browser-media-images@0.7.19
- jazz-react@0.7.19
## 0.0.83
### Patch Changes
- jazz-react@0.7.18
- jazz-tools@0.7.18
- jazz-browser-media-images@0.7.18
## 0.0.82
### Patch Changes
- jazz-react@0.7.17
- jazz-tools@0.7.17
- jazz-browser-media-images@0.7.17
## 0.0.81
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.16
- jazz-browser-media-images@0.7.16
- jazz-react@0.7.16
## 0.0.80
### Patch Changes
- Updated dependencies
- jazz-react@0.7.15
## 0.0.79
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.14
- jazz-react@0.7.14
- jazz-browser-media-images@0.7.14
## 0.0.78
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.13
- jazz-browser-media-images@0.7.13
- jazz-react@0.7.13
## 0.0.77
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.12
- jazz-browser-media-images@0.7.12
- jazz-react@0.7.12
## 0.0.76
### Patch Changes
- jazz-react@0.7.11
- jazz-tools@0.7.11
- jazz-browser-media-images@0.7.11
## 0.0.75
### Patch Changes
- jazz-react@0.7.10
- jazz-tools@0.7.10
- jazz-browser-media-images@0.7.10
## 0.0.74
### Patch Changes
- jazz-react@0.7.9
- jazz-tools@0.7.9
- jazz-browser-media-images@0.7.9
## 0.0.73
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
- jazz-browser-media-images@0.7.8
- jazz-react@0.7.8
## 0.0.72
### Patch Changes
- Updated dependencies [9fdc91c]
- jazz-react@0.7.7
## 0.0.71
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
- jazz-browser-media-images@0.7.6
- jazz-react@0.7.6
## 0.0.70
### Patch Changes
- Updated dependencies
- jazz-react@0.7.5
- jazz-browser-media-images@0.7.5
## 0.0.69
### Patch Changes
- Updated dependencies
- jazz-react@0.7.4
## 0.0.68
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.3
- jazz-browser-media-images@0.7.3
- jazz-react@0.7.3
## 0.0.67
### Patch Changes
- Updated dependencies
- jazz-react@0.7.2
## 0.0.66
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.1
- jazz-browser-media-images@0.7.1
- jazz-react@0.7.1
## 0.0.65
### Patch Changes
- Updated dependencies [8636319]
- Updated dependencies [8636319]
- Updated dependencies [1a35307]
- Updated dependencies [96c494f]
- Updated dependencies [59c18c3]
- Updated dependencies [19f52b7]
- Updated dependencies [8636319]
- Updated dependencies [1a35307]
- Updated dependencies [d8fe2b1]
- Updated dependencies [19004b4]
- Updated dependencies [a78f168]
- Updated dependencies [52675c9]
- Updated dependencies [129e2c1]
- Updated dependencies [6d49e9b]
- Updated dependencies [1cfa279]
- Updated dependencies [704af7d]
- Updated dependencies [e97f730]
- Updated dependencies [460478f]
- Updated dependencies [6b0418f]
- Updated dependencies [e299c3e]
- Updated dependencies [ed5643a]
- Updated dependencies [bde684f]
- Updated dependencies [c4151fc]
- Updated dependencies [63374cc]
- Updated dependencies [01ac646]
- Updated dependencies [a5e68a4]
- Updated dependencies [8636319]
- Updated dependencies [952982e]
- Updated dependencies [1a35307]
- Updated dependencies [5fa277c]
- Updated dependencies [60d5ca2]
- Updated dependencies [21771c4]
- Updated dependencies [77c2b56]
- Updated dependencies [63374cc]
- Updated dependencies [d2e03ff]
- Updated dependencies [354bdcd]
- Updated dependencies [ece35b3]
- Updated dependencies [60d5ca2]
- Updated dependencies [69ac514]
- Updated dependencies [f8a5c46]
- Updated dependencies [f0f6f1b]
- Updated dependencies [e5eed5b]
- Updated dependencies [1a44f87]
- Updated dependencies [627d895]
- Updated dependencies [1200aae]
- Updated dependencies [63374cc]
- Updated dependencies [ece35b3]
- Updated dependencies [38d4410]
- Updated dependencies [85d2b62]
- Updated dependencies [fd86c11]
- Updated dependencies [52675c9]
- jazz-tools@0.7.0
- jazz-browser-media-images@0.7.0
- jazz-react@0.7.0
## 0.0.65-alpha.42
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.42
- jazz-browser-media-images@0.7.0-alpha.40
- jazz-react@0.7.0-alpha.42
## 0.0.65-alpha.41
### Patch Changes
- jazz-tools@0.7.0-alpha.41
- jazz-browser-media-images@0.7.0-alpha.39
- jazz-react@0.7.0-alpha.41
## 0.0.65-alpha.40
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.40
## 0.0.65-alpha.39
### Patch Changes
- jazz-react@0.7.0-alpha.39
- jazz-tools@0.7.0-alpha.39
- jazz-browser-media-images@0.7.0-alpha.38
## 0.0.65-alpha.38
### Patch Changes
- Updated dependencies
- Updated dependencies
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.38
- jazz-react@0.7.0-alpha.38
- jazz-browser-media-images@0.7.0-alpha.37
## 0.0.65-alpha.37
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.37
- jazz-browser-media-images@0.7.0-alpha.36
- jazz-tools@0.7.0-alpha.37
## 0.0.65-alpha.36
### Patch Changes
- Updated dependencies [1a35307]
- Updated dependencies [1a35307]
- Updated dependencies [6b0418f]
- Updated dependencies [1a35307]
- jazz-tools@0.7.0-alpha.36
- jazz-react@0.7.0-alpha.36
- jazz-browser-media-images@0.7.0-alpha.35
## 0.0.65-alpha.35
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.35
- jazz-react@0.7.0-alpha.35
- jazz-browser-media-images@0.7.0-alpha.34
## 0.0.65-alpha.34
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.34
- jazz-browser-media-images@0.7.0-alpha.33
- jazz-react@0.7.0-alpha.34
## 0.0.65-alpha.33
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.33
## 0.0.65-alpha.32
### Patch Changes
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.32
- jazz-react@0.7.0-alpha.32
- jazz-browser-media-images@0.7.0-alpha.32
## 0.0.65-alpha.31
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.31
- jazz-browser-media-images@0.7.0-alpha.31
- jazz-react@0.7.0-alpha.31
## 0.0.65-alpha.30
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.30
- jazz-browser-media-images@0.7.0-alpha.30
- jazz-react@0.7.0-alpha.30
## 0.0.65-alpha.29
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.29
- jazz-browser-media-images@0.7.0-alpha.29
- jazz-react@0.7.0-alpha.29
## 0.0.65-alpha.28
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.28
- jazz-browser-media-images@0.7.0-alpha.28
- jazz-react@0.7.0-alpha.28
## 0.0.65-alpha.27
### Patch Changes
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.27
- jazz-browser-media-images@0.7.0-alpha.27
- jazz-react@0.7.0-alpha.27
## 0.0.65-alpha.26
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.26
- jazz-browser-media-images@0.7.0-alpha.26
- jazz-react@0.7.0-alpha.26
## 0.0.65-alpha.25
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.25
- jazz-browser-media-images@0.7.0-alpha.25
- jazz-react@0.7.0-alpha.25
## 0.0.65-alpha.24
### Patch Changes
- Updated dependencies
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.24
- jazz-browser-media-images@0.7.0-alpha.24
- jazz-react@0.7.0-alpha.24
## 0.0.65-alpha.23
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.23
- jazz-browser-media-images@0.7.0-alpha.23
- jazz-react@0.7.0-alpha.23
## 0.0.65-alpha.22
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.22
- jazz-browser-media-images@0.7.0-alpha.22
- jazz-react@0.7.0-alpha.22
## 0.0.65-alpha.21
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.21
- jazz-tools@0.7.0-alpha.21
- jazz-browser-media-images@0.7.0-alpha.21
## 0.0.65-alpha.20
### Patch Changes
- Updated dependencies
- Updated dependencies
- jazz-react@0.7.0-alpha.20
- jazz-tools@0.7.0-alpha.20
- jazz-browser-media-images@0.7.0-alpha.20
## 0.0.65-alpha.19
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.19
- jazz-browser-media-images@0.7.0-alpha.19
- jazz-react@0.7.0-alpha.19
## 0.0.65-alpha.18
### Patch Changes
- jazz-browser-media-images@0.7.0-alpha.18
- jazz-react@0.7.0-alpha.18
## 0.0.65-alpha.17
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.17
- jazz-browser-media-images@0.7.0-alpha.17
- jazz-react@0.7.0-alpha.17
## 0.0.65-alpha.16
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.16
- jazz-browser-media-images@0.7.0-alpha.16
- jazz-react@0.7.0-alpha.16
## 0.0.65-alpha.15
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.15
- jazz-browser-media-images@0.7.0-alpha.15
- jazz-react@0.7.0-alpha.15
## 0.0.65-alpha.14
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.14
- jazz-browser-media-images@0.7.0-alpha.14
- jazz-react@0.7.0-alpha.14
## 0.0.65-alpha.13
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.13
- jazz-browser-media-images@0.7.0-alpha.13
- jazz-react@0.7.0-alpha.13
## 0.0.65-alpha.12
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.12
- jazz-react@0.7.0-alpha.12
- jazz-tools@0.7.0-alpha.12
## 0.0.65-alpha.11
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.11
- jazz-react@0.7.0-alpha.11
- jazz-tools@0.7.0-alpha.11
## 0.0.65-alpha.10
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.10
- jazz-react@0.7.0-alpha.10
- jazz-tools@0.7.0-alpha.10
## 0.0.65-alpha.9
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.9
- jazz-react@0.7.0-alpha.9
- jazz-tools@0.7.0-alpha.9
## 0.0.65-alpha.8
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.8
- jazz-react@0.7.0-alpha.8
- jazz-tools@0.7.0-alpha.8
## 0.0.65-alpha.7
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.7
- jazz-react@0.7.0-alpha.7
- jazz-tools@0.7.0-alpha.7
## 0.0.65-alpha.6
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.6
- jazz-react@0.7.0-alpha.6
- jazz-tools@0.7.0-alpha.6
## 0.0.65-alpha.5
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.5
- jazz-react@0.7.0-alpha.5
- jazz-tools@0.7.0-alpha.5
## 0.0.65-alpha.4
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.4
- jazz-browser-media-images@0.7.0-alpha.4
- jazz-react@0.7.0-alpha.4
## 0.0.65-alpha.3
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.3
- jazz-browser-media-images@0.7.0-alpha.3
- jazz-react@0.7.0-alpha.3
## 0.0.65-alpha.2
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.2
- jazz-react@0.7.0-alpha.2
- jazz-tools@0.7.0-alpha.2
## 0.0.65-alpha.1
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.1
- jazz-react@0.7.0-alpha.1
- jazz-tools@0.7.0-alpha.1
## 0.0.65-alpha.0
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.7.0-alpha.0
- jazz-react@0.7.0-alpha.0
- jazz-tools@0.7.0-alpha.0
## 0.0.64
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.6.0
## 0.0.63
### Patch Changes
- Updated dependencies
- jazz-browser-media-images@0.5.0
- jazz-react@0.5.0
- jazz-react-auth-local@0.4.16

View File

@@ -1,58 +1,35 @@
# Jazz Todo List Example
# Jazz Rate-My-Pet List Example
Live version: https://example-todo.jazz.tools
Live version: https://example-pets.jazz.tools
## Installing & running the example locally
Start by checking out just the example app to a folder:
(this requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation))
Start by checking out `jazz`
```bash
npx degit gardencmp/jazz/examples/todo jazz-example-todo
cd jazz-example-todo
git clone https://github.com/gardencmp/jazz.git
cd jazz/examples/pets
pnpm pack --pack-destination /tmp
mkdir -p ~/jazz-examples/pets # or any other directory
tar -xf /tmp/jazz-example-pets-* --strip-components 1 -C ~/jazz-examples/pets
cd ~/jazz-examples/pets
```
(This ensures that you have the example app without git history or our multi-package monorepo)
This ensures that you have the example app without git history and independent of the Jazz multi-package monorepo.
Install dependencies:
```bash
npm install
pnpm install
```
Start the dev server:
```bash
npm run dev
pnpm dev
```
## Structure
- [`src/basicComponents`](./src/basicComponents) contains simple components to build the UI, unrelated to Jazz (powered by [shadcn/ui](https://ui.shadcn.com))
- [`src/components`](./src/components/) contains helper components that do contain Jazz-specific logic, but are not super relevant to understand the basics of Jazz and CoJSON
- [`src/0_main.tsx`](./src/0_main.tsx), [`src/1_types.ts`](./src/1_types.ts), [`src/2_App.tsx`](./src/2_App.tsx), [`src/3_TodoTable.tsx`](./src/3_TodoTable.tsx), [`src/router.ts`](./src/router.ts) - the main files for this example, see the walkthrough below
## Walkthrough
### Main parts
- The top-level provider `<WithJazz/>`: [`src/0_main.tsx`](./src/0_main.tsx)
- Defining the data model with CoJSON: [`src/1_types.ts`](./src/1_types.ts)
- Creating todo projects & routing in `<App/>`: [`src/2_App.tsx`](./src/2_App.tsx)
- Reactively rendering a todo project as a table, adding and editing tasks: [`src/3_TodoTable.tsx`](./src/3_TodoTable.tsx)
### Helpers
- Getting user profiles in `<NameBadge/>`: [`src/components/NameBadge.tsx`](./src/components/NameBadge.tsx)
- (not yet commented) Creating invite links/QR codes with `<InviteButton/>`: [`src/components/InviteButton.tsx`](./src/components/InviteButton.tsx)
- (not yet commented) `location.hash`-based routing and accepting invite links with `useSimpleHashRouterThatAcceptsInvites()` in [`src/router.ts`](./src/router.ts)
This is the whole Todo List app!
## Questions / problems / feedback
If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or open an issue or PR to fix something that seems wrong.
@@ -62,4 +39,4 @@ If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or
By default, the example app uses [Jazz Global Mesh](https://jazz.tools/mesh) (`wss://sync.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running `npx cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?sync=ws://localhost:4200`), or by setting the `sync` parameter of the `<WithJazz>` provider component in [./src/0_main.tsx](./src/0_main.tsx).
You can also run a local sync server by running `npx cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?sync=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/2_main.tsx](./src/2_main.tsx).

View File

@@ -8,6 +8,6 @@
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/0_main.tsx"></script>
<script type="module" src="/src/2_main.tsx"></script>
</body>
</html>

View File

@@ -1,9 +1,9 @@
job "example-todo$BRANCH_SUFFIX" {
job "example-pets$BRANCH_SUFFIX" {
region = "global"
datacenters = ["*"]
group "static" {
count = 8
count = 4
network {
port "http" {
@@ -41,7 +41,7 @@ job "example-todo$BRANCH_SUFFIX" {
service {
tags = ["public"]
name = "example-todo$BRANCH_SUFFIX"
name = "example-pets$BRANCH_SUFFIX"
port = "http"
provider = "consul"
}

View File

@@ -1,14 +1,19 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.2",
"version": "0.0.94",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"format": "prettier --write './src/**/*.{ts,tsx}'",
"preview": "vite preview"
},
"lint-staged": {
"*.{ts,tsx}": "eslint --fix",
"*.{js,jsx,mdx,json}": "prettier --write"
},
"dependencies": {
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-slot": "^1.0.2",
@@ -16,16 +21,18 @@
"@types/qrcode": "^1.5.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "^0.1.13",
"jazz-react-auth-local": "^0.1.13",
"jazz-browser-media-images": "workspace:*",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.16.0",
"react-router-dom": "^6.16.0",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"uniqolor": "^1.1.0",
"use-debounce": "^9.0.4"
"uniqolor": "^1.1.0"
},
"devDependencies": {
"@types/react": "^18.2.15",

View File

@@ -1,38 +0,0 @@
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import { WithJazz } from "jazz-react";
import { LocalAuth } from "jazz-react-auth-local";
import { ThemeProvider, TitleAndLogo } from "./basicComponents/index.ts";
import { PrettyAuthUI } from "./components/Auth.tsx";
import App from "./2_App.tsx";
/** Walkthrough: The top-level provider `<WithJazz/>`
*
* This shows how to use the top-level provider `<WithJazz/>`,
* which provides the rest of the app with a `LocalNode` (used through `useJazz` later),
* based on `LocalAuth` that uses PassKeys (aka WebAuthn) to store a user's account secret
* - no backend needed. */
const appName = "Jazz Rate My Pet Example";
const auth = LocalAuth({
appName,
Component: PrettyAuthUI,
});
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ThemeProvider>
<TitleAndLogo name={appName} />
<WithJazz auth={auth}>
<App />
</WithJazz>
</ThemeProvider>
</React.StrictMode>
);
/** Walkthrough: Continue with ./1_types.ts */

View File

@@ -0,0 +1,59 @@
import {
Account,
CoList,
CoMap,
CoStream,
ImageDefinition,
Profile,
co,
} from "jazz-tools";
/** Walkthrough: Defining the data model with CoJSON
*
* Here, we define our main data model of TODO
*
* TODO
**/
export const ReactionTypes = [
"aww",
"love",
"haha",
"wow",
"tiny",
"chonkers",
] as const;
export type ReactionType = (typeof ReactionTypes)[number];
export class PetReactions extends CoStream.Of(co.json<ReactionType>()) {}
export class PetPost extends CoMap {
name = co.string;
image = co.ref(ImageDefinition);
reactions = co.ref(PetReactions);
}
export class ListOfPosts extends CoList.Of(co.ref(PetPost)) {}
export class PetAccountRoot extends CoMap {
posts = co.ref(ListOfPosts);
}
export class PetAccount extends Account {
profile = co.ref(Profile);
root = co.ref(PetAccountRoot);
migrate(this: PetAccount, creationProps?: { name: string }) {
super.migrate(creationProps);
if (!this._refs.root) {
this.root = PetAccountRoot.create(
{
posts: ListOfPosts.create([], { owner: this }),
},
{ owner: this },
);
}
}
}
/** Walkthrough: Continue with ./2_App.tsx */

View File

@@ -1,27 +0,0 @@
import { CoMap, CoID, BinaryCoStream, CoStream } from "cojson";
/** Walkthrough: Defining the data model with CoJSON
*
* Here, we define our main data model of TODO
*
* TODO
**/
export type PetPost = CoMap<{
name: string;
image: CoID<BinaryCoStream>;
reactions: CoID<PetReactions>;
}>;
export type ReactionType =
| "aww"
| "love"
| "haha"
| "wow"
| "tiny"
| "chonkers"
| "good";
export type PetReactions = CoStream<ReactionType>;
/** Walkthrough: Continue with ./2_App.tsx */

View File

@@ -1,50 +0,0 @@
import { useCallback } from "react";
import { useJazz } from "jazz-react";
import { PetPost } from "./1_types";
import { Button } from "./basicComponents";
import { useSimpleHashRouterThatAcceptsInvites } from "./router";
import { PetPostUI } from "./4_PetPostUI";
import { CreatePetPostForm } from "./4_CreatePetPostForm";
/** Walkthrough: Creating pet posts & routing in `<App/>`
*
* <App> is the main app component, handling client-side routing based
* on the CoValue ID (CoID) of our PetPost, stored in the URL hash
* - which can also contain invite links.
*/
export default function App() {
// A `LocalNode` represents a local view of loaded & created CoValues.
// It is associated with a current user account, which will determine
// access rights to CoValues. We get it from the top-level provider `<WithJazz/>`.
const { localNode, logOut } = useJazz();
// This sets up routing and accepting invites, skip for now
const [currentPetPostID, navigateToPetPostID] =
useSimpleHashRouterThatAcceptsInvites<PetPost>(localNode);
return (
<div className="flex flex-col h-full items-center justify-start gap-10 pt-10 pb-10 px-5">
{currentPetPostID ? (
<PetPostUI petPostID={currentPetPostID} />
) : (
<CreatePetPostForm onCreate={navigateToPetPostID} />
)}
<Button
onClick={() => {
navigateToPetPostID(undefined);
logOut();
}}
variant="outline"
>
Log Out
</Button>
</div>
);
}
/** Walkthrough: continue with ./3_TodoTable.tsx */

View File

@@ -0,0 +1,124 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { Link, RouterProvider, createHashRouter } from "react-router-dom";
import "./index.css";
import { createJazzReactContext, PasskeyAuth } from "jazz-react";
import {
Button,
ThemeProvider,
TitleAndLogo,
} from "./basicComponents/index.ts";
import { PrettyAuthUI } from "./components/Auth.tsx";
import { NewPetPostForm } from "./3_NewPetPostForm.tsx";
import { RatePetPostUI } from "./4_RatePetPostUI.tsx";
import { PetAccount, PetPost } from "./1_schema.ts";
/** Walkthrough: The top-level provider `<WithJazz/>`
*
* This shows how to use the top-level provider `<WithJazz/>`,
* which provides the rest of the app with a `LocalNode` (used through `useJazz` later),
* based on `LocalAuth` that uses PassKeys (aka WebAuthn) to store a user's account secret
* - no backend needed. */
const appName = "Jazz Rate My Pet Example";
const auth = PasskeyAuth<PetAccount>({
appName,
Component: PrettyAuthUI,
accountSchema: PetAccount,
});
const Jazz = createJazzReactContext({
auth,
peer: "wss://mesh.jazz.tools/?key=you@example.com",
});
// eslint-disable-next-line react-refresh/only-export-components
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ThemeProvider>
<TitleAndLogo name={appName} />
<div className="flex flex-col h-full items-center justify-start gap-10 pt-10 pb-10 px-5">
<Jazz.Provider loading={<div>Loading</div>}>
<App />
</Jazz.Provider>
</div>
</ThemeProvider>
</React.StrictMode>,
);
/** Walkthrough: Creating pet posts & routing in `<App/>`
*
* <App> is the main app component, handling client-side routing based
* on the CoValue ID (CoID) of our PetPost, stored in the URL hash
* - which can also contain invite links.
*/
export default function App() {
const { logOut } = useAccount();
const router = createHashRouter([
{
path: "/",
element: <PostOverview />,
},
{
path: "/new",
element: <NewPetPostForm />,
},
{
path: "/pet/:petPostId",
element: <RatePetPostUI />,
},
{
path: "/invite/*",
element: <p>Accepting invite...</p>,
},
]);
useAcceptInvite({
invitedObjectSchema: PetPost,
onAccept: (petPostID) => router.navigate("/pet/" + petPostID),
});
return (
<>
<RouterProvider router={router} />
<Button
onClick={() => router.navigate("/").then(logOut)}
variant="outline"
>
Log Out
</Button>
</>
);
}
export function PostOverview() {
const { me } = useAccount();
const myPosts = me.root?.posts;
return (
<>
{myPosts?.length ? (
<>
<h1>My posts</h1>
{myPosts.map(
(post) =>
post && (
<Link key={post.id} to={"/pet/" + post.id}>
{post.name}
</Link>
),
)}
</>
) : undefined}
<Link to="/new">New post</Link>
</>
);
}

View File

@@ -0,0 +1,106 @@
import { ChangeEvent, useCallback, useState } from "react";
import { useNavigate } from "react-router";
import { createImage } from "jazz-browser-media-images";
import { PetPost, PetReactions } from "./1_schema";
import { Input, Button } from "./basicComponents";
import { useAccount, useCoState } from "./2_main";
import { CoMap, Group, ID, ImageDefinition, co } from "jazz-tools";
import { ProgressiveImg } from "jazz-react";
/** Walkthrough: TODO
*/
class PartialPetPost extends CoMap {
name = co.string;
image = co.ref(ImageDefinition, { optional: true });
reactions = co.ref(PetReactions);
}
export function NewPetPostForm() {
const { me } = useAccount();
const navigate = useNavigate();
const [newPostId, setNewPostId] = useState<ID<PartialPetPost> | undefined>(
undefined,
);
const newPetPost = useCoState(PartialPetPost, newPostId);
const onChangeName = useCallback(
(name: string) => {
if (newPetPost) {
newPetPost.name = name;
} else {
const petPostGroup = Group.create({ owner: me });
const petPost = PartialPetPost.create(
{
name,
reactions: PetReactions.create([], { owner: me }),
},
{ owner: petPostGroup },
);
setNewPostId(petPost.id);
}
},
[me, newPetPost],
);
const onImageSelected = useCallback(
async (event: ChangeEvent<HTMLInputElement>) => {
if (!newPetPost || !event.target.files) return;
const image = await createImage(event.target.files[0], {
owner: newPetPost._owner,
});
newPetPost.image = image;
},
[newPetPost],
);
const onSubmit = useCallback(() => {
if (!newPetPost) return;
const myPosts = me.root?.posts;
if (!myPosts) {
throw new Error("No posts list found");
}
myPosts.push(newPetPost as PetPost);
navigate("/pet/" + newPetPost.id);
}, [me.root?.posts, newPetPost, navigate]);
return (
<div className="flex flex-col gap-10">
<p>Share your pet with friends!</p>
<Input
type="text"
placeholder="Pet Name"
className="text-3xl py-6"
onChange={(event) => onChangeName(event.target.value)}
value={newPetPost?.name || ""}
/>
{newPetPost?.image ? (
<ProgressiveImg image={newPetPost.image}>
{({ src }) => (
<img className="w-80 max-w-full rounded" src={src} />
)}
</ProgressiveImg>
) : (
<Input
type="file"
disabled={!newPetPost?.name}
onChange={onImageSelected}
/>
)}
{newPetPost?.name && newPetPost?.image && (
<Button onClick={onSubmit}>Submit Post</Button>
)}
</div>
);
}

View File

@@ -1,103 +0,0 @@
import { useCallback, useState } from "react";
import { BinaryCoStream, CoID } from "cojson";
import {
useBinaryStream,
useJazz,
useTelepathicState,
} from "jazz-react";
import { PetPost, PetReactions, ReactionType } from "./1_types";
import {
Input,
Button,
} from "./basicComponents";
import { InviteButton } from "./components/InviteButton";
import { NameBadge } from "./components/NameBadge";
import { useDebouncedCallback } from "use-debounce";
import { createBinaryStreamHandler } from "jazz-react";
/** Walkthrough: TODO
*/
export function CreatePetPostForm({
onCreate,
}: {
onCreate: (id: CoID<PetPost>) => void;
}) {
const { localNode } = useJazz();
const [creatingPostId, setCreatingPostId] = useState<
CoID<PetPost> | undefined
>(undefined);
const creatingPetPost = useTelepathicState(creatingPostId);
const onChangeName = useDebouncedCallback((name: string) => {
let petPost = creatingPetPost;
if (!petPost) {
const petPostGroup = localNode.createGroup();
petPost = petPostGroup.createMap<PetPost>();
const reactions = petPostGroup.createStream<PetReactions>();
petPost = petPost.edit((petPost) => {
petPost.set("reactions", reactions.id);
});
setCreatingPostId(petPost.id);
}
petPost.edit((petPost) => {
petPost.set("name", name);
});
}, 200);
const onImageCreated = useCallback(
(image: BinaryCoStream) => {
if (!creatingPetPost) throw new Error("Never get here");
creatingPetPost.edit((petPost) => {
petPost.set("image", image.id);
});
},
[creatingPetPost]
);
const image = useBinaryStream(creatingPetPost?.get("image"));
return (
<div>
<Input
type="text"
placeholder="Pet Name"
onChange={event => onChangeName(event.target.value)}
value={creatingPetPost?.get("name")}
/>
{image ? (
<img src={image.blobURL} />
) : (
creatingPetPost && (
<Input
type="file"
onChange={createBinaryStreamHandler(
onImageCreated,
creatingPetPost.group
)}
/>
)
)}
{creatingPetPost?.get("name") && creatingPetPost?.get("image") && (
<Button
onClick={() => {
onCreate(creatingPetPost.id);
}}
>
Submit Post
</Button>
)}
</div>
);
}

View File

@@ -1,18 +0,0 @@
import { useCallback } from "react";
import { CoID } from "cojson";
import { useTelepathicState } from "jazz-react";
import { PetPost } from "./1_types";
import { InviteButton } from "./components/InviteButton";
import { NameBadge } from "./components/NameBadge";
/** Walkthrough: TODO
*/
export function PetPostUI({ petPostID }: { petPostID: CoID<PetPost> }) {
return (<div>TODO</div>);
}

View File

@@ -0,0 +1,120 @@
import { useParams } from "react-router";
import { PetPost, PetReactions, ReactionTypes } from "./1_schema";
import { ShareButton } from "./components/ShareButton";
import { Button, Skeleton } from "./basicComponents";
import uniqolor from "uniqolor";
import { ID } from "jazz-tools";
import { useCoState } from "./2_main";
import { ProgressiveImg } from "jazz-react";
/** Walkthrough: TODO
*/
const reactionEmojiMap: {
[reaction in (typeof ReactionTypes)[number]]: string;
} = {
aww: "😍",
love: "❤️",
haha: "😂",
wow: "😮",
tiny: "🐥",
chonkers: "🐘",
};
export function RatePetPostUI() {
const petPostID = useParams<{ petPostId: ID<PetPost> }>().petPostId;
const petPost = useCoState(PetPost, petPostID);
return (
<div className="flex flex-col gap-8">
<div className="flex justify-between">
<h1 className="text-3xl font-bold">{petPost?.name}</h1>
<ShareButton petPost={petPost} />
</div>
<ProgressiveImg image={petPost?.image}>
{({ src }) => (
<img className="w-80 max-w-full rounded" src={src} />
)}
</ProgressiveImg>
<div className="flex justify-between max-w-xs flex-wrap">
{ReactionTypes.map((reactionType) => (
<Button
key={reactionType}
variant={
petPost?.reactions?.byMe?.value === reactionType
? "default"
: "outline"
}
onClick={() => {
petPost?.reactions?.push(reactionType);
}}
title={`React with ${reactionType}`}
className="text-2xl px-2"
>
{reactionEmojiMap[reactionType]}
</Button>
))}
</div>
{petPost?._owner.myRole() === "admin" && petPost.reactions && (
<ReactionOverview petReactions={petPost.reactions} />
)}
</div>
);
}
function ReactionOverview({ petReactions }: { petReactions: PetReactions }) {
return (
<div>
<h2>Reactions</h2>
<div className="flex flex-col gap-1">
{ReactionTypes.map((reactionType) => {
const reactionsOfThisType = Object.values(
petReactions,
).filter((entry) => entry.value === reactionType);
if (reactionsOfThisType.length === 0) return null;
return (
<div
className="flex gap-2 items-center"
key={reactionType}
>
{reactionEmojiMap[reactionType]}{" "}
{reactionsOfThisType.map((reaction, idx) =>
reaction.by?.profile?.name ? (
<span
className="rounded-full py-0.5 px-2 text-xs"
style={uniqueColoring(reaction.by.id)}
key={reaction.by.id}
>
{reaction.by.profile.name}
</span>
) : (
<Skeleton
className="mt-1 w-[50px] h-[1em] rounded-full"
key={idx}
/>
),
)}
</div>
);
})}
</div>
</div>
);
}
function uniqueColoring(seed: string) {
const darkMode = window.matchMedia("(prefers-color-scheme: dark)").matches;
return {
color: uniqolor(seed, { lightness: darkMode ? 80 : 20 }).color,
background: uniqolor(seed, { lightness: darkMode ? 20 : 80 }).color,
};
}

View File

@@ -1,7 +1,12 @@
export function TitleAndLogo({name}: {name: string}) {
return <>
<div className="flex items-center gap-2 justify-center mt-5">
import { Toaster } from ".";
export function TitleAndLogo({ name }: { name: string }) {
return (
<>
<div className="flex items-center gap-2 justify-center mt-5">
<img src="jazz-logo.png" className="h-5" /> {name}
</div>
</>
}
<Toaster />
</>
);
}

View File

@@ -1,4 +1,7 @@
export { Button } from "./ui/button";
export { Input } from "./ui/input";
export { Toaster } from "./ui/toaster";
export { useToast } from "./ui/use-toast";
export { Skeleton } from "./ui/skeleton";
export { TitleAndLogo } from "./TitleAndLogo";
export { ThemeProvider } from "./themeProvider";

View File

@@ -1,6 +1,6 @@
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
return twMerge(clsx(inputs));
}

View File

@@ -25,7 +25,7 @@ export function ThemeProvider({
...props
}: ThemeProviderProps) {
const [theme, setTheme] = useState(
() => localStorage.getItem(storageKey) || defaultTheme
() => localStorage.getItem(storageKey) || defaultTheme,
);
useEffect(() => {
@@ -35,7 +35,7 @@ export function ThemeProvider({
if (theme === "system") {
const systemTheme = window.matchMedia(
"(prefers-color-scheme: dark)"
"(prefers-color-scheme: dark)",
).matches
? "dark"
: "light";
@@ -62,6 +62,7 @@ export function ThemeProvider({
);
}
// eslint-disable-next-line react-refresh/only-export-components
export const useTheme = () => {
const context = useContext(ThemeProviderContext);

View File

@@ -1,56 +1,58 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/basicComponents/lib/utils"
import { cn } from "@/basicComponents/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
},
);
Button.displayName = "Button";
export { Button, buttonVariants }
// eslint-disable-next-line react-refresh/only-export-components
export { Button, buttonVariants };

View File

@@ -1,25 +1,25 @@
import * as React from "react"
import * as React from "react";
import { cn } from "@/basicComponents/lib/utils"
import { cn } from "@/basicComponents/lib/utils";
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
ref={ref}
{...props}
/>
);
},
);
Input.displayName = "Input";
export { Input }
export { Input };

View File

@@ -0,0 +1,15 @@
import { cn } from "@/basicComponents/lib/utils";
function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("animate-pulse rounded-md bg-muted", className)}
{...props}
/>
);
}
export { Skeleton };

View File

@@ -0,0 +1,127 @@
import * as React from "react";
import * as ToastPrimitives from "@radix-ui/react-toast";
import { cva, type VariantProps } from "class-variance-authority";
import { X } from "lucide-react";
import { cn } from "@/basicComponents/lib/utils";
const ToastProvider = ToastPrimitives.Provider;
const ToastViewport = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Viewport>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Viewport
ref={ref}
className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className,
)}
{...props}
/>
));
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
{
variants: {
variant: {
default: "border bg-background text-foreground",
destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
);
const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
return (
<ToastPrimitives.Root
ref={ref}
className={cn(toastVariants({ variant }), className)}
{...props}
/>
);
});
Toast.displayName = ToastPrimitives.Root.displayName;
const ToastAction = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Action>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Action
ref={ref}
className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className,
)}
{...props}
/>
));
ToastAction.displayName = ToastPrimitives.Action.displayName;
const ToastClose = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Close>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Close
ref={ref}
className={cn(
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
className,
)}
toast-close=""
{...props}
>
<X className="h-4 w-4" />
</ToastPrimitives.Close>
));
ToastClose.displayName = ToastPrimitives.Close.displayName;
const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title
ref={ref}
className={cn("text-sm font-semibold", className)}
{...props}
/>
));
ToastTitle.displayName = ToastPrimitives.Title.displayName;
const ToastDescription = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Description>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Description
ref={ref}
className={cn("text-sm opacity-90", className)}
{...props}
/>
));
ToastDescription.displayName = ToastPrimitives.Description.displayName;
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
type ToastActionElement = React.ReactElement<typeof ToastAction>;
export {
type ToastProps,
type ToastActionElement,
ToastProvider,
ToastViewport,
Toast,
ToastTitle,
ToastDescription,
ToastClose,
ToastAction,
};

View File

@@ -0,0 +1,41 @@
import {
Toast,
ToastClose,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
} from "@/basicComponents/ui/toast";
import { useToast } from "@/basicComponents/ui/use-toast";
export function Toaster() {
const { toasts } = useToast();
return (
<ToastProvider>
{toasts.map(function ({
id,
title,
description,
action,
...props
}) {
return (
<Toast key={id} {...props}>
<div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>}
{description && (
<ToastDescription>
{description}
</ToastDescription>
)}
</div>
{action}
<ToastClose />
</Toast>
);
})}
<ToastViewport />
</ToastProvider>
);
}

View File

@@ -0,0 +1,193 @@
// Inspired by react-hot-toast library
import * as React from "react";
import type {
ToastActionElement,
ToastProps,
} from "@/basicComponents/ui/toast";
const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1000000;
type ToasterToast = ToastProps & {
id: string;
title?: React.ReactNode;
description?: React.ReactNode;
action?: ToastActionElement;
};
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const;
let count = 0;
function genId() {
count = (count + 1) % Number.MAX_VALUE;
return count.toString();
}
type ActionType = typeof actionTypes;
type Action =
| {
type: ActionType["ADD_TOAST"];
toast: ToasterToast;
}
| {
type: ActionType["UPDATE_TOAST"];
toast: Partial<ToasterToast>;
}
| {
type: ActionType["DISMISS_TOAST"];
toastId?: ToasterToast["id"];
}
| {
type: ActionType["REMOVE_TOAST"];
toastId?: ToasterToast["id"];
};
interface State {
toasts: ToasterToast[];
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return;
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId);
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
});
}, TOAST_REMOVE_DELAY);
toastTimeouts.set(toastId, timeout);
};
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "ADD_TOAST":
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
};
case "UPDATE_TOAST":
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t,
),
};
case "DISMISS_TOAST": {
const { toastId } = action;
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId);
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id);
});
}
return {
...state,
toasts: state.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t,
),
};
}
case "REMOVE_TOAST":
if (action.toastId === undefined) {
return {
...state,
toasts: [],
};
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
};
}
};
const listeners: Array<(state: State) => void> = [];
let memoryState: State = { toasts: [] };
function dispatch(action: Action) {
memoryState = reducer(memoryState, action);
listeners.forEach((listener) => {
listener(memoryState);
});
}
type Toast = Omit<ToasterToast, "id">;
function toast({ ...props }: Toast) {
const id = genId();
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
});
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
dispatch({
type: "ADD_TOAST",
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss();
},
},
});
return {
id: id,
dismiss,
update,
};
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState);
React.useEffect(() => {
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState);
if (index > -1) {
listeners.splice(index, 1);
}
};
}, [state]);
return {
...state,
toast,
dismiss: (toastId?: string) =>
dispatch({ type: "DISMISS_TOAST", toastId }),
};
}
export { useToast, toast };

View File

@@ -1,10 +1,10 @@
import { useState } from "react";
import { LocalAuthComponent } from "jazz-react-auth-local";
import { PasskeyAuth } from "jazz-react";
import { Input, Button } from "../basicComponents";
export const PrettyAuthUI: LocalAuthComponent = ({
export const PrettyAuthUI: PasskeyAuth.Component = ({
loading,
logIn,
signUp,

View File

@@ -1,46 +0,0 @@
import { AccountID } from "cojson";
import { useProfile } from "jazz-react";
import { Skeleton } from "@/basicComponents";
import uniqolor from "uniqolor";
/** Walkthrough: Getting user profiles in `<NameBadge/>`
*
* `<NameBadge/>` uses `useProfile(accountID)`, which is a shorthand for
* useTelepathicState on an account's profile.
*
* Profiles are always a `CoMap<{name: string}>`, but they might have app-specific
* additional properties).
*
* In our case, we just display the profile name (which is set by the LocalAuth
* provider when we first create an account).
*/
export function NameBadge({ accountID }: { accountID?: AccountID }) {
const profile = useProfile(accountID);
return accountID && profile?.get("name") ? (
<span
className="rounded-full py-0.5 px-2 text-xs"
style={randomUserColor(accountID)}
>
{profile.get("name")}
</span>
) : (
<Skeleton className="mt-1 w-[50px] h-[1em] rounded-full" />
);
}
function randomUserColor(accountID: AccountID) {
const theme = window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
const brightColor = uniqolor(accountID || "", { lightness: 80 }).color;
const darkColor = uniqolor(accountID || "", { lightness: 20 }).color;
return {
color: theme == "light" ? darkColor : brightColor,
background: theme == "light" ? brightColor : darkColor,
};
}

View File

@@ -1,27 +1,27 @@
import { useState } from "react";
import { TodoProject } from "../1_types";
import { PetPost } from "../1_schema";
import { createInviteLink } from "jazz-react";
import QRCode from "qrcode";
import { useToast, Button } from "../basicComponents";
export function InviteButton({ list }: { list?: TodoProject }) {
export function ShareButton({ petPost }: { petPost?: PetPost }) {
const [existingInviteLink, setExistingInviteLink] = useState<string>();
const { toast } = useToast();
return (
list?.group.myRole() === "admin" && (
petPost?._owner.myRole() === "admin" && (
<Button
size="sm"
className="py-0"
disabled={!list}
disabled={!petPost}
variant="outline"
onClick={async () => {
let inviteLink = existingInviteLink;
if (list && !inviteLink) {
inviteLink = createInviteLink(list, "writer");
if (petPost && !inviteLink) {
inviteLink = createInviteLink(petPost, "writer");
setExistingInviteLink(inviteLink);
}
if (inviteLink) {
@@ -34,12 +34,12 @@ export function InviteButton({ list }: { list?: TodoProject }) {
description: (
<img src={qr} className="w-20 h-20" />
),
})
}),
);
}
}}
>
Invite
Share
</Button>
)
);

View File

@@ -1,37 +0,0 @@
import { useCallback, useEffect, useState } from "react";
import { CoID, LocalNode, CoValueImpl } from "cojson";
import { consumeInviteLinkFromWindowLocation } from "jazz-react";
export function useSimpleHashRouterThatAcceptsInvites<C extends CoValueImpl>(
localNode: LocalNode
) {
const [currentValueId, setCurrentValueId] = useState<CoID<C>>();
useEffect(() => {
const listener = async () => {
const acceptedInvitation = await consumeInviteLinkFromWindowLocation<C>(localNode);
if (acceptedInvitation) {
setCurrentValueId(acceptedInvitation.valueID);
window.location.hash = acceptedInvitation.valueID;
return;
}
setCurrentValueId(
(window.location.hash.slice(1) as CoID<C>) || undefined
);
};
window.addEventListener("hashchange", listener);
listener();
return () => {
window.removeEventListener("hashchange", listener);
};
}, [localNode]);
const navigateToValue = useCallback((id: CoID<C> | undefined) => {
window.location.hash = id || "";
}, []);
return [currentValueId, navigateToValue] as const;
}

View File

@@ -5,6 +5,7 @@ module.exports = {
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'prettier'
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',

View File

@@ -0,0 +1,9 @@
/** @type {import("prettier").Config} */
const config = {
trailingComma: "all",
tabWidth: 4,
semi: true,
singleQuote: false,
};
export default config;

632
examples/todo/CHANGELOG.md Normal file
View File

@@ -0,0 +1,632 @@
# jazz-example-todo
## 0.0.93
### Patch Changes
- jazz-react@0.7.29
- jazz-tools@0.7.29
## 0.0.92
### Patch Changes
- jazz-react@0.7.28
- jazz-tools@0.7.28
## 0.0.91
### Patch Changes
- jazz-react@0.7.27
## 0.0.90
### Patch Changes
- Updated dependencies
- jazz-react@0.7.26
- jazz-tools@0.7.26
## 0.0.89
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.25
- jazz-react@0.7.25
## 0.0.88
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.24
- jazz-react@0.7.24
## 0.0.87
### Patch Changes
- Updated dependencies
- jazz-react@0.7.23
- jazz-tools@0.7.23
## 0.0.86
### Patch Changes
- jazz-react@0.7.22
## 0.0.85
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.21
- jazz-react@0.7.21
## 0.0.84
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.20
- jazz-react@0.7.20
## 0.0.83
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.19
- jazz-react@0.7.19
## 0.0.82
### Patch Changes
- jazz-react@0.7.18
- jazz-tools@0.7.18
## 0.0.81
### Patch Changes
- jazz-react@0.7.17
- jazz-tools@0.7.17
## 0.0.80
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.16
- jazz-react@0.7.16
## 0.0.79
### Patch Changes
- Updated dependencies
- jazz-react@0.7.15
## 0.0.78
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.14
- jazz-react@0.7.14
## 0.0.77
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.13
- jazz-react@0.7.13
## 0.0.76
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.12
- jazz-react@0.7.12
## 0.0.75
### Patch Changes
- jazz-react@0.7.11
- jazz-tools@0.7.11
## 0.0.74
### Patch Changes
- jazz-react@0.7.10
- jazz-tools@0.7.10
## 0.0.73
### Patch Changes
- jazz-react@0.7.9
- jazz-tools@0.7.9
## 0.0.72
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.8
- jazz-react@0.7.8
## 0.0.71
### Patch Changes
- Updated dependencies [9fdc91c]
- jazz-react@0.7.7
## 0.0.70
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.6
- jazz-react@0.7.6
## 0.0.69
### Patch Changes
- Updated dependencies
- jazz-react@0.7.5
## 0.0.68
### Patch Changes
- Updated dependencies
- jazz-react@0.7.4
## 0.0.67
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.3
- jazz-react@0.7.3
## 0.0.66
### Patch Changes
- Updated dependencies
- jazz-react@0.7.2
## 0.0.65
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.1
- jazz-react@0.7.1
## 0.0.64
### Patch Changes
- Updated dependencies [8636319]
- Updated dependencies [8636319]
- Updated dependencies [1a35307]
- Updated dependencies [96c494f]
- Updated dependencies [59c18c3]
- Updated dependencies [19f52b7]
- Updated dependencies [8636319]
- Updated dependencies [1a35307]
- Updated dependencies [d8fe2b1]
- Updated dependencies [19004b4]
- Updated dependencies [a78f168]
- Updated dependencies [52675c9]
- Updated dependencies [129e2c1]
- Updated dependencies [6d49e9b]
- Updated dependencies [1cfa279]
- Updated dependencies [704af7d]
- Updated dependencies [e97f730]
- Updated dependencies [460478f]
- Updated dependencies [6b0418f]
- Updated dependencies [e299c3e]
- Updated dependencies [ed5643a]
- Updated dependencies [bde684f]
- Updated dependencies [c4151fc]
- Updated dependencies [63374cc]
- Updated dependencies [01ac646]
- Updated dependencies [a5e68a4]
- Updated dependencies [8636319]
- Updated dependencies [952982e]
- Updated dependencies [1a35307]
- Updated dependencies [5fa277c]
- Updated dependencies [60d5ca2]
- Updated dependencies [21771c4]
- Updated dependencies [77c2b56]
- Updated dependencies [63374cc]
- Updated dependencies [d2e03ff]
- Updated dependencies [354bdcd]
- Updated dependencies [ece35b3]
- Updated dependencies [60d5ca2]
- Updated dependencies [69ac514]
- Updated dependencies [f8a5c46]
- Updated dependencies [f0f6f1b]
- Updated dependencies [e5eed5b]
- Updated dependencies [1a44f87]
- Updated dependencies [627d895]
- Updated dependencies [1200aae]
- Updated dependencies [63374cc]
- Updated dependencies [ece35b3]
- Updated dependencies [38d4410]
- Updated dependencies [85d2b62]
- Updated dependencies [fd86c11]
- Updated dependencies [52675c9]
- jazz-tools@0.7.0
- jazz-react@0.7.0
## 0.0.64-alpha.42
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.42
- jazz-react@0.7.0-alpha.42
## 0.0.64-alpha.41
### Patch Changes
- jazz-tools@0.7.0-alpha.41
- jazz-react@0.7.0-alpha.41
## 0.0.64-alpha.40
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.40
## 0.0.64-alpha.39
### Patch Changes
- jazz-react@0.7.0-alpha.39
- jazz-tools@0.7.0-alpha.39
## 0.0.64-alpha.38
### Patch Changes
- Updated dependencies
- Updated dependencies
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.38
- jazz-react@0.7.0-alpha.38
## 0.0.64-alpha.37
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.37
- jazz-tools@0.7.0-alpha.37
## 0.0.64-alpha.36
### Patch Changes
- Updated dependencies [1a35307]
- Updated dependencies [1a35307]
- Updated dependencies [6b0418f]
- Updated dependencies [1a35307]
- jazz-tools@0.7.0-alpha.36
- jazz-react@0.7.0-alpha.36
## 0.0.64-alpha.35
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.35
- jazz-react@0.7.0-alpha.35
## 0.0.64-alpha.34
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.34
- jazz-react@0.7.0-alpha.34
## 0.0.64-alpha.33
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.33
## 0.0.64-alpha.32
### Patch Changes
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.32
- jazz-react@0.7.0-alpha.32
## 0.0.64-alpha.31
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.31
- jazz-react@0.7.0-alpha.31
## 0.0.64-alpha.30
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.30
- jazz-react@0.7.0-alpha.30
## 0.0.64-alpha.29
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.29
- jazz-react@0.7.0-alpha.29
## 0.0.64-alpha.28
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.28
- jazz-react@0.7.0-alpha.28
## 0.0.64-alpha.27
### Patch Changes
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.27
- jazz-react@0.7.0-alpha.27
## 0.0.64-alpha.26
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.26
- jazz-react@0.7.0-alpha.26
## 0.0.64-alpha.25
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.25
- jazz-react@0.7.0-alpha.25
## 0.0.64-alpha.24
### Patch Changes
- Updated dependencies
- Updated dependencies
- Updated dependencies
- jazz-tools@0.7.0-alpha.24
- jazz-react@0.7.0-alpha.24
## 0.0.64-alpha.23
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.23
- jazz-react@0.7.0-alpha.23
## 0.0.64-alpha.22
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.22
- jazz-react@0.7.0-alpha.22
## 0.0.64-alpha.21
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.21
- jazz-tools@0.7.0-alpha.21
## 0.0.64-alpha.20
### Patch Changes
- Updated dependencies
- Updated dependencies
- jazz-react@0.7.0-alpha.20
- jazz-tools@0.7.0-alpha.20
## 0.0.64-alpha.19
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.19
- jazz-react@0.7.0-alpha.19
## 0.0.64-alpha.18
### Patch Changes
- jazz-react@0.7.0-alpha.18
## 0.0.64-alpha.17
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.17
- jazz-react@0.7.0-alpha.17
## 0.0.64-alpha.16
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.16
- jazz-react@0.7.0-alpha.16
## 0.0.64-alpha.15
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.15
- jazz-react@0.7.0-alpha.15
## 0.0.64-alpha.14
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.14
- jazz-react@0.7.0-alpha.14
## 0.0.64-alpha.13
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.13
- jazz-react@0.7.0-alpha.13
## 0.0.64-alpha.12
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.12
- jazz-tools@0.7.0-alpha.12
## 0.0.64-alpha.11
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.11
- jazz-tools@0.7.0-alpha.11
## 0.0.64-alpha.10
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.10
- jazz-tools@0.7.0-alpha.10
## 0.0.64-alpha.9
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.9
- jazz-tools@0.7.0-alpha.9
## 0.0.64-alpha.8
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.8
- jazz-tools@0.7.0-alpha.8
## 0.0.64-alpha.7
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.7
- jazz-tools@0.7.0-alpha.7
## 0.0.64-alpha.6
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.6
- jazz-tools@0.7.0-alpha.6
## 0.0.64-alpha.5
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.5
- jazz-tools@0.7.0-alpha.5
## 0.0.64-alpha.4
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.4
- jazz-react@0.7.0-alpha.4
## 0.0.64-alpha.3
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.0-alpha.3
- jazz-react@0.7.0-alpha.3
## 0.0.64-alpha.2
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.2
- jazz-tools@0.7.0-alpha.2
## 0.0.64-alpha.1
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.1
- jazz-tools@0.7.0-alpha.1
## 0.0.64-alpha.0
### Patch Changes
- Updated dependencies
- jazz-react@0.7.0-alpha.0
- cojson@0.7.0-alpha.0
## 0.0.63
### Patch Changes
- Updated dependencies
- jazz-react@0.5.0
- jazz-react-auth-local@0.4.16

View File

@@ -4,52 +4,56 @@ Live version: https://example-todo.jazz.tools
## Installing & running the example locally
Start by checking out just the example app to a folder:
(this requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation))
Start by checking out `jazz`
```bash
npx degit gardencmp/jazz/examples/todo jazz-example-todo
cd jazz-example-todo
git clone https://github.com/gardencmp/jazz.git
cd jazz/examples/todo
pnpm pack --pack-destination /tmp
mkdir -p ~/jazz-examples/todo # or any other directory
tar -xf /tmp/jazz-example-todo-* --strip-components 1 -C ~/jazz-examples/todo
cd ~/jazz-examples/todo
```
(This ensures that you have the example app without git history or our multi-package monorepo)
This ensures that you have the example app without git history and independent of the Jazz multi-package monorepo.
Install dependencies:
```bash
npm install
pnpm install
```
Start the dev server:
```bash
npm run dev
pnpm dev
```
## Structure
- [`src/basicComponents`](./src/basicComponents) contains simple components to build the UI, unrelated to Jazz (powered by [shadcn/ui](https://ui.shadcn.com))
- [`src/components`](./src/components/) contains helper components that do contain Jazz-specific logic, but are not super relevant to understand the basics of Jazz and CoJSON
- [`src/0_main.tsx`](./src/0_main.tsx), [`src/1_types.ts`](./src/1_types.ts), [`src/2_App.tsx`](./src/2_App.tsx), [`src/3_TodoTable.tsx`](./src/3_TodoTable.tsx), [`src/router.ts`](./src/router.ts) - the main files for this example, see the walkthrough below
- [`src/basicComponents`](./src/basicComponents): simple components to build the UI, unrelated to Jazz (uses [shadcn/ui](https://ui.shadcn.com))
- [`src/components`](./src/components/): helper components that do contain Jazz-specific logic, but aren't very relevant to understand the basics of Jazz and CoJSON
- [`src/1_schema.ts`](./src/1_schema.ts),
[`src/2_main.tsx`](./src/2_main.tsx),
[`src/3_NewProjectForm.tsx`](./src/3_NewProjectForm.tsx),
[`src/4_ProjectTodoTable.tsx`](./src/4_ProjectTodoTable.tsx): the main files for this example, see the walkthrough below
## Walkthrough
### Main parts
- The top-level provider `<WithJazz/>`: [`src/0_main.tsx`](./src/0_main.tsx)
1. Defining the data model with CoJSON: [`src/1_schema.ts`](./src/1_schema.ts)
- Defining the data model with CoJSON: [`src/1_types.ts`](./src/1_types.ts)
2. The top-level provider `<WithJazz/>` and routing: [`src/2_main.tsx`](./src/2_main.tsx)
- Creating todo projects & routing in `<App/>`: [`src/2_App.tsx`](./src/2_App.tsx)
3. Creating a new todo project: [`src/3_NewProjectForm.tsx`](./src/3_NewProjectForm.tsx)
- Reactively rendering a todo project as a table, adding and editing tasks: [`src/3_TodoTable.tsx`](./src/3_TodoTable.tsx)
4. Reactively rendering a todo project as a table, adding and editing tasks: [`src/4_ProjectTodoTable.tsx`](./src/4_ProjectTodoTable.tsx)
### Helpers
- Getting user profiles in `<NameBadge/>`: [`src/components/NameBadge.tsx`](./src/components/NameBadge.tsx)
- (not yet commented) Creating invite links/QR codes with `<InviteButton/>`: [`src/components/InviteButton.tsx`](./src/components/InviteButton.tsx)
- (not yet commented) `location.hash`-based routing and accepting invite links with `useSimpleHashRouterThatAcceptsInvites()` in [`src/router.ts`](./src/router.ts)
- (not yet explained) Creating Invite Links/QR codes with `<InviteButton/>`: [`src/components/InviteButton.tsx`](./src/components/InviteButton.tsx)
This is the whole Todo List app!
@@ -62,4 +66,4 @@ If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or
By default, the example app uses [Jazz Global Mesh](https://jazz.tools/mesh) (`wss://sync.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running `npx cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?sync=ws://localhost:4200`), or by setting the `sync` parameter of the `<WithJazz>` provider component in [./src/0_main.tsx](./src/0_main.tsx).
You can also run a local sync server by running `npx cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?sync=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/2_main.tsx](./src/2_main.tsx).

View File

@@ -8,6 +8,6 @@
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/0_main.tsx"></script>
<script type="module" src="/src/2_main.tsx"></script>
</body>
</html>

View File

@@ -3,7 +3,7 @@ job "example-todo$BRANCH_SUFFIX" {
datacenters = ["*"]
group "static" {
count = 8
count = 4
network {
port "http" {

View File

@@ -1,14 +1,19 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.27",
"version": "0.0.93",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"format": "prettier --write './src/**/*.{ts,tsx}'",
"preview": "vite preview"
},
"lint-staged": {
"*.{ts,tsx}": "eslint --fix",
"*.{js,jsx,mdx,json}": "prettier --write"
},
"dependencies": {
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-slot": "^1.0.2",
@@ -16,12 +21,14 @@
"@types/qrcode": "^1.5.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "^0.1.13",
"jazz-react-auth-local": "^0.1.13",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.16.0",
"react-router-dom": "^6.16.0",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"uniqolor": "^1.1.0"
@@ -34,6 +41,7 @@
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"eslint": "^8.45.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"postcss": "^8.4.27",

View File

@@ -1,38 +0,0 @@
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import { WithJazz } from "jazz-react";
import { LocalAuth } from "jazz-react-auth-local";
import { ThemeProvider, TitleAndLogo } from "./basicComponents/index.ts";
import { PrettyAuthUI } from "./components/Auth.tsx";
import App from "./2_App.tsx";
/** Walkthrough: The top-level provider `<WithJazz/>`
*
* This shows how to use the top-level provider `<WithJazz/>`,
* which provides the rest of the app with a `LocalNode` (used through `useJazz` later),
* based on `LocalAuth` that uses PassKeys (aka WebAuthn) to store a user's account secret
* - no backend needed. */
const appName = "Jazz Todo List Example";
const auth = LocalAuth({
appName,
Component: PrettyAuthUI,
});
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ThemeProvider>
<TitleAndLogo name={appName} />
<WithJazz auth={auth}>
<App />
</WithJazz>
</ThemeProvider>
</React.StrictMode>
);
/** Walkthrough: Continue with ./1_types.ts */

View File

@@ -0,0 +1,55 @@
import { Account, CoList, CoMap, Profile, co } from "jazz-tools";
/** Walkthrough: Defining the data model with CoJSON
*
* Here, we define our main data model of tasks, lists of tasks and projects
* using CoJSON's collaborative map and list types, CoMap & CoList.
*
* CoMap values and CoLists items can contain:
* - arbitrary immutable JSON
* - other CoValues
**/
/** An individual task which collaborators can tick or rename */
export class Task extends CoMap {
done = co.boolean;
text = co.string;
}
export class ListOfTasks extends CoList.Of(co.ref(Task)) {}
/** Our top level object: a project with a title, referencing a list of tasks */
export class TodoProject extends CoMap {
title = co.string;
tasks = co.ref(ListOfTasks);
}
export class ListOfProjects extends CoList.Of(co.ref(TodoProject)) {}
/** The account root is an app-specific per-user private `CoMap`
* where you can store top-level objects for that user */
export class TodoAccountRoot extends CoMap {
projects = co.ref(ListOfProjects);
}
export class TodoAccount extends Account {
profile = co.ref(Profile);
root = co.ref(TodoAccountRoot);
/** The account migration is run on account creation and on every log-in.
* You can use it to set up the account root and any other initial CoValues you need.
*/
migrate(this: TodoAccount, creationProps?: { name: string }) {
super.migrate(creationProps);
if (!this._refs.root) {
this.root = TodoAccountRoot.create(
{
projects: ListOfProjects.create([], { owner: this }),
},
{ owner: this },
);
}
}
}
/** Walkthrough: Continue with ./2_main.tsx */

View File

@@ -1,28 +0,0 @@
import { CoMap, CoList, CoID } from "cojson";
/** Walkthrough: Defining the data model with CoJSON
*
* Here, we define our main data model of tasks, lists of tasks and projects
* using CoJSON's collaborative map and list types, CoMap & CoList.
*
* CoMap values and CoLists items can be:
* - arbitrary immutable JSON
* - references to other CoValues by their CoID
* - CoIDs are strings that look like `co_zXPuWmH1D1cKdMpDW6CMzWb3LpY`
* - In TypeScript, CoIDs take a generic parameter for the type of the
* referenced CoValue, e.g. `CoID<Task>` - to make the references precise
**/
/** An individual task which collaborators can tick or rename */
export type Task = CoMap<{ done: boolean; text: string; }>;
/** A collaborative, ordered list of task references */
export type ListOfTasks = CoList<CoID<Task>>;
/** Our top level object: a project with a title, referencing a list of tasks */
export type TodoProject = CoMap<{
title: string;
tasks: CoID<ListOfTasks>;
}>;
/** Walkthrough: Continue with ./2_App.tsx */

View File

@@ -1,78 +0,0 @@
import { useCallback } from "react";
import { useJazz } from "jazz-react";
import { TodoProject, ListOfTasks } from "./1_types";
import { SubmittableInput, Button } from "./basicComponents";
import { useSimpleHashRouterThatAcceptsInvites } from "./router";
import { TodoTable } from "./3_TodoTable";
/** Walkthrough: Creating todo projects & routing in `<App/>`
*
* <App> is the main app component, handling client-side routing based
* on the CoValue ID (CoID) of our TodoProject, stored in the URL hash
* - which can also contain invite links.
*/
export default function App() {
// A `LocalNode` represents a local view of loaded & created CoValues.
// It is associated with a current user account, which will determine
// access rights to CoValues. We get it from the top-level provider `<WithJazz/>`.
const { localNode, logOut } = useJazz();
// This sets up routing and accepting invites, skip for now
const [currentProjectId, navigateToProjectId] =
useSimpleHashRouterThatAcceptsInvites<TodoProject>(localNode);
const createProject = useCallback(
(title: string) => {
if (!title) return;
// To create a new todo project, we first create a `Group`,
// which is a scope for defining access rights (reader/writer/admin)
// of its members, which will apply to all CoValues owned by that group.
const projectGroup = localNode.createGroup();
// Then we create an empty todo project and list of tasks within that group.
const project = projectGroup.createMap<TodoProject>();
const tasks = projectGroup.createList<ListOfTasks>();
// We edit the todo project to initialise it.
// Inside the `.edit` callback we can mutate a CoValue
project.edit((project) => {
project.set("title", title);
project.set("tasks", tasks.id);
});
navigateToProjectId(project.id);
},
[localNode, navigateToProjectId]
);
return (
<div className="flex flex-col h-full items-center justify-start gap-10 pt-10 pb-10 px-5">
{currentProjectId ? (
<TodoTable projectId={currentProjectId} />
) : (
<SubmittableInput
onSubmit={createProject}
label="Create New Project"
placeholder="New project title"
/>
)}
<Button
onClick={() => {
navigateToProjectId(undefined);
logOut();
}}
variant="outline"
>
Log Out
</Button>
</div>
);
}
/** Walkthrough: continue with ./3_TodoTable.tsx */

View File

@@ -0,0 +1,132 @@
import ReactDOM from "react-dom/client";
import {
RouterProvider,
createHashRouter,
useNavigate,
} from "react-router-dom";
import "./index.css";
import { createJazzReactContext, PasskeyAuth } from "jazz-react";
import {
Button,
ThemeProvider,
TitleAndLogo,
} from "./basicComponents/index.ts";
import { PrettyAuthUI } from "./components/Auth.tsx";
import { NewProjectForm } from "./3_NewProjectForm.tsx";
import { ProjectTodoTable } from "./4_ProjectTodoTable.tsx";
import { TodoAccount, TodoProject } from "./1_schema.ts";
/**
* Walkthrough: The top-level provider `<Jazz.Provider/>`
*
* This shows how to use the top-level provider `<Jazz.Provider/>`,
* which provides the rest of the app with a controlled account (used through `useAccount` later).
* Here we use `PasskeyAuth`, which uses Passkeys (aka WebAuthn) to store a user's account secret
* - no backend needed.
*
* `<Jazz.Provider/>` also runs our account migration
*/
const appName = "Jazz Todo List Example";
const auth = PasskeyAuth<TodoAccount>({
appName,
Component: PrettyAuthUI,
accountSchema: TodoAccount,
});
const Jazz = createJazzReactContext<TodoAccount>({
auth,
peer: "wss://mesh.jazz.tools/?key=you@example.com",
});
// eslint-disable-next-line react-refresh/only-export-components
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
ReactDOM.createRoot(document.getElementById("root")!).render(
// <React.StrictMode>
<ThemeProvider>
<TitleAndLogo name={appName} />
<div className="flex flex-col h-full items-center justify-start gap-10 pt-10 pb-10 px-5">
<Jazz.Provider>
<App />
</Jazz.Provider>
</div>
</ThemeProvider>,
// </React.StrictMode>
);
/**
* Routing in `<App/>`
*
* <App> is the main app component, handling client-side routing based
* on the CoValue ID (CoID) of our TodoProject, stored in the URL hash
* - which can also contain invite links.
*/
export default function App() {
// logOut logs out the AuthProvider passed to `<Jazz.Provider/>` above.
const { logOut } = useAccount();
const router = createHashRouter([
{
path: "/",
element: <HomeScreen />,
},
{
path: "/project/:projectId",
element: <ProjectTodoTable />,
},
{
path: "/invite/*",
element: <p>Accepting invite...</p>,
},
]);
// `useAcceptInvite()` is a hook that accepts an invite link from the URL hash,
// and on success calls our callback where we navigate to the project that we were just invited to.
useAcceptInvite({
invitedObjectSchema: TodoProject,
forValueHint: "project",
onAccept: (projectID) => router.navigate("/project/" + projectID),
});
return (
<>
<RouterProvider router={router} />
<Button
onClick={() => router.navigate("/").then(logOut)}
variant="outline"
>
Log Out
</Button>
</>
);
}
function HomeScreen() {
const { me } = useAccount({
root: { projects: [{}] },
});
const navigate = useNavigate();
return (
<>
{me?.root.projects.length ? <h1>My Projects</h1> : null}
{me?.root.projects.map((project) => {
return (
<Button
key={project.id}
onClick={() => navigate("/project/" + project?.id)}
variant="ghost"
>
{project.title}
</Button>
);
})}
<NewProjectForm />
</>
);
}
/** Walkthrough: Continue with ./3_NewProjectForm.tsx */

View File

@@ -0,0 +1,51 @@
import { useCallback } from "react";
import { ListOfTasks, TodoProject } from "./1_schema";
import { SubmittableInput } from "./basicComponents";
import { useNavigate } from "react-router";
import { useAccount } from "./2_main";
import { Group } from "jazz-tools";
export function NewProjectForm() {
// `me` represents the current user account, which will determine
// access rights to CoValues. We get it from the top-level provider `<WithJazz/>`.
const { me } = useAccount();
const navigate = useNavigate();
const createProject = useCallback(
(title: string) => {
if (!title) return;
// To create a new todo project, we first create a `Group`,
// which is a scope for defining access rights (reader/writer/admin)
// of its members, which will apply to all CoValues owned by that group.
const projectGroup = Group.create({ owner: me });
// Then we create an empty todo project within that group
const project = TodoProject.create(
{
title,
tasks: ListOfTasks.create([], { owner: projectGroup }),
},
{ owner: projectGroup },
);
me.root?.projects?.push(project);
navigate("/project/" + project.id);
},
[me, navigate],
);
return (
<SubmittableInput
onSubmit={createProject}
label="Create New Project"
placeholder="New project title"
/>
);
}
/** Walkthrough: continue with ./4_ProjectTodoTable.tsx */

Some files were not shown because too many files have changed in this diff Show More