Compare commits

...

56 Commits

Author SHA1 Message Date
Guido D'Orsi
66b5f8d4c8 Merge pull request #2562 from garden-co/changeset-release/main
Version Packages
2025-06-20 18:14:32 +02:00
Guido D'Orsi
9c6ca9e864 docs: remove use client from the enableSSR snippet 2025-06-20 18:14:06 +02:00
github-actions[bot]
858386b5b1 Version Packages 2025-06-20 16:01:07 +00:00
Guido D'Orsi
f45af0a5cf Merge pull request #2552 from garden-co/feat/jazz-tools-single-package
Jazz 0.15: move all the adapters in the jazz-tools package
2025-06-20 17:57:40 +02:00
Guido D'Orsi
22af329930 docs: remove vue from tests and remove prosermirror link 2025-06-20 17:55:04 +02:00
Guido D'Orsi
fc342306e2 feat: add use client banner to react packages for a better compat with Server Components 2025-06-20 16:33:37 +02:00
Guido D'Orsi
8e4a9c7211 Merge pull request #2537 from garden-co/tests/inspector
inspector: add tests
2025-06-20 16:26:12 +02:00
Guido D'Orsi
362284f6b7 chore: remove Vue mentions 2025-06-20 16:25:22 +02:00
Guido D'Orsi
975c7bc785 docs: remove Vue examples 2025-06-20 16:22:14 +02:00
Guido D'Orsi
9a1295d4f6 docs: upgrade guide 2025-06-20 16:04:58 +02:00
Guido D'Orsi
256fdfd0db fix(ImageDefinition): share the owner between ImageDefinition and FileStream when options.owner is undefined 2025-06-20 14:48:40 +02:00
Guido D'Orsi
4cf2bf5fef chore: remove outdated comment 2025-06-20 13:24:46 +02:00
Guido D'Orsi
cae74bd051 Merge remote-tracking branch 'origin/main' into feat/jazz-tools-single-package 2025-06-20 13:21:43 +02:00
Guido D'Orsi
05a3786e23 docs: update imagedef docs for RN and Expo 2025-06-20 13:20:29 +02:00
Guido D'Orsi
0fa051a59d feat: remove experimental prefix from enableSSR and update SSR docs 2025-06-20 13:14:05 +02:00
Guido D'Orsi
1378a1ff68 chore: changeset 2025-06-20 12:15:16 +02:00
Guido D'Orsi
5ad093cb6a chore: optimize the homepage tests action 2025-06-20 12:11:36 +02:00
Guido D'Orsi
98407c2314 Merge pull request #2558 from garden-co/fix/e2e-rn-test
fix(ci): prevent Android emulator from hanging during e2e-tests gh workflow
2025-06-20 12:06:47 +02:00
Guido D'Orsi
65e14c6176 Merge pull request #2559 from garden-co/fix/o-14-0-remove-=account
docs(0-14): add a code snippet to make it clear that only the type declaration disappears
2025-06-20 11:49:52 +02:00
Guido D'Orsi
9fa79b3a5f docs(0-14): add a code snippet to make it clear that only the type declaration disappears 2025-06-20 11:19:25 +02:00
Matteo Manchi
c90222d32e fix(ci): prevent android emulator from hanging during e2e-tests gh workflow 2025-06-20 00:47:53 +02:00
Guido D'Orsi
d084b41929 chore: small fixes 2025-06-19 18:39:03 +02:00
Guido D'Orsi
65e78014fa feat: export logger from jazz-tools 2025-06-19 18:38:48 +02:00
Guido D'Orsi
d693800eb3 feat: rename the frameworks APIs to avoid collisions 2025-06-19 17:40:03 +02:00
Guido D'Orsi
4ab0b1f4b2 fix: fix jazz-tools/svelte build 2025-06-19 15:00:38 +02:00
Guido D'Orsi
9157effdb3 Merge pull request #2556 from garden-co/docs/fix-starters-links
docs(examples): fix the links to passkey examples
2025-06-19 12:35:03 +02:00
Guido D'Orsi
30b2820a8c chore: remove the svelte-package commanf 2025-06-19 12:24:52 +02:00
Guido D'Orsi
bb561e2650 docs: fixes 404 links 2025-06-19 12:19:36 +02:00
Trisha Lim
7cdf397a01 fix command for starters 2025-06-19 11:13:38 +01:00
Guido D'Orsi
44116b805b docs(examples): fix the links to passkey examples 2025-06-19 12:02:08 +02:00
Guido D'Orsi
e93c27910d chore: remove the build examples workflow 2025-06-19 11:47:23 +02:00
Guido D'Orsi
16428cace4 docs: remove twoslash from the outdated upgrade guides 2025-06-19 11:43:06 +02:00
Guido D'Orsi
f48dabaa1a docs: remove the BetterAuth links from auth/overview 2025-06-19 11:31:50 +02:00
Trisha Lim
7ff90cb81e Merge pull request #2555 from garden-co/gio/docs/helper-methods
docs: update helper methods docs
2025-06-19 10:01:01 +01:00
Giordano Ricci
3a53cc4c00 docs: update helper methods docs 2025-06-19 10:08:31 +02:00
Guido D'Orsi
ea6c50b2b8 fix: remove unused react-test-rendered depencency from examples 2025-06-19 09:59:54 +02:00
Divya
6d93f7b388 Merge pull request #2553 from garden-co/fix/cojson-queue-metric
Typo that could be causing some b0rked metrics
2025-06-18 16:39:55 -04:00
shortdiv
ebc0139559 fix: typo that could be causing some b0rked metrics 2025-06-18 15:53:16 -04:00
Guido D'Orsi
01c07e34e4 test: set jsdom as env for prosemirror 2025-06-18 19:48:53 +02:00
Guido D'Orsi
c89b94aa3b feat: update lockfile 2025-06-18 19:33:41 +02:00
Guido D'Orsi
01e2977a13 fix: build tiptap and export Plugin type from prosemirror 2025-06-18 19:29:17 +02:00
Guido D'Orsi
41354cb2c1 fix: update actions 2025-06-18 19:03:00 +02:00
Guido D'Orsi
530f9d3e11 feat: remove Vue 2025-06-18 19:02:02 +02:00
Guido D'Orsi
c965c904bc feat: move svelte and vue in jazz-tools 2025-06-18 18:42:55 +02:00
Guido D'Orsi
d527ae2db0 feat: move all the adapters in the jazz-tools package 2025-06-18 16:31:35 +02:00
Guido D'Orsi
b9261aa4c1 Merge pull request #2551 from garden-co/feat/cleanup-examples
chore: reduce the number of example apps
2025-06-18 12:55:43 +02:00
Guido D'Orsi
41dd1fa80b chore: reduce the number of example apps 2025-06-18 12:06:03 +02:00
Trisha Lim
b897e950bb clean up 2025-06-16 16:46:07 +01:00
Trisha Lim
69c92ab908 fix test 2025-06-16 16:44:46 +01:00
Trisha Lim
10e1612fd4 Revert "changeset"
This reverts commit 31b89adb03.
2025-06-16 16:07:30 +01:00
Trisha Lim
282a2798c7 undo inspector changes 2025-06-16 16:07:19 +01:00
Trisha Lim
31b89adb03 changeset 2025-06-16 15:58:15 +01:00
Trisha Lim
793787bc66 move files, create utils 2025-06-16 15:57:14 +01:00
Trisha Lim
17710122af test: grid view and table view 2025-06-16 15:41:38 +01:00
Trisha Lim
1173884769 test: group members table 2025-06-16 13:24:26 +01:00
Trisha Lim
1c40b3fd6d test: show correct icon and label for each covalue type 2025-06-16 12:43:16 +01:00
856 changed files with 3863 additions and 58849 deletions

View File

@@ -10,29 +10,12 @@
"cojson-storage-indexeddb", "cojson-storage-indexeddb",
"cojson-storage-sqlite", "cojson-storage-sqlite",
"cojson-transport-ws", "cojson-transport-ws",
"jazz-browser",
"jazz-auth-clerk",
"jazz-auth-betterauth", "jazz-auth-betterauth",
"jazz-betterauth-client-plugin", "jazz-betterauth-client-plugin",
"jazz-betterauth-server-plugin", "jazz-betterauth-server-plugin",
"jazz-react-auth-betterauth", "jazz-react-auth-betterauth",
"jazz-browser-media-images",
"jazz-expo",
"jazz-inspector",
"jazz-inspector-element",
"jazz-nodejs",
"jazz-react",
"jazz-react-core",
"jazz-react-auth-clerk",
"jazz-react-native-core",
"jazz-react-native",
"jazz-react-native-media-images",
"jazz-richtext-prosemirror",
"jazz-richtext-tiptap",
"jazz-run", "jazz-run",
"jazz-svelte", "jazz-tools"
"jazz-tools",
"jazz-vue"
] ]
], ],
"access": "public", "access": "public",

View File

@@ -1,31 +0,0 @@
name: Build Examples
on:
push:
branches: [ "main" ]
jobs:
build-examples:
runs-on: blacksmith-4vcpu-ubuntu-2204
strategy:
matrix:
example: [
"passkey-svelte",
"chat-svelte",
"file-share-svelte",
]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Setup Source Code
uses: ./.github/actions/source-code/
- name: Pnpm Build
run: |
pnpm install
pnpm turbo build;
working-directory: ./examples/${{ matrix.example }}

View File

@@ -60,7 +60,8 @@ jobs:
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics
disable-animations: true disable-animations: true
working-directory: ./examples/chat-rn-expo/ working-directory: ./examples/chat-rn-expo/
script: ./test/e2e/run.sh # killall due to this issue: https://github.com/ReactiveCircus/android-emulator-runner/issues/385
script: ./test/e2e/run.sh && killall -INT crashpad_handler || true
- name: Copy Maestro Output - name: Copy Maestro Output
if: steps.e2e_test.outcome != 'success' if: steps.e2e_test.outcome != 'success'

View File

@@ -10,9 +10,6 @@ jobs:
test: test:
timeout-minutes: 60 timeout-minutes: 60
runs-on: blacksmith-4vcpu-ubuntu-2204 runs-on: blacksmith-4vcpu-ubuntu-2204
strategy:
matrix:
project: ["homepage/homepage"]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -23,27 +20,27 @@ jobs:
uses: ./.github/actions/source-code/ uses: ./.github/actions/source-code/
- name: Install root dependencies - name: Install root dependencies
run: pnpm install && pnpm turbo build run: pnpm install && pnpm exec turbo build --filter="./packages/*"
- name: Install project dependencies - name: Install project dependencies
run: pnpm install run: pnpm install
working-directory: ./${{ matrix.project }} working-directory: ./homepage/homepage
- name: Pnpm Build - name: Pnpm Build
run: pnpm turbo build run: pnpm exec turbo build
working-directory: ./${{ matrix.project }} working-directory: ./homepage/homepage
- name: Install Playwright Browsers - name: Install Playwright Browsers
run: pnpm exec playwright install run: pnpm exec playwright install
working-directory: ./${{ matrix.project }} working-directory: ./homepage/homepage
- name: Run Playwright tests - name: Run Playwright tests
run: pnpm exec playwright test run: pnpm exec playwright test
working-directory: ./${{ matrix.project }} working-directory: ./homepage/homepage
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: failure() if: failure()
with: with:
name: ${{ hashFiles(format('{0}/package.json', matrix.project)) }}-playwright-report name: homepage-playwright-report
path: ./${{ matrix.project }}/playwright-report/ path: ./homepage/homepage/playwright-report/
retention-days: 30 retention-days: 30

View File

@@ -16,6 +16,7 @@ jobs:
project: [ project: [
"tests/e2e", "tests/e2e",
"examples/chat", "examples/chat",
"examples/chat-svelte",
"examples/clerk", "examples/clerk",
"examples/betterauth", "examples/betterauth",
"examples/file-share-svelte", "examples/file-share-svelte",
@@ -23,10 +24,9 @@ jobs:
"examples/inspector", "examples/inspector",
"examples/music-player", "examples/music-player",
"examples/organization", "examples/organization",
"examples/pets",
"starters/react-passkey-auth", "starters/react-passkey-auth",
"starters/svelte-passkey-auth", "starters/svelte-passkey-auth",
"packages/jazz-svelte" "tests/jazz-svelte"
] ]
steps: steps:

View File

@@ -11,7 +11,7 @@
"jazz-tools.json", "jazz-tools.json",
"**/ios/**", "**/ios/**",
"**/android/**", "**/android/**",
"packages/jazz-svelte/**", "tests/jazz-svelte/src/**",
"examples/*svelte*/**", "examples/*svelte*/**",
"starters/*svelte*/**", "starters/*svelte*/**",
"examples/jazz-paper-scissors/src/routeTree.gen.ts", "examples/jazz-paper-scissors/src/routeTree.gen.ts",

View File

@@ -21,11 +21,9 @@
"better-sqlite3": "^11.9.1", "better-sqlite3": "^11.9.1",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"jazz-react-auth-betterauth": "workspace:*",
"jazz-betterauth-client-plugin": "workspace:*", "jazz-betterauth-client-plugin": "workspace:*",
"jazz-betterauth-server-plugin": "workspace:*", "jazz-betterauth-server-plugin": "workspace:*",
"jazz-inspector": "workspace:*",
"jazz-react": "workspace:*",
"jazz-react-auth-betterauth": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"lucide-react": "^0.510.0", "lucide-react": "^0.510.0",
"next": "15.3.2", "next": "15.3.2",

View File

@@ -1,8 +1,8 @@
"use client"; "use client";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useAccount } from "jazz-react";
import { Account } from "jazz-tools"; import { Account } from "jazz-tools";
import { useAccount } from "jazz-tools/react";
import { import {
AppWindowMacIcon, AppWindowMacIcon,
FileTextIcon, FileTextIcon,

View File

@@ -1,21 +1,21 @@
"use client"; "use client";
import { JazzProvider } from "jazz-react";
import { AuthProvider } from "jazz-react-auth-betterauth"; import { AuthProvider } from "jazz-react-auth-betterauth";
import { JazzReactProvider } from "jazz-tools/react";
import { type ReactNode, lazy } from "react"; import { type ReactNode, lazy } from "react";
const JazzDevTools = const JazzDevTools =
process.env.NODE_ENV === "production" process.env.NODE_ENV === "production"
? () => null ? () => null
: lazy(() => : lazy(() =>
import("jazz-inspector").then((res) => ({ import("jazz-tools/inspector").then((res) => ({
default: res.JazzInspector, default: res.JazzInspector,
})), })),
); );
export function JazzAndAuth({ children }: { children: ReactNode }) { export function JazzAndAuth({ children }: { children: ReactNode }) {
return ( return (
<JazzProvider <JazzReactProvider
sync={{ sync={{
peer: "wss://cloud.jazz.tools/?key=betterauth-example@garden.co", peer: "wss://cloud.jazz.tools/?key=betterauth-example@garden.co",
}} }}
@@ -28,6 +28,6 @@ export function JazzAndAuth({ children }: { children: ReactNode }) {
{children} {children}
</AuthProvider> </AuthProvider>
<JazzDevTools /> <JazzDevTools />
</JazzProvider> </JazzReactProvider>
); );
} }

View File

@@ -1,9 +1,9 @@
"use client"; "use client";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useAccount, useIsAuthenticated } from "jazz-react";
import { useAuth } from "jazz-react-auth-betterauth"; import { useAuth } from "jazz-react-auth-betterauth";
import { Account } from "jazz-tools"; import { Account } from "jazz-tools";
import { useAccount, useIsAuthenticated } from "jazz-tools/react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { useCallback } from "react"; import { useCallback } from "react";

View File

@@ -1,8 +1,8 @@
"use client"; "use client";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useAccount, useIsAuthenticated } from "jazz-react";
import { useAuth } from "jazz-react-auth-betterauth"; import { useAuth } from "jazz-react-auth-betterauth";
import { useAccount, useIsAuthenticated } from "jazz-tools/react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { toast } from "sonner"; import { toast } from "sonner";

View File

@@ -1,5 +1,5 @@
import { Redirect, Stack } from "expo-router"; import { Redirect, Stack } from "expo-router";
import { useIsAuthenticated } from "jazz-expo"; import { useIsAuthenticated } from "jazz-tools/expo";
import React from "react"; import React from "react";
export default function HomeLayout() { export default function HomeLayout() {

View File

@@ -1,5 +1,5 @@
import { Redirect, Stack } from "expo-router"; import { Redirect, Stack } from "expo-router";
import { useIsAuthenticated } from "jazz-expo"; import { useIsAuthenticated } from "jazz-tools/expo";
export default function UnAuthenticatedLayout() { export default function UnAuthenticatedLayout() {
const isAuthenticated = useIsAuthenticated(); const isAuthenticated = useIsAuthenticated();

View File

@@ -4,7 +4,7 @@ import { secureStore } from "@clerk/clerk-expo/secure-store";
import { useFonts } from "expo-font"; import { useFonts } from "expo-font";
import { Slot, useRouter, useSegments } from "expo-router"; import { Slot, useRouter, useSegments } from "expo-router";
import * as SplashScreen from "expo-splash-screen"; import * as SplashScreen from "expo-splash-screen";
import { useIsAuthenticated, useJazzContext } from "jazz-expo"; import { useIsAuthenticated, useJazzContext } from "jazz-tools/expo";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { tokenCache } from "../cache"; import { tokenCache } from "../cache";
import { JazzAndAuth } from "../src/auth-context"; import { JazzAndAuth } from "../src/auth-context";

View File

@@ -4,10 +4,10 @@ import clsx from "clsx";
import * as Clipboard from "expo-clipboard"; import * as Clipboard from "expo-clipboard";
import * as ImagePicker from "expo-image-picker"; import * as ImagePicker from "expo-image-picker";
import { useLocalSearchParams } from "expo-router"; import { useLocalSearchParams } from "expo-router";
import { useAccount, useCoState } from "jazz-expo";
import { ProgressiveImg } from "jazz-expo";
import { createImage } from "jazz-react-native-media-images";
import { CoPlainText, Group, Loaded } from "jazz-tools"; import { CoPlainText, Group, Loaded } from "jazz-tools";
import { useAccount, useCoState } from "jazz-tools/expo";
import { ProgressiveImgNative } from "jazz-tools/expo";
import { createImageNative } from "jazz-tools/react-native-media-images";
import { useEffect, useLayoutEffect, useState } from "react"; import { useEffect, useLayoutEffect, useState } from "react";
import React, { import React, {
SafeAreaView, SafeAreaView,
@@ -104,7 +104,7 @@ export default function Conversation() {
setIsUploading(true); setIsUploading(true);
const base64Uri = `data:image/jpeg;base64,${result.assets[0].base64}`; const base64Uri = `data:image/jpeg;base64,${result.assets[0].base64}`;
const image = await createImage(base64Uri, { const image = await createImageNative(base64Uri, {
owner: chat._owner, owner: chat._owner,
maxSize: 2048, maxSize: 2048,
}); });
@@ -149,15 +149,17 @@ export default function Conversation() {
)} )}
> >
{item.image && ( {item.image && (
<ProgressiveImg image={item.image} maxWidth={1024}> <ProgressiveImgNative
{({ src, res, originalSize }) => ( image={item.image}
maxWidth={1024}
children={({ src }) => (
<Image <Image
source={{ uri: src }} source={{ uri: src }}
className="w-48 h-48 rounded-lg mb-2" className="w-48 h-48 rounded-lg mb-2"
resizeMode="cover" resizeMode="cover"
/> />
)} )}
</ProgressiveImg> />
)} )}
{item.text && ( {item.text && (
<Text <Text

View File

@@ -11,7 +11,7 @@ import React, {
} from "react-native"; } from "react-native";
import { useUser } from "@clerk/clerk-expo"; import { useUser } from "@clerk/clerk-expo";
import { useAccount } from "jazz-expo"; import { useAccount } from "jazz-tools/expo";
import { Chat } from "../../src/schema"; import { Chat } from "../../src/schema";
export default function ChatScreen() { export default function ChatScreen() {

View File

@@ -37,8 +37,6 @@
"expo-sqlite": "15.2.9", "expo-sqlite": "15.2.9",
"expo-status-bar": "~2.2.3", "expo-status-bar": "~2.2.3",
"expo-web-browser": "~14.1.6", "expo-web-browser": "~14.1.6",
"jazz-expo": "workspace:*",
"jazz-react-native-media-images": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"nativewind": "^4.1.21", "nativewind": "^4.1.21",
"react": "19.0.0", "react": "19.0.0",
@@ -56,8 +54,6 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",
"@types/react": "~19.0.14", "@types/react": "~19.0.14",
"@types/react-test-renderer": "^19.0.0",
"react-test-renderer": "18.3.1",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"typescript": "5.8.3" "typescript": "5.8.3"
}, },

View File

@@ -1,5 +1,5 @@
import { useClerk } from "@clerk/clerk-expo"; import { useClerk } from "@clerk/clerk-expo";
import { JazzProviderWithClerk } from "jazz-expo/auth/clerk"; import { JazzExpoProviderWithClerk } from "jazz-tools/expo";
import React, { PropsWithChildren } from "react"; import React, { PropsWithChildren } from "react";
import { apiKey } from "./apiKey"; import { apiKey } from "./apiKey";
@@ -7,13 +7,13 @@ export function JazzAndAuth({ children }: PropsWithChildren) {
const clerk = useClerk(); const clerk = useClerk();
return ( return (
<JazzProviderWithClerk <JazzExpoProviderWithClerk
clerk={clerk} clerk={clerk}
sync={{ sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}} }}
> >
{children} {children}
</JazzProviderWithClerk> </JazzExpoProviderWithClerk>
); );
} }

View File

@@ -18,7 +18,6 @@
"expo-secure-store": "~14.2.3", "expo-secure-store": "~14.2.3",
"expo-sqlite": "~15.2.10", "expo-sqlite": "~15.2.10",
"expo-status-bar": "~2.2.3", "expo-status-bar": "~2.2.3",
"jazz-expo": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"react": "19.0.0", "react": "19.0.0",
"react-native": "0.79.2", "react-native": "0.79.2",

View File

@@ -1,4 +1,4 @@
import { JazzProvider } from "jazz-expo"; import { JazzExpoProvider } from "jazz-tools/expo";
import React, { StrictMode } from "react"; import React, { StrictMode } from "react";
import { apiKey } from "./apiKey"; import { apiKey } from "./apiKey";
import ChatScreen from "./chat"; import ChatScreen from "./chat";
@@ -6,13 +6,13 @@ import ChatScreen from "./chat";
export default function App() { export default function App() {
return ( return (
<StrictMode> <StrictMode>
<JazzProvider <JazzExpoProvider
sync={{ sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}} }}
> >
<ChatScreen /> <ChatScreen />
</JazzProvider> </JazzExpoProvider>
</StrictMode> </StrictMode>
); );
} }

View File

@@ -14,7 +14,7 @@ import React, {
StyleSheet, StyleSheet,
} from "react-native"; } from "react-native";
import { useAccount, useCoState } from "jazz-expo"; import { useAccount, useCoState } from "jazz-tools/expo";
import { Chat, Message } from "./schema"; import { Chat, Message } from "./schema";
export default function ChatScreen() { export default function ChatScreen() {

View File

@@ -20,7 +20,6 @@
"clsx": "^2.0.0", "clsx": "^2.0.0",
"cojson": "workspace:*", "cojson": "workspace:*",
"cojson-transport-ws": "workspace:*", "cojson-transport-ws": "workspace:*",
"jazz-react-native": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"react": "19.0.0", "react": "19.0.0",
"react-native": "0.79.2", "react-native": "0.79.2",
@@ -46,11 +45,9 @@
"@rnx-kit/metro-config": "^2.0.1", "@rnx-kit/metro-config": "^2.0.1",
"@rnx-kit/metro-resolver-symlinks": "^0.2.1", "@rnx-kit/metro-resolver-symlinks": "^0.2.1",
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"@types/react-test-renderer": "^18.0.0",
"eslint": "^8.19.0", "eslint": "^8.19.0",
"pod-install": "^0.3.5", "pod-install": "^0.3.5",
"prettier": "2.8.8", "prettier": "2.8.8",
"react-test-renderer": "18.3.1",
"typescript": "5.6.2" "typescript": "5.6.2"
}, },
"engines": { "engines": {

View File

@@ -3,7 +3,7 @@ import {
useNavigationContainerRef, useNavigationContainerRef,
} from "@react-navigation/native"; } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack"; import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { JazzProvider } from "jazz-react-native"; import { JazzReactNativeProvider } from "jazz-tools/react-native";
import React, { StrictMode, useEffect, useState } from "react"; import React, { StrictMode, useEffect, useState } from "react";
import { Linking } from "react-native"; import { Linking } from "react-native";
import { apiKey } from "./apiKey"; import { apiKey } from "./apiKey";
@@ -49,7 +49,7 @@ function App() {
return ( return (
<StrictMode> <StrictMode>
<JazzProvider <JazzReactNativeProvider
sync={{ sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}} }}
@@ -67,7 +67,7 @@ function App() {
/> />
</Stack.Navigator> </Stack.Navigator>
</NavigationContainer> </NavigationContainer>
</JazzProvider> </JazzReactNativeProvider>
</StrictMode> </StrictMode>
); );
} }

View File

@@ -1,6 +1,6 @@
import Clipboard from "@react-native-clipboard/clipboard"; import Clipboard from "@react-native-clipboard/clipboard";
import { useAccount, useCoState } from "jazz-react-native";
import { CoPlainText, Group, ID, Loaded, Profile } from "jazz-tools"; import { CoPlainText, Group, ID, Loaded, Profile } from "jazz-tools";
import { useAccount, useCoState } from "jazz-tools/react-native";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { import {
Button, Button,

View File

@@ -1,4 +1,4 @@
import { useAcceptInvite } from "jazz-react-native"; import { useAcceptInviteNative } from "jazz-tools/react-native";
import React from "react"; import React from "react";
import { Text } from "react-native"; import { Text } from "react-native";
import { Chat } from "./schema"; import { Chat } from "./schema";
@@ -8,7 +8,7 @@ export function HandleInviteScreen({
}: { }: {
navigation: any; navigation: any;
}) { }) {
useAcceptInvite({ useAcceptInviteNative({
invitedObjectSchema: Chat, invitedObjectSchema: Chat,
onAccept: async (chatId) => { onAccept: async (chatId) => {
navigation.navigate("ChatScreen", { chatId }); navigation.navigate("ChatScreen", { chatId });

View File

@@ -1,5 +1,13 @@
# passkey-svelte # passkey-svelte
## 0.0.89
### Patch Changes
- Updated dependencies [1378a1f]
- Updated dependencies [0fa051a]
- jazz-tools@0.15.0
## 0.0.88 ## 0.0.88
### Patch Changes ### Patch Changes

View File

@@ -65,4 +65,4 @@ If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or
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. 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/routes/+layout.svelte](./src/routes/+layout.svelte) to `{ peer: "ws://localhost:4200" }`. You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzSvelteProvider` in [./src/routes/+layout.svelte](./src/routes/+layout.svelte) to `{ peer: "ws://localhost:4200" }`.

View File

@@ -1,6 +1,6 @@
{ {
"name": "chat-svelte", "name": "chat-svelte",
"version": "0.0.88", "version": "0.0.89",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
@@ -25,7 +25,6 @@
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.46.1", "eslint-plugin-svelte": "^2.46.1",
"globals": "^15.15.0", "globals": "^15.15.0",
"jazz-inspector-element": "workspace:*",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.4.0", "prettier-plugin-svelte": "^3.4.0",
"svelte": "^5.31.1", "svelte": "^5.31.1",
@@ -36,8 +35,6 @@
}, },
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.1.7", "@tailwindcss/vite": "^4.1.7",
"jazz-browser-media-images": "workspace:*",
"jazz-svelte": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"tailwindcss": "^4.1.7" "tailwindcss": "^4.1.7"
} }

View File

@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import 'jazz-tools/inspector/register-custom-element';
import '../app.css'; import '../app.css';
import { JazzProvider } from 'jazz-svelte'; import { JazzSvelteProvider } from 'jazz-tools/svelte';
import 'jazz-inspector-element';
import { page } from '$app/state';
import { apiKey } from '../apiKey'; import { apiKey } from '../apiKey';
import { getRandomUsername } from '$lib/utils'; import { getRandomUsername } from '$lib/utils';
let { children } = $props(); let { children } = $props();
@@ -14,14 +14,14 @@
</svelte:head> </svelte:head>
<div class="h-full bg-white text-stone-700 dark:text-stone-400 dark:bg-stone-925"> <div class="h-full bg-white text-stone-700 dark:text-stone-400 dark:bg-stone-925">
<JazzProvider <JazzSvelteProvider
sync={{ sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}` peer: `wss://cloud.jazz.tools/?key=${apiKey}`
}} }}
{defaultProfileName} {defaultProfileName}
> >
{@render children?.()} {@render children?.()}
</JazzProvider> </JazzSvelteProvider>
<jazz-inspector></jazz-inspector> <jazz-inspector></jazz-inspector>
</div> </div>

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { Chat } from '$lib/schema'; import { Chat } from '$lib/schema';
import { AccountCoState } from 'jazz-svelte'; import { AccountCoState } from 'jazz-tools/svelte';
import { Account, Group } from 'jazz-tools'; import { Account, Group } from 'jazz-tools';
const account = new AccountCoState(Account); const account = new AccountCoState(Account);

View File

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

View File

@@ -1,4 +1,3 @@
import { expect } from 'vitest';
import { ChatPage } from './pages/ChatPage'; import { ChatPage } from './pages/ChatPage';
import { test } from '@playwright/test'; import { test } from '@playwright/test';

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,61 +0,0 @@
# 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 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/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" }`.

View File

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

View File

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

@@ -1,38 +0,0 @@
{
"name": "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-browser": "workspace:*",
"jazz-tools": "workspace:*",
"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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

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

View File

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

View File

@@ -1,74 +0,0 @@
/* 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

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

Before

Width:  |  Height:  |  Size: 276 B

View File

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

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

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

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

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

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

@@ -1,35 +0,0 @@
<template>
<BubbleContainer :fromMe="lastEdit.by?.isMe">
<BubbleBody>{{ 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";
export default defineComponent({
name: "ChatBubble",
components: {
BubbleContainer,
BubbleBody,
BubbleInfo,
},
props: {
msg: {
type: Object,
required: true,
},
},
setup(props) {
const lastEdit = computed(() => props.msg._edits.text);
return {
lastEdit,
};
},
});
</script>

View File

@@ -1,40 +0,0 @@
<template>
<div
class="p-3 bg-white border-t shadow-2xl mt-auto dark:bg-transparent dark:border-stone-800"
>
<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>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
name: "ChatInput",
emits: ["submit"],
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 = "";
}
return {
inputId,
inputValue,
submitMessage,
};
},
});
</script>

View File

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

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

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

View File

@@ -1,36 +0,0 @@
import { DemoAuthBasicUI, JazzProvider } from "jazz-vue";
import { createApp, defineComponent, h } from "vue";
import App from "./App.vue";
import "./index.css";
import { apiKey } from "@/apiKey";
import router from "./router";
const RootComponent = defineComponent({
name: "RootComponent",
setup() {
return () =>
h(
JazzProvider,
{
sync: {
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
},
},
h(
DemoAuthBasicUI,
{
appName: "Jazz Vue Chat",
},
{
default: () => h(App),
},
),
);
},
});
const app = createApp(RootComponent);
app.use(router);
app.mount("#app");

View File

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

@@ -1,7 +0,0 @@
import { CoList, CoMap, CoPlainText, coField } from "jazz-tools";
export class Message extends CoMap {
text = coField.ref(CoPlainText);
}
export class Chat extends CoList.Of(coField.ref(Message)) {}

View File

@@ -1,81 +0,0 @@
<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" />
</div>
<div v-else class="flex-1 flex justify-center items-center">
Loading...
</div>
</template>
<script lang="ts">
import { CoPlainText, type ID } from "jazz-tools";
import { useCoState } from "jazz-vue";
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) {
chat?.value?.push(
Message.create(
{ text: CoPlainText.create(text, chat.value._owner) },
chat.value._owner,
),
);
}
return {
chat,
showNLastMessages,
displayedMessages,
showMoreMessages,
handleSubmit,
};
},
});
</script>

View File

@@ -1,21 +0,0 @@
<template>
<div>Creating a new chat...</div>
</template>
<script setup lang="ts">
import { Group } from "jazz-tools";
import { useAccount } from "jazz-vue";
import { useRouter } from "vue-router";
import { Chat } from "../schema";
const router = useRouter();
const { me } = useAccount();
if (me.value) {
const group = Group.create({ owner: me.value });
group.addMember("everyone", "writer");
const chat = Chat.create([], { owner: group });
router.push(`/chat/${chat.id}`);
}
</script>

View File

@@ -1,14 +0,0 @@
{
"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

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

View File

@@ -1,19 +0,0 @@
{
"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

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

@@ -60,4 +60,4 @@ If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or
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. 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/app.tsx](./src/app.tsx) to `{ peer: "ws://localhost:4200" }`. You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzReactProvider` in [./src/app.tsx](./src/app.tsx) to `{ peer: "ws://localhost:4200" }`.

View File

@@ -14,8 +14,6 @@
"dependencies": { "dependencies": {
"clsx": "^2.0.0", "clsx": "^2.0.0",
"hash-slash": "workspace:*", "hash-slash": "workspace:*",
"jazz-inspector": "workspace:*",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"lucide-react": "^0.274.0", "lucide-react": "^0.274.0",
"react": "19.0.0", "react": "19.0.0",
@@ -32,6 +30,6 @@
"postcss": "^8.4.40", "postcss": "^8.4.40",
"tailwindcss": "^4.1.10", "tailwindcss": "^4.1.10",
"typescript": "5.6.2", "typescript": "5.6.2",
"vite": "6.3.5" "vite": "^6.3.5"
} }
} }

View File

@@ -1,9 +1,9 @@
import { apiKey } from "@/apiKey.ts"; import { apiKey } from "@/apiKey.ts";
import { getRandomUsername, inIframe, onChatLoad } from "@/util.ts"; import { getRandomUsername, inIframe, onChatLoad } from "@/util.ts";
import { useIframeHashRouter } from "hash-slash"; import { useIframeHashRouter } from "hash-slash";
import { JazzInspector } from "jazz-inspector";
import { JazzProvider, useAccount } from "jazz-react";
import { Group } from "jazz-tools"; import { Group } from "jazz-tools";
import { JazzInspector } from "jazz-tools/inspector";
import { JazzReactProvider, useAccount } from "jazz-tools/react";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { ChatScreen } from "./chatScreen.tsx"; import { ChatScreen } from "./chatScreen.tsx";
@@ -55,7 +55,7 @@ const defaultProfileName = url.searchParams.get("user") ?? getRandomUsername();
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<ThemeProvider> <ThemeProvider>
<StrictMode> <StrictMode>
<JazzProvider <JazzReactProvider
sync={{ sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}} }}
@@ -63,7 +63,7 @@ createRoot(document.getElementById("root")!).render(
> >
<App /> <App />
<JazzInspector /> <JazzInspector />
</JazzProvider> </JazzReactProvider>
</StrictMode> </StrictMode>
</ThemeProvider>, </ThemeProvider>,
); );

View File

@@ -1,5 +1,5 @@
import { createImage, useAccount, useCoState } from "jazz-react";
import { Account, co } from "jazz-tools"; import { Account, co } from "jazz-tools";
import { createImage, useAccount, useCoState } from "jazz-tools/react";
import { useState } from "react"; import { useState } from "react";
import { Chat, Message } from "./schema.ts"; import { Chat, Message } from "./schema.ts";
import { import {
@@ -19,7 +19,7 @@ export function ChatScreen(props: { chatID: string }) {
const chat = useCoState(Chat, props.chatID, { const chat = useCoState(Chat, props.chatID, {
resolve: { $each: { text: true } }, resolve: { $each: { text: true } },
}); });
const account = useAccount(); const { me } = useAccount();
const [showNLastMessages, setShowNLastMessages] = useState(30); const [showNLastMessages, setShowNLastMessages] = useState(30);
if (!chat) if (!chat)
@@ -50,6 +50,10 @@ export function ChatScreen(props: { chatID: string }) {
}); });
}; };
if (!me) {
return <div>Loading...</div>;
}
return ( return (
<> <>
<ChatBody> <ChatBody>
@@ -57,7 +61,7 @@ export function ChatScreen(props: { chatID: string }) {
chat chat
.slice(-showNLastMessages) .slice(-showNLastMessages)
.reverse() // this plus flex-col-reverse on ChatBody gives us scroll-to-bottom behavior .reverse() // this plus flex-col-reverse on ChatBody gives us scroll-to-bottom behavior
.map((msg) => <ChatBubble me={account.me} msg={msg} key={msg.id} />) .map((msg) => <ChatBubble me={me} msg={msg} key={msg.id} />)
) : ( ) : (
<EmptyChatMessage /> <EmptyChatMessage />
)} )}

View File

@@ -1,6 +1,6 @@
import clsx from "clsx"; import clsx from "clsx";
import { ProgressiveImg } from "jazz-react";
import { CoPlainText, ImageDefinition } from "jazz-tools"; import { CoPlainText, ImageDefinition } from "jazz-tools";
import { ProgressiveImg } from "jazz-tools/react";
import { ImageIcon } from "lucide-react"; import { ImageIcon } from "lucide-react";
import { useId, useRef } from "react"; import { useId, useRef } from "react";

View File

@@ -13,9 +13,6 @@
}, },
"dependencies": { "dependencies": {
"@clerk/clerk-react": "^5.4.1", "@clerk/clerk-react": "^5.4.1",
"jazz-inspector": "workspace:*",
"jazz-react": "workspace:*",
"jazz-react-auth-clerk": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0" "react-dom": "19.0.0"
@@ -28,6 +25,6 @@
"@vitejs/plugin-react": "^4.5.1", "@vitejs/plugin-react": "^4.5.1",
"globals": "^15.11.0", "globals": "^15.11.0",
"typescript": "5.6.2", "typescript": "5.6.2",
"vite": "6.3.5" "vite": "^6.3.5"
} }
} }

View File

@@ -1,5 +1,5 @@
import { SignInButton, SignOutButton } from "@clerk/clerk-react"; import { SignInButton, SignOutButton } from "@clerk/clerk-react";
import { useAccount, useIsAuthenticated } from "jazz-react"; import { useAccount, useIsAuthenticated } from "jazz-tools/react";
function App() { function App() {
const { me } = useAccount(); const { me } = useAccount();

View File

@@ -3,8 +3,8 @@ import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import App from "./App.tsx"; import App from "./App.tsx";
import "./index.css"; import "./index.css";
import { JazzInspector } from "jazz-inspector"; import { JazzInspector } from "jazz-tools/inspector";
import { JazzProviderWithClerk } from "jazz-react-auth-clerk"; import { JazzReactProviderWithClerk } from "jazz-tools/react";
import { ReactNode } from "react"; import { ReactNode } from "react";
import { apiKey } from "./apiKey"; import { apiKey } from "./apiKey";
@@ -19,14 +19,14 @@ function JazzProvider({ children }: { children: ReactNode }) {
const clerk = useClerk(); const clerk = useClerk();
return ( return (
<JazzProviderWithClerk <JazzReactProviderWithClerk
clerk={clerk} clerk={clerk}
sync={{ sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}} }}
> >
{children} {children}
</JazzProviderWithClerk> </JazzReactProviderWithClerk>
); );
} }

View File

@@ -34,12 +34,10 @@
"tailwindcss": "^4.1.10", "tailwindcss": "^4.1.10",
"typescript": "5.6.2", "typescript": "5.6.2",
"typescript-eslint": "^8.0.0", "typescript-eslint": "^8.0.0",
"vite": "6.3.5" "vite": "^6.3.5"
}, },
"dependencies": { "dependencies": {
"@tailwindcss/typography": "^0.5.15", "@tailwindcss/typography": "^0.5.15",
"jazz-inspector-element": "workspace:*",
"jazz-svelte": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"lucide-svelte": "^0.463.0", "lucide-svelte": "^0.463.0",
"svelte-sonner": "^0.3.28" "svelte-sonner": "^0.3.28"

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { JazzProvider } from 'jazz-svelte'; import { JazzSvelteProvider } from 'jazz-tools/svelte';
import "jazz-inspector-element" import 'jazz-tools/inspector/register-custom-element';
import { PasskeyAuthBasicUI } from 'jazz-svelte'; import { PasskeyAuthBasicUI } from 'jazz-tools/svelte';
import { Toaster } from 'svelte-sonner'; import { Toaster } from 'svelte-sonner';
import '../app.css'; import '../app.css';
import { FileShareAccount } from '$lib/schema'; import { FileShareAccount } from '$lib/schema';
@@ -16,7 +16,7 @@
<Toaster richColors /> <Toaster richColors />
<JazzProvider <JazzSvelteProvider
AccountSchema={FileShareAccount} AccountSchema={FileShareAccount}
sync={{ sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
@@ -28,4 +28,4 @@
{@render children()} {@render children()}
</div> </div>
</PasskeyAuthBasicUI> </PasskeyAuthBasicUI>
</JazzProvider> </JazzSvelteProvider>

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { AccountCoState } from 'jazz-svelte'; import { AccountCoState } from 'jazz-tools/svelte';
import { SharedFile, FileShareAccount } from '$lib/schema'; import { SharedFile, FileShareAccount } from '$lib/schema';
import { FileStream, type Loaded } from 'jazz-tools'; import { FileStream, type Loaded } from 'jazz-tools';
import FileItem from '$lib/components/FileItem.svelte'; import FileItem from '$lib/components/FileItem.svelte';

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/stores';
import { CoState } from 'jazz-svelte'; import { CoState } from 'jazz-tools/svelte';
import { SharedFile } from '$lib/schema'; import { SharedFile } from '$lib/schema';
import { File, FileDown, Link2 } from 'lucide-svelte'; import { File, FileDown, Link2 } from 'lucide-svelte';
import { FileStream } from 'jazz-tools'; import { FileStream } from 'jazz-tools';

View File

@@ -1,5 +1,5 @@
import { sveltekit } from '@sveltejs/kit/vite'; import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from 'vitest/config'; import { defineConfig } from "vite";
export default defineConfig({ export default defineConfig({
plugins: [sveltekit()], plugins: [sveltekit()],

View File

@@ -63,4 +63,4 @@ If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or
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. 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.tsx](./src/main.tsx) to `{ peer: "ws://localhost:4200" }`. You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzReactProvider` in [./src/main.tsx](./src/main.tsx) to `{ peer: "ws://localhost:4200" }`.

View File

@@ -10,8 +10,6 @@
"format-and-lint:fix": "biome check . --write" "format-and-lint:fix": "biome check . --write"
}, },
"dependencies": { "dependencies": {
"jazz-inspector": "workspace:*",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0" "react-dom": "19.0.0"
@@ -27,6 +25,6 @@
"postcss": "^8.5.3", "postcss": "^8.5.3",
"tailwindcss": "^4.1.10", "tailwindcss": "^4.1.10",
"typescript": "5.6.2", "typescript": "5.6.2",
"vite": "6.3.5" "vite": "^6.3.5"
} }
} }

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useAccount } from "jazz-react";
import { co } from "jazz-tools"; import { co } from "jazz-tools";
import { useAccount } from "jazz-tools/react";
import { useRef, useState } from "react"; import { useRef, useState } from "react";
import { JazzAccount } from "./schema"; import { JazzAccount } from "./schema";

View File

@@ -1,5 +1,5 @@
import { JazzInspector } from "jazz-inspector"; import { JazzInspector } from "jazz-tools/inspector";
import { JazzProvider } from "jazz-react"; import { JazzReactProvider } from "jazz-tools/react";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import App from "./App.tsx"; import App from "./App.tsx";
@@ -12,7 +12,7 @@ export const APPLICATION_NAME = "Jazz File Stream Example";
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<JazzProvider <JazzReactProvider
sync={{ sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}} }}
@@ -20,6 +20,6 @@ createRoot(document.getElementById("root")!).render(
> >
<JazzInspector /> <JazzInspector />
<App /> <App />
</JazzProvider> </JazzReactProvider>
</StrictMode>, </StrictMode>,
); );

View File

@@ -75,4 +75,4 @@ If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or
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. 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.tsx](./src/main.tsx) to `{ peer: "ws://localhost:4200" }`. You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzReactProvider` in [./src/main.tsx](./src/main.tsx) to `{ peer: "ws://localhost:4200" }`.

View File

@@ -11,8 +11,6 @@
}, },
"dependencies": { "dependencies": {
"hash-slash": "workspace:*", "hash-slash": "workspace:*",
"jazz-inspector": "workspace:*",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0" "react-dom": "19.0.0"
@@ -30,6 +28,6 @@
"postcss": "^8.4.40", "postcss": "^8.4.40",
"tailwindcss": "^4.1.10", "tailwindcss": "^4.1.10",
"typescript": "5.6.2", "typescript": "5.6.2",
"vite": "6.3.5" "vite": "^6.3.5"
} }
} }

View File

@@ -1,6 +1,6 @@
import { useIframeHashRouter } from "hash-slash"; import { useIframeHashRouter } from "hash-slash";
import { useAccount, useCoState } from "jazz-react";
import { Loaded } from "jazz-tools"; import { Loaded } from "jazz-tools";
import { useAccount, useCoState } from "jazz-tools/react";
import { useState } from "react"; import { useState } from "react";
import { Errors } from "./Errors.tsx"; import { Errors } from "./Errors.tsx";
import { LinkToHome } from "./LinkToHome.tsx"; import { LinkToHome } from "./LinkToHome.tsx";

View File

@@ -1,4 +1,4 @@
import { useAccount } from "jazz-react"; import { useAccount } from "jazz-tools/react";
import { DraftBubbleTeaOrder, JazzAccount } from "./schema"; import { DraftBubbleTeaOrder, JazzAccount } from "./schema";
export function DraftIndicator() { export function DraftIndicator() {
const { me } = useAccount(JazzAccount, { const { me } = useAccount(JazzAccount, {

View File

@@ -1,4 +1,4 @@
import { useCoState } from "jazz-react"; import { useCoState } from "jazz-tools/react";
import { LinkToHome } from "./LinkToHome.tsx"; import { LinkToHome } from "./LinkToHome.tsx";
import { OrderForm } from "./OrderForm.tsx"; import { OrderForm } from "./OrderForm.tsx";
import { OrderThumbnail } from "./OrderThumbnail.tsx"; import { OrderThumbnail } from "./OrderThumbnail.tsx";

View File

@@ -1,4 +1,4 @@
import { useAccount } from "jazz-react"; import { useAccount } from "jazz-tools/react";
import { DraftIndicator } from "./DraftIndicator.tsx"; import { DraftIndicator } from "./DraftIndicator.tsx";
import { OrderThumbnail } from "./OrderThumbnail.tsx"; import { OrderThumbnail } from "./OrderThumbnail.tsx";
import { JazzAccount } from "./schema.ts"; import { JazzAccount } from "./schema.ts";

View File

@@ -1,5 +1,5 @@
import { JazzInspector } from "jazz-inspector"; import { JazzInspector } from "jazz-tools/inspector";
import { JazzProvider } from "jazz-react"; import { JazzReactProvider } from "jazz-tools/react";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import App from "./App.tsx"; import App from "./App.tsx";
@@ -9,7 +9,7 @@ import { JazzAccount } from "./schema.ts";
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<JazzProvider <JazzReactProvider
sync={{ sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}} }}
@@ -17,6 +17,6 @@ createRoot(document.getElementById("root")!).render(
> >
<App /> <App />
<JazzInspector /> <JazzInspector />
</JazzProvider> </JazzReactProvider>
</StrictMode>, </StrictMode>,
); );

View File

@@ -63,4 +63,4 @@ If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or
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. 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.tsx](./src/main.tsx) to `{ peer: "ws://localhost:4200" }`. You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzReactProvider` in [./src/main.tsx](./src/main.tsx) to `{ peer: "ws://localhost:4200" }`.

View File

@@ -10,8 +10,6 @@
"format-and-lint:fix": "biome check . --write" "format-and-lint:fix": "biome check . --write"
}, },
"dependencies": { "dependencies": {
"jazz-inspector": "workspace:*",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0" "react-dom": "19.0.0"
@@ -24,7 +22,7 @@
"@vitejs/plugin-react": "^4.5.1", "@vitejs/plugin-react": "^4.5.1",
"globals": "^15.11.0", "globals": "^15.11.0",
"typescript": "5.6.2", "typescript": "5.6.2",
"vite": "6.3.5", "vite": "^6.3.5",
"postcss": "^8.4.40", "postcss": "^8.4.40",
"tailwindcss": "^4.1.10" "tailwindcss": "^4.1.10"
} }

View File

@@ -1,4 +1,4 @@
import { ProgressiveImg, createImage, useAccount } from "jazz-react"; import { ProgressiveImg, createImage, useAccount } from "jazz-tools/react";
import { ChangeEvent, useEffect, useRef, useState } from "react"; import { ChangeEvent, useEffect, useRef, useState } from "react";
import { JazzAccount } from "./schema"; import { JazzAccount } from "./schema";

View File

@@ -1,5 +1,5 @@
import { JazzInspector } from "jazz-inspector"; import { JazzInspector } from "jazz-tools/inspector";
import { JazzProvider } from "jazz-react"; import { JazzReactProvider } from "jazz-tools/react";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import App from "./App.tsx"; import App from "./App.tsx";
@@ -9,7 +9,7 @@ import { JazzAccount } from "./schema.ts";
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<JazzProvider <JazzReactProvider
sync={{ sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`, peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}} }}
@@ -17,6 +17,6 @@ createRoot(document.getElementById("root")!).render(
> >
<App /> <App />
<JazzInspector /> <JazzInspector />
</JazzProvider> </JazzReactProvider>
</StrictMode>, </StrictMode>,
); );

View File

@@ -16,7 +16,6 @@
"cojson": "workspace:*", "cojson": "workspace:*",
"cojson-transport-ws": "workspace:*", "cojson-transport-ws": "workspace:*",
"hash-slash": "workspace:*", "hash-slash": "workspace:*",
"jazz-inspector": "workspace:*",
"jazz-tools": "workspace:*", "jazz-tools": "workspace:*",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0", "react-dom": "19.0.0",
@@ -31,6 +30,6 @@
"postcss": "^8.4.40", "postcss": "^8.4.40",
"tailwindcss": "^4.1.10", "tailwindcss": "^4.1.10",
"typescript": "5.6.2", "typescript": "5.6.2",
"vite": "6.3.5" "vite": "^6.3.5"
} }
} }

View File

@@ -1,6 +1,6 @@
import { LocalNode } from "cojson"; import { LocalNode } from "cojson";
import { Breadcrumbs, PageStack } from "jazz-inspector"; import { Breadcrumbs, PageStack } from "jazz-tools/inspector";
import type { PageInfo } from "jazz-inspector"; import type { PageInfo } from "jazz-tools/inspector";
import { usePagePath } from "./use-page-path"; import { usePagePath } from "./use-page-path";
export default function CoJsonViewer({ export default function CoJsonViewer({

View File

@@ -17,8 +17,8 @@ import {
Input, Input,
PageStack, PageStack,
Select, Select,
} from "jazz-inspector"; } from "jazz-tools/inspector";
import { AccountOrGroupText } from "jazz-inspector"; import { AccountOrGroupText } from "jazz-tools/inspector";
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { usePagePath } from "./use-page-path"; import { usePagePath } from "./use-page-path";

View File

@@ -1,5 +1,5 @@
import { CoID, RawCoValue } from "cojson"; import { CoID, RawCoValue } from "cojson";
import { PageInfo } from "jazz-inspector"; import { PageInfo } from "jazz-tools/inspector";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
export function usePagePath(defaultPath?: PageInfo[]) { export function usePagePath(defaultPath?: PageInfo[]) {

View File

@@ -1,15 +1,20 @@
import { expect, test } from "@playwright/test"; import { expect, test } from "@playwright/test";
import { createOrganization } from "./data"; import { FileStream, Group } from "jazz-tools";
import { createAccount, initializeKvStore } from "./lib"; import { createFile, createOrganization } from "./lib/data";
import {
addAccount,
createAccount,
initializeKvStore,
inspectCoValue,
} from "./lib/utils";
initializeKvStore(); initializeKvStore();
const { account, accountID, accountSecret } = await createAccount(); const { account, accountID, accountSecret } = await createAccount();
const organization = createOrganization();
test("should add and delete account in dropdown", async ({ page }) => { test("should add and delete account in dropdown", async ({ page }) => {
await page.goto("/"); await addAccount(page, accountID, accountSecret);
await page.getByLabel("Account ID").fill(accountID);
await page.getByLabel("Account secret").fill(accountSecret);
await page.getByRole("button", { name: "Add account" }).click();
await expect(page.getByText("Jazz CoValue Inspector")).toBeVisible(); await expect(page.getByText("Jazz CoValue Inspector")).toBeVisible();
await page await page
@@ -25,10 +30,7 @@ test("should add and delete account in dropdown", async ({ page }) => {
}); });
test("should inspect account", async ({ page }) => { test("should inspect account", async ({ page }) => {
await page.goto("/"); await addAccount(page, accountID, accountSecret);
await page.getByLabel("Account ID").fill(accountID);
await page.getByLabel("Account secret").fill(accountSecret);
await page.getByRole("button", { name: "Add account" }).click();
await page.getByRole("button", { name: "Inspect my account" }).click(); await page.getByRole("button", { name: "Inspect my account" }).click();
await expect(page.getByRole("heading", { name: accountID })).toBeVisible(); await expect(page.getByRole("heading", { name: accountID })).toBeVisible();
@@ -39,17 +41,11 @@ test("should inspect account", async ({ page }) => {
}); });
test("should inspect CoValue", async ({ page }) => { test("should inspect CoValue", async ({ page }) => {
await page.goto("/"); await addAccount(page, accountID, accountSecret);
await page.getByLabel("Account ID").fill(accountID);
await page.getByLabel("Account secret").fill(accountSecret);
await page.getByRole("button", { name: "Add account" }).click();
const organization = createOrganization();
await account.waitForAllCoValuesSync(); // Ensures that the organization is uploaded await account.waitForAllCoValuesSync(); // Ensures that the organization is uploaded
await page.getByLabel("CoValue ID").fill(organization.id); await inspectCoValue(page, organization.id);
await page.getByRole("button", { name: "Inspect CoValue" }).click();
await expect(page.getByText(/Garden Computing/)).toHaveCount(2); await expect(page.getByText(/Garden Computing/)).toHaveCount(2);
await expect( await expect(
@@ -77,6 +73,118 @@ test("should inspect CoValue", async ({ page }) => {
await page.getByRole("button", { name: "issues" }).click(); await page.getByRole("button", { name: "issues" }).click();
await expect(page.getByRole("table").getByRole("row")).toHaveCount(4); await expect(page.getByRole("table").getByRole("row")).toHaveCount(4);
const table = page.getByRole("table");
const row = table.getByRole("row").nth(1);
const issue = organization.projects[0].issues[0];
// Test if table is displaying the Issue data correctly
await expect(row.getByRole("cell").nth(0)).toHaveText(issue.title);
await expect(row.getByRole("cell").nth(1)).toHaveText(issue.status);
await expect(
row.getByRole("cell").nth(2).getByRole("button", { name: issue.labels.id }),
).toBeVisible();
await expect(
row
.getByRole("cell")
.nth(3)
.getByRole("button", { name: issue.reactions.id }),
).toBeVisible();
if (issue.file) {
await expect(
row.getByRole("cell").nth(4).getByRole("button", { name: issue.file.id }),
).toBeVisible();
}
if (issue.image) {
await expect(
row
.getByRole("cell")
.nth(5)
.getByRole("button", { name: issue.image.id }),
).toBeVisible();
}
// Test if CoMap/grid view is displaying Issue data correctly
await row.getByRole("button", { name: "View" }).click();
await expect(page.getByRole("table")).not.toBeVisible();
await expect(page.getByText(issue.title)).toBeVisible();
await expect(page.getByText(issue.status)).toBeVisible();
await expect(page.getByRole("button", { name: /labels/ })).toBeVisible();
await expect(page.getByRole("button", { name: /reactions/ })).toBeVisible();
await expect(page.getByRole("button", { name: /file/ })).toBeVisible();
await expect(page.getByRole("button", { name: /^image/ })).toBeVisible();
await page.pause();
await page.getByRole("button", { name: "projects" }).click(); await page.getByRole("button", { name: "projects" }).click();
await expect(page.getByRole("table").getByRole("row")).toHaveCount(5); await expect(page.getByRole("table").getByRole("row")).toHaveCount(5);
}); });
test("should show CoValue type", async ({ page }) => {
await addAccount(page, accountID, accountSecret);
await account.waitForAllCoValuesSync(); // Ensures that the organization is uploaded
// Test FileStream
const file = createFile();
await inspectCoValue(page, file.id);
await expect(page.getByText("📃 FileStream")).toBeVisible();
// Test ImageDefinition
await inspectCoValue(page, organization.image.id);
await expect(page.getByText("🖼️ Image")).toBeVisible();
// Test CoMap
await inspectCoValue(page, organization.id);
await expect(page.getByText("{} CoMap")).toBeVisible();
// Test CoList
await inspectCoValue(page, organization.projects.id);
await expect(page.getByText("☰ CoList")).toBeVisible();
// Test CoFeed
await inspectCoValue(page, organization.projects[0].issues[0].reactions.id);
await expect(page.getByText("≋ CoFeed")).toBeVisible();
// Test Account
await inspectCoValue(page, account.id);
await expect(page.getByText("👤 Account")).toBeVisible();
// Test Group
await inspectCoValue(page, organization._owner.id);
await expect(page.getByText("👥 Group")).toBeVisible();
});
test("should show Group members", async ({ page }) => {
await addAccount(page, accountID, accountSecret);
organization._owner.castAs(Group).addMember("everyone", "reader");
await account.waitForAllCoValuesSync(); // Ensures that the organization is uploaded
await inspectCoValue(page, organization.id);
const ownershipText = await page.getByText(/Owned by/).innerText();
expect(ownershipText).toContain(`Group <${organization._owner.id}>`);
await page
.getByRole("button", { name: `Group <${organization._owner.id}>` })
.click();
const table = page.getByRole("table");
const row1 = table.getByRole("row").nth(1);
await expect(row1.getByRole("cell").nth(0)).toHaveText("everyone");
await expect(row1.getByRole("cell").nth(1)).toHaveText("reader");
const row2 = table.getByRole("row").nth(2);
await expect(row2.getByRole("cell").nth(0)).toHaveText(
`Inspector test account <${account.id}>`,
);
await expect(row2.getByRole("cell").nth(1)).toHaveText("admin");
await page
.getByRole("button", { name: `Inspector test account <${account.id}>` })
.click();
await expect(page.getByText("👤 Account")).toBeVisible();
});

View File

@@ -1,4 +1,11 @@
import { co, z } from "jazz-tools"; import { FileStream, ImageDefinition, co, z } from "jazz-tools";
import {
Issue,
Organization,
Project,
ReactionType,
ReactionsList,
} from "./schema";
const projectsData: { const projectsData: {
name: string; name: string;
@@ -7,6 +14,9 @@ const projectsData: {
title: string; title: string;
status: "open" | "closed"; status: "open" | "closed";
labels: string[]; labels: string[];
reactions?: ReactionType[];
file?: boolean;
image?: boolean;
}[]; }[];
}[] = [ }[] = [
{ {
@@ -28,6 +38,9 @@ const projectsData: {
"high priority", "high priority",
"urgent", "urgent",
], ],
reactions: ["thumb-up"],
image: true,
file: true,
}, },
{ title: "Issue 2", status: "closed", labels: ["bug"] }, { title: "Issue 2", status: "closed", labels: ["bug"] },
{ title: "Issue 3", status: "open", labels: ["feature", "enhancement"] }, { title: "Issue 3", status: "open", labels: ["feature", "enhancement"] },
@@ -50,26 +63,28 @@ const projectsData: {
}, },
]; ];
const Issue = co.map({ export const createFile = () => {
title: z.string(), const file = FileStream.create();
status: z.enum(["open", "closed"]), file.start({ mimeType: "image/jpeg" });
labels: co.list(z.string()), file.push(new Uint8Array([1, 2, 3]));
}); file.end();
return file;
};
const Project = co.map({ export const createImage = () => {
name: z.string(), return ImageDefinition.create({
description: z.string(), originalSize: [1920, 1080],
issues: co.list(Issue), placeholderDataURL: "data:image/jpeg;base64,...",
}); });
};
const Organization = co.map({
name: z.string(),
projects: co.list(Project),
});
export const createOrganization = () => { export const createOrganization = () => {
return Organization.create({ return Organization.create({
name: "Garden Computing", name: "Garden Computing",
image: ImageDefinition.create({
originalSize: [1920, 1080],
placeholderDataURL: "data:image/jpeg;base64,...",
}),
projects: co.list(Project).create( projects: co.list(Project).create(
projectsData.map((project) => projectsData.map((project) =>
Project.create({ Project.create({
@@ -81,6 +96,9 @@ export const createOrganization = () => {
title: issue.title, title: issue.title,
status: issue.status, status: issue.status,
labels: co.list(z.string()).create(issue.labels), labels: co.list(z.string()).create(issue.labels),
reactions: ReactionsList.create(issue.reactions || []),
file: issue.file ? createFile() : undefined,
image: issue.image ? createImage() : undefined,
}), }),
), ),
), ),

View File

@@ -0,0 +1,29 @@
import { co, z } from "jazz-tools";
export const ReactionTypes = ["thumb-up", "thumb-down"] as const;
export type ReactionType = (typeof ReactionTypes)[number];
export const ReactionsList = co.feed(z.literal([...ReactionTypes]));
export const Issue = co.map({
title: z.string(),
status: z.enum(["open", "closed"]),
labels: co.list(z.string()),
reactions: ReactionsList,
file: z.optional(co.fileStream()),
image: z.optional(co.image()),
lead: z.optional(co.account()),
});
export const Project = co.map({
name: z.string(),
description: z.string(),
issues: co.list(Issue),
});
export const Organization = co.map({
name: z.string(),
projects: co.list(Project),
image: co.image(),
});

View File

@@ -1,13 +1,12 @@
import { Page } from "@playwright/test";
import { createWebSocketPeer } from "cojson-transport-ws"; import { createWebSocketPeer } from "cojson-transport-ws";
import { WasmCrypto } from "cojson/crypto/WasmCrypto"; import { WasmCrypto } from "cojson/crypto/WasmCrypto";
import { import {
AuthSecretStorage, AuthSecretStorage,
InMemoryKVStore, InMemoryKVStore,
KvStoreContext, KvStoreContext,
co,
createJazzContext, createJazzContext,
randomSessionProvider, randomSessionProvider,
z,
} from "jazz-tools"; } from "jazz-tools";
export const initializeKvStore = () => { export const initializeKvStore = () => {
@@ -41,3 +40,20 @@ export async function createAccount() {
return { account, ...credentials }; return { account, ...credentials };
} }
export async function addAccount(
page: Page,
accountID: string,
accountSecret: string,
) {
await page.goto("/");
await page.getByLabel("Account ID").fill(accountID);
await page.getByLabel("Account secret").fill(accountSecret);
await page.getByRole("button", { name: "Add account" }).click();
}
export async function inspectCoValue(page: Page, coValueId: string) {
await page.goto("/");
await page.getByLabel("CoValue ID").fill(coValueId);
await page.getByRole("button", { name: "Inspect CoValue" }).click();
}

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