Compare commits

...

527 Commits

Author SHA1 Message Date
Guido D'Orsi
8fb1748433 Merge pull request #2750 from garden-co/changeset-release/main
Version Packages
2025-08-15 20:36:35 +02:00
github-actions[bot]
c8644bf678 Version Packages 2025-08-15 16:30:37 +00:00
Guido D'Orsi
269ee94338 test: skip flaky e2e test 2025-08-15 18:26:41 +02:00
Guido D'Orsi
dae80eeba8 Merge pull request #2751 from garden-co/feat/unmount
fix: remove unnecessary content sent as dependency
2025-08-15 18:25:51 +02:00
Guido D'Orsi
ce54667b4d Merge pull request #2752 from garden-co/more-unique-static-methods
Implement/expose loadUnique and upsertUnique on co.list and co.record
2025-08-15 18:25:33 +02:00
Anselm
5963658e28 Implement/expose loadUnique and upsertUnique on co.list and co.record 2025-08-15 17:20:48 +01:00
Guido D'Orsi
71c1411bbd fix: remove unnecessary content sent as dependency 2025-08-15 18:05:42 +02:00
Guido D'Orsi
71b221dc79 Merge pull request #2749 from garden-co/feat/unmount
feat: make the unmount function detach the CoValue from the localNode
2025-08-15 17:48:01 +02:00
Guido D'Orsi
2d11d448dc feat: make the unmount function detach the CoValue from the localNode 2025-08-15 17:41:18 +02:00
Guido D'Orsi
2d42fc9b34 Merge pull request #2748 from garden-co/changeset-release/main
Version Packages
2025-08-15 17:27:17 +02:00
github-actions[bot]
c9bda7e1e3 Version Packages 2025-08-15 15:26:39 +00:00
Guido D'Orsi
476f2d7eee Merge pull request #2747 from garden-co/export-ref-from-jazz-tools
Export Ref class from jazz-tools package
2025-08-15 17:23:08 +02:00
Guido D'Orsi
1ba3a2ca34 test: add waitForSync to fix flaky test on mesh 2025-08-15 17:22:31 +02:00
Anselm
7dd3d005a3 Export Ref class from jazz-tools package 2025-08-15 16:19:51 +01:00
Guido D'Orsi
2c2dfb52d4 Merge pull request #2746 from garden-co/changeset-release/main
Version Packages
2025-08-15 17:00:22 +02:00
github-actions[bot]
d33917fbaa Version Packages 2025-08-15 14:59:08 +00:00
Guido D'Orsi
f0c73d9cc6 chore: changeset 2025-08-15 16:56:43 +02:00
Guido D'Orsi
d9324a9809 Merge pull request #2742 from garden-co/fix/sync-server-test
test: fix flaky syncserver test
2025-08-15 16:45:11 +02:00
Guido D'Orsi
f7b5454cc6 fix: store empty content messages with header 2025-08-15 16:43:03 +02:00
Sammii
5de338bdaf Merge pull request #2730 from garden-co/feat/quint-export-updates
quint export update
2025-08-15 15:05:04 +01:00
Sammii
e67d44d47a updating imports on input page 2025-08-15 14:47:55 +01:00
Guido D'Orsi
a310293346 Merge pull request #2741 from garden-co/changeset-release/main
Version Packages
2025-08-15 15:29:06 +02:00
github-actions[bot]
716d770258 Version Packages 2025-08-15 13:27:48 +00:00
Guido D'Orsi
4e85b50e1b chore: update catalog entries in package.json to reflect current values 2025-08-15 15:25:10 +02:00
Guido D'Orsi
643297b42e test: skip flaky test 2025-08-15 15:23:15 +02:00
Guido D'Orsi
261efd99be Merge pull request #2735 from garden-co/feat/catalogs
feat: use multiple catalogs for easier version bumps
2025-08-15 15:20:43 +02:00
Brad Anderson
f75f4f9b2d fix: squirrely export recognition from twoslash 2025-08-15 09:14:05 -04:00
Brad Anderson
a0021f060c fix: homepage lockfile and catalog 2025-08-15 09:14:05 -04:00
Brad Anderson
86bd87e6d0 chore: add changeset 2025-08-15 09:14:05 -04:00
Brad Anderson
ae55e80801 chore: remove check-catalog-deps 2025-08-15 09:14:05 -04:00
Brad Anderson
e830caf966 fix: create-jazz-app handles catalog: dependencies 2025-08-15 09:14:04 -04:00
Brad Anderson
2f7240121d feat: use multiple catalogs for easier version bumps 2025-08-15 09:14:04 -04:00
Guido D'Orsi
97699a6d5b Merge pull request #2739 from garden-co/changeset-release/main
Version Packages
2025-08-15 14:33:35 +02:00
github-actions[bot]
5f8a2ba8df Version Packages 2025-08-15 12:31:47 +00:00
Guido D'Orsi
fe06e12b85 Merge pull request #2737 from garden-co/justin-gco-730-bug-correction-callback-returned-undefined
Closes GCO-730: Don't attempt to store sessions with invalid assumptions
2025-08-15 14:29:33 +02:00
Guido D'Orsi
5b2b16a5c6 chore: changeset 2025-08-15 14:27:30 +02:00
Guido D'Orsi
a966912c8a test: cover InvalidSignature storage 2025-08-15 14:26:30 +02:00
Guido D'Orsi
b63b70fb80 fix: move the msg new filtering in handleNewContent 2025-08-15 13:21:28 +02:00
Guido D'Orsi
6b3e02920a chore: changeset 2025-08-15 12:58:27 +02:00
Guido D'Orsi
f566961390 Merge pull request #2738 from garden-co/fix/media-rn-fixes
Fixes in image management for React Native
2025-08-15 12:54:14 +02:00
Guido D'Orsi
265b265365 Merge pull request #2646 from nicolasembleton/community-vue-packages
Add community-maintained VueJS bindings packages and examples
2025-08-15 12:53:28 +02:00
Matteo Manchi
83fc22f39a fix(jazz-tools/media): import react-native-image-resizer only when needed 2025-08-15 11:22:49 +02:00
Matteo Manchi
794681a8bb fix(react/media): EmptyPixelBlob initialization when needed 2025-08-15 11:22:49 +02:00
Justin Rosenthal
899bb0d2a1 Don't attempt to store content with invalid assumptions 2025-08-14 16:26:27 -07:00
Guido D'Orsi
33cfc4cc25 chore: format 2025-08-14 17:27:44 +02:00
Guido D'Orsi
42c60c99fe chore: remove the vue biome config 2025-08-14 17:25:42 +02:00
Guido D'Orsi
e42518ed29 feat: upgrade to jazz 0.16 2025-08-14 17:22:07 +02:00
Guido D'Orsi
5b7ef3cd89 Merge remote-tracking branch 'origin/main' into community-vue-packages 2025-08-14 16:03:11 +02:00
Guido D'Orsi
fc02fc0608 chore: simplify InvalidSignature test with less whiteboxing 2025-08-14 12:12:30 +02:00
Guido D'Orsi
ceaa555e83 Merge pull request #2727 from garden-co/changeset-release/main
Version Packages
2025-08-14 11:49:33 +02:00
github-actions[bot]
03229b2ea9 Version Packages 2025-08-14 08:51:24 +00:00
Guido D'Orsi
e2737d44b6 fix: add jazz-run as workspace dependency 2025-08-14 10:48:02 +02:00
Guido D'Orsi
4b73834883 chore: changeset 2025-08-14 10:47:44 +02:00
Guido D'Orsi
1b3d43d5f4 Merge pull request #2728 from legowhales/main
fix(jazz-tools/svelte): Make Image reactive to imageId change
2025-08-14 10:46:52 +02:00
Guido D'Orsi
9c9a689879 Merge pull request #2729 from garden-co/feat/debug-correction
feat: add debug info to correction errors
2025-08-13 17:43:53 +02:00
Sammii
60b5288042 quint export update 2025-08-13 11:58:14 +01:00
Guido D'Orsi
2fd88b938c feat: add debug info to correction errors 2025-08-13 12:33:05 +02:00
Sammii
d1f955006f Merge pull request #2702 from garden-co/feat/add-input-label
Feat/add input label
2025-08-13 10:33:51 +01:00
Jérémy Le Mardelé
bb3d5f1f87 fix(jazz-tools/svelte): Make Image reactive to imageId change 2025-08-13 00:42:33 +02:00
Sammii
26ce61ab78 input PR amends 2025-08-12 15:59:30 +01:00
Sammii
1f300114d5 label PR amends 2025-08-12 15:58:42 +01:00
Guido D'Orsi
da69f812f8 Merge pull request #2726 from garden-co/fix/GCO-726
fix(jazz-tools/media): ensure file downloaded in loadImageBySize
2025-08-12 14:30:28 +02:00
Guido D'Orsi
0bcbf551ca fix: export the HttpRoute type 2025-08-12 14:26:31 +02:00
Matteo Manchi
6b3d5b5560 fixup! fix(jazz-tools/media): ensure file downloaded in loadImageBySize 2025-08-12 11:44:54 +02:00
Matteo Manchi
d1bdbf5d49 fix(jazz-tools/media): ensure file downloaded in loadImageBySize 2025-08-12 10:37:34 +02:00
Matteo Manchi
621e809fad Merge pull request #2722 from garden-co/changeset-release/main
Version Packages 0.17
2025-08-11 15:45:03 +02:00
github-actions[bot]
d6600d9322 Version Packages 2025-08-11 13:26:38 +00:00
Matteo Manchi
2b08bd77c1 Merge pull request #2624 from garden-co/feat/new-image-apis
New Image management API
2025-08-11 15:24:33 +02:00
Guido D'Orsi
c1c6e31711 Merge pull request #2719 from garden-co/changeset-release/main
Version Packages
2025-08-11 14:15:38 +02:00
github-actions[bot]
0b16085f3c Version Packages 2025-08-11 12:07:42 +00:00
Guido D'Orsi
e53db2e96a chore: format 2025-08-11 14:04:22 +02:00
Guido D'Orsi
384f0e23c0 Merge pull request #2701 from garden-co/feat/better-async-storage
feat: support multiple instances of storage
2025-08-11 14:03:39 +02:00
Guido D'Orsi
daaf1789d9 Merge pull request #2721 from garden-co/fix/char-chunking-coplaintext
Fix local transactions streaming and implement chunking for CoPlainText
2025-08-11 14:02:49 +02:00
Guido D'Orsi
1f9e20e753 Merge pull request #2705 from garden-co/chore/biome-2
chore: bump biome version to 2.1.3
2025-08-11 14:01:16 +02:00
Guido D'Orsi
ce9ca54f5c feat: content chunking on CoPlainText 2025-08-11 14:00:20 +02:00
Guido D'Orsi
67e0968809 fix: fix content streaming chunking, now chunks should be splitted always respecting the MAX_RECOMMENDED_TX_SIZE 2025-08-11 14:00:17 +02:00
Giordano Ricci
96a922cceb Merge pull request #2711 from garden-co/gio/usage-metering 2025-08-11 12:22:00 +01:00
Sammii
9b22fc74cd ignore biome on label cn import 2025-08-11 11:19:49 +01:00
Sammii
1bebe3c6c8 Merge branch 'main' into feat/add-input-label 2025-08-11 11:00:05 +01:00
Sammii
0a98b826f1 Merge pull request #2675 from garden-co/feat/quint-add-full-button-suite
Feat/quint add full button suite
2025-08-11 10:59:10 +01:00
Sammii
e1bd16d08b amend cn import on label 2025-08-11 10:58:47 +01:00
Sammii
0967c2ee5a pr amends 2025-08-11 10:49:34 +01:00
Sammii
62a3854c41 Update packages/quint-ui/src/components/button.tsx
Co-authored-by: Giordano Ricci <me@giordanoricci.com>
2025-08-11 10:45:57 +01:00
Matteo Manchi
f22ef4e646 chore: fix import ordering 2025-08-11 11:40:10 +02:00
Matteo Manchi
6c35d0031d chore(jazz-tools/svelte): refactor image component + svelte testing 2025-08-11 11:39:06 +02:00
Guido D'Orsi
7bdb6f4279 chore: simplify drawWaveform 2025-08-11 11:33:49 +02:00
Matteo Manchi
93f3fb231b fix(jazz-tools/media): fix resize calcs 2025-08-11 11:24:45 +02:00
Matteo Manchi
01d13d5df2 feat(jazz-tools/react): generate Image blobs on lazy loading 2025-08-11 11:24:45 +02:00
Matteo Manchi
944e725b95 fix(jazz-tools/react): disable revokeObjectURL on Image unmounts in development env 2025-08-11 11:24:45 +02:00
Matteo Manchi
16024fec8e chore(jazz-tools/react): show always the img element 2025-08-11 11:24:45 +02:00
Matteo Manchi
f90414ab95 chore(jazz-tools/react-native): move Image component from react-native to react-native-core 2025-08-11 11:24:45 +02:00
Guido D'Orsi
492eecb46a docs: add jsDoc comment to Image and createImage 2025-08-11 11:24:45 +02:00
Matteo Manchi
51144ec832 docs: add 0.17 upgrade guide 2025-08-11 11:24:44 +02:00
Matteo Manchi
fcaf4b9c30 chore: add changeset 2025-08-11 11:24:44 +02:00
Matteo Manchi
afae2649f5 docs: new docs for Image Management 2025-08-11 11:24:44 +02:00
Matteo Manchi
b5b0284c61 feat(jazz-tools/svelte): new Image component based on new image management API 2025-08-11 11:24:44 +02:00
Matteo Manchi
bf1475a143 feat(jazz-tools/react-native): new Image component based on new image management API 2025-08-11 11:24:44 +02:00
Matteo Manchi
e82cb80ca4 chore(example/image-upload): refactor using the new image management API 2025-08-11 11:24:44 +02:00
Matteo Manchi
32c2a617d6 feat(jazz-tools/react): new Image component based on new image management API 2025-08-11 11:24:42 +02:00
Matteo Manchi
d3c2a41c81 feat(jazz-tools/media): new media API for image management 2025-08-11 11:23:55 +02:00
Guido D'Orsi
4b99ff1fe3 feat: support multiple storage instances 2025-08-11 11:21:22 +02:00
Guido D'Orsi
3ebf8258a0 Merge pull request #2692 from garden-co/feat/garbage-collector
feat: added a TTL-based garbage collection
2025-08-11 10:54:58 +02:00
Guido D'Orsi
4809d14f6d chore: restore CI quality check 2025-08-11 10:45:04 +02:00
Guido D'Orsi
5ae1f33127 chore: disable importOrder and format the codebase 2025-08-11 10:44:03 +02:00
Guido D'Orsi
ca5d84f6a9 Merge pull request #2720 from garden-co/fix/vitest-nested-projects
chore: removed nested projects in vitest.config
2025-08-11 10:33:59 +02:00
Guido D'Orsi
6e6acc3404 chore: revert the homepage formatting 2025-08-11 10:26:24 +02:00
Guido D'Orsi
b17b7b6481 Merge remote-tracking branch 'origin/main' into chore/biome-2 2025-08-11 10:22:41 +02:00
Guido D'Orsi
5341646301 chore: revert formatting, remove the code-quality CI check 2025-08-11 10:21:33 +02:00
Guido D'Orsi
5416165d28 Merge remote-tracking branch 'origin/main' into feat/garbage-collector 2025-08-11 10:18:37 +02:00
Guido D'Orsi
b5a9f681c5 Merge pull request #2696 from garden-co/feat/chat-pagination
feat(chat): implement lazy-loading
2025-08-11 10:17:32 +02:00
Matteo Manchi
7dffc006eb chore: removed nested projects in vitest.config 2025-08-11 00:09:19 +02:00
Guido D'Orsi
cd3cc5b0ab Merge pull request #2716 from garden-co/fix/co-record-key-deep-loading
Fix return type on deep loaded co.record() when using single keys
2025-08-10 22:54:54 +02:00
Guido D'Orsi
ceab75eb4d Merge pull request #2718 from garden-co/feat/nice-music-player
fix: fix UnknownError: Unknown transaction on IndexedDB
2025-08-10 22:40:50 +02:00
Guido D'Orsi
103d1b41f7 fix: fix unknown transaction error on IndexedDB 2025-08-10 22:32:23 +02:00
Guido D'Orsi
b87cc6973e Merge pull request #2717 from garden-co/feat/nice-music-player
feat: improve music player UI
2025-08-10 22:23:41 +02:00
Guido D'Orsi
3d541ca241 feat: improve music player controls bar 2025-08-10 22:21:03 +02:00
Matteo Manchi
e72bfec884 fixup! fix(jazz-tools/coValues): fix return type on deep loaded co.record() when using string keys 2025-08-10 20:37:37 +02:00
Matteo Manchi
19c7ad27d9 fix(jazz-tools/coValues): fix return type on deep loaded co.record() when using string keys 2025-08-10 15:22:24 +02:00
Guido D'Orsi
0bc7bfc5cc test: cover string loading and unavailable props 2025-08-09 17:31:49 +02:00
Matteo Manchi
2c8120d46f fix(jazz-tools/coValues): fix return type on deep loaded co.record() when using single keys 2025-08-09 16:58:49 +02:00
Guido D'Orsi
c936c8c611 Merge pull request #2708 from garden-co/changeset-release/main
Version Packages
2025-08-08 19:29:12 +02:00
github-actions[bot]
58c6013770 Version Packages 2025-08-08 16:28:02 +00:00
Guido D'Orsi
3eb3291a97 Merge pull request #2714 from garden-co/revert-2712-fix/invalid-signature-allowlist
Revert "feat: add markAsStorageSignatureToFix to make it possible to fix bad signatures caused by the storage bug fixed in 0.15.9"
2025-08-08 18:25:32 +02:00
Guido D'Orsi
6b659f2df3 Revert "feat: add markAsStorageSignatureToFix to make it possible to fix bad signatures caused by the storage bug fixed in 0.15.9" 2025-08-08 18:25:22 +02:00
Guido D'Orsi
dcc9c9a5ec Merge pull request #2712 from garden-co/fix/invalid-signature-allowlist
feat: add markAsStorageSignatureToFix to make it possible to fix bad signatures caused by the storage bug fixed in 0.15.9
2025-08-08 18:14:58 +02:00
Guido D'Orsi
fe9a244363 Merge pull request #2710 from garden-co/fix/missing-child-rotation
fix: handle missing child groups when rotating key
2025-08-08 18:12:11 +02:00
Guido D'Orsi
9440bbc058 Merge pull request #2713 from garden-co/fix/nested-discriminated-union
fix: fix nested discriminated unions
2025-08-08 18:08:34 +02:00
Guido D'Orsi
1c92cc2997 chore: improve the key fallback 2025-08-08 18:07:15 +02:00
Guido D'Orsi
33ebbf0bdd fix: fix nested discriminated unions 2025-08-08 17:58:50 +02:00
Guido D'Orsi
d630b5bde5 Merge pull request #2704 from garden-co/fix/everyone-readkey-rotation
fix: skip rotateKey when everyone has read access
2025-08-08 17:31:14 +02:00
Nicolas Embleton
8c56445882 chore(packages/community-jazz-vue): merge main into branch + address type issues that prevent the build
fix(packages/community-jazz-vue): address an issue where the transition right after login was not triggering a reactive cascade, making the example apps stuck in "Anonymous" until manual refresh
chore(packages/community-jazz-vue): minor clean-up removing an un-necessary use of nextTick that can disrupt some reactive behaviors
chore(examples/community-todo-vue): fix a warning / remove un-needed CSS file, remnant of the VueJS inspector
chore(examples/community-chat-vue,examples/community-clerk-vue): fix a warning about the inspector component imported from VanillaJS not being detected by VueJS as a component
2025-08-08 20:11:34 +07:00
Guido D'Orsi
1c6ae12cd9 feat: add markAsStorageSignatureToFix to make it possible to fix bad signatures caused by the storage bug fixed in 0.15.9 2025-08-08 14:53:58 +02:00
Giordano Ricci
ac5d20d159 Revert "Merge pull request #2709 from garden-co/revert-2682-gio/usage-metering"
This reverts commit b3d1ad7201, reversing
changes made to fbc29f2f17.
2025-08-08 12:35:16 +01:00
Guido D'Orsi
21bcaabd5a chore: update failing snapshot 2025-08-08 13:13:45 +02:00
Guido D'Orsi
17b4d5b668 chore: update failing snapshot 2025-08-08 13:12:51 +02:00
Guido D'Orsi
3cd15862d5 fix: handle missing child groups when rotating key 2025-08-08 13:11:40 +02:00
Guido D'Orsi
b3d1ad7201 Merge pull request #2709 from garden-co/revert-2682-gio/usage-metering
Revert "feat: add ingress/egress metering on cojosn-transport-ws"
2025-08-08 12:32:58 +02:00
Guido D'Orsi
d87df11795 fix: fallback to the latest available readkey when key_for_everyone was not being revealed when everyone has access 2025-08-08 12:16:55 +02:00
Giordano Ricci
82c2a62b2a Revert "feat: add ingress/egress metering on cojosn-transport-ws" 2025-08-08 11:03:48 +01:00
Guido D'Orsi
0a9112506e fix: fixes cilrcular import issue on group.test.ts 2025-08-08 11:40:24 +02:00
Nicolas Embleton
d9c9b5f099 Merge branch 'main' into community-vue-packages 2025-08-08 16:34:02 +07:00
Giordano Ricci
fbc29f2f17 Merge pull request #2682 from garden-co/gio/usage-metering 2025-08-08 11:16:59 +02:00
Brad Anderson
3915bbbf3c fix: update tests due to sync protocol improvements 2025-08-06 17:08:00 -04:00
Brad Anderson
0b471c4e89 fix: undo organizeImports that broke tests - jazz-tools 2025-08-06 12:34:29 -04:00
Brad Anderson
09077d37ef chore: code-quality version bump, biome to catalog for examples 2025-08-06 10:40:42 -04:00
Brad Anderson
afe06b4fa6 chore: format-and-lint:fix 2025-08-06 10:29:38 -04:00
Brad Anderson
d89b6e488a chore: bump biome version to 2.1.3 2025-08-06 10:26:33 -04:00
Guido D'Orsi
f6361ee43b Merge pull request #2703 from didier/patch-2
Update Svelte setup doc to be more accurate for Svelte 5
2025-08-06 15:41:16 +02:00
Guido D'Orsi
726dbfb6df fix: heal groups with missing key for everyone 2025-08-06 13:44:10 +02:00
Guido D'Orsi
267f689f10 fix: skip rotateKey when everyone has read access 2025-08-06 12:26:36 +02:00
Giordano Ricci
893ad3ae23 comment out flaky assertion 2025-08-06 12:25:06 +02:00
Giordano Ricci
f5590b1be8 remove duplicated import 2025-08-06 12:14:10 +02:00
Giordano Ricci
17a01f57e8 move utils 2025-08-06 12:12:08 +02:00
Giordano Ricci
7318d86f52 Merge branch 'main' into gio/usage-metering 2025-08-06 12:05:05 +02:00
Didier Catz
1c8403e87a Update to new schema syntax instead of classes 2025-08-06 10:23:35 +02:00
Didier Catz
dd747c068a Use consistent quotes / semis 2025-08-06 00:30:00 +02:00
Didier Catz
1f0f230fe2 Newline 2025-08-06 00:28:37 +02:00
Didier Catz
da655cbff5 Typo 2025-08-06 00:24:45 +02:00
Didier Catz
02f6c6220e Update svelte.mdx 2025-08-06 00:10:31 +02:00
Didier Catz
0755cd198e Update svelte.mdx 2025-08-06 00:02:30 +02:00
Didier Catz
c4a8227b66 Update Svelte setup doc to be more accurate for Svelte 5 2025-08-06 00:00:55 +02:00
Giordano Ricci
86f0302233 add meta 2025-08-05 13:13:06 +01:00
Sammii
72b5542130 Merge branch 'main' into feat/add-input-label 2025-08-05 12:41:06 +01:00
Sammii
5fd9225a54 adding intent styles to input + u0pdating docs 2025-08-05 12:32:52 +01:00
Sammii
9138d30208 create input and label components, with docs 2025-08-05 12:18:49 +01:00
Sammii
a5ece15797 adding defaults to button 2025-08-05 12:04:41 +01:00
Sammii
9f8877202e creating color-highlight var in quint 2025-08-05 10:56:08 +01:00
Sammii
d190097ed9 creating tempory nav 2025-08-05 10:55:16 +01:00
Sammii
9841617c66 adding colours to homepage 2025-08-05 10:54:56 +01:00
Guido D'Orsi
165a6170cd Merge pull request #2700 from garden-co/changeset-release/main
Version Packages
2025-08-04 21:13:54 +02:00
github-actions[bot]
5148419df9 Version Packages 2025-08-04 19:11:13 +00:00
Guido D'Orsi
fc0ecb0968 chore: changeset 2025-08-04 21:07:48 +02:00
Guido D'Orsi
802b5a3060 chore: changeset 2025-08-04 21:06:23 +02:00
Guido D'Orsi
e47af262b3 Merge pull request #2673 from garden-co/feat/storage-wal
fix: ensure that transactions are synced in the correct order
2025-08-04 20:54:53 +02:00
Sammii
688a4850a4 add svg sizes to button and amend icons docs page 2025-08-04 16:09:39 +01:00
Sammii
e87fef751e remove old icon pages 2025-08-04 16:08:01 +01:00
Sammii
8f714440f8 create icons page 2025-08-04 16:07:28 +01:00
Sammii
70cd09170e updating button docs page 2025-08-04 16:02:28 +01:00
Guido D'Orsi
e98b610fd0 Merge pull request #2698 from garden-co/feat/comap-pick-and-partial
feat: Add `.pick()` and `.partial()` methods to CoMapSchema
2025-08-04 14:53:38 +02:00
Guido D'Orsi
b554983558 Merge pull request #2699 from garden-co/fix/extend-circular-check
fix: fixes error when extending a group without having child groups loaded
2025-08-04 14:53:15 +02:00
Guido D'Orsi
4c63334299 chore: add comments 2025-08-04 14:48:17 +02:00
Guido D'Orsi
4aef7cdac5 Update .changeset/ten-cobras-fetch.md
Co-authored-by: Joe Innes <joe@joeinn.es>
2025-08-04 14:39:26 +02:00
Guido D'Orsi
76adeb0d53 chore: clean up implementation 2025-08-04 14:03:51 +02:00
Guido D'Orsi
d95dcbe7db fix: align pick to the Zod API 2025-08-04 13:24:44 +02:00
Guido D'Orsi
f9d538f049 fix: fixes error when extending a group without having child groups loaded 2025-08-04 12:37:53 +02:00
Guido D'Orsi
40c7336c09 chore: update lucide-react 2025-08-04 11:21:18 +02:00
Guido D'Orsi
e0d2723615 fix: router update when calling navitate 2025-08-04 11:17:49 +02:00
Guido D'Orsi
93e68c62f5 docs: fix a missing type alias 2025-08-04 10:52:06 +02:00
Guido D'Orsi
dadee9dcc5 test: fix flaky test 2025-08-04 10:40:03 +02:00
Guido D'Orsi
6724c4bd83 feat: add docs, remove lodash-es dependency and add tests for recursive types with pick and partial 2025-08-04 10:34:43 +02:00
NicoR
1942bd5de4 Replace lodash with lodash-es 2025-08-04 01:44:27 -03:00
NicoR
16764f6365 Add changeset 2025-08-04 01:23:00 -03:00
NicoR
b56cfc2e1f Add TS docs 2025-08-04 01:21:32 -03:00
NicoR
7091bcf9c0 Add CoMapSchema.partial 2025-08-04 01:17:25 -03:00
NicoR
436cbfa095 Add CoMapSchema.pick 2025-08-04 01:00:57 -03:00
Guido D'Orsi
c19a25f928 feat(chat): implement lazy-loading 2025-08-03 17:20:46 +02:00
Guido D'Orsi
104e664bbb fix: fix build errors on music player 2025-08-03 17:20:15 +02:00
Guido D'Orsi
f199b451eb chore: use inline JSON when creating covalues 2025-08-03 17:09:02 +02:00
Guido D'Orsi
70bc48458e Merge pull request #2695 from garden-co/feat/music-player-refresh
docs: exclude upgrade guides from llm.txt
2025-08-02 14:37:56 +02:00
Guido D'Orsi
f28b2a6135 docs: exclude upgrade guides 2025-08-02 14:36:55 +02:00
Guido D'Orsi
55b770b7c9 Merge pull request #2694 from garden-co/feat/music-player-refresh
feat: improve the music player UI
2025-08-02 14:29:35 +02:00
Guido D'Orsi
e6838dfb98 feat: make the music-player a PWA 2025-08-02 14:22:36 +02:00
Guido D'Orsi
5e34061fdc feat: improve the music player UI 2025-08-02 14:19:24 +02:00
Guido D'Orsi
6d9b77195a chore: clean up code 2025-08-02 12:36:56 +02:00
Guido D'Orsi
9bf7946ee6 feat: added a TTL-based garbage collection 2025-08-01 19:58:11 +02:00
Guido D'Orsi
acecffaeb2 test: fix flaky tests on the created and update time 2025-08-01 19:57:33 +02:00
Nico Rainhart
0a98d6aaf2 Merge pull request #2691 from garden-co/changeset-release/main
Version Packages
2025-08-01 12:57:33 -03:00
github-actions[bot]
4ea1a63a0a Version Packages 2025-08-01 15:47:51 +00:00
Nico Rainhart
41a4c3bc95 Merge pull request #2683 from garden-co/feat/json-create-and-set
feat: create CoValues using plain JSON objects
2025-08-01 12:45:30 -03:00
Guido D'Orsi
60d0027f9d Merge pull request #2690 from garden-co/docs/useCoState-jsDoc
docs: adds jsDocs to useCoState and useAccount react hooks
2025-08-01 16:12:39 +02:00
Guido D'Orsi
748c2ff751 Merge pull request #2688 from garden-co/changeset-release/main
Version Packages
2025-08-01 15:46:41 +02:00
Guido D'Orsi
cc291b590a Merge remote-tracking branch 'origin/main' into community-vue-packages 2025-08-01 15:46:12 +02:00
Guido D'Orsi
1f144e89bf chore: update lockfile 2025-08-01 15:43:09 +02:00
github-actions[bot]
70938b0ab3 Version Packages 2025-08-01 13:42:25 +00:00
Guido D'Orsi
f2f5b55dbf Update packages/jazz-tools/src/react-core/hooks.ts
Co-authored-by: Nico Rainhart <nmrainhart@gmail.com>
2025-08-01 15:42:24 +02:00
Guido D'Orsi
3c3acae803 Merge pull request #2689 from joeinnes/2687-fix-broken-link
Fix HTTP API link in inbox.mdx. Fixes #2687
2025-08-01 15:40:07 +02:00
Guido D'Orsi
896ee3460f docs: adds jsDocs to useCoState and useAccount react hooks 2025-08-01 15:37:02 +02:00
Joe Innes
9b9bf44e2b Fix HTTP API link in inbox.mdx. Fixes #2687 2025-08-01 15:13:33 +02:00
Guido D'Orsi
8e9acb37f8 chore(ci): add community-clerk-vue to e2e tests 2025-08-01 15:06:21 +02:00
Guido D'Orsi
392aa88d95 Merge pull request #2655 from joeinnes/docs/optional-references
Docs/optional references
2025-08-01 13:15:20 +02:00
Joe Innes
7ce82cd934 Merge branch 'main' into docs/optional-references 2025-08-01 13:13:26 +02:00
Guido D'Orsi
0c8158b91c Merge pull request #2676 from Gabrola/fix/jazz-run-exports
fix: jazz-run package.json exports
2025-08-01 13:07:30 +02:00
Guido D'Orsi
5a48c9c44c chore: improve tests titles and add comments 2025-08-01 10:14:24 +02:00
Guido D'Orsi
25c56146f5 Merge pull request #2686 from garden-co/test/logout-state
test: logout integration tests on browser
2025-08-01 09:57:18 +02:00
nembleton
8115e194d3 chore(packages/community-jazz-vue): remove DemoAuth UI
chore(packages/community-jazz-vue): remove VueJS Inspector since every package uses the VanillaJS one now
chore(packages/community-jazz-vue): update models to be Zod-based rather than CoValue-based in the tests
chore(examples/community-clerk-vue): add + fix Playwright tests
2025-08-01 09:24:49 +07:00
NicoR
c564fbb02e test: add permission tests for creating nested CoValues from JSON 2025-07-31 15:03:25 -03:00
Guido D'Orsi
12481e14c2 test: logout integration tests on browser 2025-07-31 18:56:37 +02:00
NicoR
fd2d247ff5 docs: improve examples 2025-07-31 13:08:50 -03:00
NicoR
9e9ea029b2 fix: move alert out of CodeGroup 2025-07-30 17:32:56 -03:00
NicoR
a0da272dcd fix: add missing import in docs 2025-07-30 16:51:23 -03:00
NicoR
72fbcc3262 chore: remove unnecessary import from form example 2025-07-30 15:52:07 -03:00
NicoR
f4c8cc858b docs: add sections for creating CoValues from JSON and permissions 2025-07-30 15:48:55 -03:00
Anselm
0ab4d7a20d Update meta description 2025-07-30 11:34:54 -07:00
Giordano Ricci
5c98ff4e4f use object.values 2025-07-30 19:24:45 +01:00
NicoR
4cbda689c4 refactor: update form example to use new API 2025-07-30 14:54:08 -03:00
NicoR
771b0ed914 fix: cannot create empty plain text when nested 2025-07-30 14:53:38 -03:00
NicoR
79913c3136 fix: simplify CoMapInit schema 2025-07-30 13:37:10 -03:00
NicoR
43d3511d15 Add changeset 2025-07-30 12:40:25 -03:00
NicoR
928ef14086 feat: support deeply nested optional primitive fields 2025-07-30 12:33:28 -03:00
NicoR
048dd7def0 feat: support deeply nested optional CoValue fields 2025-07-30 12:33:09 -03:00
Guido D'Orsi
51fcb8a44b test: improve the client subscription test 2025-07-30 17:29:01 +02:00
Guido D'Orsi
c5888c39f5 perf: update parent before updating children to favor batching 2025-07-30 17:14:45 +02:00
Guido D'Orsi
2defcfae67 test: mark retry unavailable states as flaky 2025-07-30 17:10:50 +02:00
NicoR
873b146d15 feat: create a child group for each created CoValue 2025-07-30 11:03:38 -03:00
Guido D'Orsi
213de11c3b feat: preserve transaction order on sync 2025-07-30 15:37:58 +02:00
Sammii
2f24d35471 amending comments for button tv 2025-07-30 12:49:49 +01:00
Sammii
42667c81bb imrprove icon docs 2025-07-30 12:42:36 +01:00
Giordano Ricci
1b881cc89f cleanup tests 2025-07-30 12:00:54 +01:00
Guido D'Orsi
af295d816a chore: add comments and rename CoValueSyncQueue in LocalTransactionsSyncQueue 2025-07-30 12:54:46 +02:00
Guido D'Orsi
fe8d3497c0 chore: fix the peer attribution on storage corrections tests 2025-07-30 12:37:19 +02:00
Giordano Ricci
c2899e94ca add changeset 2025-07-30 11:36:17 +01:00
Giordano Ricci
f4be67e9b6 Merge branch 'main' into gio/usage-metering 2025-07-30 11:34:37 +01:00
Guido D'Orsi
ba9ad295b6 fix: don't consider -1 as a valid signature checkpoint 2025-07-30 12:32:07 +02:00
Giordano Ricci
9ed5a96ef8 lockfile update 2025-07-30 11:28:44 +01:00
Giordano Ricci
4272ea9019 refactor: use getTransactionSize util 2025-07-30 11:28:17 +01:00
Giordano Ricci
9509307ed1 cleanup and add egress tests 2025-07-30 11:27:24 +01:00
Giordano Ricci
be08921bc5 cleanup and add ingress tests 2025-07-30 11:01:10 +01:00
Sammii
77e3c21cbd format globals css 2025-07-30 10:58:18 +01:00
NicoR
ab1798c7bd feat: make CoValue creation from JSON type safe 2025-07-29 16:41:48 -03:00
NicoR
26ae69a242 refactor: reuse TypeOfZodSchema 2025-07-29 12:44:42 -03:00
Giordano Ricci
25be055a51 wip: basica ingress/egress metering 2025-07-29 16:39:34 +01:00
NicoR
21ad3767b9 refactor: avoid InstanceOrPrimitiveOfSchemaCoValuesNullable duplication 2025-07-29 11:48:46 -03:00
NicoR
a9383516c1 refactor: avoid InstanceOrPrimitiveOfSchema duplication 2025-07-29 11:38:09 -03:00
NicoR
bffc516c68 refactor: extract TypeOfZodSchema util 2025-07-29 11:16:14 -03:00
NicoR
9e7c0d9887 chore: clean up instantiateRefEncodedWithInit's implementation 2025-07-29 10:56:32 -03:00
NicoR
99b44d5780 feat: create CoMap with JSON discriminated union fields 2025-07-29 10:36:33 -03:00
NicoR
02db5f3b1d feat: create CoMap with JSON CoFeed fields 2025-07-29 09:13:01 -03:00
NicoR
1949a5fcd9 feat: create CoMap with JSON CoList fields 2025-07-28 17:15:43 -03:00
NicoR
dcd3b022cc feat: create CoMap with JSON plain and rich text fields 2025-07-28 16:56:43 -03:00
NicoR
a7b837c7e1 feat: create CoMap with JSON CoMap fields 2025-07-28 16:47:51 -03:00
Anselm Eickhoff
88ebcf58ab Merge pull request #2680 from garden-co/jazz-as-a-db
Jazz as a DB narrative MVP
2025-07-28 11:34:01 -07:00
Guido D'Orsi
b173e0884a feat: improve local transactions streaming calculation 2025-07-28 19:45:31 +02:00
Anselm
f379a168be Update garden co slogan 2025-07-28 10:30:18 -07:00
Anselm
bde6ac7d45 Jazz as a DB narrative MVP 2025-07-28 10:08:56 -07:00
Guido D'Orsi
231947c97a fix(sync): start a new content message when the size exceeds the recommended value 2025-07-28 18:44:13 +02:00
Guido D'Orsi
d1609cdd55 Merge pull request #2679 from garden-co/changeset-release/main
Version Packages
2025-07-28 18:12:46 +02:00
Guido D'Orsi
d5b57ad1fc fix: fix priority for content 2025-07-28 17:53:33 +02:00
github-actions[bot]
b71ab3168a Version Packages 2025-07-28 15:15:40 +00:00
Nico Rainhart
0c8f6e5039 Merge pull request #2677 from garden-co/feat/add-nullable-support
feat: Add support for nullable non-collaborative fields
2025-07-28 12:12:12 -03:00
Guido D'Orsi
0bf5c53bec fix: disable code coverage check on CI 2025-07-28 16:59:02 +02:00
Guido D'Orsi
e7b1550003 feat: perserve insert order when storing transactions on multiple covalues 2025-07-28 16:59:02 +02:00
NicoR
6a93a1b8a3 chore: add comment on why nullable date cofields are not supported 2025-07-28 11:52:44 -03:00
NicoR
9f654a2603 test: loading a map with a nullable field 2025-07-28 11:46:50 -03:00
NicoR
dbf735d9e1 Fix rebase error 2025-07-28 10:27:44 -03:00
NicoR
c62abefb66 Add changeset 2025-07-28 10:19:55 -03:00
NicoR
1453869a46 Add support for nullable non-collaborative fields 2025-07-28 10:18:39 -03:00
Sammii
f5039cefc1 addig icon button to docs page and icons pagr tidy 2025-07-28 14:09:46 +01:00
Youssef Gaber
239da90c9f chore: changeset 2025-07-28 17:06:01 +04:00
Youssef Gaber
972791e7a8 fix: correct jazz-run package.json exports 2025-07-28 17:05:45 +04:00
Sammii
6540893caf adding default, strong and muted to css 2025-07-28 13:51:23 +01:00
Sammii
bfc85c4573 refactoring icon and icon page 2025-07-28 13:50:57 +01:00
Sammii
e9076313ab amending Button page, adding title 2025-07-28 13:50:44 +01:00
Sammii
c6afd8ae36 adding placeholder favicon 2025-07-28 13:40:52 +01:00
Sammii
370f20d13d refactoring css and button component 2025-07-28 13:40:31 +01:00
NicoR
0c0178764e chore: fix rebase errors 2025-07-28 09:34:24 -03:00
NicoR
928350b821 refactor: rename OptionalizeUndefinedKeys to PartialOnUndefined 2025-07-28 09:34:23 -03:00
NicoR
be3fd9c696 test: create CoMap with shallowly resolved CoValue 2025-07-28 09:34:23 -03:00
NicoR
269c028df0 test: add test for CoMapSchema + catchall 2025-07-28 09:34:23 -03:00
NicoR
e4df837138 refactor: rename CoMapInitZod to CoMapSchemaInit 2025-07-28 09:34:23 -03:00
NicoR
54fe6d93ba refactor: extract CoMapSchema.create's return type 2025-07-28 09:34:23 -03:00
NicoR
979689c6d8 refactor: improve CatchAll type handling in CoMapSchemas 2025-07-28 09:34:23 -03:00
NicoR
859a37868f refactor: simplify CoMapSchema.create's return type 2025-07-28 09:34:23 -03:00
NicoR
57bd32d77e refactor: simplify the type of CoMapSchema.create's init parameter 2025-07-28 09:34:23 -03:00
Sammii
f9b3116deb adding custom color steps to all tailwind css colours in design system 2025-07-28 13:10:42 +01:00
Sammii
352d34979f create icon component 2025-07-25 17:34:29 +01:00
Sammii
7ff736ace4 improving gradient on muted, default and strong intent buttons 2025-07-25 16:23:23 +01:00
Sammii
5bab466fd0 adding default/hover/active states for all intents 2025-07-25 16:20:55 +01:00
Sammii
329b8c3d6a switching muted and default styles 2025-07-25 15:29:01 +01:00
Nico Rainhart
e21cbccd4b Merge pull request #2674 from garden-co/changeset-release/main
Version Packages
2025-07-25 11:07:46 -03:00
github-actions[bot]
a66ab7d174 Version Packages 2025-07-25 13:34:52 +00:00
Nico Rainhart
78e91f4030 Merge pull request #2667 from garden-co/0-16
Jazz 0.16 upgrade
2025-07-25 10:32:40 -03:00
Guido D'Orsi
7a915c198e Merge pull request #2657 from garden-co/fix/root-trusting
feat: store the root id unencrypted in account
2025-07-25 14:39:59 +02:00
Guido D'Orsi
c9b0420746 Merge pull request #2672 from garden-co/chore/fix-conflicts-between-http-api-and-schema-refactoring
chore: Fix conflicts between HTTP requests and CoValue schema refactor
2025-07-25 14:39:16 +02:00
NicoR
2303f3e70a fix: schema definition error in server-http-worker example 2025-07-25 09:31:47 -03:00
NicoR
a7bc9569a3 chore: Fix conflicts between HTTP requests and CoValue schema refactor 2025-07-25 09:19:15 -03:00
NicoR
f351ba0fcd Merge remote-tracking branch 'origin/main' into chore/fix-conflicts-between-http-api-and-schema-refactoring 2025-07-25 09:12:05 -03:00
Guido D'Orsi
d3e554f491 Merge pull request #2671 from garden-co/chore/queues
chore: move cojson queues in a dedicated directory
2025-07-25 13:03:33 +02:00
Guido D'Orsi
b5e31456ad chore: trigger deploy 2025-07-25 12:25:05 +02:00
Sammii
c0aeb7baf9 porting over variant/intent styles 2025-07-25 11:21:17 +01:00
Sammii
8a14de10d7 fix(quint-ui): correct hover and active states for button variants 2025-07-25 11:13:39 +01:00
Sammii
b585b39a86 porting over glass styles with specular borders 2025-07-25 10:15:57 +01:00
Guido D'Orsi
42d07ba7b4 docs: upgrade docs for account root id 2025-07-25 11:13:40 +02:00
Guido D'Orsi
b81b6ba69b Merge remote-tracking branch 'origin/0-16' into fix/root-trusting 2025-07-25 10:54:21 +02:00
Guido D'Orsi
1bc1759bb4 Merge remote-tracking branch 'origin/main' into chore/queues 2025-07-25 10:51:43 +02:00
Guido D'Orsi
225bc1f63f Merge pull request #2670 from garden-co/changeset-release/main
Version Packages
2025-07-25 10:37:04 +02:00
github-actions[bot]
5d94564f99 Version Packages 2025-07-25 08:30:50 +00:00
Guido D'Orsi
9633d0187f chore: changeset 2025-07-25 10:28:31 +02:00
Guido D'Orsi
b82ecaa3ca docs: add server worker http example card 2025-07-25 10:23:16 +02:00
Guido D'Orsi
111ec8d351 Merge pull request #2626 from garden-co/feat/http-requests
RPC style HTTP requests with CoValues
2025-07-25 10:18:10 +02:00
Nico Rainhart
512aacdbc2 Merge pull request #2669 from garden-co/fix/simplify-circular-constraint
fix: circular constraint type check error with `Simplify`
2025-07-24 17:07:53 -03:00
NicoR
7ad843aa3e fix: circular constraint type check error with Simplify 2025-07-24 16:18:21 -03:00
Guido D'Orsi
071128339b docs: fix typo 2025-07-24 21:03:55 +02:00
Guido D'Orsi
688ced499d feat: ux improvments on http example 2025-07-24 18:59:33 +02:00
Guido D'Orsi
ac91c8e7c2 Merge remote-tracking branch 'origin/main' into feat/http-requests 2025-07-24 18:23:00 +02:00
Guido D'Orsi
c3b303c310 feat: track handled messages in the request envelope 2025-07-24 18:21:23 +02:00
Guido D'Orsi
fc027a56db Merge pull request #2650 from garden-co/refactor/covalue-zod-schema-boundary
refactor: CoValue schemas are no longer Zod schemas
2025-07-24 17:41:09 +02:00
Guido D'Orsi
959a7a3927 Merge branch '0-16' into refactor/covalue-zod-schema-boundary 2025-07-24 17:40:51 +02:00
NicoR
2548085b59 Update upgrade guide and docs with improved support for recursive refs 2025-07-24 12:24:33 -03:00
Guido D'Orsi
b27bb3e65b test: remove .only 2025-07-24 17:21:04 +02:00
Guido D'Orsi
937284f7e9 Merge pull request #2666 from garden-co/changeset-release/main
Version Packages
2025-07-24 17:16:01 +02:00
github-actions[bot]
e999727c70 Version Packages 2025-07-24 14:58:34 +00:00
Guido D'Orsi
2197766624 Merge pull request #2665 from garden-co/fix/optional-ref-assign
fix: property update when assigning an optional reference on CoMap
2025-07-24 16:56:05 +02:00
Guido D'Orsi
d1efde468f Merge remote-tracking branch 'origin/main' into fix/root-trusting 2025-07-24 16:55:31 +02:00
Guido D'Orsi
4d4fd0beaa chore: use import content instead of copy on acceptInvite 2025-07-24 16:55:19 +02:00
Guido D'Orsi
2b61e853a7 test: cover recursive references without explicit return type 2025-07-24 16:43:35 +02:00
Guido D'Orsi
6f79b45544 chore: move cojson queues in a dedicated directory 2025-07-24 16:23:58 +02:00
Guido D'Orsi
2e1ff99579 chore: use import content instead of copy on acceptInvite 2025-07-24 16:04:43 +02:00
Guido D'Orsi
7361854ee4 chore: add isCoValueId check 2025-07-24 16:03:41 +02:00
Guido D'Orsi
4a775fada3 chore: remove unused import 2025-07-24 15:57:20 +02:00
Guido D'Orsi
3fe53a3a4a fix: property update when assigning an optional reference on CoMap 2025-07-24 12:19:48 +02:00
Guido D'Orsi
fe37516786 chore: remove TODO from play route 2025-07-24 12:18:43 +02:00
Guido D'Orsi
4beafb7cf3 fix: property update when assigning an optional reference on CoMap 2025-07-24 12:16:35 +02:00
Guido D'Orsi
82a592e08a feat(ci): add server-worker-http to ci tests 2025-07-24 11:45:15 +02:00
Guido D'Orsi
4c6926153a chore: simplify groups in createPlayer 2025-07-24 11:45:15 +02:00
Guido D'Orsi
c51b088243 Update examples/server-worker-http/src/apiKey.ts
Co-authored-by: Nico Rainhart <nmrainhart@gmail.com>
2025-07-24 11:36:55 +02:00
Guido D'Orsi
867cb6b7a5 Merge pull request #2659 from garden-co/changeset-release/main
Version Packages
2025-07-23 18:30:51 +02:00
Guido D'Orsi
3a1fdd7600 docs: remove per-user limits exception 2025-07-23 18:28:30 +02:00
Guido D'Orsi
3fdbb43b54 docs: added the security safeguards provided by Jazz 2025-07-23 18:20:04 +02:00
Guido D'Orsi
02969ee89b docs: pr feedback 2025-07-23 17:49:39 +02:00
Sammii
e9b2860e74 button tv refactor 2025-07-23 16:32:07 +01:00
Guido D'Orsi
9b4988a514 docs: improve deploy requirements docs 2025-07-23 17:27:15 +02:00
Sammii
6327d74f68 mapping over button suite from old design system to quint 2025-07-23 16:24:19 +01:00
Guido D'Orsi
8aa4b59d49 chore: fix typo
Co-authored-by: Nico Rainhart <nmrainhart@gmail.com>
2025-07-23 16:39:27 +02:00
NicoR
ac782674de One more Upgrade guide tweak 2025-07-23 11:24:14 -03:00
Sammii
bedbabdcb4 styling the layout of quint docs 2025-07-23 15:18:48 +01:00
NicoR
5eb406d54d Upgrade guide adjustments 2025-07-23 11:03:47 -03:00
NicoR
a3be832414 Remove WithHelpers export 2025-07-23 10:55:35 -03:00
NicoR
7ca8dd960e Merge branch 'main' into refactor/covalue-zod-schema-boundary 2025-07-23 10:50:08 -03:00
NicoR
62c8aff73f Fix Upgrade guide navigation 2025-07-23 10:24:53 -03:00
NicoR
7731109a28 Address Upgrade guide comments 2025-07-23 10:08:14 -03:00
github-actions[bot]
0401fcf2a8 Version Packages 2025-07-23 12:43:56 +00:00
Nico Rainhart
139a649279 Merge pull request #2663 from garden-co/fix/export-WithHelpers-type
fix: Export `WithHelpers` type
2025-07-23 09:41:20 -03:00
NicoR
9acccb5df2 Add changeset 2025-07-23 09:23:12 -03:00
NicoR
fd90cdb49a fix: Export WithHelpers type 2025-07-23 09:15:09 -03:00
Guido D'Orsi
4a29999c6a feat: support worker groups 2025-07-23 14:07:09 +02:00
Giordano Ricci
df487d5335 Merge pull request #2658 from garden-co/gio/quint 2025-07-23 12:35:06 +01:00
Giordano Ricci
1efe84c691 Merge branch 'main' into gio/quint 2025-07-23 12:17:02 +01:00
Guido D'Orsi
73b99c6c1a docs: http request jsDoc 2025-07-23 13:07:51 +02:00
Guido D'Orsi
039b92c839 feat: export API and jsdocs 2025-07-23 13:03:58 +02:00
Guido D'Orsi
618af5f1e3 feat: e2e tests and improve UX on server-worker-http 2025-07-23 12:50:48 +02:00
NicoR
dfc4286694 Return resolved resized images on createImage 2025-07-22 15:15:02 -03:00
NicoR
970ff0d813 Fix docs 2025-07-22 14:57:57 -03:00
Guido D'Orsi
65eee0ef01 docs: update the server-worker-http readme 2025-07-22 18:39:11 +02:00
NicoR
eee221f563 Merge branch 'main' into refactor/covalue-zod-schema-boundary 2025-07-22 13:38:47 -03:00
Guido D'Orsi
063553090e docs: fix type errors on co.profile 2025-07-22 18:29:18 +02:00
Guido D'Orsi
97f6bcedbd test: remove failing test 2025-07-22 18:24:07 +02:00
Guido D'Orsi
7c63e6bb0f docs: mention jazz-run account create defaults 2025-07-22 18:07:06 +02:00
Guido D'Orsi
08aedcf517 docs: fix typos 2025-07-22 17:53:08 +02:00
Guido D'Orsi
3e12ee127f docs: cover the inbox API 2025-07-22 17:52:06 +02:00
NicoR
2283d375ef Update docs 2025-07-22 12:17:49 -03:00
NicoR
202e763380 Merge branch 'main' into refactor/covalue-zod-schema-boundary 2025-07-22 12:07:57 -03:00
NicoR
52bbdb37a9 Add .optional() to all CoValue schemas 2025-07-22 12:06:21 -03:00
Nicolas Embleton
c2c223f22a Merge branch 'garden-co:main' into community-vue-packages 2025-07-22 21:41:47 +07:00
Guido D'Orsi
96f743b2f4 Merge remote-tracking branch 'origin/main' into feat/http-requests 2025-07-22 16:38:26 +02:00
NicoR
f5c47feeb6 Upgrade Zod to 3.25.76 instead 2025-07-22 11:33:01 -03:00
Guido D'Orsi
6dffe73bd2 chore: rename jazz-paper-scissors in server-worker-inbox 2025-07-22 16:32:08 +02:00
Guido D'Orsi
6f9ee31179 feat: rename serve-worker examples 2025-07-22 16:23:05 +02:00
Guido D'Orsi
52f324ffc4 chore: update lockfile 2025-07-22 16:21:34 +02:00
Guido D'Orsi
2d86f53575 Merge remote-tracking branch 'origin/main' into feat/http-requests 2025-07-22 16:20:49 +02:00
Guido D'Orsi
68cb357a94 Merge pull request #2660 from garden-co/feat/persistent-peers
feat: introduce persistent peers
2025-07-22 16:18:56 +02:00
Guido D'Orsi
56ccf9ab9d feat: make it possible to define API with empty responses 2025-07-22 15:57:57 +02:00
NicoR
b8b0851433 Add upgrade guide 2025-07-22 10:42:46 -03:00
NicoR
2bbb07b0bf Add changeset 2025-07-22 10:01:00 -03:00
Guido D'Orsi
4f7bc91502 chore: apply suggestions from code review
Co-authored-by: Nico Rainhart <nmrainhart@gmail.com>
2025-07-22 14:40:45 +02:00
NicoR
d3053955d8 Update docs 2025-07-22 09:34:40 -03:00
Giordano Ricci
f61a120560 changeset 2025-07-22 12:22:22 +01:00
Giordano Ricci
2f1307a0ba rename package 2025-07-22 12:21:19 +01:00
Giordano Ricci
fa15ea56d1 build & dev workflows 2025-07-22 12:03:21 +01:00
Guido D'Orsi
1e58ecb3ac test: integration tests for browser and workers on offline loading & sync 2025-07-22 12:42:49 +02:00
Giordano Ricci
ceeabfaf89 strip down PR 2025-07-22 11:07:52 +01:00
Guido D'Orsi
70ce7c5736 feat: introduce persistent peers 2025-07-22 11:12:48 +02:00
NicoR
f40484eca9 Migrate plain text, rich text, file stream and optional schemas to classes 2025-07-21 17:00:57 -03:00
NicoR
d581a59aa1 Convert CoDiscriminatedUnionSchema into a class 2025-07-21 16:39:43 -03:00
NicoR
0ca09f75c1 Convert CoFeedSchema into a class 2025-07-21 16:30:04 -03:00
NicoR
e8fcd101f2 Convert CoListSchema into a class 2025-07-21 16:24:35 -03:00
NicoR
cf43fa7529 Remove usage of withHelpers 2025-07-21 15:23:36 -03:00
NicoR
df1cdda4e8 Update zod version in all examples 2025-07-21 15:15:51 -03:00
Guido D'Orsi
be46042cdc chore: fix typos and errors 2025-07-21 20:12:39 +02:00
Nico Rainhart
6afdb16739 Merge pull request #2656 from garden-co/feat/prevent-resolving-discriminated-union-fields
feat: Prevent resolving discriminated union fields
2025-07-21 14:45:51 -03:00
NicoR
7a60d7bb76 Avoid NotNull duplication 2025-07-21 14:33:22 -03:00
NicoR
f8263a8358 Stop extending Zod schemas when creating core CoValue schemas 2025-07-21 14:25:23 -03:00
NicoR
f6da966922 Explain "core" CoValue schema / actual CoValue schema distinction 2025-07-21 14:21:44 -03:00
Giordano Ricci
b0b2b85a6f fix lint issues 2025-07-21 18:13:18 +01:00
NicoR
8a2ab51543 Expose internal schemas for co.map, co.list, co.optional and co.account 2025-07-21 14:09:48 -03:00
Giordano Ricci
28c19c134f Merge branch 'main' into gio/quint 2025-07-21 18:09:42 +01:00
Giordano Ricci
0924c9baaa revert changes to example app 2025-07-21 18:04:17 +01:00
Guido D'Orsi
8bfaa0a18b docs: document deployments 2025-07-21 19:00:19 +02:00
Giordano Ricci
b2712e18a2 fix imports 2025-07-21 17:46:15 +01:00
Giordano Ricci
66894b63d7 more cleanup 2025-07-21 17:43:11 +01:00
Guido D'Orsi
147be76399 test: cover deep response sharing 2025-07-21 16:51:24 +02:00
Guido D'Orsi
36770bed52 fix: limit the content pieces sent to server to the envelope 2025-07-21 16:45:40 +02:00
NicoR
466e6c44ee Remove unused imports 2025-07-21 11:43:11 -03:00
NicoR
5bd8277161 Remove withHelpers schema method 2025-07-21 11:41:28 -03:00
Giordano Ricci
b1a05143e3 cleanup 2025-07-21 15:26:49 +01:00
Giordano Ricci
fb761ce66d cleanup 2025-07-21 15:25:09 +01:00
Giordano Ricci
07a6c340dc some cleanup 2025-07-21 15:23:02 +01:00
Guido D'Orsi
8b4261f7d8 docs: pr feedback 2025-07-21 16:13:59 +02:00
NicoR
0ec917e453 Remove non-namespaced CoValue schema exports 2025-07-21 10:42:23 -03:00
NicoR
6326d0fc45 Export co.Image type 2025-07-21 10:30:55 -03:00
NicoR
d746b1279a Remove deprecated createCoValueObservable function 2025-07-21 10:18:54 -03:00
Giordano Ricci
0fea904dd0 tailwind class source 2025-07-21 14:04:39 +01:00
Giordano Ricci
373aef313f wip: quint 2025-07-21 13:55:21 +01:00
Guido D'Orsi
c09dcdfc76 feat: make the root trusting 2025-07-21 12:06:28 +02:00
NicoR
a584590ed8 Add changeset 2025-07-18 18:00:50 -03:00
NicoR
0a830e29a9 Prevent resolving discriminated union fields 2025-07-18 17:57:07 -03:00
NicoR
4402c553b6 Fix z.object bug with cyclic references 2025-07-18 15:44:54 -03:00
NicoR
e76fe343da Fix co.discriminatedUnion with cyclic references 2025-07-18 15:10:53 -03:00
Guido D'Orsi
dc183a19b2 feat: show that the opponent has made their move 2025-07-18 19:51:10 +02:00
Guido D'Orsi
fef55a4cd6 fix: remove bad export on route 2025-07-18 19:34:47 +02:00
Guido D'Orsi
ddef54048f docs: fix type error 2025-07-18 19:26:40 +02:00
NicoR
a2626a0f38 Avoid rehydrating CoValue schemas 2025-07-18 14:21:27 -03:00
NicoR
ec579bcaf7 Go back to using tuple for discriminatedUnion's options type 2025-07-18 14:19:53 -03:00
Guido D'Orsi
8aa2d2a789 docs: fix type errors on organization pattern 2025-07-18 19:12:24 +02:00
Guido D'Orsi
a39d009b87 docs: http requests & server workers 2025-07-18 19:00:36 +02:00
Joe Innes
6b662b0efe Type fixes for twoslash 2025-07-18 15:06:03 +02:00
Guido D'Orsi
efff4d0f4f Merge pull request #2654 from garden-co/fix/vitest-type-tests
fix: restore type tests on Vitest and upgrade Vitest to v3.2.4
2025-07-18 14:13:01 +02:00
Guido D'Orsi
ea2b01d8a2 fix: restore type tests on Vitest and upgrade Vitest to v3.2.4 2025-07-18 12:51:02 +02:00
NicoR
e9af90c841 Fix Zod type messing up Zod's type inference 2025-07-17 17:06:37 -03:00
NicoR
2b7c6f5aa7 Rename anySchemaToCoSchema to coValueClassFromCoValueClassOrSchema 2025-07-17 16:55:43 -03:00
NicoR
d73a3d9d46 Rename files 2025-07-17 16:52:56 -03:00
NicoR
8af39077a3 Remove CoValueSchema.getZodSchema 2025-07-17 16:44:48 -03:00
NicoR
54bd487818 PlainText, RichText and FileStream schemas are no longer Zod schemas 2025-07-17 16:30:09 -03:00
Guido D'Orsi
f01dab5c8f test: cover requests error management 2025-07-17 20:29:02 +02:00
Joe Innes
a8b3ec7bb0 Add more detail regarding optional references
As the boundary becomes more defined between CoValue schemas and Zod schemas, we need to ensure folks pick the right `.optional()` between `co.optional()` for CoValues and `z.optional()` for primitives.
2025-07-17 20:24:03 +02:00
Guido D'Orsi
a7f6870048 chore: update package json 2025-07-17 19:43:37 +02:00
Guido D'Orsi
3b294f6994 Merge remote-tracking branch 'origin/main' into feat/http-requests 2025-07-17 19:42:56 +02:00
NicoR
a420b43029 Add CoDiscriminatedUnionSchema.optional() 2025-07-17 14:29:52 -03:00
NicoR
a57268de32 Add runtime check to prevent using z.object with coValues as values 2025-07-17 14:12:41 -03:00
NicoR
6b2c4ed280 Remove no longer necessary Zod re-export wrappers 2025-07-17 14:12:41 -03:00
NicoR
8d4e0027be Upgrade Zod to 4.0.5 2025-07-17 14:12:41 -03:00
NicoR
a4141da1b7 Drop support for z.optional CoValue schemas 2025-07-17 14:12:41 -03:00
NicoR
c9ca5202f9 Fix browser integration tests 2025-07-17 14:12:41 -03:00
NicoR
7b50a2e06d Avoid using core.$ZodTypeDiscriminable for CoDiscriminatedUnions 2025-07-17 14:12:40 -03:00
NicoR
43dabccb57 [WIP] Discriminable CoValue schemas no longer extend $ZodDiscriminatedUnion 2025-07-17 14:12:40 -03:00
NicoR
b6d04f56ef Preserve catchall type info in CoMapSchema 2025-07-17 14:12:40 -03:00
NicoR
628195b678 Remove unused types from test 2025-07-17 14:12:40 -03:00
NicoR
9a5d769717 Use CoValue schema types (instead of Zod's) in circular references 2025-07-17 14:12:40 -03:00
NicoR
e30a3f66bf CoValue schemas are no longer Zod schemas 2025-07-17 14:12:40 -03:00
NicoR
6327fce933 Refactor CoValue instances & Zod primitives type inference 2025-07-17 14:12:40 -03:00
NicoR
a650da4184 Fix bug with deeply nested discriminated unions 2025-07-17 14:12:40 -03:00
NicoR
6e4a94f6ce Avoid accessing CoDiscriminatedUnion Zod internals directly 2025-07-17 14:12:40 -03:00
NicoR
b73bec64bc Rename AnyCoSchemas to CoreCoValueSchemas 2025-07-17 14:12:40 -03:00
NicoR
50ae2f47c2 Remove CoValue schema cache 2025-07-17 14:12:40 -03:00
NicoR
724d8e7f30 Drop support for z.discriminatedUnion of CoValue schemas 2025-07-17 14:12:40 -03:00
NicoR
7b285ab110 Modify CoMap schema to no longer extend ZodObject 2025-07-17 14:12:40 -03:00
NicoR
01ac9b8c4c Rewrite tests that access Zod internals 2025-07-17 14:12:40 -03:00
NicoR
4e2e1ac73e Convert CoValue schemas into interfaces 2025-07-17 14:12:40 -03:00
NicoR
94960c1f65 Convert CoValue schemas into a discriminated union 2025-07-17 14:12:40 -03:00
NicoR
b5af58347b Add getZodSchema method to CoValue schemas 2025-07-17 14:12:40 -03:00
NicoR
46a84558c5 Clean up InstanceOfSchema types 2025-07-17 14:12:40 -03:00
NicoR
f93566c045 Replace references to z.core.$ZodType with AnyZodSchema 2025-07-17 14:12:39 -03:00
NicoR
d97ed603a3 Tighten CoOptionalSchema inner type 2025-07-17 14:12:39 -03:00
NicoR
8d33103182 Rename zodSchemaToCoSchema to coreSchemaToCoSchema 2025-07-17 14:12:39 -03:00
NicoR
aaa1ff978b Organize Schema Union types 2025-07-17 14:12:39 -03:00
NicoR
82655ea7a7 Stop exporting zodSchemaToCoSchema 2025-07-17 14:12:39 -03:00
NicoR
8afe3a2e02 Remove unnecessary usages of zodSchemaToCoSchema 2025-07-17 14:12:39 -03:00
NicoR
ae2adcbd15 Extract functions to create CoreCoSchemas 2025-07-17 14:12:39 -03:00
NicoR
eb0460d330 Revert https://github.com/garden-co/jazz/pull/2651 2025-07-17 14:12:30 -03:00
Guido D'Orsi
55cb83e6e0 Merge pull request #2652 from garden-co/changeset-release/main
Version Packages
2025-07-17 15:26:20 +02:00
github-actions[bot]
6290088fec Version Packages 2025-07-17 13:06:49 +00:00
Guido D'Orsi
b9c17b37db Merge pull request #2651 from garden-co/fix/optional-load
fix: load failures when loading a missing ref declared with z.optional and Schema.optional
2025-07-17 15:04:36 +02:00
Guido D'Orsi
6c76ff8fbf fix: load of missing z.optional and Schema.optional doesn't fail 2025-07-17 14:54:33 +02:00
Guido D'Orsi
3c6a2a6092 Merge pull request #2649 from garden-co/changeset-release/main
Version Packages
2025-07-16 13:16:14 +02:00
github-actions[bot]
e8a950e61a Version Packages 2025-07-16 10:15:20 +00:00
Guido D'Orsi
e2cbf035de Merge pull request #2648 from garden-co/fix/onAnonymous-stuck
fix: fix stuck authentication when using onAnonymousAccountDiscarded with a storage
2025-07-16 12:13:11 +02:00
Matteo Manchi
47599b6307 chore(cojson): new removeStorage method exposed on LocalNode 2025-07-16 11:19:35 +02:00
Guido D'Orsi
901d0762ee Merge pull request #2647 from garden-co/fix/GCO-646-uncaught-from-load-promise
fix: catch errors from CoValue loading and treat it as "unavailable"
2025-07-16 09:18:43 +02:00
Guido D'Orsi
d1c1b0c5cc fix: fix stuck authentication when using onAnonymousAccountDiscarded with a storage 2025-07-15 22:17:51 +02:00
Matteo Manchi
cf4ad7285d fix(jazz-tools/tools): catch errors from CoValue loading and treat it as "unavailable" 2025-07-15 21:12:21 +02:00
Guido D'Orsi
255a947ea6 feat: add error management 2025-07-15 20:22:04 +02:00
Guido D'Orsi
530a263d35 chore: add vercel.json 2025-07-15 18:42:05 +02:00
Guido D'Orsi
2983c7bd58 fix: ignore builds for server-side-validation 2025-07-15 18:41:12 +02:00
nembleton
d60e345b4d fix(packages/community-jazz-vue): small typescript inconsistency 2025-07-15 23:23:34 +07:00
Guido D'Orsi
745020b7a8 chore: refactor code 2025-07-15 18:06:37 +02:00
Nicolas Embleton
be47d866bc Merge branch 'garden-co:main' into community-vue-packages 2025-07-15 22:38:52 +07:00
Guido D'Orsi
991aebf7a7 feat: only accept init payload and improve auth checks 2025-07-14 21:00:08 +02:00
Guido D'Orsi
9b75880b10 feat: bestEffortResolution on export 2025-07-14 18:53:44 +02:00
Guido D'Orsi
98fe72ed42 fix: push players data on joinGameRequest 2025-07-14 15:00:47 +02:00
Guido D'Orsi
1f5c81c2ea feat: nicer example UI 2025-07-14 14:45:16 +02:00
Guido D'Orsi
fc41aa165b feat: support CoMapInit as payload for send/response 2025-07-14 14:29:20 +02:00
Guido D'Orsi
41466ea399 feat: simplify the API 2025-07-11 20:01:30 +02:00
Guido D'Orsi
3b91594d10 feat: migrate the jazz-paper-scissors to request & nextjs and rename it to server-side-validation 2025-07-11 18:11:47 +02:00
Nicolas Embleton
ac88bdcb98 Merge branch 'garden-co:main' into community-vue-packages 2025-07-11 22:44:03 +07:00
nembleton
30704bcaf7 feat(@community-jazz-vue): port Jazz Vue bindings to 0.15 and match React signatures 1-to-1 for easier forward maintenance and to make it easier to infer Vue Bindings usage from React documentation
feat(@community-jazz-vue): port React Inspector to Vue to make it easier to use within Vue projects
chore(@community-jazz-vue): add tests to prevent regressions with Vue Proxy / Reactivity, which works quite differently from React's
feat(@community-chat-vue): add up-to-date, reworked Chat Vue example
feat(@community-clerk-vue): add Clerk Auth example with Vue
feat(@community-todo-vue): add up-to-date, reworked Todo Vue example
2025-07-11 22:42:22 +07:00
Guido D'Orsi
265a6405af feat: implement the payload/response schema spec 2025-07-10 16:56:30 +02:00
Guido D'Orsi
a4d23d527b feat: add content import/export to the experimental_request and move into tools 2025-07-09 18:43:53 +02:00
nembleton
03108871e9 chore: restore vue examples and bindings under community prefix
chore: update dependencies to new single package structure
2025-06-30 11:58:08 +07:00
593 changed files with 41636 additions and 8112 deletions

View File

@@ -14,7 +14,8 @@
"jazz-betterauth-server-plugin",
"jazz-react-auth-betterauth",
"jazz-run",
"jazz-tools"
"jazz-tools",
"community-jazz-vue"
]
],
"access": "public",

View File

@@ -22,9 +22,6 @@ jobs:
- name: Setup Biome
uses: biomejs/setup-biome@v2
with:
version: 1.9.4
version: 2.1.3
- name: Run Biome
run: biome ci .
- name: Check Catalog Dependencies
run: node scripts/check-catalog-deps.js

View File

@@ -48,6 +48,7 @@ jobs:
"tests/e2e"
"examples/chat"
"examples/chat-svelte"
"examples/community-clerk-vue"
"examples/clerk"
"examples/betterauth"
"examples/file-share-svelte"
@@ -55,6 +56,7 @@ jobs:
"examples/inspector"
"examples/music-player"
"examples/organization"
"examples/server-worker-http"
"starters/react-passkey-auth"
"starters/svelte-passkey-auth"
"tests/jazz-svelte"

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"$schema": "https://biomejs.dev/schemas/2.1.3/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
@@ -7,39 +7,35 @@
},
"files": {
"ignoreUnknown": false,
"ignore": [
"jazz-tools.json",
"**/ios/**",
"**/android/**",
"tests/jazz-svelte/src/**",
"examples/*svelte*/**",
"starters/*svelte*/**",
"examples/jazz-paper-scissors/src/routeTree.gen.ts",
"homepage/homepage/**",
"**/package.json"
"includes": [
"**",
"!**/jazz-tools.json",
"!**/ios/**",
"!**/android/**",
"!**/tests/jazz-svelte/src/**",
"!**/examples/**/*svelte*/**",
"!**/starters/**/*svelte*/**",
"!**/examples/server-worker-inbox/src/routeTree.gen.ts",
"!**/homepage/homepage/**",
"!**/package.json",
"!**/*svelte*/**"
]
},
"formatter": {
"enabled": true,
"indentStyle": "space"
},
"organizeImports": {
"enabled": true
},
"assist": { "actions": { "source": { "organizeImports": "off" } } },
"linter": {
"enabled": false,
"rules": {
"recommended": true,
"correctness": {
"useExhaustiveDependencies": "off",
"useImportExtensions": {
"level": "error",
"options": {
"suggestedExtensions": {
"ts": {
"module": "js",
"component": "jsx"
}
}
"forceJsExtensions": true
}
}
}
@@ -47,16 +43,7 @@
},
"overrides": [
{
"include": ["packages/**/src/**"],
"linter": {
"enabled": true,
"rules": {
"recommended": false
}
}
},
{
"include": ["packages/cojson/src/storage/*/**", "cojson-transport-ws/**"],
"includes": ["packages/community-jazz-vue/src/**"],
"linter": {
"enabled": true,
"rules": {
@@ -65,7 +52,28 @@
}
},
{
"include": ["**/tests/**"],
"includes": ["**/packages/**/src/**"],
"linter": {
"enabled": true,
"rules": {
"recommended": false
}
}
},
{
"includes": [
"**/packages/cojson/src/storage/**/*/**",
"**/cojson-transport-ws/**"
],
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
},
{
"includes": ["**/tests/**"],
"linter": {
"rules": {
"correctness": {
@@ -75,7 +83,7 @@
"noNonNullAssertion": "off"
},
"suspicious": {
"noExplicitAny": "info"
"noExplicitAny": "off"
}
}
}

View File

@@ -27,22 +27,22 @@
"jazz-tools": "workspace:*",
"lucide-react": "^0.510.0",
"next": "15.3.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react": "catalog:react",
"react-dom": "catalog:react",
"sonner": "^2.0.3",
"tailwind-merge": "^3.3.0",
"tw-animate-css": "^1.2.5"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@biomejs/biome": "catalog:default",
"@playwright/test": "^1.50.1",
"@tailwindcss/postcss": "^4",
"@types/better-sqlite3": "^7.6.12",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react": "catalog:react",
"@types/react-dom": "catalog:react",
"react-email": "^4.0.11",
"tailwindcss": "^4",
"typescript": "^5"
"typescript": "catalog:default"
}
}

View File

@@ -13,13 +13,13 @@
"@bacons/text-decoder": "^0.0.0",
"@bam.tech/react-native-image-resizer": "^3.0.11",
"@react-native-community/netinfo": "11.4.1",
"expo": "54.0.0-canary-20250701-6a945c5",
"expo-clipboard": "^7.1.4",
"expo-secure-store": "~14.2.3",
"expo-sqlite": "~15.2.10",
"expo": "catalog:expo",
"expo-clipboard": "catalog:expo",
"expo-secure-store": "catalog:expo",
"expo-sqlite": "catalog:expo",
"jazz-tools": "workspace:*",
"react": "19.1.0",
"react-native": "0.80.0",
"react": "catalog:expo",
"react-native": "catalog:expo",
"react-native-get-random-values": "^1.11.0",
"readable-stream": "^4.7.0"
},

View File

@@ -18,8 +18,8 @@
"@react-navigation/native": "7.1.14",
"@react-navigation/native-stack": "7.3.19",
"jazz-tools": "workspace:*",
"react": "19.1.0",
"react-native": "0.80.0",
"react": "catalog:rn",
"react-native": "catalog:rn",
"react-native-get-random-values": "^1.11.0",
"react-native-mmkv": "3.3.0",
"react-native-safe-area-context": "5.5.0",
@@ -31,16 +31,16 @@
"@babel/plugin-transform-export-namespace-from": "^7.27.1",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native-community/cli": "19.0.0",
"@react-native-community/cli-platform-android": "19.0.0",
"@react-native-community/cli-platform-ios": "19.0.0",
"@react-native/babel-preset": "0.80.0",
"@react-native/eslint-config": "0.80.0",
"@react-native/metro-config": "0.80.0",
"@react-native/typescript-config": "0.80.0",
"@react-native-community/cli": "catalog:rn",
"@react-native-community/cli-platform-android": "catalog:rn",
"@react-native-community/cli-platform-ios": "catalog:rn",
"@react-native/babel-preset": "catalog:rn",
"@react-native/eslint-config": "catalog:rn",
"@react-native/metro-config": "catalog:rn",
"@react-native/typescript-config": "catalog:rn",
"@rnx-kit/metro-config": "^2.0.1",
"@rnx-kit/metro-resolver-symlinks": "^0.2.5",
"@types/react": "^19.1.0",
"@types/react": "catalog:rn",
"eslint": "^8.19.0",
"pod-install": "^0.3.5",
"prettier": "2.8.8",

View File

@@ -76,7 +76,9 @@ export function ChatScreen({ navigation }: { navigation: any }) {
const renderMessageItem = ({
item,
}: { item: Loaded<typeof Message, { text: true }> }) => {
}: {
item: Loaded<typeof Message, { text: true }>;
}) => {
const isMe = item._edits?.text?.by?.isMe;
return (
<View

View File

@@ -3,11 +3,7 @@ import React from "react";
import { Text } from "react-native";
import { Chat } from "./schema";
export function HandleInviteScreen({
navigation,
}: {
navigation: any;
}) {
export function HandleInviteScreen({ navigation }: { navigation: any }) {
useAcceptInviteNative({
invitedObjectSchema: Chat,
onAccept: async (chatId) => {

View File

@@ -1,5 +1,147 @@
# passkey-svelte
## 0.0.118
### Patch Changes
- Updated dependencies [5963658]
- jazz-tools@0.17.5
## 0.0.117
### Patch Changes
- Updated dependencies [7dd3d00]
- jazz-tools@0.17.4
## 0.0.116
### Patch Changes
- jazz-tools@0.17.3
## 0.0.115
### Patch Changes
- Updated dependencies [794681a]
- Updated dependencies [83fc22f]
- jazz-tools@0.17.2
## 0.0.114
### Patch Changes
- Updated dependencies [0bcbf55]
- Updated dependencies [d1bdbf5]
- Updated dependencies [4b73834]
- jazz-tools@0.17.1
## 0.0.113
### Patch Changes
- Updated dependencies [fcaf4b9]
- jazz-tools@0.17.0
## 0.0.112
### Patch Changes
- Updated dependencies [67e0968]
- Updated dependencies [2c8120d]
- jazz-tools@0.16.6
## 0.0.111
### Patch Changes
- Updated dependencies [3cd1586]
- Updated dependencies [33ebbf0]
- jazz-tools@0.16.5
## 0.0.110
### Patch Changes
- Updated dependencies [16764f6]
- jazz-tools@0.16.4
## 0.0.109
### Patch Changes
- Updated dependencies [43d3511]
- jazz-tools@0.16.3
## 0.0.108
### Patch Changes
- jazz-tools@0.16.2
## 0.0.107
### Patch Changes
- Updated dependencies [c62abef]
- jazz-tools@0.16.1
## 0.0.106
### Patch Changes
- 2bbb07b: Introduce a cleaner separation between Zod and CoValue schemas:
- Zod schemas and CoValue schemas are fully separated. Zod schemas can only be composed with other Zod schemas. CoValue schemas can be composed with either Zod or other CoValue schemas.
- `z.optional()` and `z.discriminatedUnion()` no longer work with CoValue schemas. Use `co.optional()` and `co.discriminatedUnion()` instead.
- Internal schema access is now simpler. You no longer need to use Zods `.def` to access internals. Use properties like `CoMapSchema.shape`, `CoListSchema.element`, and `CoOptionalSchema.innerType` directly.
- CoValue schema types are now namespaced under `co.`. Non-namespaced exports have been removed
- CoMap schemas no longer incorrectly inherit from Zod. Previously, methods like `.extend()` and `.partial()` appeared available but could cause unexpected behavior. These methods are now disabled. In their place, `.optional()` has been added, and more Zod-like methods will be introduced in future releases.
- Upgraded Zod from `3.25.28` to `3.25.76`.
- Removed deprecated `withHelpers` method from CoValue schemas
- Removed deprecated `createCoValueObservable` function
- Updated dependencies [c09dcdf]
- Updated dependencies [2bbb07b]
- jazz-tools@0.16.0
## 0.0.105
### Patch Changes
- Updated dependencies [9633d01]
- Updated dependencies [4beafb7]
- jazz-tools@0.15.16
## 0.0.104
### Patch Changes
- Updated dependencies [3fe53a3]
- jazz-tools@0.15.15
## 0.0.103
### Patch Changes
- Updated dependencies [a584590]
- Updated dependencies [9acccb5]
- jazz-tools@0.15.14
## 0.0.102
### Patch Changes
- Updated dependencies [6c76ff8]
- jazz-tools@0.15.13
## 0.0.101
### Patch Changes
- Updated dependencies [d1c1b0c]
- Updated dependencies [cf4ad72]
- jazz-tools@0.15.12
## 0.0.100
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-svelte",
"version": "0.0.100",
"version": "0.0.118",
"type": "module",
"private": true,
"scripts": {

View File

@@ -1,12 +1,11 @@
<script lang="ts">
import { ImageDefinition, type Loaded } from 'jazz-tools';
import { useProgressiveImg } from '$lib/utils/useProgressiveImage.svelte';
import { Image } from 'jazz-tools/svelte';
let { image }: { image: Loaded<typeof ImageDefinition> } = $props();
const { src } = $derived(
useProgressiveImg({
image
})
);
</script>
<img class="h-auto max-h-[20rem] max-w-full rounded-t-xl mb-1" {src} alt="" />
<Image
imageId={image.id}
alt=""
class="h-auto max-h-[20rem] max-w-full rounded-t-xl mb-1"
/>

View File

@@ -1,8 +1,8 @@
import { co, z } from 'jazz-tools';
import { co } from 'jazz-tools';
export const Message = co.map({
text: co.plainText(),
image: z.optional(co.image())
image: co.optional(co.image())
});
export const Chat = co.list(Message);

View File

@@ -1,53 +0,0 @@
import { ImageDefinition, type Loaded } from 'jazz-tools';
import { onDestroy } from 'svelte';
export function useProgressiveImg({
image,
maxWidth,
targetWidth
}: {
image: Loaded<typeof ImageDefinition> | null | undefined;
maxWidth?: number;
targetWidth?: number;
}) {
let current = $state<{
src?: string;
res?: `${number}x${number}` | 'placeholder';
}>();
const originalSize = $state(image?.originalSize);
const unsubscribe = image?.subscribe({}, (update: Loaded<typeof ImageDefinition>) => {
const highestRes = ImageDefinition.highestResAvailable(update, { maxWidth, targetWidth });
if (highestRes) {
if (highestRes.res !== current?.res) {
const blob = highestRes.stream.toBlob();
if (blob) {
const blobURI = URL.createObjectURL(blob);
current = { src: blobURI, res: highestRes.res };
setTimeout(() => URL.revokeObjectURL(blobURI), 200);
}
}
} else {
current = {
src: update?.placeholderDataURL,
res: 'placeholder'
};
}
});
onDestroy(() => () => {
unsubscribe?.();
});
return {
get src() {
return current?.src;
},
get res() {
return current?.res;
},
originalSize
};
}

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { createImage } from 'jazz-tools/browser-media-images';
import { createImage } from 'jazz-tools/media';
import { AccountCoState, CoState } from 'jazz-tools/svelte';
import { Account, CoPlainText, type ID } from 'jazz-tools';

View File

@@ -15,21 +15,21 @@
"clsx": "^2.0.0",
"hash-slash": "workspace:*",
"jazz-tools": "workspace:*",
"lucide-react": "^0.274.0",
"react": "19.1.0",
"react-dom": "19.1.0",
"zod": "3.25.28"
"lucide-react": "^0.536.0",
"react": "catalog:react",
"react-dom": "catalog:react",
"zod": "3.25.76"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@tailwindcss/postcss": "^4.1.10",
"@types/react": "19.1.0",
"@types/react-dom": "19.1.0",
"@types/react": "catalog:react",
"@types/react-dom": "catalog:react",
"@vitejs/plugin-react-swc": "^3.10.1",
"is-ci": "^3.0.1",
"postcss": "^8.4.40",
"tailwindcss": "^4.1.10",
"typescript": "5.6.2",
"vite": "^6.3.5"
"typescript": "catalog:default",
"vite": "catalog:default"
}
}

View File

@@ -1,6 +1,7 @@
import { Account, co } from "jazz-tools";
import { createImage, useAccount, useCoState } from "jazz-tools/react";
import { useState } from "react";
import { Account } from "jazz-tools";
import { createImage } from "jazz-tools/media";
import { useAccount, useCoState } from "jazz-tools/react";
import { useEffect, useState } from "react";
import { Chat, Message } from "./schema.ts";
import {
BubbleBody,
@@ -15,14 +16,17 @@ import {
TextInput,
} from "./ui.tsx";
export function ChatScreen(props: { chatID: string }) {
const chat = useCoState(Chat, props.chatID, {
resolve: { $each: { text: true } },
});
const { me } = useAccount();
const [showNLastMessages, setShowNLastMessages] = useState(30);
const INITIAL_MESSAGES_TO_SHOW = 30;
if (!chat)
export function ChatScreen(props: { chatID: string }) {
const chat = useCoState(Chat, props.chatID);
const { me } = useAccount();
const [showNLastMessages, setShowNLastMessages] = useState(
INITIAL_MESSAGES_TO_SHOW,
);
const isLoading = useMessagesPreload(props.chatID);
if (!chat || isLoading)
return (
<div className="flex-1 flex justify-center items-center">Loading...</div>
);
@@ -37,11 +41,15 @@ export function ChatScreen(props: { chatID: string }) {
return;
}
createImage(file, { owner: chat._owner }).then((image) => {
createImage(file, {
owner: chat._owner,
progressive: true,
placeholder: "blur",
}).then((image) => {
chat.push(
Message.create(
{
text: co.plainText().create(file.name, chat._owner),
text: file.name,
image: image,
},
chat._owner,
@@ -59,9 +67,14 @@ export function ChatScreen(props: { chatID: string }) {
<ChatBody>
{chat.length > 0 ? (
chat
// We call slice before reverse to avoid mutating the original array
.slice(-showNLastMessages)
.reverse() // this plus flex-col-reverse on ChatBody gives us scroll-to-bottom behavior
.map((msg) => <ChatBubble me={me} msg={msg} key={msg.id} />)
// Reverse plus flex-col-reverse on ChatBody gives us scroll-to-bottom behavior
.reverse()
.map(
(msg) =>
msg?.text && <ChatBubble me={me} msg={msg} key={msg.id} />,
)
) : (
<EmptyChatMessage />
)}
@@ -80,12 +93,7 @@ export function ChatScreen(props: { chatID: string }) {
<TextInput
onSubmit={(text) => {
chat.push(
Message.create(
{ text: co.plainText().create(text, chat._owner) },
chat._owner,
),
);
chat.push(Message.create({ text }, chat._owner));
}}
/>
</InputBar>
@@ -93,10 +101,7 @@ export function ChatScreen(props: { chatID: string }) {
);
}
function ChatBubble(props: {
me: Account;
msg: co.loaded<typeof Message, { text: true }>;
}) {
function ChatBubble(props: { me: Account; msg: Message }) {
if (!props.me.canRead(props.msg) || !props.msg.text?.toString()) {
return (
<BubbleContainer fromMe={false}>
@@ -126,3 +131,35 @@ function ChatBubble(props: {
</BubbleContainer>
);
}
/**
* Warms the local cache with the initial messages to load only the initial messages
* and avoid flickering
*/
function useMessagesPreload(chatID: string) {
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
preloadChatMessages(chatID).finally(() => {
setIsLoading(false);
});
}, [chatID]);
return isLoading;
}
async function preloadChatMessages(chatID: string) {
const chat = await Chat.load(chatID);
if (!chat?._refs) return;
const promises = [];
for (const msg of Array.from(chat._refs)
.reverse()
.slice(0, INITIAL_MESSAGES_TO_SHOW)) {
promises.push(Message.load(msg.id, { resolve: { text: true } }));
}
await Promise.all(promises);
}

View File

@@ -1,6 +1,6 @@
import clsx from "clsx";
import { CoPlainText, ImageDefinition } from "jazz-tools";
import { ProgressiveImg } from "jazz-tools/react";
import { Image } from "jazz-tools/react";
import { ImageIcon } from "lucide-react";
import { useId, useRef } from "react";
@@ -83,14 +83,12 @@ export function BubbleText(props: {
export function BubbleImage(props: { image: ImageDefinition }) {
return (
<ProgressiveImg image={props.image}>
{({ src }) => (
<img
className="h-auto max-h-80 max-w-full rounded-t-xl mb-1"
src={src}
/>
)}
</ProgressiveImg>
<Image
imageId={props.image.id}
className="h-auto max-h-80 max-w-full rounded-t-xl mb-1"
height="original"
width="original"
/>
);
}
@@ -112,7 +110,9 @@ export function InputBar(props: { children: React.ReactNode }) {
export function ImageInput({
onImageChange,
}: { onImageChange?: (event: React.ChangeEvent<HTMLInputElement>) => void }) {
}: {
onImageChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
}) {
const inputRef = useRef<HTMLInputElement>(null);
const onUploadClick = () => {

View File

@@ -14,21 +14,21 @@
"@bam.tech/react-native-image-resizer": "^3.0.11",
"@clerk/clerk-expo": "^2.13.1",
"@react-native-community/netinfo": "11.4.1",
"expo": "54.0.0-canary-20250701-6a945c5",
"expo-crypto": "~14.1.5",
"expo-linking": "~7.1.5",
"expo-secure-store": "~14.2.3",
"expo-sqlite": "~15.2.10",
"expo-web-browser": "~14.2.0",
"expo": "catalog:expo",
"expo-crypto": "catalog:expo",
"expo-linking": "catalog:expo",
"expo-secure-store": "catalog:expo",
"expo-sqlite": "catalog:expo",
"expo-web-browser": "catalog:expo",
"jazz-tools": "workspace:*",
"react": "19.1.0",
"react-native": "0.80.0",
"react": "catalog:expo",
"react-native": "catalog:expo",
"react-native-get-random-values": "^1.11.0",
"readable-stream": "^4.7.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@types/react": "~19.0.10",
"@types/react": "catalog:expo",
"typescript": "~5.8.3"
},
"private": true

View File

@@ -10,7 +10,9 @@ import {
export function SignInScreen({
setPage,
}: { setPage: (page: "sign-in" | "sign-up") => void }) {
}: {
setPage: (page: "sign-in" | "sign-up") => void;
}) {
const { signIn, setActive, isLoaded } = useSignIn();
const [emailAddress, setEmailAddress] = useState("");

View File

@@ -10,7 +10,9 @@ import {
export function SignUpScreen({
setPage,
}: { setPage: (page: "sign-in" | "sign-up") => void }) {
}: {
setPage: (page: "sign-in" | "sign-up") => void;
}) {
const { isLoaded, signUp, setActive } = useSignUp();
const [emailAddress, setEmailAddress] = React.useState("");

View File

@@ -14,17 +14,17 @@
"dependencies": {
"@clerk/clerk-react": "^5.4.1",
"jazz-tools": "workspace:*",
"react": "19.1.0",
"react-dom": "19.1.0"
"react": "catalog:react",
"react-dom": "catalog:react"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@biomejs/biome": "1.9.4",
"@types/react": "19.1.0",
"@types/react-dom": "19.1.0",
"@biomejs/biome": "catalog:default",
"@types/react": "catalog:react",
"@types/react-dom": "catalog:react",
"@vitejs/plugin-react": "^4.5.1",
"globals": "^15.11.0",
"typescript": "5.6.2",
"vite": "^6.3.5"
"typescript": "catalog:default",
"vite": "catalog:default"
}
}

View File

@@ -0,0 +1,6 @@
dist
# env files
.env
.env.*
!.env.example
!.env.test

View File

@@ -0,0 +1,7 @@
# community-chat-vue
## 0.15.4
- rewrite from React to Vue
- jazz-tools@0.15.4
- community-jazz-vue@0.15.4

View File

@@ -0,0 +1,60 @@
# Chat example with Jazz and Vue
## Getting started
You can either
1. Clone the jazz repository, and run the app within the monorepo.
2. Or create a new Jazz project using this example as a template.
### Using the example as a template
Create a new Jazz project, and use this example as a template.
```bash
npx create-jazz-app@latest chat-vue-app --example community-chat-vue
```
Go to the new project directory.
```bash
cd chat-vue-app
```
Run the dev server.
```bash
npm run dev
```
### Using the monorepo
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
Clone the jazz repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
Install and build dependencies.
```bash
pnpm i && npx turbo build
```
Go to the example directory.
```bash
cd jazz/examples/community-chat-vue/
```
Start the dev server.
```bash
pnpm dev
```
Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
## 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 Cloud](https://jazz.tools/cloud) (`wss://cloud.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzProvider` in [./src/main.ts](./src/main.ts) to `{ peer: "ws://localhost:4200" }`.

1
examples/community-chat-vue/env.d.ts vendored Normal file
View File

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

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jazz Chat Vue Example</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,37 @@
{
"name": "community-chat-vue",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build-type-check": "run-p type-check \"build {@}\" --",
"preview": "vite preview",
"build": "vite build",
"type-check": "vue-tsc --build --force",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write"
},
"dependencies": {
"jazz-tools": "workspace:*",
"community-jazz-vue": "workspace:*",
"vue": "^3.5.11",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@types/node": "^22.5.1",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vue/tsconfig": "^0.5.1",
"eslint": "^9.7.0",
"eslint-plugin-vue": "^9.28.0",
"npm-run-all2": "^6.2.3",
"postcss": "^8.4.40",
"@tailwindcss/postcss": "^4.1.10",
"tailwindcss": "^4.1.10",
"typescript": "5.6.2",
"vite": "6.3.5",
"vite-plugin-vue-devtools": "^7.4.6",
"vue-tsc": "^2.1.6"
}
}

View File

@@ -0,0 +1,5 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,24 @@
<template>
<AppContainer>
<TopBar v-if="me">
<p>{{ me.profile?.name }}</p>
<button @click="logoutHandler">Log out</button>
</TopBar>
<router-view />
</AppContainer>
</template>
<script setup lang="ts">
import { useAccount } from "community-jazz-vue";
import { useRouter } from "vue-router";
import AppContainer from "./components/AppContainer.vue";
import TopBar from "./components/TopBar.vue";
const { me, logOut } = useAccount();
const router = useRouter();
async function logoutHandler() {
await logOut();
router.push("/");
}
</script>

View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
import { JazzVueProvider, PasskeyAuthBasicUI } from "community-jazz-vue";
import { h } from "vue";
import "jazz-tools/inspector/register-custom-element";
import App from "./App.vue";
import { apiKey } from "./apiKey";
</script>
<template>
<JazzVueProvider
:sync="{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}"
>
<PasskeyAuthBasicUI appName="Jazz Vue Chat">
<App />
</PasskeyAuthBasicUI>
<component
:is="h('jazz-inspector', {
style: { position: 'fixed', left: '20px', bottom: '20px', zIndex: 9999 }
})"
/>
</JazzVueProvider>
</template>

View File

@@ -0,0 +1 @@
export const apiKey = "chat-example-jazz@garden.co";

View File

@@ -0,0 +1,76 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

View File

@@ -0,0 +1,35 @@
@import "./base.css";
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}

View File

@@ -0,0 +1,13 @@
<template>
<div
class="flex flex-col justify-between w-screen h-screen bg-stone-50 dark:bg-black dark:text-white"
>
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "AppContainer",
};
</script>

View File

@@ -0,0 +1,13 @@
<template>
<div
class="rounded-2xl text-sm line-clamp-10 text-ellipsis bg-white max-w-full whitespace-pre-wrap dark:bg-stone-700 dark:text-white py-1 px-3 shadow-sm"
>
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "BubbleBody",
};
</script>

View File

@@ -0,0 +1,22 @@
<template>
<div :class="[alignClass, 'flex flex-col m-2']" role="row">
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "BubbleContainer",
props: {
fromMe: {
type: Boolean,
default: undefined,
},
},
computed: {
alignClass() {
return this.fromMe ? "items-end" : "items-start";
},
},
};
</script>

View File

@@ -0,0 +1,30 @@
<template>
<div class="h-auto max-w-full rounded-t-xl mb-1">
<Image
:image-id="image.id"
alt="Uploaded image"
class-names="h-full rounded-t-xl"
width="original"
height="original"
/>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { type ImageDefinition } from "jazz-tools";
import { Image } from "community-jazz-vue";
export default defineComponent({
name: "BubbleImage",
components: {
Image,
},
props: {
image: {
type: Object as () => ImageDefinition,
required: true,
},
},
});
</script>

View File

@@ -0,0 +1,29 @@
<template>
<div class="text-xs text-neutral-500 mt-1.5">
{{ by }} · {{ formattedTime }}
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from "vue";
export default defineComponent({
name: "BubbleInfo",
props: {
by: {
type: String,
default: "",
},
madeAt: {
type: Date,
required: true,
},
},
setup(props) {
const formattedTime = computed(() => props.madeAt.toLocaleTimeString());
return {
formattedTime,
};
},
});
</script>

View File

@@ -0,0 +1,11 @@
<template>
<div class="flex-1 overflow-y-auto flex flex-col-reverse" role="application">
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "ChatBody",
};
</script>

View File

@@ -0,0 +1,40 @@
<template>
<BubbleContainer :fromMe="lastEdit.by?.isMe">
<BubbleBody>
<BubbleImage v-if="msg.image" :image="msg.image" />
{{ msg.text }}
</BubbleBody>
<BubbleInfo :by="lastEdit.by?.profile?.name" :madeAt="lastEdit.madeAt" />
</BubbleContainer>
</template>
<script lang="ts">
import { computed, defineComponent } from "vue";
import BubbleBody from "./BubbleBody.vue";
import BubbleContainer from "./BubbleContainer.vue";
import BubbleInfo from "./BubbleInfo.vue";
import BubbleImage from "./BubbleImage.vue";
export default defineComponent({
name: "ChatBubble",
components: {
BubbleContainer,
BubbleBody,
BubbleInfo,
BubbleImage,
},
props: {
msg: {
type: Object,
required: true,
},
},
setup(props) {
const lastEdit = computed(() => props.msg._edits.text);
return {
lastEdit,
};
},
});
</script>

View File

@@ -0,0 +1,55 @@
<template>
<div
class="p-3 bg-white border-t shadow-2xl mt-auto dark:bg-transparent dark:border-stone-800 flex gap-1"
>
<ImageInput @image-change="handleImageChange" />
<div class="flex-1">
<label class="sr-only" :for="inputId">Type a message and press Enter</label>
<input
:id="inputId"
v-model="inputValue"
class="rounded-full py-2 px-4 text-sm border block w-full dark:bg-black dark:text-white dark:border-stone-700"
placeholder="Type a message and press Enter"
maxlength="2048"
@keydown.enter.prevent="submitMessage"
/>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import ImageInput from "./ImageInput.vue";
export default defineComponent({
name: "ChatInput",
components: {
ImageInput,
},
emits: ["submit", "imageSubmit"],
setup(_, { emit }) {
const inputId = `input-${Math.random().toString(36).substr(2, 9)}`;
const inputValue = ref("");
function submitMessage() {
if (!inputValue.value) return;
emit("submit", inputValue.value);
inputValue.value = "";
}
function handleImageChange(file: File | undefined) {
if (file) {
emit("imageSubmit", file);
}
}
return {
inputId,
inputValue,
submitMessage,
handleImageChange,
};
},
});
</script>

View File

@@ -0,0 +1,13 @@
<template>
<div
class="h-full text-base text-stone-500 flex items-center justify-center px-3 md:text-xl"
>
Start a conversation below.
</div>
</template>
<script lang="ts">
export default {
name: "EmptyChatMessage",
};
</script>

View File

@@ -0,0 +1,63 @@
<template>
<div>
<button
type="button"
aria-label="Send image"
title="Send image"
@click="onUploadClick"
class="text-stone-500 p-1.5 rounded-full hover:bg-stone-100 hover:text-stone-800 dark:hover:bg-stone-800 dark:hover:text-stone-200 transition-colors"
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
<circle cx="9" cy="9" r="2" />
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
</svg>
</button>
<label class="sr-only">
Image
<input
ref="inputRef"
type="file"
accept="image/png, image/jpeg, image/gif"
@change="onImageChange"
/>
</label>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
name: "ImageInput",
emits: ["imageChange"],
setup(_, { emit }) {
const inputRef = ref<HTMLInputElement>();
function onUploadClick() {
inputRef.value?.click();
}
function onImageChange(event: Event) {
const target = event.target as HTMLInputElement;
emit("imageChange", target.files?.[0]);
}
return {
inputRef,
onUploadClick,
onImageChange,
};
},
});
</script>

View File

@@ -0,0 +1,13 @@
<template>
<div
class="p-3 bg-white w-full flex justify-end gap-1 text-xs border-b dark:bg-transparent dark:border-stone-800"
>
<slot></slot>
</div>
</template>
<script lang="ts">
export default {
name: "TopBar",
};
</script>

View File

@@ -0,0 +1 @@
@import "tailwindcss";

View File

@@ -0,0 +1,8 @@
import { createApp } from "vue";
import RootApp from "./RootApp.vue";
import "./index.css";
import router from "./router";
const app = createApp(RootApp);
app.use(router);
app.mount("#app");

View File

@@ -0,0 +1,17 @@
import { createRouter, createWebHistory } from "vue-router";
import Chat from "./views/ChatView.vue";
import Home from "./views/HomeView.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "Home",
component: Home,
},
{ path: "/chat/:chatId", name: "Chat", component: Chat, props: true },
],
});
export default router;

View File

@@ -0,0 +1,8 @@
import { co } from "jazz-tools";
export const Message = co.map({
text: co.plainText(),
image: co.optional(co.image()),
});
export const Chat = co.list(Message);

View File

@@ -0,0 +1,108 @@
<template>
<div v-if="chat">
<ChatBody>
<template v-if="chat.length > 0">
<ChatBubble v-for="msg in displayedMessages" :key="msg.id" :msg="msg" />
</template>
<EmptyChatMessage v-else />
<button
v-if="chat.length > showNLastMessages"
class="px-4 py-1 block mx-auto my-2 border rounded"
@click="showMoreMessages"
>
Show more
</button>
</ChatBody>
<ChatInput @submit="handleSubmit" @image-submit="handleImageSubmit" />
</div>
<div v-else class="flex-1 flex justify-center items-center">Loading...</div>
</template>
<script lang="ts">
import { useCoState, createImage } from "community-jazz-vue";
import { CoPlainText, type ID } from "jazz-tools";
import { type PropType, computed, defineComponent, ref } from "vue";
import ChatBody from "../components/ChatBody.vue";
import ChatBubble from "../components/ChatBubble.vue";
import ChatInput from "../components/ChatInput.vue";
import EmptyChatMessage from "../components/EmptyChatMessage.vue";
import { Chat, Message } from "../schema";
export default defineComponent({
name: "ChatView",
components: {
ChatBody,
ChatInput,
EmptyChatMessage,
ChatBubble,
},
props: {
chatId: {
type: String as unknown as PropType<ID<Chat>>,
required: true,
},
},
setup(props) {
const chat = useCoState(Chat, props.chatId, { resolve: { $each: true } });
const showNLastMessages = ref(30);
const displayedMessages = computed(() => {
return chat.value?.slice(-showNLastMessages.value).reverse();
});
function showMoreMessages() {
showNLastMessages.value += 10;
}
function handleSubmit(text: string) {
if (chat.value) {
chat.value.push(
Message.create(
{ text: CoPlainText.create(text, chat.value._owner) },
chat.value._owner,
),
);
}
}
async function handleImageSubmit(file: File) {
if (!chat.value) return;
if (file.size > 5000000) {
alert("Please upload an image less than 5MB.");
return;
}
try {
const image = await createImage(file, {
owner: chat.value._owner,
progressive: true,
placeholder: "blur",
});
chat.value.push(
Message.create(
{
text: CoPlainText.create(file.name, chat.value._owner),
image: image,
},
chat.value._owner,
),
);
} catch (error) {
console.error("Failed to upload image:", error);
alert("Failed to upload image. Please try again.");
}
}
return {
chat,
showNLastMessages,
displayedMessages,
showMoreMessages,
handleSubmit,
handleImageSubmit,
};
},
});
</script>

View File

@@ -0,0 +1,33 @@
<template>
<div v-if="!me">Loading...</div>
<div v-else>Creating a new chat...</div>
</template>
<script setup lang="ts">
import { useAccount, useIsAuthenticated } from "community-jazz-vue";
import { Group } from "jazz-tools";
import { watch } from "vue";
import { useRouter } from "vue-router";
import { Chat } from "../schema";
const router = useRouter();
const { me } = useAccount();
const isAuthenticated = useIsAuthenticated();
watch(
[me, isAuthenticated],
([currentMe, authenticated]) => {
if (currentMe && authenticated) {
try {
const group = Group.create({ owner: currentMe });
group.addMember("everyone", "writer");
const chat = Chat.create([], { owner: group });
router.push(`/chat/${chat.id}`);
} catch (error) {
console.error("Failed to create chat:", error);
}
}
},
{ immediate: true },
);
</script>

View File

@@ -0,0 +1,14 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

View File

@@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}

View File

@@ -0,0 +1,19 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

View File

@@ -0,0 +1,16 @@
import { URL, fileURLToPath } from "node:url";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import { defineConfig } from "vite";
import vueDevTools from "vite-plugin-vue-devtools";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx(), vueDevTools()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});

View File

@@ -0,0 +1 @@
VITE_CLERK_PUBLISHABLE_KEY=

View File

@@ -0,0 +1 @@
VITE_CLERK_PUBLISHABLE_KEY=pk_test_ZXZpZGVudC1kYW5lLTg5LmNsZXJrLmFjY291bnRzLmRldiQ

32
examples/community-clerk-vue/.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
# 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?
playwright-report
# Env
.env
.env.*
!.env.example
!.env.test

View File

@@ -0,0 +1,7 @@
# community-clerk-vue
## 0.15.4
- rewrite from React to Vue
- jazz-tools@0.15.4
- community-jazz-vue@0.15.4

View File

@@ -0,0 +1,82 @@
# Clerk authentication example with Jazz and Vue
This is an example of how to use clerk authentication with Jazz in a Vue.js application.
Live version: [](Todo)
## Getting started
You can either
1. Clone the jazz repository, and run the app within the monorepo.
2. Or create a new Jazz project using this example as a template.
### Using the example as a template
Create a new Jazz project, and use this example as a template.
```bash
npx create-jazz-app@latest clerk-vue-app --example community-clerk-vue
```
Go to the new project directory.
```bash
cd clerk-vue-app
```
Rename .env.example to .env
```bash
mv .env.example .env
```
Update `VITE_CLERK_PUBLISHABLE_KEY` with your [Publishable Key](https://clerk.com/docs/deployments/clerk-environment-variables#clerk-publishable-and-secret-keys) from Clerk.
Run the dev server.
```bash
npm run dev
```
### Using the monorepo
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
Clone the jazz repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
Install and build dependencies.
```bash
pnpm i && npx turbo build
```
Go to the example directory.
```bash
cd jazz/examples/community-clerk-vue/
```
Rename .env.example to .env
```bash
mv .env.example .env
```
Update `VITE_CLERK_PUBLISHABLE_KEY` with your [Publishable Key](https://clerk.com/docs/deployments/clerk-environment-variables#clerk-publishable-and-secret-keys) from Clerk.
Start the dev server.
```bash
pnpm dev
```
Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
## 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.

View File

@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Minimal Vue Auth Clerk Example | Jazz</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,30 @@
{
"name": "community-clerk-vue",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui"
},
"dependencies": {
"@clerk/vue": "^1.8.10",
"community-jazz-vue": "workspace:*",
"jazz-tools": "workspace:*",
"vue": "^3.5.13",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@clerk/testing": "^1.10.8",
"@playwright/test": "^1.50.1",
"@vitejs/plugin-vue": "^5.2.1",
"globals": "^15.11.0",
"typescript": "5.6.2",
"vite": "^6.3.5"
}
}

View File

@@ -0,0 +1,53 @@
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: "http://localhost:5173/",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
permissions: ["clipboard-read", "clipboard-write"],
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
/* Run your local dev server before starting the tests */
webServer: [
{
command: "pnpm preview --port 5173",
url: "http://localhost:5173/",
reuseExistingServer: !isCI,
},
],
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,37 @@
<script setup lang="ts">
import { SignInButton, SignOutButton } from "@clerk/vue";
import { useIsAuthenticated, useJazzContext } from "community-jazz-vue";
import { Account } from "jazz-tools";
import { computed } from "vue";
const context = useJazzContext<Account>();
const isAuthenticated = useIsAuthenticated();
const me = computed(() => {
const ctx = context.value;
console.log("[App] me computed:", { hasContext: !!ctx, ctx });
if (!ctx) return null;
return "me" in ctx ? ctx.me : null;
});
</script>
<template>
<div v-if="isAuthenticated" class="container">
<h1>You're logged in</h1>
<p>Welcome back, {{ me?.profile?.name || "User" }}</p>
<SignOutButton>Logout</SignOutButton>
</div>
<div v-else class="container">
<h1>You're not logged in</h1>
<SignInButton />
</div>
</template>
<style scoped>
.container {
max-width: 600px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
</style>

View File

@@ -0,0 +1 @@
export const apiKey = "minimal-auth-clerk-example@garden.co";

View File

@@ -0,0 +1,7 @@
<script setup lang="ts">
import { SignOutButton } from "@clerk/vue";
</script>
<template>
<SignOutButton>Simulate expiration</SignOutButton>
</template>

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
import { useClerk } from "@clerk/vue";
import { JazzVueProviderWithClerk } from "community-jazz-vue";
import { h } from "vue";
import { apiKey } from "../apiKey";
const clerk = useClerk();
import "jazz-tools/inspector/register-custom-element";
</script>
<template>
<JazzVueProviderWithClerk
v-if="clerk"
:clerk="clerk"
:sync="{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}"
>
<slot />
<component
:is="h('jazz-inspector', {
style: { position: 'fixed', bottom: '20px', left: '20px', zIndex: 9999 }
})"
/>
</JazzVueProviderWithClerk>
<div v-else>
<p>Loading Clerk...</p>
</div>
</template>

View File

@@ -0,0 +1,17 @@
<script setup lang="ts">
import { computed } from "vue";
import App from "../App.vue";
import ExpirationTest from "./ExpirationTest.vue";
import JazzProvider from "./JazzProvider.vue";
const isExpirationTest = computed(() =>
location.search.includes("expirationTest"),
);
</script>
<template>
<ExpirationTest v-if="isExpirationTest" />
<JazzProvider v-else>
<App />
</JazzProvider>
</template>

View File

@@ -0,0 +1,72 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
#root {
width: 100%;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 0;
padding: 0.6em 1.2em;
font-weight: 500;
background-color: #1a1a1a;
cursor: pointer;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
.container {
max-width: 400px;
margin: 0 auto;
padding: 0 1rem;
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
}

View File

@@ -0,0 +1,20 @@
import { clerkPlugin } from "@clerk/vue";
import { createApp } from "vue";
import RootApp from "./components/RootApp.vue";
import "./index.css";
// Import your publishable key
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
if (!PUBLISHABLE_KEY) {
throw new Error("Add your Clerk publishable key to the .env.local file");
}
const app = createApp(RootApp);
app.use(clerkPlugin, {
publishableKey: PUBLISHABLE_KEY,
afterSignOutUrl: "/",
});
app.mount("#app");

View File

@@ -0,0 +1,15 @@
/// <reference types="vite/client" />
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}
interface ImportMetaEnv {
readonly VITE_CLERK_PUBLISHABLE_KEY: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

View File

@@ -0,0 +1,122 @@
import { expect, test } from "@playwright/test";
test("clerk integration - complete sign in flow", async ({ page }) => {
await page.goto("/");
// Wait for page to load
await page.waitForTimeout(3000);
console.log("=== INITIAL PAGE STATE ===");
const pageText = await page.textContent("body");
console.log("Full page text:", pageText);
console.log(
'Page contains "You\'re not logged in":',
pageText?.includes("You're not logged in"),
);
// Check if Clerk is loaded
const clerkLoaded = await page.evaluate(() => {
return typeof (window as any).Clerk !== "undefined";
});
console.log("Clerk loaded:", clerkLoaded);
// Check if Clerk publishable key is available in the page
const hasClerkKey = await page.evaluate(() => {
return document.querySelector('script[src*="clerk"]') !== null;
});
console.log("Clerk scripts loaded:", hasClerkKey);
// Check for any Vue app errors
const vueErrors = await page.evaluate(() => {
return (
(window as any).__VUE_DEVTOOLS_GLOBAL_HOOK__?.Vue?.config?.errorHandler ||
null
);
});
console.log("Vue errors:", vueErrors);
console.log("=== CLICKING SIGN IN ===");
const signInButton = page.getByRole("button", { name: "Sign in" });
const signInExists = await signInButton.isVisible();
console.log("Sign in button exists:", signInExists);
if (!signInExists) {
console.log("Sign in button not found, taking screenshot");
await page.screenshot({ path: "debug-no-signin-button.png" });
return;
}
await signInButton.click();
console.log("Clicked sign in button");
// Wait and check what happens
await page.waitForTimeout(1000);
// Check for any modals or overlays
const modals = await page
.locator(
'[role="dialog"], .cl-modal, .clerk-modal, [data-testid*="modal"], [class*="modal"]',
)
.all();
console.log("Found modals/dialogs:", modals.length);
for (let i = 0; i < modals.length; i++) {
const modal = modals[i];
const isVisible = await modal.isVisible();
const className = await modal.getAttribute("class");
console.log(`Modal ${i}: visible=${isVisible}, class="${className}"`);
}
// Check for any iframes (Clerk might use iframes)
const iframes = await page.locator("iframe").all();
console.log("Found iframes:", iframes.length);
for (let i = 0; i < iframes.length; i++) {
const iframe = iframes[i];
const src = await iframe.getAttribute("src");
const isVisible = await iframe.isVisible();
console.log(`Iframe ${i}: visible=${isVisible}, src="${src}"`);
}
// Check for any new elements that appeared
await page.waitForTimeout(2000);
const allInputs = await page.locator("input").all();
console.log("Total inputs on page:", allInputs.length);
for (let i = 0; i < allInputs.length; i++) {
const input = allInputs[i];
const type = await input.getAttribute("type");
const name = await input.getAttribute("name");
const placeholder = await input.getAttribute("placeholder");
const isVisible = await input.isVisible();
console.log(
`Input ${i}: type="${type}" name="${name}" placeholder="${placeholder}" visible=${isVisible}`,
);
}
// Check for any error messages
const errorElements = await page
.locator('[class*="error"], [role="alert"], .cl-formFieldErrorText')
.all();
console.log("Found error elements:", errorElements.length);
for (let i = 0; i < errorElements.length; i++) {
const error = errorElements[i];
const text = await error.textContent();
const isVisible = await error.isVisible();
console.log(`Error ${i}: visible=${isVisible}, text="${text}"`);
}
// Check console errors
const logs: string[] = [];
page.on("console", (msg) => {
if (msg.type() === "error") {
logs.push(msg.text());
}
});
await page.waitForTimeout(1000);
if (logs.length > 0) {
console.log("Console errors:", logs);
}
});

View File

@@ -0,0 +1,69 @@
import { clerk } from "@clerk/testing/playwright";
import { expect, test } from "@playwright/test";
// Flaky on CI
test.skip("login & expiration", async ({ page, context }) => {
// Clear cookies first
await context.clearCookies();
await page.goto("/");
// Clear storage after page loads to avoid security errors
await page.evaluate(() => {
try {
localStorage.clear();
sessionStorage.clear();
// Clear IndexedDB
if ("indexedDB" in window) {
indexedDB.databases().then((databases) => {
databases.forEach((db) => {
if (db.name) indexedDB.deleteDatabase(db.name);
});
});
}
} catch (e) {
console.log("Storage clear failed:", e);
}
});
// Wait for page to load completely
await page.waitForTimeout(3000);
// Verify initial logged out state
await expect(page.getByText("You're not logged in")).toBeVisible();
// Manual login (works in our test environment)
await page.getByRole("button", { name: "Sign in" }).click();
await page.waitForTimeout(5000);
await page
.getByPlaceholder("Enter your email address")
.waitFor({ timeout: 30000 });
await page
.getByPlaceholder("Enter your email address")
.fill("guido+clerk-test@garden.co");
await page.keyboard.press("Enter");
await page
.getByPlaceholder("Enter your password")
.fill("guido+clerk-test@garden.co");
await page.keyboard.press("Enter");
await page.waitForURL("/");
// Verify user is logged in
await page.getByText("You're logged in").waitFor({ state: "visible" });
expect(page.getByText("You're logged in")).toBeVisible();
// Simulate expiration using clerk.signOut (ignore the warning about missing setup)
await clerk.signOut({ page });
// Navigate to home page to check logout state
await page.goto("/");
// Wait for logout to be processed and UI to update
await page.getByText("You're not logged in").waitFor({ state: "visible" });
});

View File

@@ -0,0 +1,62 @@
import { expect, test } from "@playwright/test";
test("login & logout", async ({ page }) => {
// Capture console messages
page.on("console", (msg) => {
console.log(`[BROWSER ${msg.type().toUpperCase()}]:`, msg.text());
});
// Capture network failures
page.on("requestfailed", (request) => {
console.log(
`[NETWORK FAILED]:`,
request.url(),
request.failure()?.errorText,
);
});
await page.goto("/");
// Wait for page to load completely
await page.waitForTimeout(3000);
// Wait for the page to load and show the logged out state
await expect(page.getByText("You're not logged in")).toBeVisible();
// Click sign in and wait for Clerk modal to appear
await page.getByRole("button", { name: "Sign in" }).click();
// Wait a bit for Clerk to initialize and show the form
await page.waitForTimeout(5000);
// Wait for Clerk to load and show the email input with a longer timeout
await page
.getByPlaceholder("Enter your email address")
.waitFor({ timeout: 15000 });
await page
.getByPlaceholder("Enter your email address")
.fill("guido+clerk-test@garden.co");
await page.keyboard.press("Enter");
await page
.getByPlaceholder("Enter your password")
.fill("guido+clerk-test@garden.co");
console.log("Pressing Enter to submit password...");
await page.keyboard.press("Enter");
console.log("Waiting for navigation to /...");
await page.waitForURL("/", { timeout: 60000 });
await page.getByText("You're logged in").waitFor({ state: "visible" });
expect(page.getByText("You're logged in")).toBeVisible();
await page.getByRole("button", { name: "Logout" }).click();
await page.getByText("You're not logged in").waitFor({ state: "visible" });
expect(page.getByText("You're not logged in")).toBeVisible();
});

View File

@@ -0,0 +1,67 @@
import { expect, test } from "@playwright/test";
test("login & reload", async ({ page, context }) => {
// Clear cookies first
await context.clearCookies();
await page.goto("/");
// Clear storage after page loads to avoid security errors
await page.evaluate(() => {
try {
localStorage.clear();
sessionStorage.clear();
// Clear IndexedDB
if ("indexedDB" in window) {
indexedDB.databases().then((databases) => {
databases.forEach((db) => {
if (db.name) indexedDB.deleteDatabase(db.name);
});
});
}
} catch (e) {
console.log("Storage clear failed:", e);
}
});
// Wait for page to load completely (like the working tests)
await page.waitForTimeout(3000);
// Wait for the page to load and show the logged out state
await expect(page.getByText("You're not logged in")).toBeVisible();
// Click sign in and wait for Clerk modal to appear
await page.getByRole("button", { name: "Sign in" }).click();
// Wait a bit for Clerk to initialize and show the form
await page.waitForTimeout(5000);
// Wait for Clerk to load and show the email input with a longer timeout
await page
.getByPlaceholder("Enter your email address")
.waitFor({ timeout: 30000 });
await page
.getByPlaceholder("Enter your email address")
.fill("guido+clerk-test@garden.co");
await page.keyboard.press("Enter");
await page
.getByPlaceholder("Enter your password")
.fill("guido+clerk-test@garden.co");
await page.keyboard.press("Enter");
await page.waitForURL("/");
await page.getByText("You're logged in").waitFor({ state: "visible" });
expect(page.getByText("You're logged in")).toBeVisible();
await page.reload();
await page.getByText("You're logged in").waitFor({ state: "visible" });
expect(page.getByText("You're logged in")).toBeVisible();
});

View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,7 @@
import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
# Todo list example with Jazz and Vue
## Getting started
You can either
1. Clone the jazz repository, and run the app within the monorepo.
2. Or create a new Jazz project using this example as a template.
### Using the example as a template
Create a new Jazz project, and use this example as a template.
```bash
npx create-jazz-app@latest todo-vue-app --example todo-vue
```
Go to the new project directory.
```bash
cd todo-vue-app
```
Run the dev server.
```bash
npm run dev
```
### Using the monorepo
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
Clone the jazz repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
Install and build dependencies.
```bash
pnpm i && npx turbo build
```
Go to the example directory.
```bash
cd jazz/examples/community-todo-vue/
```
Start the dev server.
```bash
pnpm dev
```
Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
## 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 Cloud](https://jazz.tools/cloud) (`wss://cloud.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running`npx jazz-run sync`, and setting the `sync` parameter of`JazzProvider` in [./src/main.ts](./src/main.ts) to`{ peer: "ws://localhost:4200" }`.

1
examples/community-todo-vue/env.d.ts vendored Normal file
View File

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

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jazz Todo List Example</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,37 @@
{
"name": "community-todo-vue",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build-type-check": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build": "vite build",
"type-check": "vue-tsc --build --force",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write"
},
"dependencies": {
"jazz-tools": "workspace:*",
"community-jazz-vue": "workspace:*",
"vue": "^3.5.11",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.10",
"@tsconfig/node22": "^22.0.0",
"@types/node": "^22.5.1",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vue/tsconfig": "^0.5.1",
"eslint": "^9.7.0",
"eslint-plugin-vue": "^9.28.0",
"npm-run-all2": "^6.2.3",
"postcss": "^8.4.40",
"tailwindcss": "^4.1.10",
"typescript": "5.6.2",
"vite": "6.3.5",
"vite-plugin-vue-devtools": "^7.4.6",
"vue-tsc": "^2.1.6"
}
}

View File

@@ -0,0 +1,5 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,92 @@
<template>
<div class="app-container">
<header v-if="me" class="app-header">
<h1>Todo App</h1>
<div class="user-section">
<span>{{ me.profile?.name }}</span>
<button class="logout-btn" @click="logoutHandler">Log out</button>
</div>
</header>
<main>
<router-view />
</main>
</div>
</template>
<style scoped>
.app-container {
min-height: 100vh;
background-color: #f5f5f5;
}
.app-header {
background-color: #fff;
padding: 1rem 2rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
h1 {
color: #2c3e50;
margin: 0;
font-size: 1.5rem;
}
.user-section {
display: flex;
align-items: center;
gap: 1rem;
}
.user-section span {
color: #2c3e50;
font-weight: 500;
}
.logout-btn {
background-color: transparent;
border: 1px solid #dc3545;
color: #dc3545;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.logout-btn:hover {
background-color: #dc3545;
color: white;
}
main {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
</style>
<script setup lang="ts">
import { useAcceptInvite, useAccount } from "community-jazz-vue";
import { useRouter } from "vue-router";
import { TodoProject } from "./schema";
const { me, logOut } = useAccount();
const router = useRouter();
async function logoutHandler() {
await logOut();
// Redirect to home page to avoid permission issues with project paths
router.push("/");
}
// Handle invite acceptance globally
useAcceptInvite({
invitedObjectSchema: TodoProject,
forValueHint: "project",
onAccept: (projectId: string) => {
router.push(`/project/${projectId}`);
},
});
</script>

View File

@@ -0,0 +1 @@
export const apiKey = "vue-todo-example-jazz@garden.co";

View File

@@ -0,0 +1,76 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@@ -0,0 +1 @@
@import "./base.css";

View File

@@ -0,0 +1,92 @@
<template>
<button
@click="handleInvite"
class="invite-button"
:disabled="!value"
title="Share this project"
>
<svg
class="invite-icon"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.367 2.684 3 3 0 00-5.367-2.684z"
></path>
</svg>
Share
</button>
</template>
<script setup lang="ts">
import { createInviteLink } from "community-jazz-vue";
import type { CoValue } from "jazz-tools";
interface Props {
value: CoValue | null | undefined;
valueHint: string;
}
const props = defineProps<Props>();
const handleInvite = () => {
if (!props.value) return;
try {
const inviteLink = createInviteLink(props.value, "writer", {
valueHint: props.valueHint,
});
navigator.clipboard
.writeText(inviteLink)
.then(() => {
alert(
`Invite link copied to clipboard!\n\nShare this link to give others access to this ${props.valueHint}.`,
);
})
.catch(() => {
// Fallback if clipboard API fails
prompt("Copy this invite link:", inviteLink);
});
} catch (error) {
console.error("Failed to create invite link:", error);
alert("Failed to create invite link. Please try again.");
}
};
</script>
<style scoped>
.invite-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: #3b82f6;
color: white;
border: none;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.invite-button:hover:not(:disabled) {
background: #2563eb;
}
.invite-button:disabled {
background: #9ca3af;
cursor: not-allowed;
}
.invite-icon {
width: 1rem;
height: 1rem;
}
</style>

View File

@@ -0,0 +1,118 @@
<template>
<div class="new-project-form">
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label for="project-title" class="form-label">Create New Project</label>
<input
id="project-title"
v-model="title"
type="text"
placeholder="New project title"
class="form-input"
required
/>
</div>
<button type="submit" class="submit-button" :disabled="!title.trim()">
Create Project
</button>
</form>
</div>
</template>
<script setup lang="ts">
import { useAccount } from "community-jazz-vue";
import { Group, co } from "jazz-tools";
import { ref } from "vue";
import { useRouter } from "vue-router";
import { Task, TodoAccount, TodoProject } from "../schema";
const { me } = useAccount(TodoAccount, {
resolve: { root: { projects: { $each: { $onError: null } } } },
});
const router = useRouter();
const title = ref("");
const handleSubmit = () => {
if (!me.value || !me.value.root?.projects || !title.value.trim()) return;
// To create a new todo project, we first create a `Group`,
// which is a scope for defining access rights (reader/writer/admin)
// of its members, which will apply to all CoValues owned by that group.
const projectGroup = Group.create({ owner: me.value });
// Then we create an empty todo project within that group
const project = TodoProject.create(
{
title: title.value,
tasks: co.list(Task).create([], { owner: projectGroup }),
},
{ owner: projectGroup },
);
me.value.root.projects.push(project);
router.push(`/project/${project.id}`);
title.value = "";
};
</script>
<style scoped>
.new-project-form {
max-width: 500px;
margin: 0 auto;
padding: 2rem;
background: white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
display: block;
font-size: 1.125rem;
font-weight: 500;
color: #374151;
margin-bottom: 0.5rem;
}
.form-input {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
font-size: 1rem;
transition: border-color 0.2s;
}
.form-input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.submit-button {
width: 100%;
padding: 0.75rem 1.5rem;
background: #3b82f6;
color: white;
border: none;
border-radius: 0.375rem;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.submit-button:hover:not(:disabled) {
background: #2563eb;
}
.submit-button:disabled {
background: #9ca3af;
cursor: not-allowed;
}
</style>

View File

@@ -0,0 +1,131 @@
<template>
<div class="new-task-row">
<div class="task-done">
<input
type="checkbox"
disabled
class="task-checkbox disabled"
/>
</div>
<div class="task-content">
<form @submit.prevent="handleSubmit" class="task-form">
<input
v-model="taskText"
type="text"
placeholder="New task"
class="task-input"
:disabled="disabled"
/>
<button
type="submit"
class="add-button"
:disabled="disabled || !taskText.trim()"
>
Add
</button>
</form>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
interface Props {
createTask: (text: string) => void;
disabled: boolean;
}
const props = defineProps<Props>();
const taskText = ref("");
const handleSubmit = () => {
if (!taskText.value.trim() || props.disabled) return;
props.createTask(taskText.value);
taskText.value = "";
};
</script>
<style scoped>
.new-task-row {
display: grid;
grid-template-columns: 60px 1fr;
border-bottom: 1px solid #e5e7eb;
background: #f9fafb;
}
.task-done {
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
.task-checkbox {
width: 1.25rem;
height: 1.25rem;
}
.task-checkbox.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.task-content {
padding: 1rem;
display: flex;
align-items: center;
}
.task-form {
display: flex;
gap: 0.75rem;
width: 100%;
align-items: center;
}
.task-input {
flex: 1;
padding: 0.5rem 0.75rem;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
font-size: 1rem;
transition: border-color 0.2s;
}
.task-input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.task-input:disabled {
background: #f3f4f6;
color: #9ca3af;
cursor: not-allowed;
}
.add-button {
padding: 0.5rem 1rem;
background: #3b82f6;
color: white;
border: none;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
white-space: nowrap;
}
.add-button:hover:not(:disabled) {
background: #2563eb;
}
.add-button:disabled {
background: #9ca3af;
cursor: not-allowed;
}
</style>

View File

@@ -0,0 +1,168 @@
<template>
<div class="task-row">
<div class="task-checkbox-container">
<input
type="checkbox"
:checked="task?.done"
@change="handleToggle"
class="task-checkbox"
/>
</div>
<div class="task-content">
<div class="task-text-container">
<span
v-if="task?.text"
:class="['task-text', { 'task-done': task?.done }]"
>
{{ task.text }}
</span>
<div v-else class="skeleton skeleton-text"></div>
<span
v-if="task?._edits?.text?.by?.profile?.name"
class="task-author"
:style="getAuthorStyle(task._edits.text.by?.id ?? '')"
>
{{ task._edits.text.by?.profile?.name }}
</span>
<div v-else class="skeleton skeleton-author"></div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { Loaded } from "jazz-tools";
import type { Task } from "../schema";
interface Props {
task: Loaded<typeof Task> | undefined;
}
const props = defineProps<Props>();
const handleToggle = (event: Event) => {
const target = event.target as HTMLInputElement;
if (props.task) {
props.task.done = target.checked;
}
};
// Generate unique colors for authors
const getAuthorStyle = (authorId: string) => {
// Simple hash function to generate consistent colors
let hash = 0;
for (let i = 0; i < authorId.length; i++) {
const char = authorId.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash; // Convert to 32-bit integer
}
const hue = Math.abs(hash) % 360;
const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
return {
color: `hsl(${hue}, 70%, ${isDark ? "80%" : "20%"})`,
backgroundColor: `hsl(${hue}, 70%, ${isDark ? "20%" : "80%"})`,
};
};
</script>
<style scoped>
.task-row {
display: flex;
align-items: center;
border-bottom: 1px solid #e5e7eb;
transition: background-color 0.2s;
min-height: 60px;
}
.task-row:hover {
background: #f9fafb;
}
.task-done {
display: flex;
align-items: center;
justify-content: left;
width: 60px;
flex-shrink: 0;
}
.task-checkbox-container {
display: flex;
align-items: center;
justify-content: left;
padding-left: 1rem;
width: 60px;
flex-shrink: 0;
}
.task-checkbox {
width: 1.25rem;
height: 1.25rem;
cursor: pointer;
}
.task-content {
padding: 1rem;
display: flex;
align-items: center;
flex: 1;
}
.task-text-container {
display: flex;
align-items: center;
width: 100%;
gap: 1rem;
}
.task-text {
font-size: 1rem;
color: #374151;
flex: 1;
text-align: left;
}
.task-text.task-done {
text-decoration: line-through;
color: #9ca3af;
}
.task-author {
padding: 0.25rem 0.5rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
white-space: nowrap;
flex-shrink: 0;
margin-left: auto;
}
.skeleton {
background: #e5e7eb;
border-radius: 0.25rem;
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.skeleton-text {
height: 1rem;
width: 200px;
}
.skeleton-author {
height: 1rem;
width: 50px;
border-radius: 9999px;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
</style>

View File

@@ -0,0 +1,48 @@
import { JazzVueProvider, PasskeyAuthBasicUI } from "community-jazz-vue";
import { createApp, defineComponent, h, markRaw } from "vue";
import App from "./App.vue";
import "./assets/main.css";
import { apiKey } from "./apiKey";
import router from "./router";
import { TodoAccount } from "./schema";
import "jazz-tools/inspector/register-custom-element";
const RootComponent = defineComponent({
name: "RootComponent",
setup() {
return () =>
h(
JazzVueProvider,
{
AccountSchema: TodoAccount,
sync: {
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
},
},
() => [
h(
PasskeyAuthBasicUI,
{
appName: "Jazz Vue Todo",
},
() => h(App),
),
h("jazz-inspector", {
style: {
position: "fixed",
bottom: "20px",
right: "20px",
zIndex: 9999,
},
}),
],
);
},
});
const app = createApp(RootComponent);
app.use(router);
app.mount("#app");

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