Compare commits

...

694 Commits

Author SHA1 Message Date
Anselm
87ef6d5064 Pre-release 2024-09-22 09:06:01 -07:00
Anselm
e2ec51d3db Merge branch 'trishalim-jazz-311' 2024-09-22 08:50:17 -07:00
Anselm Eickhoff
566be392da Merge pull request #412 from gardencmp/trishalim-jazz-312
Fix footer overlaps side nav in docs
2024-09-22 08:47:08 -07:00
Anselm
7497ed2a7c Clean up nav related components and fix footer after botched merge 2024-09-22 07:16:36 -07:00
Anselm
13b0dfc38f Merge branch 'main' into trishalim-jazz-312 2024-09-22 07:09:52 -07:00
Anselm Eickhoff
1debe44cf1 Merge pull request #424 from gardencmp/feature/binarycostream-reconnection-test
test(BinaryCoStream): add a reconnection e2e test
2024-09-22 06:53:07 -07:00
Anselm Eickhoff
b737e14069 Merge pull request #411 from gardencmp/trishalim-jazz-311
Create separate pages for each section in the docs
2024-09-20 11:45:55 -07:00
Anselm
82fd148370 Add static params for packages 2024-09-20 10:58:12 -07:00
Guido D'Orsi
d0fc6dbc13 test(BinaryCoStrea): add a reconnection e2e test 2024-09-20 18:35:01 +02:00
Trisha Lim
f588c3f265 Move packages documentation to separate routes 2024-09-20 17:26:43 +01:00
Anselm Eickhoff
659e80bbf4 Merge pull request #356 from gardencmp/anselm-jazz-284
Design system setup
2024-09-20 16:15:50 +01:00
Anselm
3585e2acde Fix lint and dep errors 2024-09-20 07:44:13 -07:00
Anselm
98554523c8 Kind of start using components on the docs/guide page 2024-09-20 14:45:04 +01:00
Anselm
dbeaa35110 Use/extract components for mesh page 2024-09-20 14:30:11 +01:00
Anselm
61c719c728 Merge branch 'main' into anselm-jazz-284 2024-09-20 14:05:07 +01:00
Anselm Eickhoff
48fddecd30 Merge pull request #420 from gardencmp/trishalim-jazz-313 2024-09-20 10:57:49 +01:00
Trisha Lim
f5779077df Fix horizontal overflow on tablet in docs 2024-09-19 18:43:26 +01:00
Trisha Lim
5149fb6fbf Fix horizontal overflow on tablet in docs 2024-09-19 17:51:16 +01:00
Trisha Lim
dcc6134180 Clip text that is too long 2024-09-19 16:28:17 +01:00
Trisha Lim
ecb336829d Fix footer overlaps side nav in docs 2024-09-19 16:28:17 +01:00
Trisha Lim
2778f919a6 Use dynamic route for packages 2024-09-19 16:05:25 +01:00
Trisha Lim
ab6fcc7a7a Add metadata to each package page 2024-09-19 12:29:37 +01:00
Trisha Lim
aa284802ab Use Link instead of anchor tag 2024-09-19 11:40:15 +01:00
Trisha Lim
a6af56bc2e Add section for "Simple Public Sharing" 2024-09-19 11:36:29 +01:00
Anselm Eickhoff
751f0b4d10 Merge pull request #410 from gardencmp/feature/fix-playlist-invitation-add 2024-09-19 08:30:03 +01:00
Trisha Lim
a233eb6396 Remove sections that don't exist 2024-09-18 21:01:52 +01:00
Trisha Lim
c91d9b99be Move packages documentation to separate routes 2024-09-18 20:52:58 +01:00
Guido D'Orsi
a5b798a257 fix(musicPlayer): fix the playlist check on invitation 2024-09-18 21:48:44 +02:00
Anselm Eickhoff
18b71442ca Merge pull request #394 from gardencmp/gudorsi-jazz-300
fix(BinaryCoStream): set the stream content message as low priority in the WS outgoing channel
2024-09-18 17:36:52 +01:00
Guido D'Orsi
f350e902a8 changeset 2024-09-18 18:35:39 +02:00
Guido D'Orsi
38a6447887 fix(priority): group detection and only set binary costream as low 2024-09-18 18:29:27 +02:00
Anselm Eickhoff
39da70558b Merge pull request #408 from gardencmp/trishalim-jazz-310
Separate chat code example to 2 tabs
2024-09-18 16:50:34 +01:00
Anselm Eickhoff
73f62966ed Merge pull request #405 from gardencmp/trishalim-jazz-309
Change mono font to Commit Mono
2024-09-18 16:47:35 +01:00
Trisha Lim
c41e035b67 Change mono font to Commit Mono 2024-09-18 16:43:05 +01:00
Trisha Lim
f4c466ea30 Change mono font to IBM Plex Mono 2024-09-18 16:43:05 +01:00
Trisha Lim
91e9a267f0 Match accent color with Jazz logo 2024-09-18 16:33:53 +01:00
Trisha Lim
d1ec24a05b Dark mode styling 2024-09-18 16:28:38 +01:00
Guido D'Orsi
657159e034 chore(PeerState): sort attributes declaration 2024-09-18 17:20:41 +02:00
Guido D'Orsi
0327913e53 chore: comments 2024-09-18 17:19:06 +02:00
Trisha Lim
5ad9d652be Fix build, rewrite ChatCodeExample to CodeExampleTabs 2024-09-18 16:16:02 +01:00
Guido D'Orsi
3cde94a94b chore: fix sync.test.ts type errors 2024-09-18 16:59:20 +02:00
Guido D'Orsi
a7f036c71f Merge remote-tracking branch 'origin/main' into gudorsi-jazz-300 2024-09-18 16:58:21 +02:00
Guido D'Orsi
20b47ad8af feat: simpler priority system and extract content priority from headers 2024-09-18 16:55:05 +02:00
Trisha Lim
6f2eac383d Styling code area 2024-09-18 15:43:27 +01:00
Trisha Lim
5d40e83297 Design tabs for chat code example 2024-09-18 14:19:57 +01:00
Anselm Eickhoff
7721ccffaf Merge pull request #403 from gardencmp/JAZZ-308/check-example-app-urls
fix(examples): updated URLs for example apps
2024-09-17 14:24:56 +01:00
pax-k
aa7edc8e3f fix(examples): updated URLs for example apps 2024-09-17 16:23:33 +03:00
Anselm Eickhoff
bba1728988 Merge pull request #399 from gardencmp/JAZZ-305/integrate-test-coverage
feat: added test coverage
2024-09-17 09:16:24 +01:00
Guido D'Orsi
f1dff3e3fd test: fix sync.test.ts tests 2024-09-16 23:47:48 +02:00
Guido D'Orsi
d5604b9eb5 chore: generalize PriorityBasedMessageQueue 2024-09-16 23:40:16 +02:00
Guido D'Orsi
3acf61e2c3 feat: move the priority management from the websocket peer to the peer state 2024-09-16 23:37:57 +02:00
Guido D'Orsi
3a0d25d048 feat: extract PeerState and refactor it into a class 2024-09-16 23:37:57 +02:00
Guido D'Orsi
28720043a4 chore(cojson-transport-ws): add tests and split code 2024-09-16 23:37:57 +02:00
Guido D'Orsi
79491353da fix: fixes errors in sync due the extra push param 2024-09-16 23:37:57 +02:00
Guido D'Orsi
35eb8829ea feat(cojson-transport-ws): interleaved weighed round robin 2024-09-16 23:37:57 +02:00
Guido D'Orsi
5cd38d5974 fix(BinaryCoStream): set the stream content message as low priority in the WS outgoing stream 2024-09-16 23:37:57 +02:00
pax-k
30f1fa1557 feat: added test coverage 2024-09-16 20:48:18 +03:00
Anselm Eickhoff
fe035c3b3f Merge pull request #392 from gardencmp/feature/BinaryCoStream-e2e-ci+
chore: run e2e/BinaryCoStream tests on CI
2024-09-16 16:31:02 +01:00
Guido D'Orsi
1efe8ee6c7 chore: run e2e/BinaryCoStream tests on CI 2024-09-12 18:36:15 +02:00
Guido D'Orsi
f410a8540b fix: in the BinaryCoStream e2e test retry failed subscribes 2024-09-12 18:35:37 +02:00
Anselm Eickhoff
acb6249292 Merge pull request #385 from gardencmp/feature/sync-duration-test 2024-09-11 09:46:01 +01:00
Guido D'Orsi
77c38a1c4b chore(ci): remove BinaryCoStream from the CI tests 2024-09-10 18:14:11 +02:00
Guido D'Orsi
6e1d56a9b0 feat: add more size choices and reduce the default to 1KB 2024-09-10 18:14:11 +02:00
Guido D'Orsi
d10a7b9c76 test: e2e tests for the BinaryCoStream sync 2024-09-10 18:14:11 +02:00
Anselm Eickhoff
209f295399 Merge pull request #388 from gardencmp/feature/auth-laoding 2024-09-09 18:39:10 +01:00
Guido D'Orsi
cac2ec9aa1 changeset 2024-09-09 19:06:43 +02:00
Guido D'Orsi
30a9e7a94f fix(jazz-react): mark the auth as loading when authState is not ready 2024-09-09 19:05:52 +02:00
Anselm Eickhoff
af5adc37ca Merge pull request #386 from gardencmp/fix/music-player-example 2024-09-09 11:51:04 +01:00
Guido D'Orsi
af12226998 fix(musicPlayer): move the upload of example data outside of the initial migration 2024-09-09 12:24:27 +02:00
Anselm Eickhoff
b8bf440f84 Merge pull request #384 from gardencmp/fix-lockfile 2024-09-06 17:31:58 +01:00
Guido D'Orsi
5198a249d1 fix: align deps 2024-09-06 16:51:46 +02:00
Guido D'Orsi
918322bc4a fix lockfile 2024-09-06 16:50:30 +02:00
Anselm Eickhoff
b66791d81a Merge pull request #383 from gardencmp/feature/monorepo-improvements
test(deepLoading): fix skipped test
2024-09-06 15:47:04 +01:00
Anselm Eickhoff
a2fbd9ed22 Merge pull request #334 from gardencmp/feature/music-player-example
feat(example): add a music player example
2024-09-06 15:34:17 +01:00
Guido D'Orsi
89111c5bf3 test(deepLoading): fix skipped test 2024-09-06 16:05:41 +02:00
Anselm Eickhoff
d4a358d0f9 Merge pull request #382 from gardencmp/feature/monorepo-improvements 2024-09-05 16:53:43 +01:00
Guido D'Orsi
daf24d65f6 chore: fix or skip failing tests 2024-09-05 17:44:50 +02:00
Guido D'Orsi
9517cef183 chore: fix monorepo linting action 2024-09-05 17:38:17 +02:00
Guido D'Orsi
f70950cc82 feat(ci): run unit tests on PR 2024-09-05 17:17:53 +02:00
Guido D'Orsi
ec3dffa3e4 feat: add sherif to check monorepo dependencies 2024-09-05 17:17:35 +02:00
Guido D'Orsi
207bea3c1c tiny typo 2024-09-04 23:55:33 +02:00
Guido D'Orsi
fcae20d77c feat(musicPlayer): walkthrough until 3_actions 2024-09-04 23:54:22 +02:00
Guido D'Orsi
0a542941ad feat: add docs on the schema file 2024-09-04 23:54:06 +02:00
Guido D'Orsi
5dceef8c03 fix: avoid adding duplicate playlists when accepting 2024-09-04 23:54:06 +02:00
Guido D'Orsi
fb01eb42f1 fix: remove the example audio auto-delete 2024-09-04 23:54:06 +02:00
Guido D'Orsi
e1e3a8352e docs: update the README.md and cleanup the changelog 2024-09-04 23:54:06 +02:00
Guido D'Orsi
09678c4277 chore: merge jazz and main 2024-09-04 23:54:06 +02:00
Guido D'Orsi
5b83669368 feat: other UI improvements 2024-09-04 23:54:06 +02:00
Guido D'Orsi
30c8c922a3 feat: improve the click areas and add the Space keybinding 2024-09-04 23:54:06 +02:00
Guido D'Orsi
e05b327eb5 feat: player controls, example audio and ui fixes 2024-09-04 23:54:06 +02:00
Guido D'Orsi
06665cbbf7 feat: improve the add to playlist section nand invitation flow 2024-09-04 23:54:06 +02:00
Guido D'Orsi
c0425df2ee fix: make the playlist invite work 2024-09-04 23:54:06 +02:00
Guido D'Orsi
42cbef8063 feat(music-player): implement playlist page and sharing 2024-09-04 23:54:06 +02:00
Guido D'Orsi
d1abf06621 feat: improve the playing stability and clean up the code 2024-09-04 23:54:06 +02:00
Guido D'Orsi
1c00020859 feat: implement the basic interactions with the player (upload and play) 2024-09-04 23:54:06 +02:00
Guido D'Orsi
00dd9cea99 feat: setup the boilerplate for the musicPlayer example 2024-09-04 23:54:04 +02:00
Anselm Eickhoff
607cd0ab97 Merge pull request #367 from gardencmp/gudorsi-jazz-141 2024-09-04 21:06:05 +01:00
Anselm Eickhoff
9f65e56b21 Update forty-plants-kiss.md 2024-09-04 21:05:13 +01:00
Anselm Eickhoff
82c5d1bf5c Merge pull request #376 from gardencmp/JAZZ-295/implement-password-manager-demo 2024-09-04 16:49:49 +01:00
pax-k
deda3e571f fix(password-manager): tsconfig 2024-09-04 18:03:43 +03:00
Anselm Eickhoff
2d4039c5d2 Merge pull request #378 from gardencmp/JAZZ-296/data-in-inspector-flashes-into-view-then-disappears
fix(inspector): subscribe to latent covalues instead of loading them immediately
2024-09-04 14:24:52 +01:00
pax-k
63dd9190a2 chore: format 2024-09-04 16:16:33 +03:00
pax-k
3dfa630923 chore: cleanup 2024-09-04 16:08:08 +03:00
pax-k
63daf6af09 chore: changeset 2024-09-04 16:00:42 +03:00
pax-k
c85ccd96aa fix(inspector): subscribe to latent covalues instead of loading them immediately 2024-09-04 15:58:48 +03:00
Guido D'Orsi
403b430ee4 chore: changeset 2024-09-03 18:34:04 +02:00
pax-k
f58845ef74 fix(password-manager): correct main script reference 2024-09-03 17:19:23 +03:00
pax-k
38fad35945 chore: password manager demo cleanup 2024-09-03 15:58:40 +03:00
pax-k
2533740a7c feat: password manager demo (wip) 2024-09-03 13:21:20 +03:00
Guido D'Orsi
109a7d6128 chore: use Effect.never to keep the server up, remove LocalNode comment 2024-09-03 11:45:10 +02:00
Anselm Eickhoff
ec1906e262 Merge pull request #368 from gardencmp/JAZZ-278/investigate-bug-when-assigning-null-values-to-corefs-in-applydiff 2024-09-03 09:24:46 +01:00
pax-k
6f8028253d chore: changeset 2024-09-02 17:50:54 +03:00
pax-k
0704a76006 fix: handle null values for co.refs 2024-09-02 16:49:53 +03:00
Anselm Eickhoff
ab93a0b679 Fix #353 2024-09-02 12:11:24 +01:00
Guido D'Orsi
ed85560547 feat: rename command and add persistence 2024-09-02 11:36:52 +02:00
Guido D'Orsi
a242adf3b3 docs: update cojson-simple-sync command 2024-09-01 17:12:53 +02:00
Guido D'Orsi
1a9c7ecefd feat(jazz-run): add a command to spin a local sync server 2024-09-01 17:10:29 +02:00
Anselm Eickhoff
aded528a27 Merge pull request #357 from gardencmp/gudorsi-jazz-281 2024-08-31 17:50:00 +01:00
Guido D'Orsi
152ae9865e chore(ci): debug error with artifacts 2024-08-31 18:21:24 +02:00
Guido D'Orsi
8bd92b44f5 chore(ci): increase expect timeout to 15000 on CI 2024-08-31 17:06:11 +02:00
Guido D'Orsi
8d15e04fd0 chore(ci): fix Install Playwright Browsers 2024-08-30 19:41:07 +02:00
Guido D'Orsi
b7f3ece0f1 chore: fix action indentation 2024-08-30 19:38:32 +02:00
Guido D'Orsi
4c94ae3da2 test(pets): test incoming reaction 2024-08-30 19:34:14 +02:00
Guido D'Orsi
cd23ce0a51 feat: github action for playwright tests 2024-08-30 19:19:07 +02:00
Guido D'Orsi
8538036af7 test(pets): use pom 2024-08-30 19:13:35 +02:00
Anselm
6ee7133924 Homepage mostly extracted 2024-08-30 16:23:28 +01:00
Anselm
4fa19ece52 Pull nav into design system 2024-08-29 19:22:21 +01:00
Anselm
875ac4b84f An attempt to pull out copy and make the landign page tsx again 2024-08-29 19:09:53 +01:00
Anselm
346e797447 Pull out more stuff 2024-08-29 18:51:17 +01:00
Anselm
7f6d778301 First extraction 🏗️ 2024-08-29 11:20:17 +01:00
Guido D'Orsi
40287a5682 feat(pets): add an e2e test suite 2024-08-29 02:26:28 +02:00
Anselm
bcfe4b794e Basic connection to design system 2024-08-28 19:03:51 +01:00
Anselm
7a27193ceb Include design system in homepage pnpm workspace 2024-08-28 18:36:36 +01:00
Anselm
6db14cefa0 Merge remote-tracking branch 'sammii-design-system/main' into anselm-jazz-284 2024-08-28 18:33:32 +01:00
Anselm
b57e1a64ac Move into subfolders in preparation of move into monorepo 2024-08-28 18:29:38 +01:00
Sammii
a1b83cceea whipping up a design system 2024-08-28 17:59:39 +01:00
Sammii
cd29438304 create next app 2024-08-28 15:01:15 +01:00
Samantha Haylock
7695a817ca first commit 2024-08-27 10:41:32 +01:00
Anselm Eickhoff
2ddce1a2de Merge pull request #327 from gardencmp/gudorsi-jazz-254 2024-08-22 19:36:30 +01:00
Guido D'Orsi
49a8b54a8a Add changesets 2024-08-22 20:33:51 +02:00
Anselm Eickhoff
04b15c7d4a Merge pull request #345 from gardencmp/gudorsi-jazz-261 2024-08-22 18:37:37 +01:00
Guido D'Orsi
5909e7e894 fix(pets): set the ownership of reactions to the write group 2024-08-22 19:25:30 +02:00
Anselm Eickhoff
7a3d519970 Merge pull request #337 from gardencmp/feat/jazz-255-loadasblob
fix(BinaryCoStream): resolve the loadAsBlob call only when the stream id ended
2024-08-22 15:00:13 +01:00
Anselm
35bbcd94e6 Add changesets 2024-08-22 14:59:40 +01:00
Anselm Eickhoff
f6629b2b58 Merge pull request #336 from gardencmp/anselm-jazz-264
Adopt neverthrow throughout cojson
2024-08-22 14:34:45 +01:00
Anselm
b4891db9fa Release 2024-08-22 14:31:02 +01:00
Anselm
4e16575f97 Hotifx release 2024-08-22 14:29:31 +01:00
Anselm
12ab20ecd9 Use queueable fork 2024-08-22 14:29:03 +01:00
Anselm
2c3a40c94b Hotfix release 2024-08-22 14:06:35 +01:00
Anselm
0d8175ba1c Use fork of queueable 2024-08-22 14:06:08 +01:00
Anselm Eickhoff
311ed74709 Delete oldExamples directory 2024-08-22 13:45:57 +01:00
Anselm Eickhoff
c86301fcfd Delete oldPackages/jazz-browser-auth0 directory 2024-08-22 13:45:47 +01:00
Anselm
032f69f692 Fix jazz-run deps even more 2024-08-22 12:49:34 +01:00
Anselm
6dc52b2a6d Fix jazz-run dependencies 2024-08-22 12:47:35 +01:00
Anselm
1232c0240a Hotifx release 2024-08-22 12:03:33 +01:00
Anselm
55fa74f44a Hack to make jazz-run create account work 2024-08-22 12:02:50 +01:00
Guido D'Orsi
627e04151d fix(CoMapInit): type validation on falsy values for required refs 2024-08-22 02:35:58 +02:00
Anselm
5d91f9f8dc Hotifx release 2024-08-21 16:20:55 +01:00
Anselm
08f1f77834 Stop using tryAddTransactionsAsync 2024-08-21 16:20:35 +01:00
Anselm
ea882aba63 Hotfix release 2024-08-21 15:59:16 +01:00
Anselm
513a78ab9b Better logging for failed transactions 2024-08-21 15:58:38 +01:00
Anselm
406ab9b0da Hotfix release 2024-08-21 15:24:27 +01:00
Anselm
140f6616cb Remove WS buffer filling log message 2024-08-21 15:23:41 +01:00
Anselm
b09589b15e Hotfix release 2024-08-21 11:39:18 +01:00
Anselm
00638897f4 only one async transaction per covalue again 2024-08-21 11:36:47 +01:00
Anselm Eickhoff
4cd68c1930 minimal inspector readme 2024-08-21 11:20:39 +01:00
Guido D'Orsi
f1f3607e28 test(BinaryCoStream): add tests for loadAsBlob 2024-08-20 19:46:13 +02:00
Guido D'Orsi
7b9f503b9e test(coStream): add tests for isBinaryStreamEnded 2024-08-20 19:35:11 +02:00
Guido D'Orsi
a18f44399c fix(BinaryCoStream): resolve the loadAsBlob call only when the stream is ended 2024-08-20 19:20:36 +02:00
Anselm
5094e6d536 Hotfix release 2024-08-20 18:03:01 +01:00
Anselm
39242e7f68 Start introducing neverthrow, make tryAddNewTransactionsAsync and handleNewContent less throwy 2024-08-20 18:02:26 +01:00
Anselm Eickhoff
be7e208b1c Merge pull request #333 from gardencmp/anselm-jazz-265
Make sure errors in storage peer completely crash jazz context instead of failing silently
2024-08-20 14:09:19 +01:00
Anselm
c3bffbf4de Release 2024-08-20 14:02:53 +01:00
Anselm
d46467f318 Hotfix release 2024-08-20 12:58:09 +01:00
Anselm
6d21400803 Get rid of simulated errors 2024-08-20 12:57:50 +01:00
Anselm
db53161296 Hotfix release 2024-08-20 12:46:15 +01:00
Anselm
cb4a116cec Make simulated errors even more likely 2024-08-20 12:45:34 +01:00
Anselm
013199b9b2 Increase chance of simulated storage bug 2024-08-20 11:43:28 +01:00
Anselm
a8b74ff703 Hotfix release 2024-08-20 11:43:20 +01:00
Anselm
b1985a9161 Throw properly on peer that should crash on close 2024-08-20 11:42:53 +01:00
Anselm
3bf512719f Hotfix release 2024-08-20 11:22:44 +01:00
Anselm
d83ed69d41 Allow crashing whole local node on peer errors 2024-08-20 11:20:20 +01:00
Anselm
fdde8db664 Hotfix release 2024-08-19 17:19:40 +01:00
Anselm
dd5581ba2d Handle ws closing while buffering 2024-08-19 17:19:01 +01:00
Anselm
07fe2b9dcf Release 2024-08-19 16:45:35 +01:00
Anselm
b297c93b80 Release fixes 2024-08-19 15:08:08 +01:00
Anselm
d2e62e5b44 Reduce log level on loading message 2024-08-19 14:50:26 +01:00
Anselm
fe73ce7514 Make failed transaction log message leaner 2024-08-19 14:48:57 +01:00
Anselm Eickhoff
0fed16cea4 Merge pull request #323 from gardencmp/callumflack-jazz-240
fix: chat example URL
2024-08-19 11:15:39 +01:00
Callum Flack
08804ad435 fix: chat example URL 2024-08-19 11:03:33 +10:00
Anselm
79fa7724ad Release 2024-08-15 19:35:11 +01:00
Anselm
4604c2184a Adapt type of applyDiff to make CoMaps fully subclassable again 2024-08-15 19:34:47 +01:00
Anselm
11bac697fb Release 2024-08-15 19:23:19 +01:00
Anselm
96cec27f89 Close both ends of the peer on gracefulShutdown 2024-08-15 19:22:59 +01:00
Anselm
bcd412b8f9 Properly close connecting websockets 2024-08-15 19:22:19 +01:00
Anselm
6b456e2841 Properly close and delete peer on incoming disconnect/timeout 2024-08-15 19:10:22 +01:00
Anselm
1df72b3dc8 Reintroduce nomad example deploy, but only on main 2024-08-15 19:08:42 +01:00
Anselm
402d692739 Don't deploy examples to Nomad anymore 2024-08-15 17:02:40 +01:00
Anselm
fad46b2fb5 Release 2024-08-15 16:37:47 +01:00
Anselm
0153c80cf2 Immediately resolve an already-open websocket 2024-08-15 16:37:23 +01:00
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
596 changed files with 56135 additions and 62767 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

@@ -3,26 +3,45 @@ name: Build and Deploy
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
build-examples:
runs-on: ubuntu-latest
strategy:
matrix:
example: ["todo", "pets", "twit", "file-drop"]
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: actions/setup-node@v3
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
cache: 'yarn'
cache-dependency-path: yarn.lock
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
@@ -34,14 +53,10 @@ jobs:
username: gardencmp
password: ${{ secrets.GITHUB_TOKEN }}
- name: Nuke Workspace
- name: Pnpm Build
run: |
rm package.json yarn.lock;
- name: Yarn Build
run: |
yarn install --frozen-lockfile;
yarn build;
pnpm install
pnpm turbo build;
working-directory: ./examples/${{ matrix.example }}
- name: Docker Build & Push
@@ -53,12 +68,13 @@ jobs:
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
deploy-examples:
runs-on: ubuntu-latest
needs: build
needs: build-examples
strategy:
matrix:
example: ["todo", "pets", "twit", "file-drop"]
example: ["chat", "pets", "todo", "inspector"]
# example: ["twit", "chat", "counter-js-auth0", "pets", "twit", "file-drop", "inspector"]
steps:
- uses: actions/checkout@v3
@@ -86,5 +102,5 @@ jobs:
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;
NOMAD_ADDR=${{ secrets.NOMAD_ADDR }} nomad job run job-instance.nomad;
working-directory: ./examples/${{ matrix.example }}

21
.github/workflows/monorepo-linting.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Monorepo linting
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
monorepo-linting:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.node-version'
- name: Run sherif
run: npx sherif@1.0.0

64
.github/workflows/playwright.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Playwright Tests
on:
push:
branches: ["main"]
pull_request:
types: [opened, synchronize, reopened]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
matrix:
project: ["e2e/BinaryCoStream", "examples/pets"]
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Pnpm Build
run: pnpm turbo build;
working-directory: ./${{ matrix.project }}
- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
working-directory: ./${{ matrix.project }}
- name: Run Playwright tests
run: pnpm exec playwright test
working-directory: ./${{ matrix.project }}
- uses: actions/upload-artifact@v4
if: failure()
with:
name: ${{ hashFiles(format('{0}/package.json', matrix.project)) }}-playwright-report
path: ./${{ matrix.project }}/playwright-report/
retention-days: 30

47
.github/workflows/unit-test.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: Unit Tests
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- 'main'
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Pnpm Build
run: pnpm turbo build;
- name: Unit Tests
run: pnpm test

5
.gitignore vendored
View File

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

1
.node-version Normal file
View File

@@ -0,0 +1 @@
20

View File

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

15815
DOCS.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
Copyright 2023, Garden Computing, Inc.
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

118
README.md
View File

@@ -1,116 +1,12 @@
# Jazz - instant sync
<sub>Homepage: [jazz.tools](https://jazz.tools) &mdash; Docs: [DOCS.md](./DOCS.md) &mdash; Community & support: [Discord](https://discord.gg/utDMjHYg42) &mdash; Updates: [Twitter](https://twitter.com/jazz_tools) & [Email](https://gcmp.io/news)</sub>
**Jazz is an open-source toolkit for building apps with *secure sync.***
Quickly build and ship apps with:
- **Cross-device sync**
- **Collaborative features** (incl. real-time multiplayer)
- **Instantly reacting UIs**
- Local-first storage & offline support
- File upload and real-time media streaming
# What is *secure sync*?
**Sync** means that, *instead of making API requests*, you:
- **Read and write data as if it was local** &mdash; from anywhere in your app.
- **Always have data synced to wherever it's needed, instantly:** to other devices of the same user, to other users, to your backend, to your local machine for debugging, etc.
**Secure** means that, *instead of relying on your API or DB for access control*, you:
- **Set fine-grained, role-based permissions in `Group`s** that are **synced along with your data**.
- **Permissions *verifiably enforced* everywhere,** using encryption & signatures under the hood.
- **Change roles dynamically** for evolving teams, expiring invite links and more.
# What's special about Jazz?
Compared to other libraries and frameworks for local-first, sync-based or real-time apps, these are some of the things that make Jazz unique:
- **Jazz is a *batteries-included,* vertically integrated toolkit,** offering everything you need to build an app, including auth, permissions, data model, sync, conflict resolution, blob storage, file uploads, real-time media streaming and more.
- **Jazz has a *small API surface* of only a few abstractions to learn,** which combine in powerful ways to implement a broad set of features.
- **Jazz *granularly* loads and caches *only the data that is needed*,** combining *local-first* instant UI reactivity and offline support with the on-demand data efficiency of conventional APIs
- **Jazz supports end-to-end encryption, but doesn't require it,** allowing you to either manage your user's secret keys for them (based on existing auth flows) or letting your users
- **Jazz is based on CoJSON, a soon-to-be *open standard,*** which means that there will be a whole ecosystem of compatible libraries and frameworks in a variety of environments &mdash; and it will be easy to achieve (secure) interop between Jazz/CoJSON-based apps and services.
# Jazz Global Mesh
Jazz is open source and you can run your own sync & storage server, but to really provide you with everything you need, we're also running
**[Jazz Global Mesh](https://jazz.tools/mesh)**, a globally distributed mesh of servers optimized for:
- **Ultra-low-latency sync** (with geo-aware edge caching and optimal routing)
- **Low-cost, reliable storage**
# Jazz - Instant sync
**Jazz Global Mesh is free for small volumes of data** and it's the **default syncing peer,** so you can **start building multi-user Jazz apps with persistent data in minutes,** using only frontend code!
# Getting started
**Jazz is an open-source toolkit for building apps with *distributed state.***
## Example App Walkthrough
- 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)
**For now the best tutorial is the walkthrough of the [Todo List Example App](#todo-list).**
## General Scenarios
### Building a new, entirely sync-based React app
1. Define your data model with [cojson Collaborative Values (CoValues)](./DOCS.md#covalue).
2. Implement permission logic using [cojson Groups](./DOCS.md#group).
3. Build a user interface with [jazz-react](./DOCS.md#jazz-react) and [auto-sub](./DOCS.md#useautosubid).
### Gradually adding sync to an existing React app
Gradually migrate app features to use sync:
1. Define data model for small aspect of your app with [cojson Collaborative Values (CoValues)](./DOCS.md#covalue).
- Schema adapters/importers for Prisma/Drizzle/PostgreSQL introspection coming soon.
2. Map existing permission logic with [cojson Groups](./DOCS.md#group) & integrate existing auth.
- Auth integrations coming soon.
3. Replace some of the React state and API requests in your UI with [jazz-react](./DOCS.md#jazz-react) and [auto-sub](./DOCS.md#useautosubid).
# Example Apps
## Todo List
**A simple collaborative todo list app.**
Live version: https://example-todo.jazz.tools
Source code & walkthrough: [`./examples/todo`](./examples/todo)
Demonstrates:
- Defining a data model with `CoMap`s and `CoList`s
- Creating data and setting permissions with `Group`s
- Fetching, rendering & editing data from nested `CoValue`s with reactive synced queries
## Rate-My-Pet
**A simple social polling app.**
Live version: https://example-pets.jazz.tools
Source code (walkthrough coming soon): [`./examples/pets`](./examples/pets)
Demonstrates:
- Implementing per-account data streams (reactions) with `CoStream`s
- Implementing image upload and progressive image streaming using helpers from `jazz-react-media-images` (on top of CoJSON's `BinaryCoStreams` & `ImageDefinition` convention)
# Documentation & API Reference
For now, docs are hosted in a single well-structured markdown file: [`./DOCS.md`](./DOCS.md).
- [Package Overview](./DOCS.md#overview)
- [`jazz-react` API](./DOCS.md#jazz-react)
- [`cojson` API](./DOCS.md#cojson)
- [`jazz-browser-media-images` API](./DOCS.md#jazz-browser-media-images)
In the future we'll build a dedicated docs page on the Jazz homepage.
----
Copyright 2023 &mdash; Garden Computing, Inc.
Copyright 2024 &mdash; Garden Computing, Inc.

30
e2e/BinaryCoStream/.gitignore vendored Normal file
View File

@@ -0,0 +1,30 @@
# 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?
sync-db/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/

View File

@@ -3,11 +3,12 @@
<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 File Drop Example</title>
<title>Jazz Chat Example</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/2_main.tsx"></script>
<script type="module" src="/src/app.tsx"></script>
</body>
</html>

View File

@@ -0,0 +1,34 @@
{
"name": "@jazz-e2e/binarycostream",
"private": true,
"version": "0.0.81",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "playwright test",
"test:ui": "playwright test --ui"
},
"lint-staged": {
"*.{js,jsx,mdx,json}": "prettier --write"
},
"dependencies": {
"cojson": "workspace:*",
"hash-slash": "workspace:*",
"is-ci": "^3.0.1",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@playwright/test": "^1.46.1",
"@types/node": "^22.5.1",
"@types/react": "^18.2.19",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-react-swc": "^3.3.2",
"typescript": "^5.3.3",
"vite": "^5.0.10"
}
}

View File

@@ -0,0 +1,49 @@
import { defineConfig, devices } from "@playwright/test";
import isCI from "is-ci";
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./tests",
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: isCI,
/* Retry on CI only */
retries: isCI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: isCI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: isCI ? "http://localhost:4173/" : "http://localhost:5173",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
/* Run your local dev server before starting the tests */
webServer: isCI ? {
command: "pnpm preview",
url: "http://localhost:4173/",
} : undefined,
});

View File

@@ -0,0 +1,42 @@
import { Account, BinaryCoStream, ID } from "jazz-tools";
import { useEffect } from "react";
import { useAccount, useCoState } from "./jazz";
import { UploadedFile } from "./schema";
import { waitForCoValue } from "./lib/waitForCoValue";
async function getUploadedFile(
me: Account,
uploadedFileId: ID<UploadedFile>) {
const uploadedFile = await waitForCoValue(UploadedFile, uploadedFileId, me, Boolean, {})
uploadedFile.coMapDownloaded = true;
await BinaryCoStream.loadAsBlob(uploadedFile._refs.file.id, me);
return uploadedFile;
}
export function DownloaderPeer(props: { testCoMapId: ID<UploadedFile> }) {
const account = useAccount();
const testCoMap = useCoState(UploadedFile, props.testCoMapId, {});
useEffect(() => {
getUploadedFile(account.me, props.testCoMapId).then(value => {
value.syncCompleted = true;
});
}, []);
return (
<>
<h1>Downloader Peer</h1>
<div>Fetching: {props.testCoMapId}</div>
<div data-testid="result">
Covalue: {Boolean(testCoMap?.id) ? "Downloaded" : "Not Downloaded"}
</div>
<div data-testid="result">
File:{" "}
{Boolean(testCoMap?.syncCompleted) ? "Downloaded" : "Not Downloaded"}
</div>
</>
);
}

View File

@@ -0,0 +1,86 @@
import { ID } from "jazz-tools";
import { useEffect, useState } from "react";
import { useAccount, useCoState } from "./jazz";
import { createCredentiallessIframe } from "./lib/createCredentiallessIframe";
import { generateTestFile } from "./lib/generateTestFile";
import { getDownloaderPeerUrl } from "./lib/getDownloaderPeerUrl";
import { UploadedFile } from "./schema";
import { waitForCoValue } from "./lib/waitForCoValue";
import { getDefaultFileSize, getIsAutoUpload } from "./lib/searchParams";
import { BytesRadioGroup } from "./lib/BytesRadioGroup";
export function UploaderPeer() {
const account = useAccount();
const [uploadedFileId, setUploadedFileId] = useState<
ID<UploadedFile> | undefined
>(undefined);
const [syncDuration, setSyncDuration] = useState<number | null>(null);
const [bytes, setBytes] = useState(getDefaultFileSize);
const testFile = useCoState(UploadedFile, uploadedFileId, {});
async function uploadTestFile() {
if (!account) return;
// Mark the sync start
performance.mark("sync-start");
const file = await generateTestFile(account.me, bytes);
// Create a credential-less iframe to spawn the downloader peer
const iframe = createCredentiallessIframe(getDownloaderPeerUrl(file));
document.body.appendChild(iframe);
setSyncDuration(null);
setUploadedFileId(file.id);
// The downloader peer will set the syncCompleted to true when the download is complete.
// We use this to measure the sync duration.
await waitForCoValue(
UploadedFile,
file.id,
account.me,
(value) => value.syncCompleted,
{}
);
iframe.remove();
// Calculate the sync duration
performance.mark("sync-end");
const measure = performance.measure("sync", "sync-start", "sync-end");
setSyncDuration(measure.duration);
}
useEffect(() => {
if (getIsAutoUpload()) {
uploadTestFile();
}
}, []);
return (
<>
<BytesRadioGroup selectedValue={bytes} onChange={setBytes} />
<button onClick={uploadTestFile}>Upload Test File</button>
{uploadedFileId && <div>{uploadedFileId}</div>}
{syncDuration && (
<div data-testid="sync-duration">
Sync Duration: {syncDuration.toFixed(2)}ms
</div>
)}
{uploadedFileId && (
<div data-testid="result">
Sync Completed: {String(Boolean(syncDuration))}
</div>
)}
{testFile?.coMapDownloaded && (
<div data-testid="co-map-downloaded">
CoMap synced!
</div>
)}
</>
);
}

View File

@@ -0,0 +1,24 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { DownloaderPeer } from "./DownloaderPeer";
import { Jazz } from "./jazz";
import { UploaderPeer } from "./UploaderPeer";
import { getValueId } from "./lib/searchParams";
function Main() {
const valueId = getValueId();
if (valueId) {
return <DownloaderPeer testCoMapId={valueId} />;
}
return <UploaderPeer />;
}
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<Jazz.Provider>
<Main />
</Jazz.Provider>
</React.StrictMode>,
);

View File

@@ -0,0 +1,38 @@
import { createJazzReactContext, DemoAuth } from "jazz-react";
import { useEffect } from "react";
import { getValueId } from "./lib/searchParams";
function AutoLoginComponent(props: {
appName: string;
loading: boolean;
existingUsers: string[];
logInAs: (existingUser: string) => void;
signUp: (username: string) => void;
}) {
useEffect(() => {
if (props.loading) return;
props.signUp("Test User");
}, [props.loading]);
return <div>Signing up...</div>;
}
const key = getValueId()
? `downloader-e2e@jazz.tools`
: `uploader-e2e@jazz.tools`;
const localSync = new URLSearchParams(location.search).has("localSync");
const Jazz = createJazzReactContext({
auth: DemoAuth({
appName: "BinaryCoStream Sync",
Component: AutoLoginComponent,
}),
peer: localSync
? `ws://localhost:4200?key=${key}`
: `wss://mesh.jazz.tools/?key=${key}`,
});
export const { useAccount, useCoState } = Jazz;
export { Jazz };

View File

@@ -0,0 +1,67 @@
export function BytesRadioGroup(props: {
selectedValue: number;
onChange: (value: number) => void;
}) {
return (
<p>
<BytesRadioInput
label="1KB"
value={1e3}
selectedValue={props.selectedValue}
onChange={props.onChange} />
<BytesRadioInput
label="10KB"
value={1e4}
selectedValue={props.selectedValue}
onChange={props.onChange} />
<BytesRadioInput
label="100KB"
value={1e5}
selectedValue={props.selectedValue}
onChange={props.onChange} />
<BytesRadioInput
label="150KB"
value={1e5 + 5e4}
selectedValue={props.selectedValue}
onChange={props.onChange} />
<BytesRadioInput
label="200KB"
value={2e6}
selectedValue={props.selectedValue}
onChange={props.onChange} />
<BytesRadioInput
label="500KB"
value={5e6}
selectedValue={props.selectedValue}
onChange={props.onChange} />
<BytesRadioInput
label="1MB"
value={1e6}
selectedValue={props.selectedValue}
onChange={props.onChange} />
<BytesRadioInput
label="10MB"
value={1e7}
selectedValue={props.selectedValue}
onChange={props.onChange} />
</p>
);
}
function BytesRadioInput(props: {
label: string;
value: number;
selectedValue: number;
onChange: (value: number) => void;
}) {
return (
<label>
<input
type="radio"
name="bytes"
value={props.value}
checked={props.value === props.selectedValue}
onChange={() => props.onChange(props.value)} />
{props.label}
</label>
);
}

View File

@@ -0,0 +1,21 @@
/**
* Creates a credentialess iframe that can be used to test the sync
* in an isolated environment. (no storage sharing)
*
* see: https://developer.mozilla.org/en-US/docs/Web/Security/IFrame_credentialless
*/
export function createCredentiallessIframe(url: string) {
const iframe = document.createElement("iframe");
// @ts-ignore
iframe.credentialless = true;
iframe.src = url;
iframe.style.width = "300px";
iframe.style.height = "300px";
iframe.style.border = "1px solid black";
iframe.style.position = "absolute";
iframe.style.top = "0";
iframe.style.right = "0";
iframe.setAttribute("data-testid", "downloader-peer");
return iframe;
}

View File

@@ -0,0 +1,27 @@
import { Account, Group, BinaryCoStream } from "jazz-tools";
import { UploadedFile } from "../schema";
export async function generateTestFile(me: Account, bytes: number) {
const group = Group.create({ owner: me });
group.addMember("everyone", "writer");
const ownership = { owner: group };
const testFile = UploadedFile.create(
{
file: await BinaryCoStream.createFromBlob(
new Blob(['1'.repeat(bytes)], { type: 'image/png' }),
ownership
),
syncCompleted: false,
coMapDownloaded: false,
},
ownership
);
const url = new URL(window.location.href);
url.searchParams.set("valueId", testFile.id);
return testFile;
}

View File

@@ -0,0 +1,8 @@
import { UploadedFile } from "src/schema";
export function getDownloaderPeerUrl(value: UploadedFile) {
const url = new URL(window.location.href);
url.searchParams.set("valueId", value.id);
return url.toString();
}

View File

@@ -0,0 +1,14 @@
import { ID } from "jazz-tools";
import { UploadedFile } from "../schema";
export function getValueId() {
return new URLSearchParams(location.search).get("valueId") as ID<UploadedFile> | undefined ?? undefined;
}
export function getIsAutoUpload() {
return new URLSearchParams(location.search).has("auto");
}
export function getDefaultFileSize() {
return parseInt(new URLSearchParams(location.search).get("fileSize") ?? 1e3.toString());
}

View File

@@ -0,0 +1,39 @@
import {
Account,
CoValue,
CoValueClass,
DepthsIn,
ID,
subscribeToCoValue,
} from "jazz-tools";
export function waitForCoValue<T extends CoValue>(
coMap: CoValueClass<T>,
valueId: ID<T>,
account: Account,
predicate: (value: T) => boolean,
depth: DepthsIn<T>
) {
return new Promise<T>((resolve) => {
function subscribe() {
const unsubscribe = subscribeToCoValue(
coMap,
valueId,
account,
depth,
(value) => {
if (predicate(value)) {
resolve(value);
unsubscribe();
}
},
() => {
unsubscribe();
setTimeout(subscribe, 100);
}
);
}
subscribe();
});
}

View File

@@ -0,0 +1,7 @@
import { BinaryCoStream, co, CoMap } from "jazz-tools";
export class UploadedFile extends CoMap {
file = co.ref(BinaryCoStream);
syncCompleted = co.boolean;
coMapDownloaded = co.boolean;
}

View File

@@ -0,0 +1,36 @@
import { test, expect } from "@playwright/test";
import { setTimeout } from "node:timers/promises";
test.describe("BinaryCoStream - Sync", () => {
test("should sync a file between the two peers", async ({ page }) => {
await page.goto("/");
await page.getByRole("button", { name: "Upload Test File" }).click();
await page.getByTestId("sync-duration").waitFor();
await expect(page.getByTestId("result")).toHaveText("Sync Completed: true");
});
test("should handle reconnections", async ({ page, browser }) => {
const context = browser.contexts()[0];
await page.goto("/?fileSize=" + 1e6); // 1MB file
await page.getByRole("button", { name: "Upload Test File" }).click();
// Wait for the coMapDonwloaded signal to ensure that the iframe is loaded
await page.getByTestId("co-map-downloaded").waitFor();
await context.setOffline(true);
// Wait for the ping system to detect the offline state
await setTimeout(10000);
await context.setOffline(false);
// Wait for the sync to complete
await page.getByTestId("sync-duration").waitFor();
await expect(page.getByTestId("result")).toHaveText("Sync Completed: true");
});
});

View File

@@ -0,0 +1,25 @@
{
"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": "."
},
"include": ["src"],
}

View File

@@ -0,0 +1,10 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
build: {
minify: false
}
})

View File

@@ -9,10 +9,5 @@ module.exports = {
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
rules: {},
}

View File

@@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
sync-db/

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

@@ -0,0 +1,854 @@
# jazz-example-chat
## 0.0.82
### Patch Changes
- Updated dependencies [49a8b54]
- Updated dependencies [35bbcd9]
- Updated dependencies [6f80282]
- Updated dependencies [35bbcd9]
- Updated dependencies [cac2ec9]
- Updated dependencies [f350e90]
- jazz-tools@0.7.35
- cojson@0.7.35
- jazz-react@0.7.35
## 0.0.81
### Patch Changes
- Updated dependencies [5d91f9f]
- Updated dependencies [5094e6d]
- Updated dependencies [b09589b]
- Updated dependencies [2c3a40c]
- Updated dependencies [4e16575]
- Updated dependencies [ea882ab]
- cojson@0.7.34
- jazz-react@0.7.34
- jazz-tools@0.7.34
## 0.0.81-neverthrow.6
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.8
- jazz-react@0.7.34-neverthrow.8
- jazz-tools@0.7.34-neverthrow.8
## 0.0.81-neverthrow.5
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.7
- jazz-react@0.7.34-neverthrow.7
- jazz-tools@0.7.34-neverthrow.7
## 0.0.81-neverthrow.4
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.4
- jazz-react@0.7.34-neverthrow.4
- jazz-tools@0.7.34-neverthrow.4
## 0.0.81-neverthrow.3
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.3
- jazz-react@0.7.34-neverthrow.3
- jazz-tools@0.7.34-neverthrow.3
## 0.0.81-neverthrow.2
### Patch Changes
- jazz-react@0.7.34-neverthrow.2
## 0.0.81-neverthrow.1
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.1
- jazz-react@0.7.34-neverthrow.1
- jazz-tools@0.7.34-neverthrow.1
## 0.0.81-neverthrow.0
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.0
- jazz-react@0.7.34-neverthrow.0
- jazz-tools@0.7.34-neverthrow.0
## 0.0.80
### Patch Changes
- Updated dependencies [b297c93]
- Updated dependencies [3bf5127]
- Updated dependencies [a8b74ff]
- Updated dependencies [db53161]
- cojson@0.7.33
- jazz-react@0.7.33
- jazz-tools@0.7.33
## 0.0.80-hotfixes.5
### Patch Changes
- Updated dependencies
- cojson@0.7.33-hotfixes.5
- jazz-react@0.7.33-hotfixes.5
- jazz-tools@0.7.33-hotfixes.5
## 0.0.80-hotfixes.4
### Patch Changes
- Updated dependencies
- cojson@0.7.33-hotfixes.4
- jazz-react@0.7.33-hotfixes.4
- jazz-tools@0.7.33-hotfixes.4
## 0.0.80-hotfixes.3
### Patch Changes
- Updated dependencies
- cojson@0.7.33-hotfixes.3
- jazz-react@0.7.33-hotfixes.3
- jazz-tools@0.7.33-hotfixes.3
## 0.0.80-hotfixes.2
### Patch Changes
- jazz-react@0.7.33-hotfixes.2
## 0.0.80-hotfixes.1
### Patch Changes
- jazz-react@0.7.33-hotfixes.1
## 0.0.80-hotfixes.0
### Patch Changes
- Updated dependencies
- cojson@0.7.33-hotfixes.0
- jazz-react@0.7.33-hotfixes.0
- jazz-tools@0.7.33-hotfixes.0
## 0.0.79
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.32
- jazz-react@0.7.32
## 0.0.78
### Patch Changes
- Updated dependencies
- cojson@0.7.31
- jazz-react@0.7.31
- jazz-tools@0.7.31
## 0.0.77
### Patch Changes
- jazz-react@0.7.30
## 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

View File

@@ -1,64 +1,42 @@
# Jazz Todo List Example
# Jazz Chat Example
Live version: https://example-todo.jazz.tools
Live version: [https://chat.jazz.tools](https://chat.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/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 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): 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_types.ts`](./src/1_types.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
1. 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)
3. Creating a new todo project: [`src/3_NewProjectForm.tsx`](./src/3_NewProjectForm.tsx)
4. Reactively rendering a todo project as a table, adding and editing tasks: [`src/4_ProjectTodoTable.tsx`](./src/4_ProjectTodoTable.tsx)
### Helpers
- (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!
## 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).
You can also run a local sync server by running `npx jazz-run sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=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

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

View File

@@ -1,24 +1,29 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.44",
"version": "0.0.82",
"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",
"hashroute": "^0.1.2",
"jazz-react": "^0.4.14",
"jazz-react-auth-local": "^0.4.14",
"cojson": "workspace:*",
"hash-slash": "workspace:*",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",
@@ -31,18 +36,19 @@
"uniqolor": "^1.1.0"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/qrcode": "^1.5.1",
"@types/react": "^18.2.19",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"eslint": "^8.45.0",
"eslint": "^8.46.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": "^4.4.5"
"typescript": "^5.3.3",
"vite": "^5.0.10"
}
}

View File

@@ -1,35 +1,43 @@
import { WithJazz, useJazz, DemoAuth } from 'jazz-react';
import ReactDOM from 'react-dom/client';
import { HashRoute } from 'hashroute';
import { ChatWindow } from './chatWindow.tsx';
import { Chat } from './dataModel.ts';
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;
ReactDOM.createRoot(document.getElementById('root')!).render(
<WithJazz auth={DemoAuth({ appName: 'Jazz Chat Example' })} apiKey="api_z9d034j3t34ht034ir">
<App />
</WithJazz>,
);
function App() {
return <div className='flex flex-col items-center justify-between w-screen h-screen p-2 dark:bg-black dark:text-white'>
<button onClick={useJazz().logOut} className='rounded mb-5 px-2 py-1 bg-stone-200 dark:bg-stone-800 dark:text-white self-end'>
Log Out
</button>
{HashRoute({
'/': <Home />,
'/chat/:id': (id) => <ChatWindow chatId={id as Chat['id']} />,
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>
</div>;
}
function Home() {
const { me } = useJazz();
return <button className='rounded py-2 px-4 bg-stone-200 dark:bg-stone-800 dark:text-white my-auto'
onClick={() => {
const group = me.createGroup().addMember('everyone', 'writer');
const chat = group.createList<Chat>();
location.hash = '/chat/' + chat.id;
}}>
Create New Chat
</button>
}
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

@@ -1,43 +0,0 @@
import { useAutoSub } from 'jazz-react';
import { Chat, Message } from './dataModel.ts';
export function ChatWindow(props: { chatId: Chat['id'] }) {
const chat = useAutoSub(props.chatId);
return chat ? <div className='w-full max-w-xl h-full flex flex-col items-stretch'>
{
chat.map((msg, i) => (
<ChatBubble key={msg?.id}
text={msg?.text}
by={chat.meta.edits[i].by?.profile?.name}
byMe={chat.meta.edits[i].by?.isMe}
at={chat.meta.edits[i].at} />
))
}
<ChatInput onSubmit={(text) => {
const msg = chat.meta.group.createMap<Message>({ text });
chat.append(msg.id);
}}/>
</div> : <div>Loading...</div>;
}
function ChatBubble(props: { text?: string, by?: string, at?: Date, byMe?: boolean }) {
return <div className={`${props.byMe ? 'items-end' : 'items-start'} 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.text }
</div>
<div className='text-xs text-neutral-500 ml-2'>
{ props.by } { props.at?.getHours() }:{ props.at?.getMinutes() }
</div>
</div>;
}
function ChatInput(props: { onSubmit: (text: string) => void }) {
return <input className='rounded p-2 border mt-auto dark:bg-black dark:text-white 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

@@ -1,4 +0,0 @@
import { CoMap, CoList } from 'cojson';
export type Chat = CoList<Message['id']>;
export type Message = CoMap<{ text: string }>;

View File

@@ -1,64 +0,0 @@
# Jazz Todo List Example
Live version: https://example-todo.jazz.tools
## Installing & running the example locally
Start by checking out just the example app to a folder:
```bash
npx degit gardencmp/jazz/examples/todo jazz-example-todo
cd jazz-example-todo
```
(This ensures that you have the example app without git history or our multi-package monorepo)
Install dependencies:
```bash
npm install
```
Start the dev server:
```bash
npm run dev
```
## Structure
- [`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_types.ts`](./src/1_types.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
1. 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)
3. Creating a new todo project: [`src/3_NewProjectForm.tsx`](./src/3_NewProjectForm.tsx)
4. Reactively rendering a todo project as a table, adding and editing tasks: [`src/4_ProjectTodoTable.tsx`](./src/4_ProjectTodoTable.tsx)
### Helpers
- (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!
## 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

@@ -1,5 +0,0 @@
import { CoMap, BinaryCoStream } from "cojson";
export type FileBundle = CoMap<{
[filename: string]: BinaryCoStream['id']
}>;

View File

@@ -1,187 +0,0 @@
import React, { ChangeEvent, useCallback, useState } from "react";
import ReactDOM from "react-dom/client";
import {
RouterProvider,
createHashRouter,
useNavigate,
useParams,
} from "react-router-dom";
import "./index.css";
import { WithJazz, useJazz, useAcceptInvite, useAutoSub } from "jazz-react";
import { LocalAuth } from "jazz-react-auth-local";
import {
Button,
Input,
ThemeProvider,
TitleAndLogo,
} from "./basicComponents/index.ts";
import { PrettyAuthUI } from "./components/Auth.tsx";
import { FileBundle } from "./1_types.ts";
import {
createBinaryStreamFromBlob,
readBlobFromBinaryStream,
} from "jazz-browser";
import { DownloadIcon } from "lucide-react";
const appName = "Jazz File Drop Example";
const auth = LocalAuth({
appName,
Component: PrettyAuthUI,
});
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">
<WithJazz auth={auth}>
<App />
</WithJazz>
</div>
</ThemeProvider>
</React.StrictMode>
);
function App() {
// logOut logs out the AuthProvider passed to `<WithJazz/>` above.
const { logOut } = useJazz();
const router = createHashRouter([
{
path: "/",
element: <FileDropUI />,
},
{
path: "/bundle/:bundleId",
element: <FileDropUIPage />,
},
{
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((bundleId) => router.navigate("/v/" + bundleId));
return (
<>
<RouterProvider router={router} />
<Button
onClick={() => router.navigate("/").then(logOut)}
variant="outline"
>
Log Out
</Button>
</>
);
}
export function FileDropUIPage() {
const { bundleId } = useParams<{ bundleId: FileBundle["id"] }>();
return <FileDropUI bundleId={bundleId} />;
}
export function FileDropUI({ bundleId }: { bundleId?: FileBundle["id"] }) {
const navigate = useNavigate();
const { me, localNode } = useJazz();
const fileBundle = useAutoSub(bundleId);
const [progressMessage, setProgressMessage] = useState<{
[name: string]: string;
}>({});
const onChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
let fileBundleToUse = fileBundle?.meta.coValue;
let isFirstUpload = false;
if (!fileBundleToUse) {
const group = me.createGroup().addMember("everyone", "reader");
fileBundleToUse = group.createMap<FileBundle>();
isFirstUpload = true;
}
const files = [...(event.target.files || [])];
Promise.all(
files.map((file) =>
createBinaryStreamFromBlob(
file,
fileBundleToUse!.group,
{ type: "binary" },
(progress) =>
setProgressMessage((old) => ({
...old,
[file.name]: `Creating ${Math.round(
progress * 100
)}%`,
}))
).then((stream) => {
fileBundleToUse!.set(file.name, stream.id);
})
)
).then(() => {
if (isFirstUpload) {
navigate("/bundle/" + fileBundleToUse!.id);
}
});
event.target.value = "";
},
[me, navigate, fileBundle]
);
return (
<div className="max-w-full p-5 w-[40rem]">
<h1 className="text-3xl font-bold mb-5">File Drop</h1>
{[
...new Set([
...Object.keys(fileBundle || {}),
...Object.keys(progressMessage),
]),
].map((name) => (
<div className="mb-5 flex justify-between" key={name}>
{name} {progressMessage[name]}
<Button
size="sm"
disabled={!(name in (fileBundle || {}))}
onClick={() => {
const streamId = fileBundle?.meta.coValue.get(name);
streamId &&
readBlobFromBinaryStream(
streamId,
localNode,
false,
(progress) =>
setProgressMessage((old) => ({
...old,
[name]: `Loading ${Math.round(
progress * 100
)}%`,
}))
).then((blob) => {
if (!blob) return;
const url = URL.createObjectURL(blob);
window.open(url, "_blank");
});
}}
>
<DownloadIcon />
</Button>
</div>
))}
{(!fileBundle || fileBundle.meta.group.myRole() === "admin") && (
<Input type="file" onChange={onChange} multiple />
)}
</div>
);
}
/** Walkthrough: Continue with ./3_NewProjectForm.tsx */

View File

@@ -1,39 +0,0 @@
import { Input } from "@/basicComponents/ui/input";
import { Button } from "@/basicComponents/ui/button";
export function SubmittableInput({
onSubmit,
label,
placeholder,
disabled,
}: {
onSubmit: (text: string) => void;
label: string;
placeholder: string;
disabled?: boolean;
}) {
return (
<form
className="flex flex-row items-center gap-3"
onSubmit={(e) => {
e.preventDefault();
const textEl = e.currentTarget.elements.namedItem(
"text"
) as HTMLInputElement;
onSubmit(textEl.value);
textEl.value = "";
}}
>
<Input
className="-ml-3 -my-2 flex-grow flex-3 text-base"
name="text"
placeholder={placeholder}
autoComplete="off"
disabled={disabled}
/>
<Button asChild type="submit" className="flex-shrink flex-1 cursor-pointer">
<Input type="submit" value={label} disabled={disabled} />
</Button>
</form>
);
}

View File

@@ -1,10 +0,0 @@
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,17 +0,0 @@
export { Button } from "./ui/button";
export { Checkbox } from "./ui/checkbox";
export { Input } from "./ui/input";
export { Skeleton } from "./ui/skeleton";
export { Toaster } from "./ui/toaster";
export { useToast } from "./ui/use-toast";
export { SubmittableInput } from "./SubmittableInput";
export { TitleAndLogo } from "./TitleAndLogo";
export { ThemeProvider } from "./themeProvider";
export {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "./ui/table";

View File

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

View File

@@ -1,72 +0,0 @@
import { createContext, useContext, useEffect, useState } from "react";
type ThemeProviderProps = {
children: React.ReactNode;
defaultTheme?: string;
storageKey?: string;
};
type ThemeProviderState = {
theme: string;
setTheme: (theme: string) => void;
};
const initialState = {
theme: "system",
setTheme: () => null,
};
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
export function ThemeProvider({
children,
defaultTheme = "system",
storageKey = "vite-ui-theme",
...props
}: ThemeProviderProps) {
const [theme, setTheme] = useState(
() => localStorage.getItem(storageKey) || defaultTheme
);
useEffect(() => {
const root = window.document.documentElement;
root.classList.remove("light", "dark");
if (theme === "system") {
const systemTheme = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches
? "dark"
: "light";
root.classList.add(systemTheme);
return;
}
root.classList.add(theme);
}, [theme]);
const value = {
theme,
setTheme: (theme: string) => {
localStorage.setItem(storageKey, theme);
setTheme(theme);
},
};
return (
<ThemeProviderContext.Provider {...props} value={value}>
{children}
</ThemeProviderContext.Provider>
);
}
export const useTheme = () => {
const context = useContext(ThemeProviderContext);
if (context === undefined)
throw new Error("useTheme must be used within a ThemeProvider");
return context;
};

View File

@@ -1,56 +0,0 @@
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"
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",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
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"
export { Button, buttonVariants }

View File

@@ -1,28 +0,0 @@
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { Check } from "lucide-react"
import { cn } from "@/basicComponents/lib/utils"
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")}
>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
))
Checkbox.displayName = CheckboxPrimitive.Root.displayName
export { Checkbox }

View File

@@ -1,25 +0,0 @@
import * as React from "react"
import { cn } from "@/basicComponents/lib/utils"
export interface InputProps
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"
export { Input }

View File

@@ -1,15 +0,0 @@
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

@@ -1,114 +0,0 @@
import * as React from "react"
import { cn } from "@/basicComponents/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn("bg-primary font-medium text-primary-foreground", className)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}

View File

@@ -1,127 +0,0 @@
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

@@ -1,33 +0,0 @@
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

@@ -1,192 +0,0 @@
// 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,48 +0,0 @@
import { useState } from "react";
import { LocalAuthComponent } from "jazz-react-auth-local";
import { Input, Button } from "../basicComponents";
export const PrettyAuthUI: LocalAuthComponent = ({
loading,
logIn,
signUp,
}) => {
const [username, setUsername] = useState<string>("");
return (
<div className="w-full h-full flex items-center justify-center p-5">
{loading ? (
<div>Loading...</div>
) : (
<div className="w-72 flex flex-col gap-4">
<form
className="w-72 flex flex-col gap-2"
onSubmit={(e) => {
e.preventDefault();
signUp(username);
}}
>
<Input
placeholder="Display name"
value={username}
onChange={(e) => setUsername(e.target.value)}
autoComplete="webauthn"
className="text-base"
/>
<Button asChild>
<Input
type="submit"
value="Sign Up as new account"
/>
</Button>
</form>
<Button onClick={logIn}>
Log In with existing account
</Button>
</div>
)}
</div>
);
};

View File

@@ -1,45 +0,0 @@
import { useState } from "react";
import QRCode from "qrcode";
import { useToast, Button } from "../basicComponents";
import { CoValue } from "cojson";
import { Resolved, createInviteLink } from "jazz-react";
export function InviteButton<T extends CoValue>({ value }: { value?: Resolved<T> }) {
const [existingInviteLink, setExistingInviteLink] = useState<string>();
const { toast } = useToast();
return (
value?.meta.group?.myRole() === "admin" && (
<Button
size="sm"
className="py-0"
disabled={!value.meta.group || !value.id}
variant="outline"
onClick={async () => {
let inviteLink = existingInviteLink;
if (value.meta.group && value.id && !inviteLink) {
inviteLink = createInviteLink(value, "writer");
setExistingInviteLink(inviteLink);
}
if (inviteLink) {
const qr = await QRCode.toDataURL(inviteLink, {
errorCorrectionLevel: "L",
});
navigator.clipboard.writeText(inviteLink).then(() =>
toast({
title: "Copied invite link to clipboard!",
description: (
<img src={qr} className="w-20 h-20" />
),
})
);
}
}}
>
Invite
</Button>
)
);
}

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

@@ -0,0 +1,26 @@
# 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?
sync-db/

View File

@@ -0,0 +1,234 @@
# jazz-example-chat
## 0.0.60
### Patch Changes
- 63daf6a: fix(inspector): subscribe to latent covalues instead of loading them immediately
- Updated dependencies [35bbcd9]
- Updated dependencies [f350e90]
- cojson@0.7.35
- cojson-transport-ws@0.7.35
## 0.0.59
### Patch Changes
- Updated dependencies [5d91f9f]
- Updated dependencies [5094e6d]
- Updated dependencies [b09589b]
- Updated dependencies [2c3a40c]
- Updated dependencies [406ab9b]
- Updated dependencies [4e16575]
- Updated dependencies [ea882ab]
- cojson@0.7.34
- cojson-transport-ws@0.7.34
## 0.0.59-neverthrow.6
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.8
- cojson-transport-ws@0.7.34-neverthrow.8
## 0.0.59-neverthrow.5
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.7
- cojson-transport-ws@0.7.34-neverthrow.7
## 0.0.59-neverthrow.4
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.4
- cojson-transport-ws@0.7.34-neverthrow.4
## 0.0.59-neverthrow.3
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.3
- cojson-transport-ws@0.7.34-neverthrow.3
## 0.0.59-neverthrow.2
### Patch Changes
- Updated dependencies
- cojson-transport-ws@0.7.34-neverthrow.2
## 0.0.59-neverthrow.1
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.1
- cojson-transport-ws@0.7.34-neverthrow.1
## 0.0.59-neverthrow.0
### Patch Changes
- Updated dependencies
- cojson@0.7.34-neverthrow.0
- cojson-transport-ws@0.7.34-neverthrow.0
## 0.0.58
### Patch Changes
- Updated dependencies [fdde8db]
- Updated dependencies [b297c93]
- Updated dependencies [07fe2b9]
- Updated dependencies [3bf5127]
- Updated dependencies [a8b74ff]
- Updated dependencies [db53161]
- cojson-transport-ws@0.7.33
- cojson@0.7.33
## 0.0.58-hotfixes.5
### Patch Changes
- Updated dependencies
- cojson@0.7.33-hotfixes.5
- cojson-transport-ws@0.7.33-hotfixes.5
## 0.0.58-hotfixes.4
### Patch Changes
- Updated dependencies
- cojson@0.7.33-hotfixes.4
- cojson-transport-ws@0.7.33-hotfixes.4
## 0.0.58-hotfixes.3
### Patch Changes
- Updated dependencies
- cojson-transport-ws@0.7.33-hotfixes.3
- cojson@0.7.33-hotfixes.3
## 0.0.58-hotfixes.2
### Patch Changes
- Updated dependencies
- cojson-transport-ws@0.7.33-hotfixes.2
## 0.0.58-hotfixes.1
### Patch Changes
- Updated dependencies
- cojson-transport-ws@0.7.33-hotfixes.1
## 0.0.58-hotfixes.0
### Patch Changes
- Updated dependencies
- cojson@0.7.33-hotfixes.0
- cojson-transport-ws@0.7.33-hotfixes.0
## 0.0.57
### Patch Changes
- Updated dependencies
- Updated dependencies
- cojson-transport-ws@0.7.31
- cojson@0.7.31
## 0.0.56
### Patch Changes
- Updated dependencies
- cojson-transport-ws@0.7.30
## 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,7 @@
# Jazz Inspector
Live address: https://inspector.jazz.tools
Use this to visually inspect a Jazz account or other CoValue.
For now, you can get your account credentials from the `jazz-logged-in-secret` local-storage key from within your Jazz app.

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

@@ -1,4 +1,4 @@
job "twit$BRANCH_SUFFIX" {
job "inspector$BRANCH_SUFFIX" {
region = "global"
datacenters = ["*"]
@@ -41,7 +41,7 @@ job "twit$BRANCH_SUFFIX" {
service {
tags = ["public"]
name = "twit$BRANCH_SUFFIX"
name = "inspector$BRANCH_SUFFIX"
port = "http"
provider = "consul"
}

View File

@@ -1,10 +1,10 @@
{
"name": "jazz-example-file-drop",
"name": "jazz-inspector",
"private": true,
"version": "0.0.61",
"version": "0.0.60",
"type": "module",
"scripts": {
"dev": "vite --port 6610",
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
@@ -13,34 +13,36 @@
"@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",
"jazz-react": "^0.4.14",
"jazz-react-auth-local": "^0.4.14",
"cojson": "workspace:*",
"cojson-transport-ws": "workspace:*",
"hash-slash": "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/qrcode": "^1.5.1",
"@types/react": "^18.2.19",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"eslint": "^8.45.0",
"eslint": "^8.46.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": "^4.4.5"
"typescript": "^5.3.3",
"vite": "^5.0.10"
}
}

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

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,222 @@
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,
};
}
function subscribeToCoValue(
coValueId: CoID<RawCoValue>,
node: LocalNode,
callback: (result: Awaited<ReturnType<typeof resolveCoValue>>) => void,
) {
return node.subscribe(coValueId, (value) => {
if (value === "unavailable") {
callback({
value: undefined,
snapshot: "unavailable",
type: null,
extendedType: undefined,
});
} else {
const snapshot = value.toJSON() as JSONObject;
const type = value.type as CoJsonType;
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 {
const children = Object.values(snapshot).slice(0, 10);
if (
children.every(
(c) => typeof c === "string" && c.startsWith("co_"),
) &&
children.length > 3
) {
extendedType = "record";
}
}
}
callback({
value,
snapshot,
type,
extendedType,
});
}
});
}
export function useResolvedCoValue(
coValueId: CoID<RawCoValue>,
node: LocalNode,
) {
const [result, setResult] =
useState<Awaited<ReturnType<typeof resolveCoValue>>>();
useEffect(() => {
let isMounted = true;
const unsubscribe = subscribeToCoValue(coValueId, node, (newResult) => {
if (isMounted) {
setResult(newResult);
}
});
return () => {
isMounted = false;
unsubscribe();
};
}, [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(() => {
let isMounted = true;
const unsubscribes: (() => void)[] = [];
coValueIds.forEach((coValueId, index) => {
const unsubscribe = subscribeToCoValue(
coValueId,
node,
(newResult) => {
if (isMounted) {
setResults((prevResults) => {
const newResults = [...prevResults];
newResults[index] = newResult;
return newResults;
});
}
},
);
unsubscribes.push(unsubscribe);
});
return () => {
isMounted = false;
unsubscribes.forEach((unsubscribe) => unsubscribe());
};
}, [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>;
}

View File

@@ -1,6 +1,5 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',

View File

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

View File

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

View File

@@ -0,0 +1,13 @@
# jazz-example-musicplayer
## 0.0.2
### Patch Changes
- Updated dependencies [49a8b54]
- Updated dependencies [6f80282]
- Updated dependencies [35bbcd9]
- Updated dependencies [cac2ec9]
- Updated dependencies [f350e90]
- jazz-tools@0.7.35
- jazz-react@0.7.35

View File

@@ -0,0 +1,42 @@
# Jazz Music Player Example
Live version: https://music-demo.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/musicPlayer
pnpm pack --pack-destination /tmp
mkdir -p ~/jazz-examples/musicPlayer # or any other directory
tar -xf /tmp/jazz-example-musicPlayer-* --strip-components 1 -C ~/jazz-examples/musicPlayer
cd ~/jazz-examples/musicPlayer
```
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/?peer=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

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

View File

@@ -1,4 +1,4 @@
job "example-file-drop$BRANCH_SUFFIX" {
job "example-musicPlayer$BRANCH_SUFFIX" {
region = "global"
datacenters = ["*"]
@@ -41,7 +41,7 @@ job "example-file-drop$BRANCH_SUFFIX" {
service {
tags = ["public"]
name = "example-file-drop$BRANCH_SUFFIX"
name = "example-pets$BRANCH_SUFFIX"
port = "http"
provider = "consul"
}

View File

@@ -0,0 +1,44 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.2",
"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": {
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*",
"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"
},
"devDependencies": {
"@types/react": "^18.2.19",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"eslint": "^8.46.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.3.3",
"vite": "^5.0.10"
}
}

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